diff options
Diffstat (limited to 'src/main/java/com/beust/jcommander/ParameterDescription.java')
-rw-r--r-- | src/main/java/com/beust/jcommander/ParameterDescription.java | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/src/main/java/com/beust/jcommander/ParameterDescription.java b/src/main/java/com/beust/jcommander/ParameterDescription.java new file mode 100644 index 0000000..2ef2d5f --- /dev/null +++ b/src/main/java/com/beust/jcommander/ParameterDescription.java @@ -0,0 +1,364 @@ +/** + * Copyright (C) 2010 the original author or authors. + * See the notice.md file distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.beust.jcommander; + +import com.beust.jcommander.validators.NoValidator; +import com.beust.jcommander.validators.NoValueValidator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +public class ParameterDescription { + private Object m_object; + + private WrappedParameter m_wrappedParameter; + private Parameter m_parameterAnnotation; + private DynamicParameter m_dynamicParameterAnnotation; + + /** The field/method */ + private Parameterized m_parameterized; + /** Keep track of whether a value was added to flag an error */ + private boolean m_assigned = false; + private ResourceBundle m_bundle; + private String m_description; + private JCommander m_jCommander; + private Object m_default; + /** Longest of the names(), used to present usage() alphabetically */ + private String m_longestName = ""; + + public ParameterDescription(Object object, DynamicParameter annotation, + Parameterized parameterized, + ResourceBundle bundle, JCommander jc) { + if (! Map.class.isAssignableFrom(parameterized.getType())) { + throw new ParameterException("@DynamicParameter " + parameterized.getName() + + " should be of type " + + "Map but is " + parameterized.getType().getName()); + } + + m_dynamicParameterAnnotation = annotation; + m_wrappedParameter = new WrappedParameter(m_dynamicParameterAnnotation); + init(object, parameterized, bundle, jc); + } + + public ParameterDescription(Object object, Parameter annotation, Parameterized parameterized, + ResourceBundle bundle, JCommander jc) { + m_parameterAnnotation = annotation; + m_wrappedParameter = new WrappedParameter(m_parameterAnnotation); + init(object, parameterized, bundle, jc); + } + + /** + * Find the resource bundle in the annotations. + * @return + */ + @SuppressWarnings("deprecation") + private ResourceBundle findResourceBundle(Object o) { + ResourceBundle result = null; + + Parameters p = o.getClass().getAnnotation(Parameters.class); + if (p != null && ! isEmpty(p.resourceBundle())) { + result = ResourceBundle.getBundle(p.resourceBundle(), Locale.getDefault()); + } else { + com.beust.jcommander.ResourceBundle a = o.getClass().getAnnotation( + com.beust.jcommander.ResourceBundle.class); + if (a != null && ! isEmpty(a.value())) { + result = ResourceBundle.getBundle(a.value(), Locale.getDefault()); + } + } + + return result; + } + + private boolean isEmpty(String s) { + return s == null || "".equals(s); + } + + private void initDescription(String description, String descriptionKey, String[] names) { + m_description = description; + if (! "".equals(descriptionKey)) { + if (m_bundle != null) { + m_description = m_bundle.getString(descriptionKey); + } else { +// JCommander.getConsole().println("Warning: field " + object.getClass() + "." + field.getName() +// + " has a descriptionKey but no bundle was defined with @ResourceBundle, using " + +// "default description:'" + m_description + "'"); + } + } + + for (String name : names) { + if (name.length() > m_longestName.length()) m_longestName = name; + } + } + + @SuppressWarnings("unchecked") + private void init(Object object, Parameterized parameterized, ResourceBundle bundle, + JCommander jCommander) { + m_object = object; + m_parameterized = parameterized; + m_bundle = bundle; + if (m_bundle == null) { + m_bundle = findResourceBundle(object); + } + m_jCommander = jCommander; + + if (m_parameterAnnotation != null) { + String description; + if (Enum.class.isAssignableFrom(parameterized.getType()) + && m_parameterAnnotation.description().isEmpty()) { + description = "Options: " + EnumSet.allOf((Class<? extends Enum>) parameterized.getType()); + }else { + description = m_parameterAnnotation.description(); + } + initDescription(description, m_parameterAnnotation.descriptionKey(), + m_parameterAnnotation.names()); + } else if (m_dynamicParameterAnnotation != null) { + initDescription(m_dynamicParameterAnnotation.description(), + m_dynamicParameterAnnotation.descriptionKey(), + m_dynamicParameterAnnotation.names()); + } else { + throw new AssertionError("Shound never happen"); + } + + try { + m_default = parameterized.get(object); + } catch (Exception e) { + } + + // + // Validate default values, if any and if applicable + // + if (m_default != null) { + if (m_parameterAnnotation != null) { + validateDefaultValues(m_parameterAnnotation.names()); + } + } + } + + private void validateDefaultValues(String[] names) { + String name = names.length > 0 ? names[0] : ""; + validateValueParameter(name, m_default); + } + + public String getLongestName() { + return m_longestName; + } + + public Object getDefault() { + return m_default; + } + + public String getDescription() { + return m_description; + } + + public Object getObject() { + return m_object; + } + + public String getNames() { + StringBuilder sb = new StringBuilder(); + String[] names = m_wrappedParameter.names(); + for (int i = 0; i < names.length; i++) { + if (i > 0) sb.append(", "); + sb.append(names[i]); + } + return sb.toString(); + } + + public WrappedParameter getParameter() { + return m_wrappedParameter; + } + + public Parameterized getParameterized() { + return m_parameterized; + } + + private boolean isMultiOption() { + Class<?> fieldType = m_parameterized.getType(); + return fieldType.equals(List.class) || fieldType.equals(Set.class) + || m_parameterized.isDynamicParameter(); + } + + public void addValue(String value) { + addValue(value, false /* not default */); + } + + /** + * @return true if this parameter received a value during the parsing phase. + */ + public boolean isAssigned() { + return m_assigned; + } + + + public void setAssigned(boolean b) { + m_assigned = b; + } + + /** + * Add the specified value to the field. First, validate the value if a + * validator was specified. Then look up any field converter, then any type + * converter, and if we can't find any, throw an exception. + */ + public void addValue(String value, boolean isDefault) { + p("Adding " + (isDefault ? "default " : "") + "value:" + value + + " to parameter:" + m_parameterized.getName()); + String name = m_wrappedParameter.names()[0]; + if (m_assigned && ! isMultiOption() && !m_jCommander.isParameterOverwritingAllowed() || isNonOverwritableForced()) { + throw new ParameterException("Can only specify option " + name + " once."); + } + + validateParameter(name, value); + + Class<?> type = m_parameterized.getType(); + + Object convertedValue = m_jCommander.convertValue(this, value); + validateValueParameter(name, convertedValue); + boolean isCollection = Collection.class.isAssignableFrom(type); + + if (isCollection) { + @SuppressWarnings("unchecked") + Collection<Object> l = (Collection<Object>) m_parameterized.get(m_object); + if (l == null || fieldIsSetForTheFirstTime(isDefault)) { + l = newCollection(type); + m_parameterized.set(m_object, l); + } + if (convertedValue instanceof Collection) { + l.addAll((Collection) convertedValue); + } else { // if (isMainParameter || m_parameterAnnotation.arity() > 1) { + l.add(convertedValue); +// } else { +// l. + } + } else { + m_wrappedParameter.addValue(m_parameterized, m_object, convertedValue); + } + if (! isDefault) m_assigned = true; + } + + private void validateParameter(String name, String value) { + Class<? extends IParameterValidator> validator = m_wrappedParameter.validateWith(); + if (validator != null) { + validateParameter(this, validator, name, value); + } + } + + private void validateValueParameter(String name, Object value) { + Class<? extends IValueValidator> validator = m_wrappedParameter.validateValueWith(); + if (validator != null) { + validateValueParameter(validator, name, value); + } + } + + public static void validateValueParameter(Class<? extends IValueValidator> validator, + String name, Object value) { + try { + if (validator != NoValueValidator.class) { + p("Validating value parameter:" + name + " value:" + value + " validator:" + validator); + } + validator.newInstance().validate(name, value); + } catch (InstantiationException e) { + throw new ParameterException("Can't instantiate validator:" + e); + } catch (IllegalAccessException e) { + throw new ParameterException("Can't instantiate validator:" + e); + } + } + + public static void validateParameter(ParameterDescription pd, + Class<? extends IParameterValidator> validator, + String name, String value) { + try { + if (validator != NoValidator.class) { + p("Validating parameter:" + name + " value:" + value + " validator:" + validator); + } + validator.newInstance().validate(name, value); + if (IParameterValidator2.class.isAssignableFrom(validator)) { + IParameterValidator2 instance = (IParameterValidator2) validator.newInstance(); + instance.validate(name, value, pd); + } + } catch (InstantiationException e) { + throw new ParameterException("Can't instantiate validator:" + e); + } catch (IllegalAccessException e) { + throw new ParameterException("Can't instantiate validator:" + e); + } catch(ParameterException ex) { + throw ex; + } catch(Exception ex) { + throw new ParameterException(ex); + } + } + + /* + * Creates a new collection for the field's type. + * + * Currently only List and Set are supported. Support for + * Queues and Stacks could be useful. + */ + @SuppressWarnings("unchecked") + private Collection<Object> newCollection(Class<?> type) { + if (SortedSet.class.isAssignableFrom(type)) return new TreeSet(); + else if (LinkedHashSet.class.isAssignableFrom(type)) return new LinkedHashSet(); + else if (Set.class.isAssignableFrom(type)) return new HashSet(); + else if (List.class.isAssignableFrom(type)) return new ArrayList(); + else { + throw new ParameterException("Parameters of Collection type '" + type.getSimpleName() + + "' are not supported. Please use List or Set instead."); + } + } + + /* + * Tests if its the first time a non-default value is + * being added to the field. + */ + private boolean fieldIsSetForTheFirstTime(boolean isDefault) { + return (!isDefault && !m_assigned); + } + + private static void p(String string) { + if (System.getProperty(JCommander.DEBUG_PROPERTY) != null) { + JCommander.getConsole().println("[ParameterDescription] " + string); + } + } + + @Override + public String toString() { + return "[ParameterDescription " + m_parameterized.getName() + "]"; + } + + public boolean isDynamicParameter() { + return m_dynamicParameterAnnotation != null; + } + + public boolean isHelp() { + return m_wrappedParameter.isHelp(); + } + + public boolean isNonOverwritableForced() { + return m_wrappedParameter.isNonOverwritableForced(); + } +} |