diff --git a/pom.xml b/pom.xml index 574f804f..cb1d208b 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,14 @@ + + + net.sf.opencsv + opencsv + 2.0 + + + diff --git a/src/main/java/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition.java b/src/main/java/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition.java index 6b8c068e..51d2226d 100644 --- a/src/main/java/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition.java +++ b/src/main/java/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition.java @@ -11,6 +11,7 @@ import hudson.util.FormValidation; import java.io.File; +import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.util.ArrayList; @@ -19,6 +20,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedHashMap; import javax.servlet.ServletException; @@ -32,6 +36,8 @@ import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; +import au.com.bytecode.opencsv.CSVReader; + public class ExtendedChoiceParameterDefinition extends ParameterDefinition { private static final long serialVersionUID = -2946187268529865645L; @@ -44,6 +50,10 @@ public class ExtendedChoiceParameterDefinition extends ParameterDefinition { public static final String PARAMETER_TYPE_RADIO = "PT_RADIO"; public static final String PARAMETER_TYPE_TEXT_BOX = "PT_TEXTBOX"; + + public static final String PARAMETER_TYPE_MULTI_LEVEL_SINGLE_SELECT = "PT_MULTI_LEVEL_SINGLE_SELECT"; + + public static final String PARAMETER_TYPE_MULTI_LEVEL_MULTI_SELECT = "PT_MULTI_LEVEL_MULTI_SELECT"; @Extension public static class DescriptorImpl extends ParameterDescriptor { @@ -52,7 +62,7 @@ public String getDisplayName() { return Messages.ExtendedChoiceParameterDefinition_DisplayName(); } - public FormValidation doCheckPropertyFile(@QueryParameter final String propertyFile, @QueryParameter final String propertyKey) throws IOException, ServletException { + public FormValidation doCheckPropertyFile(@QueryParameter final String propertyFile, @QueryParameter final String propertyKey, @QueryParameter final String type) throws IOException, ServletException { if(StringUtils.isBlank(propertyFile)) { return FormValidation.ok(); } @@ -76,7 +86,12 @@ public FormValidation doCheckPropertyFile(@QueryParameter final String propertyF return FormValidation.warning(Messages.ExtendedChoiceParameterDefinition_PropertyFileDoesntExist(), propertyFile); } - if(StringUtils.isNotBlank(propertyKey)) { + if( type.equals(PARAMETER_TYPE_MULTI_LEVEL_SINGLE_SELECT) + || type.equals(PARAMETER_TYPE_MULTI_LEVEL_MULTI_SELECT)) + { + return FormValidation.ok(); + } + else if(StringUtils.isNotBlank(propertyKey)) { if(project.getProperty(propertyKey) != null) { return FormValidation.ok(); } @@ -89,18 +104,20 @@ public FormValidation doCheckPropertyFile(@QueryParameter final String propertyF } } - public FormValidation doCheckPropertyKey(@QueryParameter final String propertyFile, @QueryParameter final String propertyKey) throws IOException, ServletException { - return doCheckPropertyFile(propertyFile, propertyKey); + public FormValidation doCheckPropertyKey(@QueryParameter final String propertyFile, @QueryParameter final String propertyKey, + @QueryParameter final String type) throws IOException, ServletException { + return doCheckPropertyFile(propertyFile, propertyKey, type); } public FormValidation doCheckDefaultPropertyFile(@QueryParameter final String defaultPropertyFile, - @QueryParameter final String defaultPropertyKey) throws IOException, ServletException { - return doCheckPropertyFile(defaultPropertyFile, defaultPropertyKey); + @QueryParameter final String defaultPropertyKey, @QueryParameter final String type) throws IOException, ServletException { + return doCheckPropertyFile(defaultPropertyFile, defaultPropertyKey, type); } public FormValidation doCheckDefaultPropertyKey(@QueryParameter final String defaultPropertyFile, - @QueryParameter final String defaultPropertyKey) throws IOException, ServletException { - return doCheckPropertyFile(defaultPropertyFile, defaultPropertyKey); + @QueryParameter final String defaultPropertyKey, @QueryParameter final String type) throws IOException, ServletException + { + return doCheckPropertyFile(defaultPropertyFile, defaultPropertyKey, type); } } @@ -111,7 +128,7 @@ public FormValidation doCheckDefaultPropertyKey(@QueryParameter final String def private String type; private String value; - + private String propertyFile; private String propertyKey; @@ -194,9 +211,9 @@ public ParameterValue createValue(StaplerRequest request) { } return null; } - + @Override - public ParameterValue createValue(StaplerRequest request, JSONObject jO) { + public ParameterValue createValue(StaplerRequest request, JSONObject jO) { Object value = jO.get("value"); String strValue = ""; if(value instanceof String) { @@ -204,7 +221,29 @@ public ParameterValue createValue(StaplerRequest request, JSONObject jO) { } else if(value instanceof JSONArray) { JSONArray jsonValues = (JSONArray)value; - strValue = StringUtils.join(jsonValues.iterator(), getMultiSelectDelimiter()); + if ( type.equals(PARAMETER_TYPE_MULTI_LEVEL_SINGLE_SELECT) + || type.equals(PARAMETER_TYPE_MULTI_LEVEL_MULTI_SELECT)) + { + final int valuesBetweenLevels = this.value.split(",").length; + + Iterator it = jsonValues.iterator(); + for (int i = 1; it.hasNext(); i++) + { + String nextValue = it.next().toString(); + if (i % valuesBetweenLevels == 0) + { + if (strValue.length() > 0) + { + strValue += getMultiSelectDelimiter(); + } + strValue += nextValue; + } + } + } + else + { + strValue = StringUtils.join(jsonValues.iterator(), getMultiSelectDelimiter()); + } } if(quoteValue) { @@ -224,7 +263,8 @@ public ParameterValue getDefaultParameterValue() { } return super.getDefaultParameterValue(); } - + + // note that computeValue is not called by multiLevel.jelly private String computeValue(String value, String propertyFilePath, String propertyKey) { if(!StringUtils.isBlank(propertyFile) && !StringUtils.isBlank(propertyKey)) { try { @@ -295,7 +335,156 @@ public void setDefaultPropertyKey(String defaultPropertyKey) { public String getEffectiveValue() { return computeValue(value, propertyFile, propertyKey); } + + private ArrayList columnIndicesForDropDowns(String[] headerColumns) + { + ArrayList columnIndicesForDropDowns = new ArrayList(); + + String[] dropDownNames = value.split(","); + + for (String dropDownName : dropDownNames) + { + for (int i = 0; i < headerColumns.length; ++i) + { + if (headerColumns[i].equals(dropDownName)) + { + columnIndicesForDropDowns.add(new Integer(i)); + } + } + } + + return columnIndicesForDropDowns; + } + + LinkedHashMap> calculateChoicesByDropdownId() throws Exception + { + List fileLines = + new CSVReader(new FileReader(propertyFile), '\t').readAll(); + + if (fileLines.size() < 2) + { + throw new Exception("Multi level tab delimited file must have at least 2 " + + "lines (one for the header, and one or more for the data)"); + } + + ArrayList columnIndicesForDropDowns = + columnIndicesForDropDowns(fileLines.get(0)); + + List dataLines = fileLines.subList(1, fileLines.size()); + + LinkedHashMap> choicesByDropdownId = + new LinkedHashMap>(); + + String prefix = getName() + " dropdown MultiLevelMultiSelect 0"; + choicesByDropdownId.put(prefix, new LinkedHashSet()); + for (int i=0; i < columnIndicesForDropDowns.size(); ++i) + { + String prettyCurrentColumnName = value.split(",")[i]; + prettyCurrentColumnName = prettyCurrentColumnName.toLowerCase(); + prettyCurrentColumnName = prettyCurrentColumnName.replace("_", " "); + + for (String[] dataLine : dataLines) + { + String priorLevelDropdownId = prefix; + String currentLevelDropdownId = prefix; + + int column = 0; + for (int j=0; j <= i; ++j) + { + column = columnIndicesForDropDowns.get(j); + + if (j < i) + { + priorLevelDropdownId += " " + dataLine[column]; + } + currentLevelDropdownId += " " + dataLine[column]; + } + if (i != columnIndicesForDropDowns.size() - 1) + { + choicesByDropdownId.put(currentLevelDropdownId, new LinkedHashSet()); + } + LinkedHashSet choicesForPriorDropdown + = choicesByDropdownId.get(priorLevelDropdownId); + choicesForPriorDropdown.add("Select a " + prettyCurrentColumnName + + "..."); + choicesForPriorDropdown.add(dataLine[column]); + } + } + + return choicesByDropdownId; + } + + public String getMultiLevelDropdownIds() throws Exception + { + String dropdownIds = new String(); + + LinkedHashMap> choicesByDropdownId = + calculateChoicesByDropdownId(); + + for (String id : choicesByDropdownId.keySet()) + { + if (dropdownIds.length() > 0) + { + dropdownIds += ","; + } + dropdownIds += id; + } + + return dropdownIds; + + /* dropdownIds is of a form like this: + return name + " dropdown MultiLevelMultiSelect 0," + // next select the source of the genome -- each genome gets a seperate dropdown id:" + + name + " dropdown MultiLevelMultiSelect 0 HG18,dropdown MultiLevelMultiSelect 0 ZZ23," + // next select the cell type of the source -- each source gets a seperate dropdown id + + name + " dropdown MultiLevelMultiSelect 0 HG18 Diffuse large B-cell lymphoma, dropdown MultiLevelMultiSelect 0 HG18 Multiple Myeloma," + + name + " dropdown MultiLevelMultiSelect 0 ZZ23 Neuroblastoma," + // next select the name from the cell type -- each cell type gets a seperate dropdown id + + name + " dropdown MultiLevelMultiSelect 0 HG18 Diffuse large B-cell lymphoma LY1," + + name + " dropdown MultiLevelMultiSelect 0 HG18 Multiple Myeloma MM1S," + + name + " dropdown MultiLevelMultiSelect 0 ZZ23 Neuroblastoma BE2C," + + name + " dropdown MultiLevelMultiSelect 0 ZZ23 Neuroblastoma SKNAS";*/ + } + + public Map getChoicesByDropdownId() throws Exception + { + LinkedHashMap> choicesByDropdownId = + calculateChoicesByDropdownId(); + + Map collapsedMap = new LinkedHashMap(); + + for (String dropdownId : choicesByDropdownId.keySet()) + { + String choices = new String(); + for (String choice : choicesByDropdownId.get(dropdownId)) + { + if (choices.length() > 0) + { + choices += ","; + } + choices += choice; + } + + collapsedMap.put(dropdownId, choices); + } + + /* collapsedMap is of a form like this: + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0", "Select a genome...,HG18,ZZ23"); + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0 HG18", "Select a source...,Diffuse large B-cell lymphoma,Multiple Myeloma"); + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0 ZZ23", "Select a source...,Neuroblastoma"); + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0 HG18 Diffuse large B-cell lymphoma","Select a cell type...,LY1"); + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0 HG18 Multiple Myeloma","Select a cell type...,MM1S"); + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0 ZZ23 Neuroblastoma","Select a cell type...,BE2C,SKNAS"); + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0 HG18 Diffuse large B-cell lymphoma LY1","Select a name...,LY1_BCL6_DMSO,LY1_BCL6_JQ1"); + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0 HG18 Multiple Myeloma MM1S", "Select a name...,MM1S_BRD4_150nM_JQ1,MM1S_BRD4_500nM_JQ1"); + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0 ZZ23 Neuroblastoma BE2C", "Select a name...,BE2C_BRD4"); + collapsedMap.put(name + " dropdown MultiLevelMultiSelect 0 ZZ23 Neuroblastoma SKNAS", "Select a name...,SKNAS_H3K4ME3"); + */ + + return collapsedMap; + } + public String getValue() { return value; } @@ -347,5 +536,4 @@ public void setDefaultPropertyFile(String defaultPropertyFile) { public Map getDefaultValueMap() { return computeDefaultValueMap(); } - } diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/config.jelly b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/config.jelly index 978f7508..d2a71fa9 100644 --- a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/config.jelly +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/config.jelly @@ -52,8 +52,24 @@ - - + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-defaultPropertyFile.html b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-defaultPropertyFile.html new file mode 100644 index 00000000..aab6f58e --- /dev/null +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-defaultPropertyFile.html @@ -0,0 +1,3 @@ +
+ Absolute path (specified without using environment variables). +
diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-defaultValue.html b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-defaultValue.html new file mode 100644 index 00000000..799dfb41 --- /dev/null +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-defaultValue.html @@ -0,0 +1,4 @@ +
+ Initial selection of the single-select or mult-select box. +

In case of the multi-select box, default value can be a comma separated string. +
diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-propertyFile.html b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-propertyFile.html new file mode 100644 index 00000000..98d018c5 --- /dev/null +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-propertyFile.html @@ -0,0 +1,13 @@ +
+ The properties file + is a collection of key,value pairs of the form key=value1,value2,... +

+ Property files may reference other properties. For example: +
+prop1=a,b,c,d,e
+prop2=${prop1},f,g,h +
+ The properties file can be placed anywhere on the file-system that Jenkins can access. +

+ This property file has different meaning for multi-level select -- see the parameter type help for more info. +
diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-propertyKey.html b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-propertyKey.html new file mode 100644 index 00000000..d71ffe5f --- /dev/null +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-propertyKey.html @@ -0,0 +1,10 @@ +
+ The property of the property file to use. +

+ For example, if the property file was the following: +
+ prop1=a,b,c,d,e
+ prop2=1,2,3,4 +
+ Then you could specify the property as either prop1 or prop2. +
\ No newline at end of file diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-type.html b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-type.html new file mode 100644 index 00000000..f344c63b --- /dev/null +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-type.html @@ -0,0 +1,24 @@ +
+ The type of parameter: +
    +
  • Single Select: user chooses a single selection from a drop down menu, populated by either explicit values (see Value field below) or a property file (see Property File and Property Key fields below)

  • +
  • Multi Select: a user can choose multiple selections from a multi-line box, populated by either explicit values (see Value field below) or a property file (see Property File and Property Key fields below)

  • +
  • Check Boxes: user can check off zero or more check boxes, labeled by either explicit values (see Value field below) or a property file (see Property File and Property Key fields below)

  • +
  • Multi-Level Single Select: user chooses a selection from a drop down, and then a another drop down appears with selections that depend on the first value, and upon second selection a third drop down may appear depending on the first two selections, and so on.

    + The property file is a tab delimited file, with levels defined in columns and choices defined in rows. For example, to have a 2 level selection where you first select a country and then a city, you could specify a file such as the following:

    +
    +Country	City
    +United States	San Francisco
    +United States	Chicago
    +Mexico	Mexico City
    +Mexico	Cancun
    +            
    + This would result in a first drop down with the options "Select a country...", "United States", and "Mexico" (the initial selection is "Select a country...", which serves as a label for the drop down). After the user selects a country, a "City" drop down would appear. If United States was chosen first, then San Francisco and Chicago would be options, but if Mexico was selected then instead Mexico City and Cancun would be options. +

    + The columns that should represent levels must be specified in the value field. For example "Country,City" could be valid values. +

    + Note that default values are not supported for multi-level selects.

    +
  • +
  • Multi-Level Multi Select: same as single select, but after all levels are chosen, a button appears to "Select another..." and an additional multi-level selection is presented. +
+
diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-value.html b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-value.html new file mode 100644 index 00000000..4c111abc --- /dev/null +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/help-value.html @@ -0,0 +1,7 @@ +
+ Comma separated list of values for the single select or multi-select box. +

This field can be left blank if the comma separated values need to be + picked up from a properties file (set via 'Property File' and 'Property Key'). +

+ This value has different meaning for multi-level select -- see the parameter type help for more info. +
diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/index.jelly b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/index.jelly index f77a4c04..a9512bdd 100644 --- a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/index.jelly +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/index.jelly @@ -11,8 +11,8 @@
- - + + @@ -30,7 +30,13 @@ + + + + + +
- \ No newline at end of file + diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/multiLevel.jelly b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/multiLevel.jelly new file mode 100644 index 00000000..cf5c2206 --- /dev/null +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterDefinition/multiLevel.jelly @@ -0,0 +1,177 @@ + + + + + + + + + + + + + +
    + +
+ +

+ +

+
diff --git a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterValue/value.jelly b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterValue/value.jelly index 4ee8ae45..a3602b24 100644 --- a/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterValue/value.jelly +++ b/src/main/resources/com/cwctravel/hudson/plugins/extended_choice_parameter/ExtendedChoiceParameterValue/value.jelly @@ -10,4 +10,4 @@ ${it.value} - \ No newline at end of file +