| /** |
| * 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(); |
| } |
| } |