package com.enioka.jqm.test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.enioka.jqm.jpamodel.JobDef.PathType;
import com.enioka.jqm.jpamodel.JobDefParameter;

/**
 * Helper class to define a new payload. The other way to define a new payload is to import an XML file.<br>
 * Payloads created through this class must be present inside the class path.
 * 
 */
public class TestJobDefinition
{
    // Identity
    String description;
    String name;

    // Where the payload is
    String javaClassName;
    String path = "/dev/null";
    PathType pathType = PathType.MEMORY;

    // How it should run
    String queueName;
    boolean highlander = false;

    // Parameters
    Map<String, String> parameters = new HashMap<String, String>();

    // Optional classifiers
    String application, module, keyword1, keyword2, keyword3;

    // Class loading options
    String specificIsolationContext;
    boolean childFirstClassLoader = false;
    List<String> hiddenJavaClasses = new ArrayList<String>();
    boolean classLoaderTracing = false;

    ///////////////////////////////////////////////////////////////////////////
    // CONSTRUCTION
    ///////////////////////////////////////////////////////////////////////////

    private TestJobDefinition()
    {}

    public static TestJobDefinition createFromClassPath(String name, String description, Class<? extends Object> testedClass)
    {
        TestJobDefinition res = new TestJobDefinition();
        res.name = name;
        res.description = description;
        res.javaClassName = testedClass.getCanonicalName();
        res.path = "/dev/null";
        res.pathType = PathType.MEMORY;
        return res;
    }

    public static TestJobDefinition createFromJar(String name, String description, String testedClassCanonicalName, String jarPath)
    {
        TestJobDefinition res = new TestJobDefinition();
        res.name = name;
        res.description = description;
        res.javaClassName = testedClassCanonicalName;
        res.path = jarPath;
        res.pathType = PathType.FS;
        return res;
    }

    ///////////////////////////////////////////////////////////////////////////
    // FLUID API
    ///////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////
    // Compulsory parameters

    String getDescription()
    {
        return description;
    }

    void setDescription(String description)
    {
        this.description = description;
    }

    String getName()
    {
        return name;
    }

    void setName(String name)
    {
        this.name = name;
    }

    String getJavaClassName()
    {
        return javaClassName;
    }

    void setJavaClassName(String javaClassName)
    {
        this.javaClassName = javaClassName;
    }

    String getPath()
    {
        return path;
    }

    void setPath(String path)
    {
        this.path = path;
    }

    PathType getPathType()
    {
        return pathType;
    }

    void setPathType(PathType pathType)
    {
        this.pathType = pathType;
    }

    String getQueueName()
    {
        return queueName;
    }

    // end compulsory parameters
    ///////////////////////////////////////////////////////////////////////////

    /**
     * The jobs will run on this queue. If no set (or set to null) will run on the default queue.
     * 
     * @param queueName
     */
    public TestJobDefinition setQueueName(String queueName)
    {
        this.queueName = queueName;
        return this;
    }

    boolean isHighlander()
    {
        return highlander;
    }

    /**
     * This enables Highlander mode. See the documentation on Highlander jobs.
     */
    public TestJobDefinition enableHighlander()
    {
        this.highlander = true;
        return this;
    }

    List<JobDefParameter> getParameters()
    {
        List<JobDefParameter> prms = new ArrayList<JobDefParameter>(this.parameters.size());
        for (Map.Entry<String, String> e : this.parameters.entrySet())
        {
            JobDefParameter prm = new JobDefParameter();
            prm.setKey(e.getKey());
            prm.setValue(e.getValue());
            prms.add(prm);
        }
        return prms;
    }

    /**
     * Add a parameter.
     * 
     * @param key
     *            must be unique
     * @param value
     */
    public TestJobDefinition addParameter(String key, String value)
    {
        this.parameters.put(key, value);
        return this;
    }

    String getApplication()
    {
        return application;
    }

    /**
     * An optional classifier.
     * 
     * @param application
     */
    public TestJobDefinition setApplication(String application)
    {
        this.application = application;
        return this;
    }

    String getModule()
    {
        return module;
    }

    /**
     * An optional classifier.
     * 
     * @param module
     */
    public TestJobDefinition setModule(String module)
    {
        this.module = module;
        return this;
    }

    String getKeyword1()
    {
        return keyword1;
    }

    /**
     * An optional classifier.
     * 
     * @param keyword1
     */
    public TestJobDefinition setKeyword1(String keyword1)
    {
        this.keyword1 = keyword1;
        return this;
    }

    String getKeyword2()
    {
        return keyword2;
    }

    /**
     * An optional classifier.
     * 
     * @param keyword2
     */
    public TestJobDefinition setKeyword2(String keyword2)
    {
        this.keyword2 = keyword2;
        return this;
    }

    String getKeyword3()
    {
        return keyword3;
    }

    /**
     * An optional classifier.
     * 
     * @param keyword3
     */
    public TestJobDefinition setKeyword3(String keyword3)
    {
        this.keyword3 = keyword3;
        return this;
    }

    String getSpecificIsolationContext()
    {
        return specificIsolationContext;
    }

    /**
     * By default jobs run inside a dedicated class loader thrown out after the run. By setting this to a non null value, this job will run
     * inside a re-sued class loader. All jobs using the same specificIsolationContext share the same class loader.
     * 
     * @param specificIsolationContext
     *            name of the class loader to use or null to use default behaviour.
     */
    public TestJobDefinition setSpecificIsolationContext(String specificIsolationContext)
    {
        this.specificIsolationContext = specificIsolationContext;
        return this;
    }

    boolean isChildFirstClassLoader()
    {
        return childFirstClassLoader;
    }

    /**
     * This enables child-first class loading.<br>
     * Note that if the payload is actually inside your unit test class path, this won't do much.
     * 
     * @param childFirstClassLoader
     */
    public TestJobDefinition setChildFirstClassLoader(boolean childFirstClassLoader)
    {
        this.childFirstClassLoader = childFirstClassLoader;
        return this;
    }

    String getHiddenJavaClasses()
    {
        return String.join(",", hiddenJavaClasses);
    }

    /**
     * This prevents the given class from being loaded by a parent class loader. Id es: if the class is not directly available to the
     * payload (inside the payload libraries for example) it will not be loaded.
     * 
     * @param canonicalClassName
     */
    public TestJobDefinition addHiddenJavaClasses(String canonicalClassName)
    {
        this.hiddenJavaClasses.add(canonicalClassName);
        return this;
    }

    boolean isClassLoaderTracing()
    {
        return classLoaderTracing;
    }

    /**
     * Enable verbose class loading fot his payload.
     */
    public TestJobDefinition enableClassLoaderTracing()
    {
        this.classLoaderTracing = true;
        return this;
    }

}

@@ -7148,50 +7148,250 @@
     
-return String.join(%22,%22, hiddenJavaClasses)
+String res = %22%22;%0A        for (String s : hiddenJavaClasses)%0A        %7B%0A            res += s + %22,%22;%0A        %7D%0A        if (hiddenJavaClasses.size() %3E 0)%0A        %7B%0A            return res.substring(0, res.length() - 2);%0A        %7D%0A        return %22%22
 ;%0A  

Fix for Java 6/7 builds