Next version of JABA
[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.URL;\r
22 import java.util.ArrayList;\r
23 import java.util.HashSet;\r
24 import java.util.List;\r
25 import java.util.Set;\r
26 import java.util.TreeSet;\r
27 \r
28 import javax.xml.bind.ValidationException;\r
29 import javax.xml.bind.annotation.XmlAccessType;\r
30 import javax.xml.bind.annotation.XmlAccessorType;\r
31 import javax.xml.bind.annotation.XmlAttribute;\r
32 import javax.xml.bind.annotation.XmlElement;\r
33 \r
34 import compbio.util.SysPrefs;\r
35 import compbio.util.Util;\r
36 \r
37 /**\r
38  * Command line option/flag or multiple exclusive options with no value. Example\r
39  * -protein, -dna, -auto\r
40  * \r
41  * @author pvtroshin\r
42  * \r
43  *         Date October 2009\r
44  * @param <T>\r
45  *            type of executable\r
46  */\r
47 @XmlAccessorType(XmlAccessType.FIELD)\r
48 public class Option<T> implements Argument<T> {\r
49 \r
50     @XmlElement(required = true)\r
51     protected String description;\r
52 \r
53     @XmlElement(required = true)\r
54     Set<String> optionNames = new HashSet<String>();\r
55 \r
56     @XmlElement(required = true)\r
57     protected String name;\r
58 \r
59     @XmlAttribute\r
60     protected boolean isRequired;\r
61     @XmlElement\r
62     protected URL furtherDetails;\r
63     @XmlElement\r
64     protected String defaultValue;\r
65 \r
66     Option() {\r
67         // Has to have no arg constructor for JAXB\r
68     }\r
69 \r
70     public Option(String name, String description) {\r
71         this.name = name;\r
72         this.description = description;\r
73     }\r
74 \r
75     /**\r
76      * Human readable name of the option\r
77      */\r
78     public String getName() {\r
79         return name;\r
80     }\r
81 \r
82     public void setName(String name) {\r
83         this.name = name;\r
84     }\r
85 \r
86     /**\r
87      * A long description of the Option\r
88      */\r
89     public String getDescription() {\r
90         return description;\r
91     }\r
92 \r
93     public void setDescription(String description) {\r
94         this.description = description;\r
95     }\r
96 \r
97     /**\r
98      * The URL where further details about the option can be found\r
99      */\r
100     public URL getFurtherDetails() {\r
101         return furtherDetails;\r
102     }\r
103 \r
104     public void setFurtherDetails(URL furtherDetails) {\r
105         this.furtherDetails = furtherDetails;\r
106     }\r
107 \r
108     /**\r
109      * A default value of the option. Defaults to command line argument name\r
110      * e.g. -auto\r
111      */\r
112     public String getDefaultValue() {\r
113         return defaultValue;\r
114     }\r
115 \r
116     /**\r
117      * Sets one of the values defined in optionList as default. Attempting set\r
118      * the value not listed there will result in WrongParameter exception\r
119      * \r
120      * @param defaultVal\r
121      * @throws WrongParameterException\r
122      *             is thrown if the defaultValue is not found in optionList\r
123      */\r
124     public void setDefaultValue(String defaultVal)\r
125             throws WrongParameterException {\r
126         if (optionNames.isEmpty()) {\r
127             throw new IllegalStateException("Please define optionNames first!");\r
128         }\r
129         if (!valueExist(defaultVal, getOptionNames())) {\r
130             throw new WrongParameterException(\r
131                     "Attempting to set illegal defaultValue '" + defaultVal\r
132                             + "' which is not defined optionNames for option: "\r
133                             + this);\r
134         }\r
135         this.defaultValue = defaultVal;\r
136     }\r
137 \r
138     static boolean valueExist(String testValue, List<String> values) {\r
139         assert !Util.isEmpty(testValue);\r
140         for (String val : values) {\r
141             if (testValue.equalsIgnoreCase(val)) {\r
142                 return true;\r
143             }\r
144         }\r
145         return false;\r
146     }\r
147 \r
148     /**\r
149      * Flag that indicated that this option must be specified in the command\r
150      * line for an executable to run\r
151      * \r
152      * @return true is the option is required, false otherwise\r
153      */\r
154     public boolean isRequired() {\r
155         return isRequired;\r
156     }\r
157 \r
158     public void setRequired(boolean isRequired) {\r
159         this.isRequired = isRequired;\r
160     }\r
161 \r
162     /**\r
163      * \r
164      * @return List of option names\r
165      */\r
166     public List<String> getOptionNames() {\r
167         return new ArrayList<String>(optionNames);\r
168     }\r
169 \r
170     public void setOptionNames(Set<String> optionNames) {\r
171         this.optionNames = new HashSet<String>(optionNames);\r
172     }\r
173 \r
174     /**\r
175      * Adds an option to the optionName list\r
176      * \r
177      * @param value\r
178      * @return modified optionName list\r
179      */\r
180     public Set<String> addOptionNames(String... value) {\r
181         for (String v : value) {\r
182             boolean added = this.optionNames.add(v);\r
183             assert added : "Duplicated optionName is detected!";\r
184         }\r
185         return this.optionNames;\r
186     }\r
187 \r
188     @Override\r
189     public String toString() {\r
190         String value = "Option name: " + this.name + SysPrefs.newlinechar;\r
191         value += "Description: " + this.description + SysPrefs.newlinechar;\r
192         if (!Util.isEmpty(defaultValue)) {\r
193             value += "Default value: " + this.defaultValue\r
194                     + SysPrefs.newlinechar;\r
195         }\r
196         value += "URL: " + this.furtherDetails + SysPrefs.newlinechar;\r
197         value += "Is required: " + this.isRequired + SysPrefs.newlinechar;\r
198         if (!this.optionNames.isEmpty()) {\r
199             Set<String> sortedPosval = new TreeSet<String>(this.optionNames);\r
200             value += "Option Names: " + SysPrefs.newlinechar;\r
201             for (String val : sortedPosval) {\r
202                 value += val + SysPrefs.newlinechar;\r
203             }\r
204         }\r
205         return value;\r
206     }\r
207 \r
208     /**\r
209      * Convert the option to the command string.\r
210      * \r
211      * @return If only one optionName is defined, than it is returned, if many\r
212      *         option names are defined, then the defaultValue is returned.\r
213      *         Option must have a default value if there are many optionNames to\r
214      *         be valid.\r
215      */\r
216     public String toCommand(String nameValueSeparator) {\r
217         if (optionNames.size() == 1) {\r
218             return optionNames.iterator().next();\r
219         }\r
220         return getDefaultValue();\r
221     }\r
222 \r
223     @Override\r
224     public boolean equals(Object obj) {\r
225         if (obj == null) {\r
226             return false;\r
227         }\r
228         Option<?> objArg = null;\r
229         if (obj instanceof Option<?>) {\r
230             objArg = (Option<?>) obj;\r
231         } else {\r
232             return false;\r
233         }\r
234         if (!Util.isEmpty(objArg.name) && !Util.isEmpty(name)) {\r
235             if (!objArg.name.equals(this.name)) {\r
236                 return false;\r
237             }\r
238         }\r
239         if (!Util.isEmpty(objArg.description) && !Util.isEmpty(description)) {\r
240             if (!objArg.description.equals(this.description)) {\r
241                 return false;\r
242             }\r
243         }\r
244         if (objArg.isRequired != this.isRequired) {\r
245             return false;\r
246         }\r
247         if (!Util.isEmpty(objArg.defaultValue) && !Util.isEmpty(defaultValue)) {\r
248             if (!objArg.defaultValue.equals(this.defaultValue)) {\r
249                 return false;\r
250             }\r
251         }\r
252         if (objArg.optionNames.size() != this.optionNames.size()) {\r
253             return false;\r
254         }\r
255         int matchCount = 0;\r
256         for (String oname : objArg.optionNames) {\r
257             if (Util.isEmpty(oname)) {\r
258                 continue;\r
259             }\r
260             for (String thisoname : this.optionNames) {\r
261                 if (oname.equals(thisoname)) {\r
262                     matchCount++;\r
263                     break;\r
264                 }\r
265             }\r
266         }\r
267         if (matchCount != objArg.optionNames.size()) {\r
268             return false;\r
269         }\r
270         return true;\r
271     }\r
272 \r
273     @Override\r
274     public int hashCode() {\r
275         int code = this.name.hashCode() * this.description.hashCode();\r
276         if (this.isRequired) {\r
277             code += this.furtherDetails.hashCode() * 3;\r
278         } else {\r
279             if (defaultValue != null) {\r
280                 code += this.defaultValue.hashCode() * 2;\r
281             }\r
282         }\r
283         if (this.description != null) {\r
284             code += this.description.hashCode() * 4;\r
285         }\r
286 \r
287         return code;\r
288     }\r
289 \r
290     /**\r
291      * List of possible optionNames\r
292      */\r
293     @Override\r
294     public List<String> getPossibleValues() {\r
295         return new ArrayList<String>(optionNames);\r
296     }\r
297 \r
298     @Override\r
299     public void setValue(String dValue) throws WrongParameterException {\r
300         this.setDefaultValue(dValue);\r
301     }\r
302 \r
303     /**\r
304      * Validate the option\r
305      * \r
306      * @throws ValidationException\r
307      *             is the option is invalid. This happens if option does not\r
308      *             have a default value but have multiple option names, or no\r
309      *             option names is defined\r
310      */\r
311     void validate() throws ValidationException {\r
312         if (optionNames == null) {\r
313             throw new ValidationException(\r
314                     "Option names are not defined for option: " + this);\r
315         }\r
316         if (optionNames.size() > 1 && Util.isEmpty(getDefaultValue())) {\r
317             throw new ValidationException(\r
318                     "Default value is required as multiple optionNames are defined for option: "\r
319                             + this);\r
320         }\r
321         if (Util.isEmpty(name)) {\r
322             throw new ValidationException("No name is defined for option: "\r
323                     + this);\r
324         }\r
325     }\r
326 }