• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2010 the original author or authors.
3  * See the notice.md file distributed with this work for additional
4  * information regarding copyright ownership.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 package com.beust.jcommander;
20 
21 import com.beust.jcommander.validators.NoValidator;
22 import com.beust.jcommander.validators.NoValueValidator;
23 
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.EnumSet;
27 import java.util.HashSet;
28 import java.util.LinkedHashSet;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.ResourceBundle;
33 import java.util.Set;
34 import java.util.SortedSet;
35 import java.util.TreeSet;
36 
37 public class ParameterDescription {
38   private Object m_object;
39 
40   private WrappedParameter m_wrappedParameter;
41   private Parameter m_parameterAnnotation;
42   private DynamicParameter m_dynamicParameterAnnotation;
43 
44   /** The field/method */
45   private Parameterized m_parameterized;
46   /** Keep track of whether a value was added to flag an error */
47   private boolean m_assigned = false;
48   private ResourceBundle m_bundle;
49   private String m_description;
50   private JCommander m_jCommander;
51   private Object m_default;
52   /** Longest of the names(), used to present usage() alphabetically */
53   private String m_longestName = "";
54 
ParameterDescription(Object object, DynamicParameter annotation, Parameterized parameterized, ResourceBundle bundle, JCommander jc)55   public ParameterDescription(Object object, DynamicParameter annotation,
56       Parameterized parameterized,
57       ResourceBundle bundle, JCommander jc) {
58     if (! Map.class.isAssignableFrom(parameterized.getType())) {
59       throw new ParameterException("@DynamicParameter " + parameterized.getName()
60           + " should be of type "
61           + "Map but is " + parameterized.getType().getName());
62     }
63 
64     m_dynamicParameterAnnotation = annotation;
65     m_wrappedParameter = new WrappedParameter(m_dynamicParameterAnnotation);
66     init(object, parameterized, bundle, jc);
67   }
68 
ParameterDescription(Object object, Parameter annotation, Parameterized parameterized, ResourceBundle bundle, JCommander jc)69   public ParameterDescription(Object object, Parameter annotation, Parameterized parameterized,
70       ResourceBundle bundle, JCommander jc) {
71     m_parameterAnnotation = annotation;
72     m_wrappedParameter = new WrappedParameter(m_parameterAnnotation);
73     init(object, parameterized, bundle, jc);
74   }
75 
76   /**
77    * Find the resource bundle in the annotations.
78    * @return
79    */
80   @SuppressWarnings("deprecation")
findResourceBundle(Object o)81   private ResourceBundle findResourceBundle(Object o) {
82     ResourceBundle result = null;
83 
84     Parameters p = o.getClass().getAnnotation(Parameters.class);
85     if (p != null && ! isEmpty(p.resourceBundle())) {
86       result = ResourceBundle.getBundle(p.resourceBundle(), Locale.getDefault());
87     } else {
88       com.beust.jcommander.ResourceBundle a = o.getClass().getAnnotation(
89           com.beust.jcommander.ResourceBundle.class);
90       if (a != null && ! isEmpty(a.value())) {
91         result = ResourceBundle.getBundle(a.value(), Locale.getDefault());
92       }
93     }
94 
95     return result;
96   }
97 
isEmpty(String s)98   private boolean isEmpty(String s) {
99     return s == null || "".equals(s);
100   }
101 
initDescription(String description, String descriptionKey, String[] names)102   private void initDescription(String description, String descriptionKey, String[] names) {
103     m_description = description;
104     if (! "".equals(descriptionKey)) {
105       if (m_bundle != null) {
106         m_description = m_bundle.getString(descriptionKey);
107       } else {
108 //        JCommander.getConsole().println("Warning: field " + object.getClass() + "." + field.getName()
109 //            + " has a descriptionKey but no bundle was defined with @ResourceBundle, using " +
110 //            "default description:'" + m_description + "'");
111       }
112     }
113 
114     for (String name : names) {
115       if (name.length() > m_longestName.length()) m_longestName = name;
116     }
117   }
118 
119   @SuppressWarnings("unchecked")
init(Object object, Parameterized parameterized, ResourceBundle bundle, JCommander jCommander)120   private void init(Object object, Parameterized parameterized, ResourceBundle bundle,
121       JCommander jCommander) {
122     m_object = object;
123     m_parameterized = parameterized;
124     m_bundle = bundle;
125     if (m_bundle == null) {
126       m_bundle = findResourceBundle(object);
127     }
128     m_jCommander = jCommander;
129 
130     if (m_parameterAnnotation != null) {
131       String description;
132       if (Enum.class.isAssignableFrom(parameterized.getType())
133           && m_parameterAnnotation.description().isEmpty()) {
134         description = "Options: " + EnumSet.allOf((Class<? extends Enum>) parameterized.getType());
135       }else {
136         description = m_parameterAnnotation.description();
137       }
138       initDescription(description, m_parameterAnnotation.descriptionKey(),
139           m_parameterAnnotation.names());
140     } else if (m_dynamicParameterAnnotation != null) {
141       initDescription(m_dynamicParameterAnnotation.description(),
142           m_dynamicParameterAnnotation.descriptionKey(),
143           m_dynamicParameterAnnotation.names());
144     } else {
145       throw new AssertionError("Shound never happen");
146     }
147 
148     try {
149       m_default = parameterized.get(object);
150     } catch (Exception e) {
151     }
152 
153     //
154     // Validate default values, if any and if applicable
155     //
156     if (m_default != null) {
157       if (m_parameterAnnotation != null) {
158         validateDefaultValues(m_parameterAnnotation.names());
159       }
160     }
161   }
162 
validateDefaultValues(String[] names)163   private void validateDefaultValues(String[] names) {
164     String name = names.length > 0 ? names[0] : "";
165     validateValueParameter(name, m_default);
166   }
167 
getLongestName()168   public String getLongestName() {
169     return m_longestName;
170   }
171 
getDefault()172   public Object getDefault() {
173    return m_default;
174   }
175 
getDescription()176   public String getDescription() {
177     return m_description;
178   }
179 
getObject()180   public Object getObject() {
181     return m_object;
182   }
183 
getNames()184   public String getNames() {
185     StringBuilder sb = new StringBuilder();
186     String[] names = m_wrappedParameter.names();
187     for (int i = 0; i < names.length; i++) {
188       if (i > 0) sb.append(", ");
189       sb.append(names[i]);
190     }
191     return sb.toString();
192   }
193 
getParameter()194   public WrappedParameter getParameter() {
195     return m_wrappedParameter;
196   }
197 
getParameterized()198   public Parameterized getParameterized() {
199     return m_parameterized;
200   }
201 
isMultiOption()202   private boolean isMultiOption() {
203     Class<?> fieldType = m_parameterized.getType();
204     return fieldType.equals(List.class) || fieldType.equals(Set.class)
205         || m_parameterized.isDynamicParameter();
206   }
207 
addValue(String value)208   public void addValue(String value) {
209     addValue(value, false /* not default */);
210   }
211 
212   /**
213    * @return true if this parameter received a value during the parsing phase.
214    */
isAssigned()215   public boolean isAssigned() {
216     return m_assigned;
217   }
218 
219 
setAssigned(boolean b)220   public void setAssigned(boolean b) {
221     m_assigned = b;
222   }
223 
224   /**
225    * Add the specified value to the field. First, validate the value if a
226    * validator was specified. Then look up any field converter, then any type
227    * converter, and if we can't find any, throw an exception.
228    */
addValue(String value, boolean isDefault)229   public void addValue(String value, boolean isDefault) {
230     p("Adding " + (isDefault ? "default " : "") + "value:" + value
231         + " to parameter:" + m_parameterized.getName());
232     String name = m_wrappedParameter.names()[0];
233     if (m_assigned && ! isMultiOption() && !m_jCommander.isParameterOverwritingAllowed() || isNonOverwritableForced()) {
234       throw new ParameterException("Can only specify option " + name + " once.");
235     }
236 
237     validateParameter(name, value);
238 
239     Class<?> type = m_parameterized.getType();
240 
241     Object convertedValue = m_jCommander.convertValue(this, value);
242     validateValueParameter(name, convertedValue);
243     boolean isCollection = Collection.class.isAssignableFrom(type);
244 
245     if (isCollection) {
246       @SuppressWarnings("unchecked")
247       Collection<Object> l = (Collection<Object>) m_parameterized.get(m_object);
248       if (l == null || fieldIsSetForTheFirstTime(isDefault)) {
249         l = newCollection(type);
250         m_parameterized.set(m_object, l);
251       }
252       if (convertedValue instanceof Collection) {
253         l.addAll((Collection) convertedValue);
254       } else { // if (isMainParameter || m_parameterAnnotation.arity() > 1) {
255         l.add(convertedValue);
256 //        } else {
257 //          l.
258       }
259     } else {
260       m_wrappedParameter.addValue(m_parameterized, m_object, convertedValue);
261     }
262     if (! isDefault) m_assigned = true;
263   }
264 
validateParameter(String name, String value)265   private void validateParameter(String name, String value) {
266     Class<? extends IParameterValidator> validator = m_wrappedParameter.validateWith();
267     if (validator != null) {
268       validateParameter(this, validator, name, value);
269     }
270   }
271 
validateValueParameter(String name, Object value)272   private void validateValueParameter(String name, Object value) {
273     Class<? extends IValueValidator> validator = m_wrappedParameter.validateValueWith();
274     if (validator != null) {
275       validateValueParameter(validator, name, value);
276     }
277   }
278 
validateValueParameter(Class<? extends IValueValidator> validator, String name, Object value)279   public static void validateValueParameter(Class<? extends IValueValidator> validator,
280       String name, Object value) {
281     try {
282       if (validator != NoValueValidator.class) {
283         p("Validating value parameter:" + name + " value:" + value + " validator:" + validator);
284       }
285       validator.newInstance().validate(name, value);
286     } catch (InstantiationException e) {
287       throw new ParameterException("Can't instantiate validator:" + e);
288     } catch (IllegalAccessException e) {
289       throw new ParameterException("Can't instantiate validator:" + e);
290     }
291   }
292 
validateParameter(ParameterDescription pd, Class<? extends IParameterValidator> validator, String name, String value)293   public static void validateParameter(ParameterDescription pd,
294       Class<? extends IParameterValidator> validator,
295       String name, String value) {
296     try {
297       if (validator != NoValidator.class) {
298         p("Validating parameter:" + name + " value:" + value + " validator:" + validator);
299       }
300       validator.newInstance().validate(name, value);
301       if (IParameterValidator2.class.isAssignableFrom(validator)) {
302         IParameterValidator2 instance = (IParameterValidator2) validator.newInstance();
303         instance.validate(name, value, pd);
304       }
305     } catch (InstantiationException e) {
306       throw new ParameterException("Can't instantiate validator:" + e);
307     } catch (IllegalAccessException e) {
308       throw new ParameterException("Can't instantiate validator:" + e);
309     } catch(ParameterException ex) {
310       throw ex;
311     } catch(Exception ex) {
312       throw new ParameterException(ex);
313     }
314   }
315 
316   /*
317    * Creates a new collection for the field's type.
318    *
319    * Currently only List and Set are supported. Support for
320    * Queues and Stacks could be useful.
321    */
322   @SuppressWarnings("unchecked")
newCollection(Class<?> type)323   private Collection<Object> newCollection(Class<?> type) {
324     if (SortedSet.class.isAssignableFrom(type)) return new TreeSet();
325     else if (LinkedHashSet.class.isAssignableFrom(type)) return new LinkedHashSet();
326     else if (Set.class.isAssignableFrom(type)) return new HashSet();
327     else if (List.class.isAssignableFrom(type)) return new ArrayList();
328     else {
329       throw new ParameterException("Parameters of Collection type '" + type.getSimpleName()
330                                   + "' are not supported. Please use List or Set instead.");
331     }
332   }
333 
334   /*
335    * Tests if its the first time a non-default value is
336    * being added to the field.
337    */
fieldIsSetForTheFirstTime(boolean isDefault)338   private boolean fieldIsSetForTheFirstTime(boolean isDefault) {
339     return (!isDefault && !m_assigned);
340   }
341 
p(String string)342   private static void p(String string) {
343     if (System.getProperty(JCommander.DEBUG_PROPERTY) != null) {
344       JCommander.getConsole().println("[ParameterDescription] " + string);
345     }
346   }
347 
348   @Override
toString()349   public String toString() {
350     return "[ParameterDescription " + m_parameterized.getName() + "]";
351   }
352 
isDynamicParameter()353   public boolean isDynamicParameter() {
354     return m_dynamicParameterAnnotation != null;
355   }
356 
isHelp()357   public boolean isHelp() {
358     return m_wrappedParameter.isHelp();
359   }
360 
isNonOverwritableForced()361   public boolean isNonOverwritableForced() {
362     return m_wrappedParameter.isNonOverwritableForced();
363   }
364 }
365