7d9e9e43ee3040464a57c08b4e8c39d82b1add06
[jabaws.git] / datamodel / compbio / metadata / Option.java
1 /* Copyright (c) 2009 Peter Troshin\r
2  *  \r
3  *  JAva Bioinformatics Analysis Web Services (JABAWS) @version: 1.0\r
4  * \r
5  *  This library is free software; you can redistribute it and/or modify it under the terms of the\r
6  *  Apache License version 2 as published by the Apache Software Foundation\r
7  * \r
8  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\r
9  *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Apache \r
10  *  License for more details.\r
11  * \r
12  *  A copy of the license is in apache_license.txt. It is also available here:\r
13  * @see: http://www.apache.org/licenses/LICENSE-2.0.txt\r
14  * \r
15  * Any republication or derived work distributed in source code form\r
16  * must include this copyright and license notice.\r
17  */\r
18 \r
19 package compbio.metadata;\r
20 \r
21 import java.net.MalformedURLException;\r
22 import java.net.URL;\r
23 import java.util.ArrayList;\r
24 import java.util.HashSet;\r
25 import java.util.List;\r
26 import java.util.Set;\r
27 import java.util.TreeSet;\r
28 \r
29 import javax.xml.bind.ValidationException;\r
30 import javax.xml.bind.annotation.XmlAccessType;\r
31 import javax.xml.bind.annotation.XmlAccessorType;\r
32 import javax.xml.bind.annotation.XmlAttribute;\r
33 import javax.xml.bind.annotation.XmlElement;\r
34 import javax.xml.bind.annotation.XmlTransient;\r
35 \r
36 import compbio.util.SysPrefs;\r
37 import compbio.util.Util;\r
38 \r
39 /**\r
40  * Command line option/flag or multiple exclusive options with no value. Example\r
41  * -protein, -dna, -auto\r
42  * \r
43  * @author pvtroshin\r
44  * \r
45  * @version 1.0 October 2009\r
46  * @param <T>\r
47  *            type of executable\r
48  */\r
49 @XmlAccessorType(XmlAccessType.FIELD)\r
50 public class Option<T> implements Argument<T> {\r
51 \r
52         @XmlElement(required = true)\r
53         protected String description;\r
54 \r
55         @XmlElement(required = true)\r
56         Set<String> optionNames = new HashSet<String>();\r
57 \r
58         @XmlElement(required = true)\r
59         protected String name;\r
60 \r
61         @XmlAttribute\r
62         protected boolean isRequired;\r
63         @XmlElement\r
64         protected String furtherDetails;\r
65         @XmlElement\r
66         protected String defaultValue;\r
67         @XmlTransient\r
68         private URL basicURL;\r
69 \r
70         Option() {\r
71                 // Has to have no arg constructor for JAXB\r
72         }\r
73 \r
74         public Option(String name, String description) throws MalformedURLException {\r
75                 this.name = name;\r
76                 this.description = description;\r
77         }\r
78 \r
79         /**\r
80          * Human readable name of the option\r
81          */\r
82         public String getName() {\r
83                 return name;\r
84         }\r
85 \r
86         public void setName(String name) {\r
87                 this.name = name;\r
88         }\r
89 \r
90         public void setBasicURL(URL url) {\r
91                 this.basicURL = url;\r
92         }\r
93         public URL getBasicURL() throws MalformedURLException {\r
94                 URL out = new URL ("http://unknown.jabaws.server.ac.uk");\r
95                 if (null != basicURL) {\r
96                         out = basicURL;\r
97                 }\r
98                 return out;\r
99         }       \r
100         /**\r
101          * A long description of the Option\r
102          */\r
103         public String getDescription() {\r
104                 return description;\r
105         }\r
106 \r
107         public void setDescription(String description) {\r
108                 this.description = description;\r
109         }\r
110 \r
111         /**\r
112          * The URL where further details about the option can be found\r
113          * @throws MalformedURLException \r
114          */\r
115         public String getFurtherDetails() {\r
116                 return furtherDetails;\r
117         }\r
118 \r
119         public void setFurtherDetails(String furtherDetails) {\r
120                 this.furtherDetails = furtherDetails;\r
121         }\r
122 \r
123         /**\r
124          * A default value of the option. Defaults to command line argument name\r
125          * e.g. -auto\r
126          */\r
127         public String getDefaultValue() {\r
128                 return defaultValue;\r
129         }\r
130 \r
131         /**\r
132          * Sets one of the values defined in optionList as default. Attempting set\r
133          * the value not listed there will result in WrongParameter exception\r
134          * \r
135          * @param defaultVal\r
136          * @throws WrongParameterException\r
137          *             is thrown if the defaultValue is not found in optionList\r
138          */\r
139         public void setDefaultValue(String defaultVal)\r
140                         throws WrongParameterException {\r
141                 if (optionNames.isEmpty()) {\r
142                         throw new IllegalStateException("Please define optionNames first!");\r
143                 }\r
144                 if (!valueExist(defaultVal, getOptionNames())) {\r
145                         throw new WrongParameterException(\r
146                                         "Attempting to set illegal defaultValue '" + defaultVal\r
147                                                         + "' which is not defined optionNames for option: "\r
148                                                         + this);\r
149                 }\r
150                 this.defaultValue = defaultVal;\r
151         }\r
152 \r
153         static boolean valueExist(String testValue, List<String> values) {\r
154                 assert !Util.isEmpty(testValue);\r
155                 for (String val : values) {\r
156                         if (testValue.equalsIgnoreCase(val)) {\r
157                                 return true;\r
158                         }\r
159                 }\r
160                 return false;\r
161         }\r
162 \r
163         /**\r
164          * Flag that indicated that this option must be specified in the command\r
165          * line for an executable to run\r
166          * \r
167          * @return true is the option is required, false otherwise\r
168          */\r
169         public boolean isRequired() {\r
170                 return isRequired;\r
171         }\r
172 \r
173         public void setRequired(boolean isRequired) {\r
174                 this.isRequired = isRequired;\r
175         }\r
176 \r
177         /**\r
178          * \r
179          * @return List of option names\r
180          */\r
181         public List<String> getOptionNames() {\r
182                 return new ArrayList<String>(optionNames);\r
183         }\r
184 \r
185         public void setOptionNames(Set<String> optionNames) {\r
186                 this.optionNames = new HashSet<String>(optionNames);\r
187         }\r
188 \r
189         /**\r
190          * Adds an option to the optionName list\r
191          * \r
192          * @param value\r
193          * @return modified optionName list\r
194          */\r
195         public Set<String> addOptionNames(String... value) {\r
196                 for (String v : value) {\r
197                         boolean added = this.optionNames.add(v);\r
198                         assert added : "Duplicated optionName is detected!";\r
199                 }\r
200                 return this.optionNames;\r
201         }\r
202 \r
203         @Override\r
204         public String toString() {\r
205                 String value = "Option name: " + this.name + SysPrefs.newlinechar;\r
206                 value += "Description: " + this.description + SysPrefs.newlinechar;\r
207                 if (!Util.isEmpty(defaultValue)) {\r
208                         value += "Default value: " + this.defaultValue + SysPrefs.newlinechar;\r
209                 }\r
210                 if (null != this.furtherDetails) {\r
211                         if (null != this.basicURL) {\r
212                         value += "URL: " + this.basicURL + this.furtherDetails + SysPrefs.newlinechar;\r
213                 }else {\r
214                         value += "Relative URL: " + this.furtherDetails + SysPrefs.newlinechar;\r
215                 }\r
216                 } else {\r
217                         value += "URL: unknown URL" + SysPrefs.newlinechar;\r
218                 }\r
219                 value += "Is required: " + this.isRequired + SysPrefs.newlinechar;\r
220                 if (!this.optionNames.isEmpty()) {\r
221                         Set<String> sortedPosval = new TreeSet<String>(this.optionNames);\r
222                         value += "Option(s): ";\r
223                         String delim = "";\r
224                         for (String val : sortedPosval) {\r
225                                 value += delim + val;\r
226                                 delim = ", ";\r
227                         }\r
228                         value += SysPrefs.newlinechar;\r
229                 }\r
230                 return value;\r
231         }\r
232 \r
233         /**\r
234          * Convert the option to the command string.\r
235          * \r
236          * @return If only one optionName is defined, than it is returned, if many\r
237          *         option names are defined, then the defaultValue is returned.\r
238          *         Option must have a default value if there are many optionNames to\r
239          *         be valid.\r
240          */\r
241         public String toCommand(String nameValueSeparator) {\r
242                 if (optionNames.size() == 1) {\r
243                         return optionNames.iterator().next();\r
244                 }\r
245                 return getDefaultValue();\r
246         }\r
247 \r
248         @Override\r
249         public boolean equals(Object obj) {\r
250                 if (obj == null) {\r
251                         return false;\r
252                 }\r
253                 Option<?> objArg = null;\r
254                 if (obj instanceof Option<?>) {\r
255                         objArg = (Option<?>) obj;\r
256                 } else {\r
257                         return false;\r
258                 }\r
259                 if (!Util.isEmpty(objArg.name) && !Util.isEmpty(name)) {\r
260                         if (!objArg.name.equals(this.name)) {\r
261                                 return false;\r
262                         }\r
263                 }\r
264                 if (!Util.isEmpty(objArg.description) && !Util.isEmpty(description)) {\r
265                         if (!objArg.description.equals(this.description)) {\r
266                                 return false;\r
267                         }\r
268                 }\r
269                 if (objArg.isRequired != this.isRequired) {\r
270                         return false;\r
271                 }\r
272                 if (!Util.isEmpty(objArg.defaultValue) && !Util.isEmpty(defaultValue)) {\r
273                         if (!objArg.defaultValue.equals(this.defaultValue)) {\r
274                                 return false;\r
275                         }\r
276                 }\r
277                 if (objArg.optionNames.size() != this.optionNames.size()) {\r
278                         return false;\r
279                 }\r
280                 int matchCount = 0;\r
281                 for (String oname : objArg.optionNames) {\r
282                         if (Util.isEmpty(oname)) {\r
283                                 continue;\r
284                         }\r
285                         for (String thisoname : this.optionNames) {\r
286                                 if (oname.equals(thisoname)) {\r
287                                         matchCount++;\r
288                                         break;\r
289                                 }\r
290                         }\r
291                 }\r
292                 if (matchCount != objArg.optionNames.size()) {\r
293                         return false;\r
294                 }\r
295                 return true;\r
296         }\r
297 \r
298         @Override\r
299         public int hashCode() {\r
300                 int code = this.name.hashCode() * this.description.hashCode();\r
301                 if (this.isRequired) {\r
302                         code += this.furtherDetails.hashCode() * 3;\r
303                 } else {\r
304                         if (defaultValue != null) {\r
305                                 code += this.defaultValue.hashCode() * 2;\r
306                         }\r
307                 }\r
308                 if (this.description != null) {\r
309                         code += this.description.hashCode() * 4;\r
310                 }\r
311 \r
312                 return code;\r
313         }\r
314 \r
315         /**\r
316          * List of possible optionNames\r
317          */\r
318         @Override\r
319         public List<String> getPossibleValues() {\r
320                 return new ArrayList<String>(optionNames);\r
321         }\r
322 \r
323         @Override\r
324         public void setValue(String dValue) throws WrongParameterException {\r
325                 this.setDefaultValue(dValue);\r
326         }\r
327 \r
328         /**\r
329          * Validate the option\r
330          * \r
331          * @throws ValidationException\r
332          *             is the option is invalid. This happens if option does not\r
333          *             have a default value but have multiple option names, or no\r
334          *             option names is defined\r
335          */\r
336         void validate() throws ValidationException {\r
337                 if (optionNames == null) {\r
338                         throw new ValidationException(\r
339                                         "Option names are not defined for option: " + this);\r
340                 }\r
341                 if (optionNames.size() > 1 && Util.isEmpty(getDefaultValue())) {\r
342                         throw new ValidationException(\r
343                                         "Default value is required as multiple optionNames are defined for option: "\r
344                                                         + this);\r
345                 }\r
346                 if (Util.isEmpty(name)) {\r
347                         throw new ValidationException("No name is defined for option: "\r
348                                         + this);\r
349                 }\r
350         }\r
351 }