summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/beust/jcommander/DynamicParameter.java6
-rw-r--r--src/main/java/com/beust/jcommander/IStringConverterFactory.java1
-rw-r--r--src/main/java/com/beust/jcommander/IStringConverterInstanceFactory.java20
-rw-r--r--src/main/java/com/beust/jcommander/JCommander.java3052
-rw-r--r--src/main/java/com/beust/jcommander/MissingCommandException.java19
-rw-r--r--src/main/java/com/beust/jcommander/Parameter.java11
-rw-r--r--src/main/java/com/beust/jcommander/ParameterDescription.java265
-rw-r--r--src/main/java/com/beust/jcommander/ParameterException.java18
-rw-r--r--src/main/java/com/beust/jcommander/Parameterized.java223
-rw-r--r--src/main/java/com/beust/jcommander/Parameters.java7
-rw-r--r--src/main/java/com/beust/jcommander/StringKey.java16
-rw-r--r--src/main/java/com/beust/jcommander/SubParameter.java17
-rw-r--r--src/main/java/com/beust/jcommander/WrappedParameter.java72
-rw-r--r--src/main/java/com/beust/jcommander/converters/BaseConverter.java6
-rw-r--r--src/main/java/com/beust/jcommander/converters/CharArrayConverter.java33
-rw-r--r--src/main/java/com/beust/jcommander/converters/DefaultListConverter.java36
-rw-r--r--src/main/java/com/beust/jcommander/converters/EnumConverter.java42
-rw-r--r--src/main/java/com/beust/jcommander/converters/InetAddressConverter.java40
-rw-r--r--src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java8
-rw-r--r--src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java45
-rw-r--r--src/main/java/com/beust/jcommander/internal/JDK6Console.java12
-rw-r--r--src/main/java/com/beust/jcommander/internal/Lists.java12
-rw-r--r--src/main/java/com/beust/jcommander/internal/Maps.java4
-rw-r--r--src/main/java/com/beust/jcommander/internal/Sets.java4
-rw-r--r--src/test/java/com/beust/jcommander/ArgMultiNameValidator.java19
-rw-r--r--src/test/java/com/beust/jcommander/CmdTest.java29
-rw-r--r--src/test/java/com/beust/jcommander/ConverterFactoryTest.java22
-rw-r--r--src/test/java/com/beust/jcommander/DefaultProviderTest.java20
-rw-r--r--src/test/java/com/beust/jcommander/DefaultValueTest.java8
-rw-r--r--src/test/java/com/beust/jcommander/FinderTest.java11
-rw-r--r--src/test/java/com/beust/jcommander/HiddenConverter.java29
-rw-r--r--src/test/java/com/beust/jcommander/HiddenParameterSplitter.java31
-rw-r--r--src/test/java/com/beust/jcommander/JCommanderTest.java2560
-rw-r--r--src/test/java/com/beust/jcommander/MethodSetterTest.java9
-rw-r--r--src/test/java/com/beust/jcommander/ParametersDelegateTest.java6
-rw-r--r--src/test/java/com/beust/jcommander/ParametersNotEmptyTest.java37
-rw-r--r--src/test/java/com/beust/jcommander/PasswordTest.java111
-rw-r--r--src/test/java/com/beust/jcommander/PositiveIntegerTest.java10
-rw-r--r--src/test/java/com/beust/jcommander/SetConverter.java2
-rw-r--r--src/test/java/com/beust/jcommander/SimpleExample.java50
-rw-r--r--src/test/java/com/beust/jcommander/TypeHierarchyTest.java111
-rw-r--r--src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java3
-rw-r--r--src/test/java/com/beust/jcommander/VariableArityTest.java11
-rw-r--r--src/test/java/com/beust/jcommander/args/ArgsEnum.java4
-rw-r--r--src/test/java/com/beust/jcommander/args/ArgsLongCommandDescription.java12
-rw-r--r--src/test/java/com/beust/jcommander/args/ArgsLongMainParameterDescription.java17
-rw-r--r--src/test/java/com/beust/jcommander/args/ArgsMultiValidate.java36
-rw-r--r--src/test/java/com/beust/jcommander/args/HiddenArgs.java36
-rw-r--r--src/test/java/com/beust/jcommander/args/SlashSeparator.java2
-rw-r--r--src/test/java/com/beust/jcommander/args/VariableArity.java10
-rw-r--r--src/test/java/com/beust/jcommander/command/CommandNoParametersAnnotation.java9
-rw-r--r--src/test/java/com/beust/jcommander/command/CommandTest.java57
-rw-r--r--src/test/java/com/beust/jcommander/converters/CharArrayConverterTest.java37
-rw-r--r--src/test/java/com/beust/jcommander/converters/InetAddressConverterTest.java62
-rw-r--r--src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java5
-rw-r--r--src/test/java/test/QuotedMainTest.java2
56 files changed, 4458 insertions, 2879 deletions
diff --git a/src/main/java/com/beust/jcommander/DynamicParameter.java b/src/main/java/com/beust/jcommander/DynamicParameter.java
index 2159c1f..bdb5010 100644
--- a/src/main/java/com/beust/jcommander/DynamicParameter.java
+++ b/src/main/java/com/beust/jcommander/DynamicParameter.java
@@ -37,14 +37,14 @@ public @interface DynamicParameter {
boolean hidden() default false;
/**
- * The validation class to use.
+ * The validation classes to use.
*/
- Class<? extends IParameterValidator> validateWith() default NoValidator.class;
+ Class<? extends IParameterValidator>[] validateWith() default NoValidator.class;
/**
* The character(s) used to assign the values.
*/
String assignment() default "=";
- Class<? extends IValueValidator> validateValueWith() default NoValueValidator.class;
+ Class<? extends IValueValidator>[] validateValueWith() default NoValueValidator.class;
}
diff --git a/src/main/java/com/beust/jcommander/IStringConverterFactory.java b/src/main/java/com/beust/jcommander/IStringConverterFactory.java
index 0e53ca0..3e26020 100644
--- a/src/main/java/com/beust/jcommander/IStringConverterFactory.java
+++ b/src/main/java/com/beust/jcommander/IStringConverterFactory.java
@@ -24,6 +24,7 @@ package com.beust.jcommander;
* your argument classes.
*
* @author cbeust
+ * @see IStringConverterInstanceFactory
*/
public interface IStringConverterFactory {
<T> Class<? extends IStringConverter<T>> getConverter(Class<T> forType);
diff --git a/src/main/java/com/beust/jcommander/IStringConverterInstanceFactory.java b/src/main/java/com/beust/jcommander/IStringConverterInstanceFactory.java
new file mode 100644
index 0000000..1a87b5b
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/IStringConverterInstanceFactory.java
@@ -0,0 +1,20 @@
+package com.beust.jcommander;
+
+/**
+ * A factory to create {@link IStringConverter} instances.
+ *
+ * This interface lets you specify your converters in one place instead of having them repeated all over your argument classes.
+ *
+ * @author simon04
+ * @see IStringConverterFactory
+ */
+public interface IStringConverterInstanceFactory {
+ /**
+ * Obtain a converter instance for parsing {@code parameter} as type {@code forType}
+ * @param parameter the parameter to parse
+ * @param forType the type class
+ * @param optionName the name of the option used on the command line
+ * @return a converter instance
+ */
+ IStringConverter<?> getConverterInstance(Parameter parameter, Class<?> forType, String optionName);
+}
diff --git a/src/main/java/com/beust/jcommander/JCommander.java b/src/main/java/com/beust/jcommander/JCommander.java
index 2e049a1..59073c6 100644
--- a/src/main/java/com/beust/jcommander/JCommander.java
+++ b/src/main/java/com/beust/jcommander/JCommander.java
@@ -7,7 +7,7 @@
* 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
+ * 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,
@@ -18,37 +18,19 @@
package com.beust.jcommander;
+import com.beust.jcommander.FuzzyMap.IKey;
+import com.beust.jcommander.converters.*;
+import com.beust.jcommander.internal.*;
+
import java.io.BufferedReader;
-import java.io.FileReader;
import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
+import java.lang.reflect.*;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
import java.util.ResourceBundle;
-
-import com.beust.jcommander.FuzzyMap.IKey;
-import com.beust.jcommander.converters.IParameterSplitter;
-import com.beust.jcommander.converters.NoConverter;
-import com.beust.jcommander.converters.StringConverter;
-import com.beust.jcommander.internal.Console;
-import com.beust.jcommander.internal.DefaultConsole;
-import com.beust.jcommander.internal.DefaultConverterFactory;
-import com.beust.jcommander.internal.JDK6Console;
-import com.beust.jcommander.internal.Lists;
-import com.beust.jcommander.internal.Maps;
-import com.beust.jcommander.internal.Nullable;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* The main class for JCommander. It's responsible for parsing the object that contains
@@ -63,1537 +45,1647 @@ import com.beust.jcommander.internal.Nullable;
* @author Cedric Beust <cedric@beust.com>
*/
public class JCommander {
- public static final String DEBUG_PROPERTY = "jcommander.debug";
-
- /**
- * A map to look up parameter description per option name.
- */
- private Map<IKey, ParameterDescription> m_descriptions;
-
- /**
- * The objects that contain fields annotated with @Parameter.
- */
- private List<Object> m_objects = Lists.newArrayList();
-
- private boolean m_firstTimeMainParameter = true;
-
- /**
- * This field/method will contain whatever command line parameter is not an option.
- * It is expected to be a List<String>.
- */
- private Parameterized m_mainParameter = null;
-
- /**
- * The object on which we found the main parameter field.
- */
- private Object m_mainParameterObject;
-
- /**
- * The annotation found on the main parameter field.
- */
- private Parameter m_mainParameterAnnotation;
-
- private ParameterDescription m_mainParameterDescription;
-
- /**
- * A set of all the parameterizeds that are required. During the reflection phase,
- * this field receives all the fields that are annotated with required=true
- * and during the parsing phase, all the fields that are assigned a value
- * are removed from it. At the end of the parsing phase, if it's not empty,
- * then some required fields did not receive a value and an exception is
- * thrown.
- */
- private Map<Parameterized, ParameterDescription> m_requiredFields = Maps.newHashMap();
-
- /**
- * A map of all the parameterized fields/methods.
- */
- private Map<Parameterized, ParameterDescription> m_fields = Maps.newHashMap();
-
- private ResourceBundle m_bundle;
-
- /**
- * A default provider returns default values for the parameters.
- */
- private IDefaultProvider m_defaultProvider;
-
- /**
- * List of commands and their instance.
- */
- private Map<ProgramName, JCommander> m_commands = Maps.newLinkedHashMap();
-
- /**
- * Alias database for reverse lookup
- */
- private Map<IKey, ProgramName> aliasMap = Maps.newLinkedHashMap();
-
- /**
- * The name of the command after the parsing has run.
- */
- private String m_parsedCommand;
-
- /**
- * The name of command or alias as it was passed to the
- * command line
- */
- private String m_parsedAlias;
-
- private ProgramName m_programName;
-
- private Comparator<? super ParameterDescription> m_parameterDescriptionComparator
- = new Comparator<ParameterDescription>() {
- @Override
- public int compare(ParameterDescription p0, ParameterDescription p1) {
- return p0.getLongestName().compareTo(p1.getLongestName());
- }
- };
-
- private int m_columnSize = 79;
-
- private boolean m_helpWasSpecified;
-
- private List<String> m_unknownArgs = Lists.newArrayList();
- private boolean m_acceptUnknownOptions = false;
- private boolean m_allowParameterOverwriting = false;
-
- private static Console m_console;
-
- /**
- * The factories used to look up string converters.
- */
- private static LinkedList<IStringConverterFactory> CONVERTER_FACTORIES = Lists.newLinkedList();
-
- static {
- CONVERTER_FACTORIES.addFirst(new DefaultConverterFactory());
- };
-
- /**
- * Creates a new un-configured JCommander object.
- */
- public JCommander() {
- }
-
- /**
- * @param object The arg object expected to contain {@link Parameter} annotations.
- */
- public JCommander(Object object) {
- addObject(object);
- createDescriptions();
- }
-
- /**
- * @param object The arg object expected to contain {@link Parameter} annotations.
- * @param bundle The bundle to use for the descriptions. Can be null.
- */
- public JCommander(Object object, @Nullable ResourceBundle bundle) {
- addObject(object);
- setDescriptionsBundle(bundle);
- }
-
- /**
- * @param object The arg object expected to contain {@link Parameter} annotations.
- * @param bundle The bundle to use for the descriptions. Can be null.
- * @param args The arguments to parse (optional).
- */
- public JCommander(Object object, ResourceBundle bundle, String... args) {
- addObject(object);
- setDescriptionsBundle(bundle);
- parse(args);
- }
-
- /**
- * @param object The arg object expected to contain {@link Parameter} annotations.
- * @param args The arguments to parse (optional).
- */
- public JCommander(Object object, String... args) {
- addObject(object);
- parse(args);
- }
-
- public static Console getConsole() {
- if (m_console == null) {
- try {
- Method consoleMethod = System.class.getDeclaredMethod("console", new Class<?>[0]);
- Object console = consoleMethod.invoke(null, new Object[0]);
- m_console = new JDK6Console(console);
- } catch (Throwable t) {
- m_console = new DefaultConsole();
- }
- }
- return m_console;
- }
-
- /**
- * Adds the provided arg object to the set of objects that this commander
- * will parse arguments into.
- *
- * @param object The arg object expected to contain {@link Parameter}
- * annotations. If <code>object</code> is an array or is {@link Iterable},
- * the child objects will be added instead.
- */
- // declared final since this is invoked from constructors
- public final void addObject(Object object) {
- if (object instanceof Iterable) {
- // Iterable
- for (Object o : (Iterable<?>) object) {
- m_objects.add(o);
- }
- } else if (object.getClass().isArray()) {
- // Array
- for (Object o : (Object[]) object) {
- m_objects.add(o);
- }
- } else {
- // Single object
- m_objects.add(object);
- }
- }
-
- /**
- * Sets the {@link ResourceBundle} to use for looking up descriptions.
- * Set this to <code>null</code> to use description text directly.
- */
- // declared final since this is invoked from constructors
- public final void setDescriptionsBundle(ResourceBundle bundle) {
- m_bundle = bundle;
- }
-
- /**
- * Parse and validate the command line parameters.
- */
- public void parse(String... args) {
- parse(true /* validate */, args);
- }
-
- /**
- * Parse the command line parameters without validating them.
- */
- public void parseWithoutValidation(String... args) {
- parse(false /* no validation */, args);
- }
-
- private void parse(boolean validate, String... args) {
- StringBuilder sb = new StringBuilder("Parsing \"");
- sb.append(join(args).append("\"\n with:").append(join(m_objects.toArray())));
- p(sb.toString());
-
- if (m_descriptions == null) createDescriptions();
- initializeDefaultValues();
- parseValues(expandArgs(args), validate);
- if (validate) validateOptions();
- }
-
- private StringBuilder join(Object[] args) {
- StringBuilder result = new StringBuilder();
- for (int i = 0; i < args.length; i++) {
- if (i > 0) result.append(" ");
- result.append(args[i]);
- }
- return result;
- }
-
- private void initializeDefaultValues() {
- if (m_defaultProvider != null) {
- for (ParameterDescription pd : m_descriptions.values()) {
- initializeDefaultValue(pd);
- }
-
- for (Map.Entry<ProgramName, JCommander> entry : m_commands.entrySet()) {
- entry.getValue().initializeDefaultValues();
- }
- }
- }
-
- /**
- * Make sure that all the required parameters have received a value.
- */
- private void validateOptions() {
- // No validation if we found a help parameter
- if (m_helpWasSpecified) {
- return;
- }
-
- if (! m_requiredFields.isEmpty()) {
- StringBuilder missingFields = new StringBuilder();
- for (ParameterDescription pd : m_requiredFields.values()) {
- missingFields.append(pd.getNames()).append(" ");
- }
- throw new ParameterException("The following "
- + pluralize(m_requiredFields.size(), "option is required: ", "options are required: ")
- + missingFields);
- }
-
- if (m_mainParameterDescription != null) {
- if (m_mainParameterDescription.getParameter().required() &&
- !m_mainParameterDescription.isAssigned()) {
- throw new ParameterException("Main parameters are required (\""
- + m_mainParameterDescription.getDescription() + "\")");
- }
- }
- }
-
- private static String pluralize(int quantity, String singular, String plural) {
- return quantity == 1 ? singular : plural;
- }
-
- /**
- * Expand the command line parameters to take @ parameters into account.
- * When @ is encountered, the content of the file that follows is inserted
- * in the command line.
- *
- * @param originalArgv the original command line parameters
- * @return the new and enriched command line parameters
- */
- private String[] expandArgs(String[] originalArgv) {
- List<String> vResult1 = Lists.newArrayList();
-
- //
- // Expand @
- //
- for (String arg : originalArgv) {
-
- if (arg.startsWith("@")) {
- String fileName = arg.substring(1);
- vResult1.addAll(readFile(fileName));
- }
- else {
- List<String> expanded = expandDynamicArg(arg);
- vResult1.addAll(expanded);
- }
- }
-
- // Expand separators
- //
- List<String> vResult2 = Lists.newArrayList();
- for (int i = 0; i < vResult1.size(); i++) {
- String arg = vResult1.get(i);
- String[] v1 = vResult1.toArray(new String[0]);
- if (isOption(v1, arg)) {
- String sep = getSeparatorFor(v1, arg);
- if (! " ".equals(sep)) {
- String[] sp = arg.split("[" + sep + "]", 2);
- for (String ssp : sp) {
- vResult2.add(ssp);
- }
+ public static final String DEBUG_PROPERTY = "jcommander.debug";
+
+ /**
+ * A map to look up parameter description per option name.
+ */
+ private Map<IKey, ParameterDescription> descriptions;
+
+ /**
+ * The objects that contain fields annotated with @Parameter.
+ */
+ private List<Object> objects = Lists.newArrayList();
+
+ private boolean firstTimeMainParameter = true;
+
+ /**
+ * This field/method will contain whatever command line parameter is not an option.
+ * It is expected to be a List<String>.
+ */
+ private Parameterized mainParameter = null;
+
+ /**
+ * The object on which we found the main parameter field.
+ */
+ private Object mainParameterObject;
+
+ /**
+ * The annotation found on the main parameter field.
+ */
+ private Parameter mainParameterAnnotation;
+
+ private ParameterDescription mainParameterDescription;
+
+ /**
+ * A set of all the parameterizeds that are required. During the reflection phase,
+ * this field receives all the fields that are annotated with required=true
+ * and during the parsing phase, all the fields that are assigned a value
+ * are removed from it. At the end of the parsing phase, if it's not empty,
+ * then some required fields did not receive a value and an exception is
+ * thrown.
+ */
+ private Map<Parameterized, ParameterDescription> requiredFields = Maps.newHashMap();
+
+ /**
+ * A map of all the parameterized fields/methods.
+ */
+ private Map<Parameterized, ParameterDescription> fields = Maps.newHashMap();
+
+ /**
+ * List of commands and their instance.
+ */
+ private Map<ProgramName, JCommander> commands = Maps.newLinkedHashMap();
+
+ /**
+ * Alias database for reverse lookup
+ */
+ private Map<IKey, ProgramName> aliasMap = Maps.newLinkedHashMap();
+
+ /**
+ * The name of the command after the parsing has run.
+ */
+ private String parsedCommand;
+
+ /**
+ * The name of command or alias as it was passed to the
+ * command line
+ */
+ private String parsedAlias;
+
+ private ProgramName programName;
+
+ private boolean helpWasSpecified;
+
+ private List<String> unknownArgs = Lists.newArrayList();
+
+ private static Console console;
+
+ private final Options options;
+
+ /**
+ * Options shared with sub commands
+ */
+ private static class Options {
+
+ private ResourceBundle bundle;
+
+ /**
+ * A default provider returns default values for the parameters.
+ */
+ private IDefaultProvider defaultProvider;
+
+ private Comparator<? super ParameterDescription> parameterDescriptionComparator
+ = new Comparator<ParameterDescription>() {
+ @Override
+ public int compare(ParameterDescription p0, ParameterDescription p1) {
+ Parameter a0 = p0.getParameterAnnotation();
+ Parameter a1 = p1.getParameterAnnotation();
+ if (a0 != null && a0.order() != -1 && a1 != null && a1.order() != -1) {
+ return Integer.compare(a0.order(), a1.order());
+ } else if (a0 != null && a0.order() != -1) {
+ return -1;
+ } else if (a1 != null && a1.order() != -1) {
+ return 1;
+ } else {
+ return p0.getLongestName().compareTo(p1.getLongestName());
+ }
+ }
+ };
+ private int columnSize = 79;
+ private boolean acceptUnknownOptions = false;
+ private boolean allowParameterOverwriting = false;
+ private boolean expandAtSign = true;
+ private int verbose = 0;
+ private boolean caseSensitiveOptions = true;
+ private boolean allowAbbreviatedOptions = false;
+ /**
+ * The factories used to look up string converters.
+ */
+ private final List<IStringConverterInstanceFactory> converterInstanceFactories = new CopyOnWriteArrayList<>();
+ private Charset atFileCharset = Charset.defaultCharset();
+ }
+
+ private JCommander(Options options) {
+ if (options == null) {
+ throw new NullPointerException("options");
+ }
+ this.options = options;
+ addConverterFactory(new DefaultConverterFactory());
+ }
+
+ /**
+ * Creates a new un-configured JCommander object.
+ */
+ public JCommander() {
+ this(new Options());
+ }
+
+ /**
+ * @param object The arg object expected to contain {@link Parameter} annotations.
+ */
+ public JCommander(Object object) {
+ this(object, (ResourceBundle) null);
+ }
+
+ /**
+ * @param object The arg object expected to contain {@link Parameter} annotations.
+ * @param bundle The bundle to use for the descriptions. Can be null.
+ */
+ public JCommander(Object object, @Nullable ResourceBundle bundle) {
+ this(object, bundle, (String[]) null);
+ }
+
+ /**
+ * @param object The arg object expected to contain {@link Parameter} annotations.
+ * @param bundle The bundle to use for the descriptions. Can be null.
+ * @param args The arguments to parse (optional).
+ */
+ public JCommander(Object object, @Nullable ResourceBundle bundle, String... args) {
+ this();
+ addObject(object);
+ if (bundle != null) {
+ setDescriptionsBundle(bundle);
+ }
+ createDescriptions();
+ if (args != null) {
+ parse(args);
+ }
+ }
+
+ /**
+ * @param object The arg object expected to contain {@link Parameter} annotations.
+ * @param args The arguments to parse (optional).
+ *
+ * @deprecated Construct a JCommander instance first and then call parse() on it.
+ */
+ @Deprecated()
+ public JCommander(Object object, String... args) {
+ this(object);
+ parse(args);
+ }
+
+ /**
+ * Disables expanding {@code @file}.
+ *
+ * JCommander supports the {@code @file} syntax, which allows you to put all your options
+ * into a file and pass this file as parameter @param expandAtSign whether to expand {@code @file}.
+ */
+ public void setExpandAtSign(boolean expandAtSign) {
+ options.expandAtSign = expandAtSign;
+ }
+
+ public static Console getConsole() {
+ if (console == null) {
+ try {
+ Method consoleMethod = System.class.getDeclaredMethod("console");
+ Object console = consoleMethod.invoke(null);
+ JCommander.console = new JDK6Console(console);
+ } catch (Throwable t) {
+ console = new DefaultConsole();
+ }
+ }
+ return console;
+ }
+
+ /**
+ * Adds the provided arg object to the set of objects that this commander
+ * will parse arguments into.
+ *
+ * @param object The arg object expected to contain {@link Parameter}
+ * annotations. If <code>object</code> is an array or is {@link Iterable},
+ * the child objects will be added instead.
+ */
+ // declared final since this is invoked from constructors
+ public final void addObject(Object object) {
+ if (object instanceof Iterable) {
+ // Iterable
+ for (Object o : (Iterable<?>) object) {
+ objects.add(o);
+ }
+ } else if (object.getClass().isArray()) {
+ // Array
+ for (Object o : (Object[]) object) {
+ objects.add(o);
+ }
} else {
- vResult2.add(arg);
+ // Single object
+ objects.add(object);
}
- } else {
- vResult2.add(arg);
- }
}
- return vResult2.toArray(new String[vResult2.size()]);
- }
+ /**
+ * Sets the {@link ResourceBundle} to use for looking up descriptions.
+ * Set this to <code>null</code> to use description text directly.
+ */
+ // declared final since this is invoked from constructors
+ public final void setDescriptionsBundle(ResourceBundle bundle) {
+ options.bundle = bundle;
+ }
- private List<String> expandDynamicArg(String arg) {
- for (ParameterDescription pd : m_descriptions.values()) {
- if (pd.isDynamicParameter()) {
- for (String name : pd.getParameter().names()) {
- if (arg.startsWith(name) && !arg.equals(name)) {
- return Arrays.asList(name, arg.substring(name.length()));
- }
+ /**
+ * Parse and validate the command line parameters.
+ */
+ public void parse(String... args) {
+ try {
+ parse(true /* validate */, args);
+ } catch(ParameterException ex) {
+ ex.setJCommander(this);
+ throw ex;
}
- }
}
- return Arrays.asList(arg);
- }
+ /**
+ * Parse the command line parameters without validating them.
+ */
+ public void parseWithoutValidation(String... args) {
+ parse(false /* no validation */, args);
+ }
- private boolean isOption(String[] args, String arg) {
- String prefixes = getOptionPrefixes(args, arg);
- return arg.length() > 0 && prefixes.indexOf(arg.charAt(0)) >= 0;
- }
+ private void parse(boolean validate, String... args) {
+ StringBuilder sb = new StringBuilder("Parsing \"");
+ sb.append(join(args).append("\"\n with:").append(join(objects.toArray())));
+ p(sb.toString());
- private ParameterDescription getPrefixDescriptionFor(String arg) {
- for (Map.Entry<IKey, ParameterDescription> es : m_descriptions.entrySet()) {
- if (arg.startsWith(es.getKey().getName())) return es.getValue();
+ if (descriptions == null) createDescriptions();
+ initializeDefaultValues();
+ parseValues(expandArgs(args), validate);
+ if (validate) validateOptions();
}
- return null;
- }
+ private StringBuilder join(Object[] args) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < args.length; i++) {
+ if (i > 0) result.append(" ");
+ result.append(args[i]);
+ }
+ return result;
+ }
- /**
- * If arg is an option, we can look it up directly, but if it's a value,
- * we need to find the description for the option that precedes it.
- */
- private ParameterDescription getDescriptionFor(String[] args, String arg) {
- ParameterDescription result = getPrefixDescriptionFor(arg);
- if (result != null) return result;
+ private void initializeDefaultValues() {
+ if (options.defaultProvider != null) {
+ for (ParameterDescription pd : descriptions.values()) {
+ initializeDefaultValue(pd);
+ }
- for (String a : args) {
- ParameterDescription pd = getPrefixDescriptionFor(arg);
- if (pd != null) result = pd;
- if (a.equals(arg)) return result;
+ for (Map.Entry<ProgramName, JCommander> entry : commands.entrySet()) {
+ entry.getValue().initializeDefaultValues();
+ }
+ }
}
- throw new ParameterException("Unknown parameter: " + arg);
- }
+ /**
+ * Make sure that all the required parameters have received a value.
+ */
+ private void validateOptions() {
+ // No validation if we found a help parameter
+ if (helpWasSpecified) {
+ return;
+ }
- private String getSeparatorFor(String[] args, String arg) {
- ParameterDescription pd = getDescriptionFor(args, arg);
+ if (!requiredFields.isEmpty()) {
+ List<String> missingFields = new ArrayList<>();
+ for (ParameterDescription pd : requiredFields.values()) {
+ missingFields.add("[" + String.join(" | ", pd.getParameter().names()) + "]");
+ }
+ String message = String.join(", ", missingFields);
+ throw new ParameterException("The following "
+ + pluralize(requiredFields.size(), "option is required: ", "options are required: ")
+ + message);
+ }
+
+ if (mainParameterDescription != null) {
+ if (mainParameterDescription.getParameter().required() &&
+ !mainParameterDescription.isAssigned()) {
+ throw new ParameterException("Main parameters are required (\""
+ + mainParameterDescription.getDescription() + "\")");
+ }
+ }
+ }
- // Could be null if only main parameters were passed
- if (pd != null) {
- Parameters p = pd.getObject().getClass().getAnnotation(Parameters.class);
- if (p != null) return p.separators();
+ private static String pluralize(int quantity, String singular, String plural) {
+ return quantity == 1 ? singular : plural;
}
- return " ";
- }
+ /**
+ * Expand the command line parameters to take @ parameters into account.
+ * When @ is encountered, the content of the file that follows is inserted
+ * in the command line.
+ *
+ * @param originalArgv the original command line parameters
+ * @return the new and enriched command line parameters
+ */
+ private String[] expandArgs(String[] originalArgv) {
+ List<String> vResult1 = Lists.newArrayList();
- private String getOptionPrefixes(String[] args, String arg) {
- ParameterDescription pd = getDescriptionFor(args, arg);
+ //
+ // Expand @
+ //
+ for (String arg : originalArgv) {
- // Could be null if only main parameters were passed
- if (pd != null) {
- Parameters p = pd.getObject().getClass()
- .getAnnotation(Parameters.class);
- if (p != null) return p.optionPrefixes();
+ if (arg.startsWith("@") && options.expandAtSign) {
+ String fileName = arg.substring(1);
+ vResult1.addAll(readFile(fileName));
+ } else {
+ List<String> expanded = expandDynamicArg(arg);
+ vResult1.addAll(expanded);
+ }
+ }
+
+ // Expand separators
+ //
+ List<String> vResult2 = Lists.newArrayList();
+ for (String arg : vResult1) {
+ if (isOption(arg)) {
+ String sep = getSeparatorFor(arg);
+ if (!" ".equals(sep)) {
+ String[] sp = arg.split("[" + sep + "]", 2);
+ for (String ssp : sp) {
+ vResult2.add(ssp);
+ }
+ } else {
+ vResult2.add(arg);
+ }
+ } else {
+ vResult2.add(arg);
+ }
+ }
+
+ return vResult2.toArray(new String[vResult2.size()]);
}
- String result = Parameters.DEFAULT_OPTION_PREFIXES;
- // See if any of the objects contains a @Parameters(optionPrefixes)
- StringBuilder sb = new StringBuilder();
- for (Object o : m_objects) {
- Parameters p = o.getClass().getAnnotation(Parameters.class);
- if (p != null && !Parameters.DEFAULT_OPTION_PREFIXES.equals(p.optionPrefixes())) {
- sb.append(p.optionPrefixes());
- }
+ private List<String> expandDynamicArg(String arg) {
+ for (ParameterDescription pd : descriptions.values()) {
+ if (pd.isDynamicParameter()) {
+ for (String name : pd.getParameter().names()) {
+ if (arg.startsWith(name) && !arg.equals(name)) {
+ return Arrays.asList(name, arg.substring(name.length()));
+ }
+ }
+ }
+ }
+
+ return Arrays.asList(arg);
}
- if (! Strings.isStringEmpty(sb.toString())) {
- result = sb.toString();
+ private boolean matchArg(String arg, IKey key) {
+ String kn = options.caseSensitiveOptions
+ ? key.getName()
+ : key.getName().toLowerCase();
+ if (options.allowAbbreviatedOptions) {
+ if (kn.startsWith(arg)) return true;
+ } else {
+ ParameterDescription pd = descriptions.get(key);
+ if (pd != null) {
+ // It's an option. If the option has a separator (e.g. -author==foo) then
+ // we only do a beginsWith match
+ String separator = getSeparatorFor(arg);
+ if (! " ".equals(separator)) {
+ if (arg.startsWith(kn)) return true;
+ } else {
+ if (kn.equals(arg)) return true;
+ }
+ } else {
+ // It's a command do a strict equality check
+ if (kn.equals(arg)) return true;
+ }
+ }
+ return false;
}
- return result;
- }
+ private boolean isOption(String passedArg) {
+ if (options.acceptUnknownOptions) return true;
- /**
- * Reads the file specified by filename and returns the file content as a string.
- * End of lines are replaced by a space.
- *
- * @param fileName the command line filename
- * @return the file content as a string.
- */
- private static List<String> readFile(String fileName) {
- List<String> result = Lists.newArrayList();
+ String arg = options.caseSensitiveOptions ? passedArg : passedArg.toLowerCase();
- try {
- BufferedReader bufRead = new BufferedReader(new FileReader(fileName));
+ for (IKey key : descriptions.keySet()) {
+ if (matchArg(arg, key)) return true;
+ }
+ for (IKey key : commands.keySet()) {
+ if (matchArg(arg, key)) return true;
+ }
- String line;
+ return false;
+ }
- // Read through file one line at time. Print line # and line
- while ((line = bufRead.readLine()) != null) {
- // Allow empty lines and # comments in these at files
- if (line.length() > 0 && ! line.trim().startsWith("#")) {
- result.add(line);
+ private ParameterDescription getPrefixDescriptionFor(String arg) {
+ for (Map.Entry<IKey, ParameterDescription> es : descriptions.entrySet()) {
+ if (arg.startsWith(es.getKey().getName())) return es.getValue();
}
- }
- bufRead.close();
+ return null;
}
- catch (IOException e) {
- throw new ParameterException("Could not read file " + fileName + ": " + e);
+
+ /**
+ * If arg is an option, we can look it up directly, but if it's a value,
+ * we need to find the description for the option that precedes it.
+ */
+ private ParameterDescription getDescriptionFor(String arg) {
+ return getPrefixDescriptionFor(arg);
}
- return result;
- }
+ private String getSeparatorFor(String arg) {
+ ParameterDescription pd = getDescriptionFor(arg);
- /**
- * Remove spaces at both ends and handle double quotes.
- */
- private static String trim(String string) {
- String result = string.trim();
- if (result.startsWith("\"") && result.endsWith("\"") && result.length() > 1) {
- result = result.substring(1, result.length() - 1);
+ // Could be null if only main parameters were passed
+ if (pd != null) {
+ Parameters p = pd.getObject().getClass().getAnnotation(Parameters.class);
+ if (p != null) return p.separators();
+ }
+
+ return " ";
}
- return result;
- }
- /**
- * Create the ParameterDescriptions for all the \@Parameter found.
- */
- private void createDescriptions() {
- m_descriptions = Maps.newHashMap();
+ /**
+ * Reads the file specified by filename and returns the file content as a string.
+ * End of lines are replaced by a space.
+ *
+ * @param fileName the command line filename
+ * @return the file content as a string.
+ */
+ private List<String> readFile(String fileName) {
+ List<String> result = Lists.newArrayList();
+
+ try (BufferedReader bufRead = Files.newBufferedReader(Paths.get(fileName), options.atFileCharset)) {
+ String line;
+ // Read through file one line at time. Print line # and line
+ while ((line = bufRead.readLine()) != null) {
+ // Allow empty lines and # comments in these at files
+ if (line.length() > 0 && !line.trim().startsWith("#")) {
+ result.add(line);
+ }
+ }
+ } catch (IOException e) {
+ throw new ParameterException("Could not read file " + fileName + ": " + e);
+ }
- for (Object object : m_objects) {
- addDescription(object);
+ return result;
}
- }
- private void addDescription(Object object) {
- Class<?> cls = object.getClass();
+ /**
+ * Remove spaces at both ends and handle double quotes.
+ */
+ private static String trim(String string) {
+ String result = string.trim();
+ if (result.startsWith("\"") && result.endsWith("\"") && result.length() > 1) {
+ result = result.substring(1, result.length() - 1);
+ }
+ return result;
+ }
- List<Parameterized> parameterizeds = Parameterized.parseArg(object);
- for (Parameterized parameterized : parameterizeds) {
- WrappedParameter wp = parameterized.getWrappedParameter();
- if (wp != null && wp.getParameter() != null) {
- Parameter annotation = wp.getParameter();
- //
- // @Parameter
- //
- Parameter p = annotation;
- if (p.names().length == 0) {
- p("Found main parameter:" + parameterized);
- if (m_mainParameter != null) {
- throw new ParameterException("Only one @Parameter with no names attribute is"
- + " allowed, found:" + m_mainParameter + " and " + parameterized);
- }
- m_mainParameter = parameterized;
- m_mainParameterObject = object;
- m_mainParameterAnnotation = p;
- m_mainParameterDescription =
- new ParameterDescription(object, p, parameterized, m_bundle, this);
+ /**
+ * Create the ParameterDescriptions for all the \@Parameter found.
+ */
+ private void createDescriptions() {
+ descriptions = Maps.newHashMap();
+
+ for (Object object : objects) {
+ addDescription(object);
+ }
+ }
+
+ private void addDescription(Object object) {
+ Class<?> cls = object.getClass();
+
+ List<Parameterized> parameterizeds = Parameterized.parseArg(object);
+ for (Parameterized parameterized : parameterizeds) {
+ WrappedParameter wp = parameterized.getWrappedParameter();
+ if (wp != null && wp.getParameter() != null) {
+ Parameter annotation = wp.getParameter();
+ //
+ // @Parameter
+ //
+ Parameter p = annotation;
+ if (p.names().length == 0) {
+ p("Found main parameter:" + parameterized);
+ if (mainParameter != null) {
+ throw new ParameterException("Only one @Parameter with no names attribute is"
+ + " allowed, found:" + mainParameter + " and " + parameterized);
+ }
+ mainParameter = parameterized;
+ mainParameterObject = object;
+ mainParameterAnnotation = p;
+ mainParameterDescription =
+ new ParameterDescription(object, p, parameterized, options.bundle, this);
+ } else {
+ ParameterDescription pd =
+ new ParameterDescription(object, p, parameterized, options.bundle, this);
+ for (String name : p.names()) {
+ if (descriptions.containsKey(new StringKey(name))) {
+ throw new ParameterException("Found the option " + name + " multiple times");
+ }
+ p("Adding description for " + name);
+ fields.put(parameterized, pd);
+ descriptions.put(new StringKey(name), pd);
+
+ if (p.required()) requiredFields.put(parameterized, pd);
+ }
+ }
+ } else if (parameterized.getDelegateAnnotation() != null) {
+ //
+ // @ParametersDelegate
+ //
+ Object delegateObject = parameterized.get(object);
+ if (delegateObject == null) {
+ throw new ParameterException("Delegate field '" + parameterized.getName()
+ + "' cannot be null.");
+ }
+ addDescription(delegateObject);
+ } else if (wp != null && wp.getDynamicParameter() != null) {
+ //
+ // @DynamicParameter
+ //
+ DynamicParameter dp = wp.getDynamicParameter();
+ for (String name : dp.names()) {
+ if (descriptions.containsKey(name)) {
+ throw new ParameterException("Found the option " + name + " multiple times");
+ }
+ p("Adding description for " + name);
+ ParameterDescription pd =
+ new ParameterDescription(object, dp, parameterized, options.bundle, this);
+ fields.put(parameterized, pd);
+ descriptions.put(new StringKey(name), pd);
+
+ if (dp.required()) requiredFields.put(parameterized, pd);
+ }
+ }
+ }
+ }
+
+ private void initializeDefaultValue(ParameterDescription pd) {
+ for (String optionName : pd.getParameter().names()) {
+ String def = options.defaultProvider.getDefaultValueFor(optionName);
+ if (def != null) {
+ p("Initializing " + optionName + " with default value:" + def);
+ pd.addValue(def, true /* default */);
+ // remove the parameter from the list of fields to be required
+ requiredFields.remove(pd.getParameterized());
+ return;
+ }
+ }
+ }
+
+ /**
+ * Main method that parses the values and initializes the fields accordingly.
+ */
+ private void parseValues(String[] args, boolean validate) {
+ // This boolean becomes true if we encounter a command, which indicates we need
+ // to stop parsing (the parsing of the command will be done in a sub JCommander
+ // object)
+ boolean commandParsed = false;
+ int i = 0;
+ boolean isDashDash = false; // once we encounter --, everything goes into the main parameter
+ while (i < args.length && !commandParsed) {
+ String arg = args[i];
+ String a = trim(arg);
+ args[i] = a;
+ p("Parsing arg: " + a);
+
+ JCommander jc = findCommandByAlias(arg);
+ int increment = 1;
+ if (!isDashDash && !"--".equals(a) && isOption(a) && jc == null) {
+ //
+ // Option
+ //
+ ParameterDescription pd = findParameterDescription(a);
+
+ if (pd != null) {
+ if (pd.getParameter().password()) {
+ increment = processPassword(args, i, pd, validate);
+ } else {
+ if (pd.getParameter().variableArity()) {
+ //
+ // Variable arity?
+ //
+ increment = processVariableArity(args, i, pd, validate);
+ } else {
+ //
+ // Regular option
+ //
+ Class<?> fieldType = pd.getParameterized().getType();
+
+ // Boolean, set to true as soon as we see it, unless it specified
+ // an arity of 1, in which case we need to read the next value
+ if ((fieldType == boolean.class || fieldType == Boolean.class)
+ && pd.getParameter().arity() == -1) {
+ // Flip the value this boolean was initialized with
+ Boolean value = (Boolean) pd.getParameterized().get(pd.getObject());
+ pd.addValue(value ? "false" : "true");
+ requiredFields.remove(pd.getParameterized());
+ } else {
+ increment = processFixedArity(args, i, pd, validate, fieldType);
+ }
+ // If it's a help option, remember for later
+ if (pd.isHelp()) {
+ helpWasSpecified = true;
+ }
+ }
+ }
+ } else {
+ if (options.acceptUnknownOptions) {
+ unknownArgs.add(arg);
+ i++;
+ while (i < args.length && !isOption(args[i])) {
+ unknownArgs.add(args[i++]);
+ }
+ increment = 0;
+ } else {
+ throw new ParameterException("Unknown option: " + arg);
+ }
+ }
+ } else {
+ //
+ // Main parameter
+ //
+ if ("--".equals(arg) && !isDashDash) {
+ isDashDash = true;
+ }
+ else if (commands.isEmpty()) {
+ //
+ // Regular (non-command) parsing
+ //
+ List mp = getMainParameter(arg);
+ String value = a; // If there's a non-quoted version, prefer that one
+ Object convertedValue = value;
+
+ if (mainParameter.getGenericType() instanceof ParameterizedType) {
+ ParameterizedType p = (ParameterizedType) mainParameter.getGenericType();
+ Type cls = p.getActualTypeArguments()[0];
+ if (cls instanceof Class) {
+ convertedValue = convertValue(mainParameter, (Class) cls, null, value);
+ }
+ }
+
+ for(final Class<? extends IParameterValidator> validator : mainParameterAnnotation.validateWith() ) {
+ ParameterDescription.validateParameter(mainParameterDescription,
+ validator,
+ "Default", value);
+ }
+
+ mainParameterDescription.setAssigned(true);
+ mp.add(convertedValue);
+ } else {
+ //
+ // Command parsing
+ //
+ if (jc == null && validate) {
+ throw new MissingCommandException("Expected a command, got " + arg, arg);
+ } else if (jc != null) {
+ parsedCommand = jc.programName.name;
+ parsedAlias = arg; //preserve the original form
+
+ // Found a valid command, ask it to parse the remainder of the arguments.
+ // Setting the boolean commandParsed to true will force the current
+ // loop to end.
+ jc.parse(validate, subArray(args, i + 1));
+ commandParsed = true;
+ }
+ }
+ }
+ i += increment;
+ }
+
+ // Mark the parameter descriptions held in fields as assigned
+ for (ParameterDescription parameterDescription : descriptions.values()) {
+ if (parameterDescription.isAssigned()) {
+ fields.get(parameterDescription.getParameterized()).setAssigned(true);
+ }
+ }
+
+ }
+
+ private class DefaultVariableArity implements IVariableArity {
+
+ @Override
+ public int processVariableArity(String optionName, String[] options) {
+ int i = 0;
+ while (i < options.length && !isOption(options[i])) {
+ i++;
+ }
+ return i;
+ }
+ }
+
+ private final IVariableArity DEFAULT_VARIABLE_ARITY = new DefaultVariableArity();
+
+ private final int determineArity(String[] args, int index, ParameterDescription pd, IVariableArity va) {
+ List<String> currentArgs = Lists.newArrayList();
+ for (int j = index + 1; j < args.length; j++) {
+ currentArgs.add(args[j]);
+ }
+ return va.processVariableArity(pd.getParameter().names()[0],
+ currentArgs.toArray(new String[0]));
+ }
+
+ /**
+ * @return the number of options that were processed.
+ */
+ private int processPassword(String[] args, int index, ParameterDescription pd, boolean validate) {
+ final int passwordArity = determineArity(args, index, pd, DEFAULT_VARIABLE_ARITY);
+ if (passwordArity == 0) {
+ // password option with password not specified, use the Console to retrieve the password
+ char[] password = readPassword(pd.getDescription(), pd.getParameter().echoInput());
+ pd.addValue(new String(password));
+ requiredFields.remove(pd.getParameterized());
+ return 1;
+ } else if (passwordArity == 1) {
+ // password option with password specified
+ return processFixedArity(args, index, pd, validate, List.class, 1);
} else {
- ParameterDescription pd =
- new ParameterDescription(object, p, parameterized, m_bundle, this);
- for (String name : p.names()) {
- if (m_descriptions.containsKey(new StringKey(name))) {
- throw new ParameterException("Found the option " + name + " multiple times");
+ throw new ParameterException("Password parameter must have at most 1 argument.");
+ }
+ }
+
+ /**
+ * @return the number of options that were processed.
+ */
+ private int processVariableArity(String[] args, int index, ParameterDescription pd, boolean validate) {
+ Object arg = pd.getObject();
+ IVariableArity va;
+ if (!(arg instanceof IVariableArity)) {
+ va = DEFAULT_VARIABLE_ARITY;
+ } else {
+ va = (IVariableArity) arg;
+ }
+
+ int arity = determineArity(args, index, pd, va);
+ int result = processFixedArity(args, index, pd, validate, List.class, arity);
+ return result;
+ }
+
+ private int processFixedArity(String[] args, int index, ParameterDescription pd, boolean validate,
+ Class<?> fieldType) {
+ // Regular parameter, use the arity to tell use how many values
+ // we need to consume
+ int arity = pd.getParameter().arity();
+ int n = (arity != -1 ? arity : 1);
+
+ return processFixedArity(args, index, pd, validate, fieldType, n);
+ }
+
+ private int processFixedArity(String[] args, int originalIndex, ParameterDescription pd, boolean validate,
+ Class<?> fieldType, int arity) {
+ int index = originalIndex;
+ String arg = args[index];
+ // Special case for boolean parameters of arity 0
+ if (arity == 0 &&
+ (Boolean.class.isAssignableFrom(fieldType)
+ || boolean.class.isAssignableFrom(fieldType))) {
+ // Flip the value this boolean was initialized with
+ Boolean value = (Boolean) pd.getParameterized().get(pd.getObject());
+ pd.addValue(value ? "false" : "true");
+ requiredFields.remove(pd.getParameterized());
+ } else if (arity == 0) {
+ throw new ParameterException("Expected a value after parameter " + arg);
+
+ } else if (index < args.length - 1) {
+ int offset = "--".equals(args[index + 1]) ? 1 : 0;
+
+ Object finalValue = null;
+ if (index + arity < args.length) {
+ for (int j = 1; j <= arity; j++) {
+ String value = trim(args[index + j + offset]);
+ finalValue = pd.addValue(arg, value, false, validate, j - 1);
+ requiredFields.remove(pd.getParameterized());
+ }
+
+ if (finalValue != null && validate) {
+ pd.validateValueParameter(arg, finalValue);
+ }
+ index += arity + offset;
+ } else {
+ throw new ParameterException("Expected " + arity + " values after " + arg);
}
- p("Adding description for " + name);
- m_fields.put(parameterized, pd);
- m_descriptions.put(new StringKey(name), pd);
+ } else {
+ throw new ParameterException("Expected a value after parameter " + arg);
+ }
+
+ return arity + 1;
+ }
+
+ /**
+ * Invoke Console.readPassword through reflection to avoid depending
+ * on Java 6.
+ */
+ private char[] readPassword(String description, boolean echoInput) {
+ getConsole().print(description + ": ");
+ return getConsole().readPassword(echoInput);
+ }
- if (p.required()) m_requiredFields.put(parameterized, pd);
- }
+ private String[] subArray(String[] args, int index) {
+ int l = args.length - index;
+ String[] result = new String[l];
+ System.arraycopy(args, index, result, 0, l);
+
+ return result;
+ }
+
+ /**
+ * @return the field that's meant to receive all the parameters that are not options.
+ *
+ * @param arg the arg that we're about to add (only passed here to output a meaningful
+ * error message).
+ */
+ private List<?> getMainParameter(String arg) {
+ if (mainParameter == null) {
+ throw new ParameterException(
+ "Was passed main parameter '" + arg + "' but no main parameter was defined in your arg class");
}
- } else if (parameterized.getDelegateAnnotation() != null) {
+
+ List<?> result = (List<?>) mainParameter.get(mainParameterObject);
+ if (result == null) {
+ result = Lists.newArrayList();
+ if (!List.class.isAssignableFrom(mainParameter.getType())) {
+ throw new ParameterException("Main parameter field " + mainParameter
+ + " needs to be of type List, not " + mainParameter.getType());
+ }
+ mainParameter.set(mainParameterObject, result);
+ }
+ if (firstTimeMainParameter) {
+ result.clear();
+ firstTimeMainParameter = false;
+ }
+ return result;
+ }
+
+ public String getMainParameterDescription() {
+ if (descriptions == null) createDescriptions();
+ return mainParameterAnnotation != null ? mainParameterAnnotation.description()
+ : null;
+ }
+
+ /**
+ * Set the program name (used only in the usage).
+ */
+ public void setProgramName(String name) {
+ setProgramName(name, new String[0]);
+ }
+
+ /**
+ * Get the program name (used only in the usage).
+ */
+ public String getProgramName(){
+ return programName == null ? null : programName.getName();
+ }
+
+ /**
+ * Set the program name
+ *
+ * @param name program name
+ * @param aliases aliases to the program name
+ */
+ public void setProgramName(String name, String... aliases) {
+ programName = new ProgramName(name, Arrays.asList(aliases));
+ }
+
+ /**
+ * Display the usage for this command.
+ */
+ public void usage(String commandName) {
+ StringBuilder sb = new StringBuilder();
+ usage(commandName, sb);
+ getConsole().println(sb.toString());
+ }
+
+ /**
+ * Store the help for the command in the passed string builder.
+ */
+ public void usage(String commandName, StringBuilder out) {
+ usage(commandName, out, "");
+ }
+
+ /**
+ * Store the help for the command in the passed string builder, indenting
+ * every line with "indent".
+ */
+ public void usage(String commandName, StringBuilder out, String indent) {
+ String description = getCommandDescription(commandName);
+ JCommander jc = findCommandByAlias(commandName);
+ if (description != null) {
+ out.append(indent).append(description);
+ out.append("\n");
+ }
+ jc.usage(out, indent);
+ }
+
+ /**
+ * @return the description of the command.
+ */
+ public String getCommandDescription(String commandName) {
+ JCommander jc = findCommandByAlias(commandName);
+ if (jc == null) {
+ throw new ParameterException("Asking description for unknown command: " + commandName);
+ }
+
+ Object arg = jc.getObjects().get(0);
+ Parameters p = arg.getClass().getAnnotation(Parameters.class);
+ ResourceBundle bundle = null;
+ String result = null;
+ if (p != null) {
+ result = p.commandDescription();
+ String bundleName = p.resourceBundle();
+ if (!"".equals(bundleName)) {
+ bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
+ } else {
+ bundle = options.bundle;
+ }
+
+ if (bundle != null) {
+ String descriptionKey = p.commandDescriptionKey();
+ if (!"".equals(descriptionKey)) {
+ result = getI18nString(bundle, descriptionKey, p.commandDescription());
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @return The internationalized version of the string if available, otherwise
+ * return def.
+ */
+ private String getI18nString(ResourceBundle bundle, String key, String def) {
+ String s = bundle != null ? bundle.getString(key) : null;
+ return s != null ? s : def;
+ }
+
+ /**
+ * Display the help on System.out.
+ */
+ public void usage() {
+ StringBuilder sb = new StringBuilder();
+ usage(sb);
+ getConsole().println(sb.toString());
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private JCommander jCommander = new JCommander();
+ private String[] args = null;
+
+ public Builder() {
+ }
+
+ /**
+ * Adds the provided arg object to the set of objects that this commander
+ * will parse arguments into.
+ *
+ * @param o The arg object expected to contain {@link Parameter}
+ * annotations. If <code>object</code> is an array or is {@link Iterable},
+ * the child objects will be added instead.
+ */
+ public Builder addObject(Object o) {
+ jCommander.addObject(o);
+ return this;
+ }
+
+ /**
+ * Sets the {@link ResourceBundle} to use for looking up descriptions.
+ * Set this to <code>null</code> to use description text directly.
+ */
+ public Builder resourceBundle(ResourceBundle bundle) {
+ jCommander.setDescriptionsBundle(bundle);
+ return this;
+ }
+
+ public Builder args(String[] args) {
+ this.args = args;
+ return this;
+ }
+
+ /**
+ * Disables expanding {@code @file}.
+ *
+ * JCommander supports the {@code @file} syntax, which allows you to put all your options
+ * into a file and pass this file as parameter @param expandAtSign whether to expand {@code @file}.
+ */
+ public Builder expandAtSign(Boolean expand) {
+ jCommander.setExpandAtSign(expand);
+ return this;
+ }
+
+ /**
+ * Set the program name (used only in the usage).
+ */
+ public Builder programName(String name) {
+ jCommander.setProgramName(name);
+ return this;
+ }
+
+ public Builder columnSize(int columnSize) {
+ jCommander.setColumnSize(columnSize);
+ return this;
+ }
+
+ /**
+ * Define the default provider for this instance.
+ */
+ public Builder defaultProvider(IDefaultProvider provider) {
+ jCommander.setDefaultProvider(provider);
+ return this;
+ }
+
+ /**
+ * Adds a factory to lookup string converters. The added factory is used prior to previously added factories.
+ * @param factory the factory determining string converters
+ */
+ public Builder addConverterFactory(IStringConverterFactory factory) {
+ jCommander.addConverterFactory(factory);
+ return this;
+ }
+
+ public Builder verbose(int verbose) {
+ jCommander.setVerbose(verbose);
+ return this;
+ }
+
+ public Builder allowAbbreviatedOptions(boolean b) {
+ jCommander.setAllowAbbreviatedOptions(b);
+ return this;
+ }
+
+ public Builder acceptUnknownOptions(boolean b) {
+ jCommander.setAcceptUnknownOptions(b);
+ return this;
+ }
+
+ public Builder allowParameterOverwriting(boolean b) {
+ jCommander.setAllowParameterOverwriting(b);
+ return this;
+ }
+
+ public Builder atFileCharset(Charset charset) {
+ jCommander.setAtFileCharset(charset);
+ return this;
+ }
+
+ public Builder addConverterInstanceFactory(IStringConverterInstanceFactory factory) {
+ jCommander.addConverterInstanceFactory(factory);
+ return this;
+ }
+
+ public Builder addCommand(Object command) {
+ jCommander.addCommand(command);
+ return this;
+ }
+
+ public Builder addCommand(String name, Object command, String... aliases) {
+ jCommander.addCommand(name, command, aliases);
+ return this;
+ }
+
+ public JCommander build() {
+ if (args != null) {
+ jCommander.parse(args);
+ }
+ return jCommander;
+ }
+ }
+
+
+ /**
+ * Store the help in the passed string builder.
+ */
+ public void usage(StringBuilder out) {
+ usage(out, "");
+ }
+
+ public void usage(StringBuilder out, String indent) {
+ if (descriptions == null) createDescriptions();
+ boolean hasCommands = !commands.isEmpty();
+ boolean hasOptions = !descriptions.isEmpty();
+
+ //indenting
+ int descriptionIndent = 6;
+ int indentCount = indent.length() + descriptionIndent;
+
//
- // @ParametersDelegate
+ // First line of the usage
//
- Object delegateObject = parameterized.get(object);
- if (delegateObject == null){
- throw new ParameterException("Delegate field '" + parameterized.getName()
- + "' cannot be null.");
+ String programName = this.programName != null ? this.programName.getDisplayName() : "<main class>";
+ StringBuilder mainLine = new StringBuilder();
+ mainLine.append(indent).append("Usage: ").append(programName);
+ if (hasOptions) mainLine.append(" [options]");
+ if (hasCommands) mainLine.append(indent).append(" [command] [command options]");
+ if (mainParameterDescription != null) {
+ mainLine.append(" ").append(mainParameterDescription.getDescription());
}
- addDescription(delegateObject);
- } else if (wp != null && wp.getDynamicParameter() != null) {
+ wrapDescription(out, indentCount, mainLine.toString());
+ out.append("\n");
+
//
- // @DynamicParameter
+ // Align the descriptions at the "longestName" column
//
- DynamicParameter dp = wp.getDynamicParameter();
- for (String name : dp.names()) {
- if (m_descriptions.containsKey(name)) {
- throw new ParameterException("Found the option " + name + " multiple times");
- }
- p("Adding description for " + name);
- ParameterDescription pd =
- new ParameterDescription(object, dp, parameterized, m_bundle, this);
- m_fields.put(parameterized, pd);
- m_descriptions.put(new StringKey(name), pd);
-
- if (dp.required()) m_requiredFields.put(parameterized, pd);
- }
- }
- }
-
-// while (!Object.class.equals(cls)) {
-// for (Field f : cls.getDeclaredFields()) {
-// p("Field:" + cls.getSimpleName() + "." + f.getName());
-// f.setAccessible(true);
-// Annotation annotation = f.getAnnotation(Parameter.class);
-// Annotation delegateAnnotation = f.getAnnotation(ParametersDelegate.class);
-// Annotation dynamicParameter = f.getAnnotation(DynamicParameter.class);
-// if (annotation != null) {
-// //
-// // @Parameter
-// //
-// Parameter p = (Parameter) annotation;
-// if (p.names().length == 0) {
-// p("Found main parameter:" + f);
-// if (m_mainParameterField != null) {
-// throw new ParameterException("Only one @Parameter with no names attribute is"
-// + " allowed, found:" + m_mainParameterField + " and " + f);
-// }
-// m_mainParameterField = parameterized;
-// m_mainParameterObject = object;
-// m_mainParameterAnnotation = p;
-// m_mainParameterDescription = new ParameterDescription(object, p, f, m_bundle, this);
-// } else {
-// for (String name : p.names()) {
-// if (m_descriptions.containsKey(name)) {
-// throw new ParameterException("Found the option " + name + " multiple times");
-// }
-// p("Adding description for " + name);
-// ParameterDescription pd = new ParameterDescription(object, p, f, m_bundle, this);
-// m_fields.put(f, pd);
-// m_descriptions.put(name, pd);
-//
-// if (p.required()) m_requiredFields.put(f, pd);
-// }
-// }
-// } else if (delegateAnnotation != null) {
-// //
-// // @ParametersDelegate
-// //
-// try {
-// Object delegateObject = f.get(object);
-// if (delegateObject == null){
-// throw new ParameterException("Delegate field '" + f.getName() + "' cannot be null.");
-// }
-// addDescription(delegateObject);
-// } catch (IllegalAccessException e) {
-// }
-// } else if (dynamicParameter != null) {
-// //
-// // @DynamicParameter
-// //
-// DynamicParameter dp = (DynamicParameter) dynamicParameter;
-// for (String name : dp.names()) {
-// if (m_descriptions.containsKey(name)) {
-// throw new ParameterException("Found the option " + name + " multiple times");
-// }
-// p("Adding description for " + name);
-// ParameterDescription pd = new ParameterDescription(object, dp, f, m_bundle, this);
-// m_fields.put(f, pd);
-// m_descriptions.put(name, pd);
-//
-// if (dp.required()) m_requiredFields.put(f, pd);
-// }
-// }
-// }
-// // Traverse the super class until we find Object.class
-// cls = cls.getSuperclass();
-// }
- }
-
- private void initializeDefaultValue(ParameterDescription pd) {
- for (String optionName : pd.getParameter().names()) {
- String def = m_defaultProvider.getDefaultValueFor(optionName);
- if (def != null) {
- p("Initializing " + optionName + " with default value:" + def);
- pd.addValue(def, true /* default */);
- return;
- }
- }
- }
-
- /**
- * Main method that parses the values and initializes the fields accordingly.
- */
- private void parseValues(String[] args, boolean validate) {
- // This boolean becomes true if we encounter a command, which indicates we need
- // to stop parsing (the parsing of the command will be done in a sub JCommander
- // object)
- boolean commandParsed = false;
- int i = 0;
- boolean isDashDash = false; // once we encounter --, everything goes into the main parameter
- while (i < args.length && ! commandParsed) {
- String arg = args[i];
- String a = trim(arg);
- args[i] = a;
- p("Parsing arg: " + a);
-
- JCommander jc = findCommandByAlias(arg);
- int increment = 1;
- if (! isDashDash && ! "--".equals(a) && isOption(args, a) && jc == null) {
+ int longestName = 0;
+ List<ParameterDescription> sorted = Lists.newArrayList();
+ for (ParameterDescription pd : fields.values()) {
+ if (!pd.getParameter().hidden()) {
+ sorted.add(pd);
+ // + to have an extra space between the name and the description
+ int length = pd.getNames().length() + 2;
+ if (length > longestName) {
+ longestName = length;
+ }
+ }
+ }
+
//
- // Option
+ // Sort the options
//
- ParameterDescription pd = findParameterDescription(a);
+ Collections.sort(sorted, getParameterDescriptionComparator());
- if (pd != null) {
- if (pd.getParameter().password()) {
- //
- // Password option, use the Console to retrieve the password
- //
- char[] password = readPassword(pd.getDescription(), pd.getParameter().echoInput());
- pd.addValue(new String(password));
- m_requiredFields.remove(pd.getParameterized());
- } else {
- if (pd.getParameter().variableArity()) {
- //
- // Variable arity?
- //
- increment = processVariableArity(args, i, pd);
- } else {
- //
- // Regular option
- //
- Class<?> fieldType = pd.getParameterized().getType();
-
- // Boolean, set to true as soon as we see it, unless it specified
- // an arity of 1, in which case we need to read the next value
- if ((fieldType == boolean.class || fieldType == Boolean.class)
- && pd.getParameter().arity() == -1) {
- pd.addValue("true");
- m_requiredFields.remove(pd.getParameterized());
- } else {
- increment = processFixedArity(args, i, pd, fieldType);
- }
- // If it's a help option, remember for later
- if (pd.isHelp()) {
- m_helpWasSpecified = true;
- }
+ //
+ // Display all the names and descriptions
+ //
+ if (sorted.size() > 0) out.append(indent).append(" Options:\n");
+ for (ParameterDescription pd : sorted) {
+ WrappedParameter parameter = pd.getParameter();
+ out.append(indent).append(" "
+ + (parameter.required() ? "* " : " ")
+ + pd.getNames()
+ + "\n");
+ wrapDescription(out, indentCount, s(indentCount) + pd.getDescription());
+ Object def = pd.getDefault();
+ if (pd.isDynamicParameter()) {
+ out.append("\n" + s(indentCount))
+ .append("Syntax: " + parameter.names()[0]
+ + "key" + parameter.getAssignment()
+ + "value");
}
- }
- } else {
- if (m_acceptUnknownOptions) {
- m_unknownArgs.add(arg);
- i++;
- while (i < args.length && ! isOption(args, args[i])) {
- m_unknownArgs.add(args[i++]);
+ if (def != null && !pd.isHelp()) {
+ String displayedDef = Strings.isStringEmpty(def.toString())
+ ? "<empty string>"
+ : def.toString();
+ out.append("\n" + s(indentCount))
+ .append("Default: " + (parameter.password() ? "********" : displayedDef));
+ }
+ Class<?> type = pd.getParameterized().getType();
+ if (type.isEnum()) {
+ out.append("\n" + s(indentCount))
+ .append("Possible Values: " + EnumSet.allOf((Class<? extends Enum>) type));
}
- increment = 0;
- } else {
- throw new ParameterException("Unknown option: " + arg);
- }
+ out.append("\n");
}
- }
- else {
+
//
- // Main parameter
+ // If commands were specified, show them as well
//
- if (! Strings.isStringEmpty(arg)) {
- if ("--".equals(arg)) {
- isDashDash = true;
- a = trim(args[++i]);
- }
- if (m_commands.isEmpty()) {
- //
- // Regular (non-command) parsing
- //
- List mp = getMainParameter(arg);
- String value = a; // If there's a non-quoted version, prefer that one
- Object convertedValue = value;
-
- if (m_mainParameter.getGenericType() instanceof ParameterizedType) {
- ParameterizedType p = (ParameterizedType) m_mainParameter.getGenericType();
- Type cls = p.getActualTypeArguments()[0];
- if (cls instanceof Class) {
- convertedValue = convertValue(m_mainParameter, (Class) cls, value);
- }
+ if (hasCommands) {
+ out.append(indent + " Commands:\n");
+ // The magic value 3 is the number of spaces between the name of the option
+ // and its description
+ for (Map.Entry<ProgramName, JCommander> commands : this.commands.entrySet()) {
+ Object arg = commands.getValue().getObjects().get(0);
+ Parameters p = arg.getClass().getAnnotation(Parameters.class);
+ if (p == null || !p.hidden()) {
+ ProgramName progName = commands.getKey();
+ String dispName = progName.getDisplayName();
+ String description = getCommandDescription(progName.getName());
+ wrapDescription(out, indentCount + descriptionIndent,
+ indent + " " + dispName + " " + description);
+ out.append("\n");
+
+ // Options for this command
+ JCommander jc = findCommandByAlias(progName.getName());
+ jc.usage(out, indent + " ");
+ out.append("\n");
+ }
}
+ }
+ }
- ParameterDescription.validateParameter(m_mainParameterDescription,
- m_mainParameterAnnotation.validateWith(),
- "Default", value);
-
- m_mainParameterDescription.setAssigned(true);
- mp.add(convertedValue);
- }
- else {
- //
- // Command parsing
- //
- if (jc == null && validate) {
- throw new MissingCommandException("Expected a command, got " + arg);
- } else if (jc != null){
- m_parsedCommand = jc.m_programName.m_name;
- m_parsedAlias = arg; //preserve the original form
-
- // Found a valid command, ask it to parse the remainder of the arguments.
- // Setting the boolean commandParsed to true will force the current
- // loop to end.
- jc.parse(subArray(args, i + 1));
- commandParsed = true;
+ private Comparator<? super ParameterDescription> getParameterDescriptionComparator() {
+ return options.parameterDescriptionComparator;
+ }
+
+ public void setParameterDescriptionComparator(Comparator<? super ParameterDescription> c) {
+ options.parameterDescriptionComparator = c;
+ }
+
+ public void setColumnSize(int columnSize) {
+ options.columnSize = columnSize;
+ }
+
+ public int getColumnSize() {
+ return options.columnSize;
+ }
+
+ /**
+ * Wrap a potentially long line to {@link #getColumnSize()}.
+ *
+ * @param out the output
+ * @param indent the indentation in spaces for lines after the first line.
+ * @param description the text to wrap. No extra spaces are inserted before {@code
+ * description}. If the first line needs to be indented prepend the
+ * correct number of spaces to {@code description}.
+ */
+ private void wrapDescription(StringBuilder out, int indent, String description) {
+ int max = getColumnSize();
+ String[] words = description.split(" ");
+ int current = 0;
+ int i = 0;
+ while (i < words.length) {
+ String word = words[i];
+ if (word.length() > max || current + 1 + word.length() <= max) {
+ out.append(word);
+ current += word.length();
+ if (i != words.length - 1) {
+ out.append(" ");
+ current++;
+ }
+ } else {
+ out.append("\n").append(s(indent)).append(word).append(" ");
+ current = indent + 1 + word.length();
}
- }
+ i++;
}
- }
- i += increment;
}
- // Mark the parameter descriptions held in m_fields as assigned
- for (ParameterDescription parameterDescription : m_descriptions.values()) {
- if (parameterDescription.isAssigned()) {
- m_fields.get(parameterDescription.getParameterized()).setAssigned(true);
- }
+ /**
+ * @return a Collection of all the \@Parameter annotations found on the
+ * target class. This can be used to display the usage() in a different
+ * format (e.g. HTML).
+ */
+ public List<ParameterDescription> getParameters() {
+ return new ArrayList<>(fields.values());
+ }
+
+ /**
+ * @return the main parameter description or null if none is defined.
+ */
+ public ParameterDescription getMainParameter() {
+ return mainParameterDescription;
}
- }
+ private void p(String string) {
+ if (options.verbose > 0 || System.getProperty(JCommander.DEBUG_PROPERTY) != null) {
+ getConsole().println("[JCommander] " + string);
+ }
+ }
- private class DefaultVariableArity implements IVariableArity {
+ /**
+ * Define the default provider for this instance.
+ */
+ public void setDefaultProvider(IDefaultProvider defaultProvider) {
+ options.defaultProvider = defaultProvider;
+ }
- @Override
- public int processVariableArity(String optionName, String[] options) {
- int i = 0;
- while (i < options.length && !isOption(options, options[i])) {
- i++;
- }
- return i;
- }
- }
- private final IVariableArity DEFAULT_VARIABLE_ARITY = new DefaultVariableArity();
-
- private int m_verbose = 0;
-
- private boolean m_caseSensitiveOptions = true;
- private boolean m_allowAbbreviatedOptions = false;
-
- /**
- * @return the number of options that were processed.
- */
- private int processVariableArity(String[] args, int index, ParameterDescription pd) {
- Object arg = pd.getObject();
- IVariableArity va;
- if (! (arg instanceof IVariableArity)) {
- va = DEFAULT_VARIABLE_ARITY;
- } else {
- va = (IVariableArity) arg;
- }
-
- List<String> currentArgs = Lists.newArrayList();
- for (int j = index + 1; j < args.length; j++) {
- currentArgs.add(args[j]);
- }
- int arity = va.processVariableArity(pd.getParameter().names()[0],
- currentArgs.toArray(new String[0]));
-
- int result = processFixedArity(args, index, pd, List.class, arity);
- return result;
- }
-
- private int processFixedArity(String[] args, int index, ParameterDescription pd,
- Class<?> fieldType) {
- // Regular parameter, use the arity to tell use how many values
- // we need to consume
- int arity = pd.getParameter().arity();
- int n = (arity != -1 ? arity : 1);
-
- return processFixedArity(args, index, pd, fieldType, n);
- }
-
- private int processFixedArity(String[] args, int originalIndex, ParameterDescription pd,
- Class<?> fieldType, int arity) {
- int index = originalIndex;
- String arg = args[index];
- // Special case for boolean parameters of arity 0
- if (arity == 0 &&
- (Boolean.class.isAssignableFrom(fieldType)
- || boolean.class.isAssignableFrom(fieldType))) {
- pd.addValue("true");
- m_requiredFields.remove(pd.getParameterized());
- } else if (index < args.length - 1) {
- int offset = "--".equals(args[index + 1]) ? 1 : 0;
-
- if (index + arity < args.length) {
- for (int j = 1; j <= arity; j++) {
- pd.addValue(trim(args[index + j + offset]));
- m_requiredFields.remove(pd.getParameterized());
- }
- index += arity + offset;
- } else {
- throw new ParameterException("Expected " + arity + " values after " + arg);
- }
- } else {
- throw new ParameterException("Expected a value after parameter " + arg);
- }
-
- return arity + 1;
- }
-
- /**
- * Invoke Console.readPassword through reflection to avoid depending
- * on Java 6.
- */
- private char[] readPassword(String description, boolean echoInput) {
- getConsole().print(description + ": ");
- return getConsole().readPassword(echoInput);
- }
-
- private String[] subArray(String[] args, int index) {
- int l = args.length - index;
- String[] result = new String[l];
- System.arraycopy(args, index, result, 0, l);
-
- return result;
- }
-
- /**
- * @return the field that's meant to receive all the parameters that are not options.
- *
- * @param arg the arg that we're about to add (only passed here to output a meaningful
- * error message).
- */
- private List<?> getMainParameter(String arg) {
- if (m_mainParameter == null) {
- throw new ParameterException(
- "Was passed main parameter '" + arg + "' but no main parameter was defined");
- }
-
- List<?> result = (List<?>) m_mainParameter.get(m_mainParameterObject);
- if (result == null) {
- result = Lists.newArrayList();
- if (! List.class.isAssignableFrom(m_mainParameter.getType())) {
- throw new ParameterException("Main parameter field " + m_mainParameter
- + " needs to be of type List, not " + m_mainParameter.getType());
- }
- m_mainParameter.set(m_mainParameterObject, result);
- }
- if (m_firstTimeMainParameter) {
- result.clear();
- m_firstTimeMainParameter = false;
- }
- return result;
- }
-
- public String getMainParameterDescription() {
- if (m_descriptions == null) createDescriptions();
- return m_mainParameterAnnotation != null ? m_mainParameterAnnotation.description()
- : null;
- }
-
-// private int longestName(Collection<?> objects) {
-// int result = 0;
-// for (Object o : objects) {
-// int l = o.toString().length();
-// if (l > result) result = l;
-// }
-//
-// return result;
-// }
-
- /**
- * Set the program name (used only in the usage).
- */
- public void setProgramName(String name) {
- setProgramName(name, new String[0]);
- }
-
- /**
- * Set the program name
- *
- * @param name program name
- * @param aliases aliases to the program name
- */
- public void setProgramName(String name, String... aliases) {
- m_programName = new ProgramName(name, Arrays.asList(aliases));
- }
-
- /**
- * Display the usage for this command.
- */
- public void usage(String commandName) {
- StringBuilder sb = new StringBuilder();
- usage(commandName, sb);
- getConsole().println(sb.toString());
- }
-
- /**
- * Store the help for the command in the passed string builder.
- */
- public void usage(String commandName, StringBuilder out) {
- usage(commandName, out, "");
- }
-
- /**
- * Store the help for the command in the passed string builder, indenting
- * every line with "indent".
- */
- public void usage(String commandName, StringBuilder out, String indent) {
- String description = getCommandDescription(commandName);
- JCommander jc = findCommandByAlias(commandName);
- if (description != null) {
- out.append(indent).append(description);
- out.append("\n");
- }
- jc.usage(out, indent);
- }
-
- /**
- * @return the description of the command.
- */
- public String getCommandDescription(String commandName) {
- JCommander jc = findCommandByAlias(commandName);
- if (jc == null) {
- throw new ParameterException("Asking description for unknown command: " + commandName);
- }
-
- Object arg = jc.getObjects().get(0);
- Parameters p = arg.getClass().getAnnotation(Parameters.class);
- ResourceBundle bundle = null;
- String result = null;
- if (p != null) {
- result = p.commandDescription();
- String bundleName = p.resourceBundle();
- if (!"".equals(bundleName)) {
- bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
- } else {
- bundle = m_bundle;
- }
-
- if (bundle != null) {
- result = getI18nString(bundle, p.commandDescriptionKey(), p.commandDescription());
- }
- }
-
- return result;
- }
-
- /**
- * @return The internationalized version of the string if available, otherwise
- * return def.
- */
- private String getI18nString(ResourceBundle bundle, String key, String def) {
- String s = bundle != null ? bundle.getString(key) : null;
- return s != null ? s : def;
- }
-
- /**
- * Display the help on System.out.
- */
- public void usage() {
- StringBuilder sb = new StringBuilder();
- usage(sb);
- getConsole().println(sb.toString());
- }
-
- /**
- * Store the help in the passed string builder.
- */
- public void usage(StringBuilder out) {
- usage(out, "");
- }
-
- public void usage(StringBuilder out, String indent) {
- if (m_descriptions == null) createDescriptions();
- boolean hasCommands = !m_commands.isEmpty();
-
- //
- // First line of the usage
- //
- String programName = m_programName != null ? m_programName.getDisplayName() : "<main class>";
- out.append(indent).append("Usage: " + programName + " [options]");
- if (hasCommands) out.append(indent).append(" [command] [command options]");
- if (m_mainParameterDescription != null) {
- out.append(" " + m_mainParameterDescription.getDescription());
- }
- out.append("\n");
-
- //
- // Align the descriptions at the "longestName" column
- //
- int longestName = 0;
- List<ParameterDescription> sorted = Lists.newArrayList();
- for (ParameterDescription pd : m_fields.values()) {
- if (! pd.getParameter().hidden()) {
- sorted.add(pd);
- // + to have an extra space between the name and the description
- int length = pd.getNames().length() + 2;
- if (length > longestName) {
- longestName = length;
- }
- }
- }
-
- //
- // Sort the options
- //
- Collections.sort(sorted, getParameterDescriptionComparator());
-
- //
- // Display all the names and descriptions
- //
- int descriptionIndent = 6;
- if (sorted.size() > 0) out.append(indent).append(" Options:\n");
- for (ParameterDescription pd : sorted) {
- WrappedParameter parameter = pd.getParameter();
- out.append(indent).append(" "
- + (parameter.required() ? "* " : " ")
- + pd.getNames()
- + "\n"
- + indent + s(descriptionIndent));
- int indentCount = indent.length() + descriptionIndent;
- wrapDescription(out, indentCount, pd.getDescription());
- Object def = pd.getDefault();
- if (pd.isDynamicParameter()) {
- out.append("\n" + s(indentCount + 1))
- .append("Syntax: " + parameter.names()[0]
- + "key" + parameter.getAssignment()
- + "value");
- }
- if (def != null) {
- String displayedDef = Strings.isStringEmpty(def.toString())
- ? "<empty string>"
- : def.toString();
- out.append("\n" + s(indentCount + 1))
- .append("Default: " + (parameter.password()?"********" : displayedDef));
- }
- Class<?> type = pd.getParameterized().getType();
- if(type.isEnum()){
- out.append("\n" + s(indentCount + 1))
- .append("Possible Values: " + EnumSet.allOf((Class<? extends Enum>) type));
- }
- out.append("\n");
- }
-
- //
- // If commands were specified, show them as well
- //
- if (hasCommands) {
- out.append(" Commands:\n");
- // The magic value 3 is the number of spaces between the name of the option
- // and its description
- for (Map.Entry<ProgramName, JCommander> commands : m_commands.entrySet()) {
- Object arg = commands.getValue().getObjects().get(0);
- Parameters p = arg.getClass().getAnnotation(Parameters.class);
- if (!p.hidden()) {
- ProgramName progName = commands.getKey();
- String dispName = progName.getDisplayName();
- out.append(indent).append(" " + dispName); // + s(spaceCount) + getCommandDescription(progName.name) + "\n");
-
- // Options for this command
- usage(progName.getName(), out, " ");
- out.append("\n");
- }
- }
- }
- }
-
- private Comparator<? super ParameterDescription> getParameterDescriptionComparator() {
- return m_parameterDescriptionComparator;
- }
-
- public void setParameterDescriptionComparator(Comparator<? super ParameterDescription> c) {
- m_parameterDescriptionComparator = c;
- }
-
- public void setColumnSize(int columnSize) {
- m_columnSize = columnSize;
- }
-
- public int getColumnSize() {
- return m_columnSize;
- }
-
- private void wrapDescription(StringBuilder out, int indent, String description) {
- int max = getColumnSize();
- String[] words = description.split(" ");
- int current = indent;
- int i = 0;
- while (i < words.length) {
- String word = words[i];
- if (word.length() > max || current + word.length() <= max) {
- out.append(" ").append(word);
- current += word.length() + 1;
- } else {
- out.append("\n").append(s(indent + 1)).append(word);
- current = indent;
- }
- i++;
- }
- }
-
- /**
- * @return a Collection of all the \@Parameter annotations found on the
- * target class. This can be used to display the usage() in a different
- * format (e.g. HTML).
- */
- public List<ParameterDescription> getParameters() {
- return new ArrayList<ParameterDescription>(m_fields.values());
- }
-
- /**
- * @return the main parameter description or null if none is defined.
- */
- public ParameterDescription getMainParameter() {
- return m_mainParameterDescription;
- }
-
- private void p(String string) {
- if (m_verbose > 0 || System.getProperty(JCommander.DEBUG_PROPERTY) != null) {
- getConsole().println("[JCommander] " + string);
- }
- }
-
- /**
- * Define the default provider for this instance.
- */
- public void setDefaultProvider(IDefaultProvider defaultProvider) {
- m_defaultProvider = defaultProvider;
-
- for (Map.Entry<ProgramName, JCommander> entry : m_commands.entrySet()) {
- entry.getValue().setDefaultProvider(defaultProvider);
- }
- }
-
- public void addConverterFactory(IStringConverterFactory converterFactory) {
- CONVERTER_FACTORIES.addFirst(converterFactory);
- }
-
- public <T> Class<? extends IStringConverter<T>> findConverter(Class<T> cls) {
- for (IStringConverterFactory f : CONVERTER_FACTORIES) {
- Class<? extends IStringConverter<T>> result = f.getConverter(cls);
- if (result != null) return result;
- }
-
- return null;
- }
-
- public Object convertValue(ParameterDescription pd, String value) {
- return convertValue(pd.getParameterized(), pd.getParameterized().getType(), value);
- }
-
- /**
- * @param type The type of the actual parameter
- * @param value The value to convert
- */
- public Object convertValue(Parameterized parameterized, Class type,
- String value) {
- Parameter annotation = parameterized.getParameter();
-
- // Do nothing if it's a @DynamicParameter
- if (annotation == null) return value;
-
- Class<? extends IStringConverter<?>> converterClass = annotation.converter();
- boolean listConverterWasSpecified = annotation.listConverter() != NoConverter.class;
-
- //
- // Try to find a converter on the annotation
- //
- if (converterClass == null || converterClass == NoConverter.class) {
- // If no converter specified and type is enum, used enum values to convert
- if (type.isEnum()){
- converterClass = type;
- } else {
- converterClass = findConverter(type);
- }
- }
-
- if (converterClass == null) {
- Type elementType = parameterized.findFieldGenericType();
- converterClass = elementType != null
- ? findConverter((Class<? extends IStringConverter<?>>) elementType)
- : StringConverter.class;
- // Check for enum type parameter
- if (converterClass == null && Enum.class.isAssignableFrom((Class) elementType)) {
- converterClass = (Class<? extends IStringConverter<?>>) elementType;
- }
- }
-
- IStringConverter<?> converter;
- Object result = null;
- try {
- String[] names = annotation.names();
- String optionName = names.length > 0 ? names[0] : "[Main class]";
- if (converterClass != null && converterClass.isEnum()) {
+ /**
+ * Adds a factory to lookup string converters. The added factory is used prior to previously added factories.
+ * @param converterFactory the factory determining string converters
+ */
+ public void addConverterFactory(final IStringConverterFactory converterFactory) {
+ addConverterInstanceFactory(new IStringConverterInstanceFactory() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public IStringConverter<?> getConverterInstance(Parameter parameter, Class<?> forType, String optionName) {
+ final Class<? extends IStringConverter<?>> converterClass = converterFactory.getConverter(forType);
+ try {
+ if(optionName == null) {
+ optionName = parameter.names().length > 0 ? parameter.names()[0] : "[Main class]";
+ }
+ return converterClass != null ? instantiateConverter(optionName, converterClass) : null;
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ throw new ParameterException(e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Adds a factory to lookup string converters. The added factory is used prior to previously added factories.
+ * @param converterInstanceFactory the factory generating string converter instances
+ */
+ public void addConverterInstanceFactory(IStringConverterInstanceFactory converterInstanceFactory) {
+ options.converterInstanceFactories.add(0, converterInstanceFactory);
+ }
+
+ private IStringConverter<?> findConverterInstance(Parameter parameter, Class<?> forType, String optionName) {
+ for (IStringConverterInstanceFactory f : options.converterInstanceFactories) {
+ IStringConverter<?> result = f.getConverterInstance(parameter, forType, optionName);
+ if (result != null) return result;
+ }
+
+ return null;
+ }
+
+ /**
+ * @param type The type of the actual parameter
+ * @param optionName
+ * @param value The value to convert
+ */
+ public Object convertValue(final Parameterized parameterized, Class type, String optionName, String value) {
+ final Parameter annotation = parameterized.getParameter();
+
+ // Do nothing if it's a @DynamicParameter
+ if (annotation == null) return value;
+
+ if(optionName == null) {
+ optionName = annotation.names().length > 0 ? annotation.names()[0] : "[Main class]";
+ }
+
+ IStringConverter<?> converter = null;
+ if (type.isAssignableFrom(List.class)) {
+ // If a list converter was specified, pass the value to it for direct conversion
+ converter = tryInstantiateConverter(optionName, annotation.listConverter());
+ }
+ if (type.isAssignableFrom(List.class) && converter == null) {
+ // No list converter: use the single value converter and pass each parsed value to it individually
+ final IParameterSplitter splitter = tryInstantiateConverter(null, annotation.splitter());
+ converter = new DefaultListConverter(splitter, new IStringConverter() {
+ @Override
+ public Object convert(String value) {
+ final Type genericType = parameterized.findFieldGenericType();
+ return convertValue(parameterized, genericType instanceof Class ? (Class) genericType : String.class, null, value);
+ }
+ });
+ }
+
+ if (converter == null) {
+ converter = tryInstantiateConverter(optionName, annotation.converter());
+ }
+ if (converter == null) {
+ converter = findConverterInstance(annotation, type, optionName);
+ }
+ if (converter == null && type.isEnum()) {
+ converter = new EnumConverter(optionName, type);
+ }
+ if (converter == null) {
+ converter = new StringConverter();
+ }
+ return converter.convert(value);
+ }
+
+ private static <T> T tryInstantiateConverter(String optionName, Class<T> converterClass) {
+ if (converterClass == NoConverter.class || converterClass == null) {
+ return null;
+ }
try {
- result = Enum.valueOf((Class<? extends Enum>) converterClass, value);
- } catch (IllegalArgumentException e) {
- try {
- result = Enum.valueOf((Class<? extends Enum>) converterClass, value.toUpperCase());
- } catch (IllegalArgumentException ex) {
- throw new ParameterException("Invalid value for " + optionName + " parameter. Allowed values:" +
- EnumSet.allOf((Class<? extends Enum>) converterClass));
+ return instantiateConverter(optionName, converterClass);
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException ignore) {
+ return null;
+ }
+ }
+
+ private static <T> T instantiateConverter(String optionName, Class<? extends T> converterClass)
+ throws InstantiationException, IllegalAccessException,
+ InvocationTargetException {
+ Constructor<T> ctor = null;
+ Constructor<T> stringCtor = null;
+ for (Constructor<T> c : (Constructor<T>[]) converterClass.getDeclaredConstructors()) {
+ c.setAccessible(true);
+ Class<?>[] types = c.getParameterTypes();
+ if (types.length == 1 && types[0].equals(String.class)) {
+ stringCtor = c;
+ } else if (types.length == 0) {
+ ctor = c;
+ }
+ }
+
+ return stringCtor != null
+ ? stringCtor.newInstance(optionName)
+ : ctor != null
+ ? ctor.newInstance()
+ : null;
+ }
+
+ /**
+ * Add a command object.
+ */
+ public void addCommand(String name, Object object) {
+ addCommand(name, object, new String[0]);
+ }
+
+ public void addCommand(Object object) {
+ Parameters p = object.getClass().getAnnotation(Parameters.class);
+ if (p != null && p.commandNames().length > 0) {
+ for (String commandName : p.commandNames()) {
+ addCommand(commandName, object);
}
- } catch (Exception e) {
- throw new ParameterException("Invalid value for " + optionName + " parameter. Allowed values:" +
- EnumSet.allOf((Class<? extends Enum>) converterClass));
- }
- } else {
- converter = instantiateConverter(optionName, converterClass);
- if (type.isAssignableFrom(List.class)
- && parameterized.getGenericType() instanceof ParameterizedType) {
-
- // The field is a List
- if (listConverterWasSpecified) {
- // If a list converter was specified, pass the value to it
- // for direct conversion
- IStringConverter<?> listConverter =
- instantiateConverter(optionName, annotation.listConverter());
- result = listConverter.convert(value);
- } else {
- // No list converter: use the single value converter and pass each
- // parsed value to it individually
- result = convertToList(value, converter, annotation.splitter());
- }
} else {
- result = converter.convert(value);
- }
- }
- } catch (InstantiationException e) {
- throw new ParameterException(e);
- } catch (IllegalAccessException e) {
- throw new ParameterException(e);
- } catch (InvocationTargetException e) {
- throw new ParameterException(e);
- }
-
- return result;
- }
-
- /**
- * Use the splitter to split the value into multiple values and then convert
- * each of them individually.
- */
- private Object convertToList(String value, IStringConverter<?> converter,
- Class<? extends IParameterSplitter> splitterClass)
- throws InstantiationException, IllegalAccessException {
- IParameterSplitter splitter = splitterClass.newInstance();
- List<Object> result = Lists.newArrayList();
- for (String param : splitter.split(value)) {
- result.add(converter.convert(param));
- }
- return result;
- }
-
- private IStringConverter<?> instantiateConverter(String optionName,
- Class<? extends IStringConverter<?>> converterClass)
- throws IllegalArgumentException, InstantiationException, IllegalAccessException,
- InvocationTargetException {
- Constructor<IStringConverter<?>> ctor = null;
- Constructor<IStringConverter<?>> stringCtor = null;
- Constructor<IStringConverter<?>>[] ctors
- = (Constructor<IStringConverter<?>>[]) converterClass.getDeclaredConstructors();
- for (Constructor<IStringConverter<?>> c : ctors) {
- Class<?>[] types = c.getParameterTypes();
- if (types.length == 1 && types[0].equals(String.class)) {
- stringCtor = c;
- } else if (types.length == 0) {
- ctor = c;
- }
- }
-
- IStringConverter<?> result = stringCtor != null
- ? stringCtor.newInstance(optionName)
- : (ctor != null
- ? ctor.newInstance()
- : null);
-
- return result;
- }
-
- /**
- * Add a command object.
- */
- public void addCommand(String name, Object object) {
- addCommand(name, object, new String[0]);
- }
-
- public void addCommand(Object object) {
- Parameters p = object.getClass().getAnnotation(Parameters.class);
- if (p != null && p.commandNames().length > 0) {
- for (String commandName : p.commandNames()) {
- addCommand(commandName, object);
- }
- } else {
- throw new ParameterException("Trying to add command " + object.getClass().getName()
- + " without specifying its names in @Parameters");
- }
- }
-
- /**
- * Add a command object and its aliases.
- */
- public void addCommand(String name, Object object, String... aliases) {
- JCommander jc = new JCommander(object);
- jc.setProgramName(name, aliases);
- jc.setDefaultProvider(m_defaultProvider);
- jc.setAcceptUnknownOptions(m_acceptUnknownOptions);
- ProgramName progName = jc.m_programName;
- m_commands.put(progName, jc);
+ throw new ParameterException("Trying to add command " + object.getClass().getName()
+ + " without specifying its names in @Parameters");
+ }
+ }
+
+ /**
+ * Add a command object and its aliases.
+ */
+ public void addCommand(String name, Object object, String... aliases) {
+ JCommander jc = new JCommander(options);
+ jc.addObject(object);
+ jc.createDescriptions();
+ jc.setProgramName(name, aliases);
+ ProgramName progName = jc.programName;
+ commands.put(progName, jc);
/*
* Register aliases
*/
- //register command name as an alias of itself for reverse lookup
- //Note: Name clash check is intentionally omitted to resemble the
- // original behaviour of clashing commands.
- // Aliases are, however, are strictly checked for name clashes.
- aliasMap.put(new StringKey(name), progName);
- for (String a : aliases) {
- IKey alias = new StringKey(a);
- //omit pointless aliases to avoid name clash exception
- if (!alias.equals(name)) {
- ProgramName mappedName = aliasMap.get(alias);
- if (mappedName != null && !mappedName.equals(progName)) {
- throw new ParameterException("Cannot set alias " + alias
- + " for " + name
- + " command because it has already been defined for "
- + mappedName.m_name + " command");
- }
- aliasMap.put(alias, progName);
- }
- }
- }
-
- public Map<String, JCommander> getCommands() {
- Map<String, JCommander> res = Maps.newLinkedHashMap();
- for (Map.Entry<ProgramName, JCommander> entry : m_commands.entrySet()) {
- res.put(entry.getKey().m_name, entry.getValue());
- }
- return res;
- }
-
- public String getParsedCommand() {
- return m_parsedCommand;
- }
-
- /**
- * The name of the command or the alias in the form it was
- * passed to the command line. <code>null</code> if no
- * command or alias was specified.
- *
- * @return Name of command or alias passed to command line. If none passed: <code>null</code>.
- */
- public String getParsedAlias() {
- return m_parsedAlias;
- }
-
- /**
- * @return n spaces
- */
- private String s(int count) {
- StringBuilder result = new StringBuilder();
- for (int i = 0; i < count; i++) {
- result.append(" ");
- }
-
- return result.toString();
- }
-
- /**
- * @return the objects that JCommander will fill with the result of
- * parsing the command line.
- */
- public List<Object> getObjects() {
- return m_objects;
- }
-
- private ParameterDescription findParameterDescription(String arg) {
- return FuzzyMap.findInMap(m_descriptions, new StringKey(arg), m_caseSensitiveOptions,
- m_allowAbbreviatedOptions);
- }
-
- private JCommander findCommand(ProgramName name) {
- return FuzzyMap.findInMap(m_commands, name,
- m_caseSensitiveOptions, m_allowAbbreviatedOptions);
-// if (! m_caseSensitiveOptions) {
-// return m_commands.get(name);
-// } else {
-// for (ProgramName c : m_commands.keySet()) {
-// if (c.getName().equalsIgnoreCase(name.getName())) {
-// return m_commands.get(c);
-// }
-// }
-// }
-// return null;
- }
-
- private ProgramName findProgramName(String name) {
- return FuzzyMap.findInMap(aliasMap, new StringKey(name),
- m_caseSensitiveOptions, m_allowAbbreviatedOptions);
- }
-
- /*
- * Reverse lookup JCommand object by command's name or its alias
- */
- private JCommander findCommandByAlias(String commandOrAlias) {
- ProgramName progName = findProgramName(commandOrAlias);
- if (progName == null) {
- return null;
- }
- JCommander jc = findCommand(progName);
- if (jc == null) {
- throw new IllegalStateException(
- "There appears to be inconsistency in the internal command database. " +
- " This is likely a bug. Please report.");
- }
- return jc;
- }
-
- /**
- * Encapsulation of either a main application or an individual command.
- */
- private static final class ProgramName implements IKey {
- private final String m_name;
- private final List<String> m_aliases;
-
- ProgramName(String name, List<String> aliases) {
- m_name = name;
- m_aliases = aliases;
- }
-
- @Override
- public String getName() {
- return m_name;
- }
-
- private String getDisplayName() {
- StringBuilder sb = new StringBuilder();
- sb.append(m_name);
- if (!m_aliases.isEmpty()) {
- sb.append("(");
- Iterator<String> aliasesIt = m_aliases.iterator();
- while (aliasesIt.hasNext()) {
- sb.append(aliasesIt.next());
- if (aliasesIt.hasNext()) {
- sb.append(",");
- }
- }
- sb.append(")");
- }
- return sb.toString();
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- ProgramName other = (ProgramName) obj;
- if (m_name == null) {
- if (other.m_name != null)
- return false;
- } else if (!m_name.equals(other.m_name))
- return false;
- return true;
+ //register command name as an alias of itself for reverse lookup
+ //Note: Name clash check is intentionally omitted to resemble the
+ // original behaviour of clashing commands.
+ // Aliases are, however, are strictly checked for name clashes.
+ aliasMap.put(new StringKey(name), progName);
+ for (String a : aliases) {
+ IKey alias = new StringKey(a);
+ //omit pointless aliases to avoid name clash exception
+ if (!alias.equals(name)) {
+ ProgramName mappedName = aliasMap.get(alias);
+ if (mappedName != null && !mappedName.equals(progName)) {
+ throw new ParameterException("Cannot set alias " + alias
+ + " for " + name
+ + " command because it has already been defined for "
+ + mappedName.name + " command");
+ }
+ aliasMap.put(alias, progName);
+ }
+ }
+ }
+
+ public Map<String, JCommander> getCommands() {
+ Map<String, JCommander> res = Maps.newLinkedHashMap();
+ for (Map.Entry<ProgramName, JCommander> entry : commands.entrySet()) {
+ res.put(entry.getKey().name, entry.getValue());
+ }
+ return res;
+ }
+
+ public String getParsedCommand() {
+ return parsedCommand;
+ }
+
+ /**
+ * The name of the command or the alias in the form it was
+ * passed to the command line. <code>null</code> if no
+ * command or alias was specified.
+ *
+ * @return Name of command or alias passed to command line. If none passed: <code>null</code>.
+ */
+ public String getParsedAlias() {
+ return parsedAlias;
+ }
+
+ /**
+ * @return n spaces
+ */
+ private String s(int count) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < count; i++) {
+ result.append(" ");
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * @return the objects that JCommander will fill with the result of
+ * parsing the command line.
+ */
+ public List<Object> getObjects() {
+ return objects;
+ }
+
+ private ParameterDescription findParameterDescription(String arg) {
+ return FuzzyMap.findInMap(descriptions, new StringKey(arg),
+ options.caseSensitiveOptions, options.allowAbbreviatedOptions);
+ }
+
+ private JCommander findCommand(ProgramName name) {
+ return FuzzyMap.findInMap(commands, name,
+ options.caseSensitiveOptions, options.allowAbbreviatedOptions);
+ }
+
+ private ProgramName findProgramName(String name) {
+ return FuzzyMap.findInMap(aliasMap, new StringKey(name),
+ options.caseSensitiveOptions, options.allowAbbreviatedOptions);
}
/*
- * Important: ProgramName#toString() is used by longestName(Collection) function
- * to format usage output.
+ * Reverse lookup JCommand object by command's name or its alias
+ */
+ private JCommander findCommandByAlias(String commandOrAlias) {
+ ProgramName progName = findProgramName(commandOrAlias);
+ if (progName == null) {
+ return null;
+ }
+ JCommander jc = findCommand(progName);
+ if (jc == null) {
+ throw new IllegalStateException(
+ "There appears to be inconsistency in the internal command database. " +
+ " This is likely a bug. Please report.");
+ }
+ return jc;
+ }
+
+ /**
+ * Encapsulation of either a main application or an individual command.
*/
- @Override
- public String toString() {
- return getDisplayName();
-
- }
- }
-
- public void setVerbose(int verbose) {
- m_verbose = verbose;
- }
-
- public void setCaseSensitiveOptions(boolean b) {
- m_caseSensitiveOptions = b;
- }
-
- public void setAllowAbbreviatedOptions(boolean b) {
- m_allowAbbreviatedOptions = b;
- }
-
- public void setAcceptUnknownOptions(boolean b) {
- m_acceptUnknownOptions = b;
- }
-
- public List<String> getUnknownOptions() {
- return m_unknownArgs;
- }
- public void setAllowParameterOverwriting(boolean b) {
- m_allowParameterOverwriting = b;
- }
-
- public boolean isParameterOverwritingAllowed() {
- return m_allowParameterOverwriting;
- }
-// public void setCaseSensitiveCommands(boolean b) {
-// m_caseSensitiveCommands = b;
-// }
-}
+ private static final class ProgramName implements IKey {
+ private final String name;
+ private final List<String> aliases;
+
+ ProgramName(String name, List<String> aliases) {
+ this.name = name;
+ this.aliases = aliases;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ private String getDisplayName() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(name);
+ if (!aliases.isEmpty()) {
+ sb.append("(");
+ Iterator<String> aliasesIt = aliases.iterator();
+ while (aliasesIt.hasNext()) {
+ sb.append(aliasesIt.next());
+ if (aliasesIt.hasNext()) {
+ sb.append(",");
+ }
+ }
+ sb.append(")");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ProgramName other = (ProgramName) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ /*
+ * Important: ProgramName#toString() is used by longestName(Collection) function
+ * to format usage output.
+ */
+ @Override
+ public String toString() {
+ return getDisplayName();
+
+ }
+ }
+
+ public void setVerbose(int verbose) {
+ options.verbose = verbose;
+ }
+
+ public void setCaseSensitiveOptions(boolean b) {
+ options.caseSensitiveOptions = b;
+ }
+
+ public void setAllowAbbreviatedOptions(boolean b) {
+ options.allowAbbreviatedOptions = b;
+ }
+
+ public void setAcceptUnknownOptions(boolean b) {
+ options.acceptUnknownOptions = b;
+ }
+
+ public List<String> getUnknownOptions() {
+ return unknownArgs;
+ }
+
+ public void setAllowParameterOverwriting(boolean b) {
+ options.allowParameterOverwriting = b;
+ }
+
+ public boolean isParameterOverwritingAllowed() {
+ return options.allowParameterOverwriting;
+ }
+
+ /**
+ * Sets the charset used to expand {@code @files}.
+ * @param charset the charset
+ */
+ public void setAtFileCharset(Charset charset) {
+ options.atFileCharset = charset;
+ }
+
+}
diff --git a/src/main/java/com/beust/jcommander/MissingCommandException.java b/src/main/java/com/beust/jcommander/MissingCommandException.java
index 1d572ab..7e8980b 100644
--- a/src/main/java/com/beust/jcommander/MissingCommandException.java
+++ b/src/main/java/com/beust/jcommander/MissingCommandException.java
@@ -26,11 +26,22 @@ package com.beust.jcommander;
@SuppressWarnings("serial")
public class MissingCommandException extends ParameterException {
- public MissingCommandException(String string) {
- super(string);
+ /**
+ * the command passed by the user.
+ */
+ private final String unknownCommand;
+
+ public MissingCommandException(String message) {
+ this(message, null);
+ }
+
+ public MissingCommandException(String message, String command) {
+ super(message);
+ this.unknownCommand = command;
}
- public MissingCommandException(Throwable t) {
- super(t);
+ public String getUnknownCommand() {
+ return unknownCommand;
}
+
}
diff --git a/src/main/java/com/beust/jcommander/Parameter.java b/src/main/java/com/beust/jcommander/Parameter.java
index d8cf87d..2fc00c5 100644
--- a/src/main/java/com/beust/jcommander/Parameter.java
+++ b/src/main/java/com/beust/jcommander/Parameter.java
@@ -90,12 +90,12 @@ public @interface Parameter {
/**
* Validate the parameter found on the command line.
*/
- Class<? extends IParameterValidator> validateWith() default NoValidator.class;
+ Class<? extends IParameterValidator>[] validateWith() default NoValidator.class;
/**
* Validate the value for this parameter.
*/
- Class<? extends IValueValidator> validateValueWith() default NoValueValidator.class;
+ Class<? extends IValueValidator>[] validateValueWith() default NoValueValidator.class;
/**
* @return true if this parameter has a variable arity. See @{IVariableArity}
@@ -122,9 +122,14 @@ public @interface Parameter {
/**
* If true, this parameter can be overwritten through a file or another appearance of the parameter
- * @return
+ * @return nc
*/
boolean forceNonOverwritable() default false;
+ /**
+ * If specified, this number will be used to order the description of this parameter when usage() is invoked.
+ * @return
+ */
+ int order() default -1;
}
diff --git a/src/main/java/com/beust/jcommander/ParameterDescription.java b/src/main/java/com/beust/jcommander/ParameterDescription.java
index 2ef2d5f..bed5ba1 100644
--- a/src/main/java/com/beust/jcommander/ParameterDescription.java
+++ b/src/main/java/com/beust/jcommander/ParameterDescription.java
@@ -21,36 +21,28 @@ 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.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.*;
import java.util.ResourceBundle;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
public class ParameterDescription {
- private Object m_object;
+ private Object object;
- private WrappedParameter m_wrappedParameter;
- private Parameter m_parameterAnnotation;
- private DynamicParameter m_dynamicParameterAnnotation;
+ private WrappedParameter wrappedParameter;
+ private Parameter parameterAnnotation;
+ private DynamicParameter dynamicParameterAnnotation;
/** The field/method */
- private Parameterized m_parameterized;
+ private Parameterized 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;
+ private boolean assigned = false;
+ private ResourceBundle bundle;
+ private String description;
+ private JCommander jCommander;
+ private Object defaultObject;
/** Longest of the names(), used to present usage() alphabetically */
- private String m_longestName = "";
+ private String longestName = "";
public ParameterDescription(Object object, DynamicParameter annotation,
Parameterized parameterized,
@@ -61,15 +53,15 @@ public class ParameterDescription {
+ "Map but is " + parameterized.getType().getName());
}
- m_dynamicParameterAnnotation = annotation;
- m_wrappedParameter = new WrappedParameter(m_dynamicParameterAnnotation);
+ dynamicParameterAnnotation = annotation;
+ wrappedParameter = new WrappedParameter(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);
+ parameterAnnotation = annotation;
+ wrappedParameter = new WrappedParameter(parameterAnnotation);
init(object, parameterized, bundle, jc);
}
@@ -100,90 +92,86 @@ public class ParameterDescription {
}
private void initDescription(String description, String descriptionKey, String[] names) {
- m_description = description;
+ this.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 + "'");
+ if (bundle != null) {
+ this.description = bundle.getString(descriptionKey);
}
}
for (String name : names) {
- if (name.length() > m_longestName.length()) m_longestName = name;
+ if (name.length() > longestName.length()) 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);
+ this.object = object;
+ this.parameterized = parameterized;
+ this.bundle = bundle;
+ if (this.bundle == null) {
+ this.bundle = findResourceBundle(object);
}
- m_jCommander = jCommander;
+ this.jCommander = jCommander;
- if (m_parameterAnnotation != null) {
+ if (parameterAnnotation != null) {
String description;
if (Enum.class.isAssignableFrom(parameterized.getType())
- && m_parameterAnnotation.description().isEmpty()) {
+ && parameterAnnotation.description().isEmpty()) {
description = "Options: " + EnumSet.allOf((Class<? extends Enum>) parameterized.getType());
}else {
- description = m_parameterAnnotation.description();
+ description = 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());
+ initDescription(description, parameterAnnotation.descriptionKey(),
+ parameterAnnotation.names());
+ } else if (dynamicParameterAnnotation != null) {
+ initDescription(dynamicParameterAnnotation.description(),
+ dynamicParameterAnnotation.descriptionKey(),
+ dynamicParameterAnnotation.names());
} else {
throw new AssertionError("Shound never happen");
}
try {
- m_default = parameterized.get(object);
+ defaultObject = 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());
+ if (defaultObject != null) {
+ if (parameterAnnotation != null) {
+ validateDefaultValues(parameterAnnotation.names());
}
}
}
private void validateDefaultValues(String[] names) {
String name = names.length > 0 ? names[0] : "";
- validateValueParameter(name, m_default);
+ validateValueParameter(name, defaultObject);
}
public String getLongestName() {
- return m_longestName;
+ return longestName;
}
public Object getDefault() {
- return m_default;
+ return defaultObject;
}
public String getDescription() {
- return m_description;
+ return description;
}
public Object getObject() {
- return m_object;
+ return object;
}
public String getNames() {
StringBuilder sb = new StringBuilder();
- String[] names = m_wrappedParameter.names();
+ String[] names = wrappedParameter.names();
for (int i = 0; i < names.length; i++) {
if (i > 0) sb.append(", ");
sb.append(names[i]);
@@ -192,17 +180,17 @@ public class ParameterDescription {
}
public WrappedParameter getParameter() {
- return m_wrappedParameter;
+ return wrappedParameter;
}
public Parameterized getParameterized() {
- return m_parameterized;
+ return parameterized;
}
private boolean isMultiOption() {
- Class<?> fieldType = m_parameterized.getType();
+ Class<?> fieldType = parameterized.getType();
return fieldType.equals(List.class) || fieldType.equals(Set.class)
- || m_parameterized.isDynamicParameter();
+ || parameterized.isDynamicParameter();
}
public void addValue(String value) {
@@ -213,12 +201,12 @@ public class ParameterDescription {
* @return true if this parameter received a value during the parsing phase.
*/
public boolean isAssigned() {
- return m_assigned;
+ return assigned;
}
public void setAssigned(boolean b) {
- m_assigned = b;
+ assigned = b;
}
/**
@@ -227,52 +215,132 @@ public class ParameterDescription {
* converter, and if we can't find any, throw an exception.
*/
public void addValue(String value, boolean isDefault) {
+ addValue(null, value, isDefault, true, -1);
+ }
+
+ Object addValue(String name, String value, boolean isDefault, boolean validate, int currentIndex) {
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()) {
+ + " to parameter:" + parameterized.getName());
+ if(name == null) {
+ name = wrappedParameter.names()[0];
+ }
+ if (currentIndex == 00 && assigned && ! isMultiOption() && !jCommander.isParameterOverwritingAllowed()
+ || isNonOverwritableForced()) {
throw new ParameterException("Can only specify option " + name + " once.");
}
- validateParameter(name, value);
+ if (validate) {
+ validateParameter(name, value);
+ }
- Class<?> type = m_parameterized.getType();
+ Class<?> type = parameterized.getType();
- Object convertedValue = m_jCommander.convertValue(this, value);
- validateValueParameter(name, convertedValue);
+ Object convertedValue = jCommander.convertValue(getParameterized(), getParameterized().getType(), name, value);
+ if (validate) {
+ validateValueParameter(name, convertedValue);
+ }
boolean isCollection = Collection.class.isAssignableFrom(type);
+ Object finalValue;
if (isCollection) {
@SuppressWarnings("unchecked")
- Collection<Object> l = (Collection<Object>) m_parameterized.get(m_object);
+ Collection<Object> l = (Collection<Object>) parameterized.get(object);
if (l == null || fieldIsSetForTheFirstTime(isDefault)) {
- l = newCollection(type);
- m_parameterized.set(m_object, l);
+ l = newCollection(type);
+ parameterized.set(object, l);
}
if (convertedValue instanceof Collection) {
- l.addAll((Collection) convertedValue);
- } else { // if (isMainParameter || m_parameterAnnotation.arity() > 1) {
- l.add(convertedValue);
-// } else {
-// l.
+ l.addAll((Collection) convertedValue);
+ } else {
+ l.add(convertedValue);
}
+ finalValue = l;
} else {
- m_wrappedParameter.addValue(m_parameterized, m_object, convertedValue);
+ // If the field type is not a collection, see if it's a type that contains @SubParameters annotations
+ List<SubParameterIndex> subParameters = findSubParameters(type);
+ if (! subParameters.isEmpty()) {
+ // @SubParameters found
+ finalValue = handleSubParameters(value, currentIndex, type, subParameters);
+ } else {
+ // No, regular parameter
+ wrappedParameter.addValue(parameterized, object, convertedValue);
+ finalValue = convertedValue;
+ }
}
- if (! isDefault) m_assigned = true;
+ if (! isDefault) assigned = true;
+
+ return finalValue;
+ }
+
+ private Object handleSubParameters(String value, int currentIndex, Class<?> type,
+ List<SubParameterIndex> subParameters) {
+ Object finalValue;// Yes, assign each following argument to the corresponding field of that object
+ SubParameterIndex sai = null;
+ for (SubParameterIndex si: subParameters) {
+ if (si.order == currentIndex) {
+ sai = si;
+ break;
+ }
+ }
+ if (sai != null) {
+ Object objectValue = parameterized.get(object);
+ try {
+ if (objectValue == null) {
+ objectValue = type.newInstance();
+ parameterized.set(object, objectValue);
+ }
+ wrappedParameter.addValue(parameterized, objectValue, value, sai.field);
+ finalValue = objectValue;
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new ParameterException("Couldn't instantiate " + type, e);
+ }
+ } else {
+ throw new ParameterException("Couldn't find where to assign parameter " + value + " in " + type);
+ }
+ return finalValue;
+ }
+
+ public Parameter getParameterAnnotation() {
+ return parameterAnnotation;
+ }
+
+ class SubParameterIndex {
+ int order = -1;
+ Field field;
+
+ public SubParameterIndex(int order, Field field) {
+ this.order = order;
+ this.field = field;
+ }
+ }
+
+ private List<SubParameterIndex> findSubParameters(Class<?> type) {
+ List<SubParameterIndex> result = new ArrayList<>();
+ for (Field field: type.getDeclaredFields()) {
+ Annotation subParameter = field.getAnnotation(SubParameter.class);
+ if (subParameter != null) {
+ SubParameter sa = (SubParameter) subParameter;
+ result.add(new SubParameterIndex(sa.order(), field));
+ }
+ }
+ return result;
}
private void validateParameter(String name, String value) {
- Class<? extends IParameterValidator> validator = m_wrappedParameter.validateWith();
- if (validator != null) {
- validateParameter(this, validator, name, value);
+ final Class<? extends IParameterValidator> validators[] = wrappedParameter.validateWith();
+ if (validators != null && validators.length > 0) {
+ for(final Class<? extends IParameterValidator> validator: validators) {
+ 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);
+ void validateValueParameter(String name, Object value) {
+ final Class<? extends IValueValidator> validators[] = wrappedParameter.validateValueWith();
+ if (validators != null && validators.length > 0) {
+ for(final Class<? extends IValueValidator> validator: validators) {
+ validateValueParameter(validator, name, value);
+ }
}
}
@@ -283,9 +351,7 @@ public class ParameterDescription {
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) {
+ } catch (InstantiationException | IllegalAccessException e) {
throw new ParameterException("Can't instantiate validator:" + e);
}
}
@@ -294,6 +360,7 @@ public class ParameterDescription {
Class<? extends IParameterValidator> validator,
String name, String value) {
try {
+
if (validator != NoValidator.class) {
p("Validating parameter:" + name + " value:" + value + " validator:" + validator);
}
@@ -302,9 +369,7 @@ public class ParameterDescription {
IParameterValidator2 instance = (IParameterValidator2) validator.newInstance();
instance.validate(name, value, pd);
}
- } catch (InstantiationException e) {
- throw new ParameterException("Can't instantiate validator:" + e);
- } catch (IllegalAccessException e) {
+ } catch (InstantiationException | IllegalAccessException e) {
throw new ParameterException("Can't instantiate validator:" + e);
} catch(ParameterException ex) {
throw ex;
@@ -336,7 +401,7 @@ public class ParameterDescription {
* being added to the field.
*/
private boolean fieldIsSetForTheFirstTime(boolean isDefault) {
- return (!isDefault && !m_assigned);
+ return (!isDefault && !assigned);
}
private static void p(String string) {
@@ -347,18 +412,18 @@ public class ParameterDescription {
@Override
public String toString() {
- return "[ParameterDescription " + m_parameterized.getName() + "]";
+ return "[ParameterDescription " + parameterized.getName() + "]";
}
public boolean isDynamicParameter() {
- return m_dynamicParameterAnnotation != null;
+ return dynamicParameterAnnotation != null;
}
public boolean isHelp() {
- return m_wrappedParameter.isHelp();
+ return wrappedParameter.isHelp();
}
public boolean isNonOverwritableForced() {
- return m_wrappedParameter.isNonOverwritableForced();
+ return wrappedParameter.isNonOverwritableForced();
}
}
diff --git a/src/main/java/com/beust/jcommander/ParameterException.java b/src/main/java/com/beust/jcommander/ParameterException.java
index 2bba7d1..41570ff 100644
--- a/src/main/java/com/beust/jcommander/ParameterException.java
+++ b/src/main/java/com/beust/jcommander/ParameterException.java
@@ -26,7 +26,6 @@ package com.beust.jcommander;
*/
@SuppressWarnings("serial")
public class ParameterException extends RuntimeException {
-
public ParameterException(Throwable t) {
super(t);
}
@@ -37,6 +36,21 @@ public class ParameterException extends RuntimeException {
public ParameterException(String string, Throwable t) {
super(string, t);
- }
+ }
+
+ private JCommander jc;
+
+ public void setJCommander(JCommander jc) {
+ this.jc = jc;
+ }
+
+ public JCommander getJCommander() {
+ return jc;
+ }
+ public void usage() {
+ if (jc != null) {
+ jc.usage();
+ }
+ }
}
diff --git a/src/main/java/com/beust/jcommander/Parameterized.java b/src/main/java/com/beust/jcommander/Parameterized.java
index ff8753b..3264008 100644
--- a/src/main/java/com/beust/jcommander/Parameterized.java
+++ b/src/main/java/com/beust/jcommander/Parameterized.java
@@ -1,14 +1,18 @@
package com.beust.jcommander;
import com.beust.jcommander.internal.Lists;
+import com.beust.jcommander.internal.Sets;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* Encapsulate a field or a method annotated with @Parameter or @DynamicParameter
@@ -16,129 +20,175 @@ import java.util.List;
public class Parameterized {
// Either a method or a field
- private Field m_field;
- private Method m_method;
- private Method m_getter;
+ private Field field;
+ private Method method;
+ private Method getter;
// Either of these two
- private WrappedParameter m_wrappedParameter;
- private ParametersDelegate m_parametersDelegate;
+ private WrappedParameter wrappedParameter;
+ private ParametersDelegate parametersDelegate;
public Parameterized(WrappedParameter wp, ParametersDelegate pd,
Field field, Method method) {
- m_wrappedParameter = wp;
- m_method = method;
- m_field = field;
- if (m_field != null) {
- m_field.setAccessible(true);
+ wrappedParameter = wp;
+ this.method = method;
+ this.field = field;
+ if (this.field != null) {
+ setFieldAccessible(this.field);
}
- m_parametersDelegate = pd;
+ parametersDelegate = pd;
+ }
+
+ /**
+ * Recursive handler for describing the set of classes while
+ * using the setOfClasses parameter as a collector
+ *
+ * @param inputClass the class to analyze
+ * @param setOfClasses the set collector to collect the results
+ */
+ private static void describeClassTree(Class<?> inputClass, Set<Class<?>> setOfClasses) {
+ // can't map null class
+ if(inputClass == null) {
+ return;
+ }
+
+ // don't further analyze a class that has been analyzed already
+ if(Object.class.equals(inputClass) || setOfClasses.contains(inputClass)) {
+ return;
+ }
+
+ // add to analysis set
+ setOfClasses.add(inputClass);
+
+ // perform super class analysis
+ describeClassTree(inputClass.getSuperclass(), setOfClasses);
+
+ // perform analysis on interfaces
+ for(Class<?> hasInterface : inputClass.getInterfaces()) {
+ describeClassTree(hasInterface, setOfClasses);
+ }
+ }
+
+ /**
+ * Given an object return the set of classes that it extends
+ * or implements.
+ *
+ * @param arg object to describe
+ * @return set of classes that are implemented or extended by that object
+ */
+ private static Set<Class<?>> describeClassTree(Class<?> inputClass) {
+ if(inputClass == null) {
+ return Collections.emptySet();
+ }
+
+ // create result collector
+ Set<Class<?>> classes = Sets.newLinkedHashSet();
+
+ // describe tree
+ describeClassTree(inputClass, classes);
+
+ return classes;
}
public static List<Parameterized> parseArg(Object arg) {
List<Parameterized> result = Lists.newArrayList();
- Class<? extends Object> cls = arg.getClass();
- while (!Object.class.equals(cls)) {
+ Class<?> rootClass = arg.getClass();
+
+ // get the list of types that are extended or implemented by the root class
+ // and all of its parent types
+ Set<Class<?>> types = describeClassTree(rootClass);
+
+ // analyze each type
+ for(Class<?> cls : types) {
+
+ // check fields
for (Field f : cls.getDeclaredFields()) {
Annotation annotation = f.getAnnotation(Parameter.class);
Annotation delegateAnnotation = f.getAnnotation(ParametersDelegate.class);
Annotation dynamicParameter = f.getAnnotation(DynamicParameter.class);
if (annotation != null) {
result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
- f, null));
+ f, null));
} else if (dynamicParameter != null) {
result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null,
- f, null));
+ f, null));
} else if (delegateAnnotation != null) {
result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
- f, null));
+ f, null));
}
}
- cls = cls.getSuperclass();
- }
- // Reassigning
- cls = arg.getClass();
- while (!Object.class.equals(cls)) {
+ // check methods
for (Method m : cls.getDeclaredMethods()) {
+ m.setAccessible(true);
Annotation annotation = m.getAnnotation(Parameter.class);
Annotation delegateAnnotation = m.getAnnotation(ParametersDelegate.class);
Annotation dynamicParameter = m.getAnnotation(DynamicParameter.class);
if (annotation != null) {
result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
- null, m));
+ null, m));
} else if (dynamicParameter != null) {
- result.add(new Parameterized(new WrappedParameter((DynamicParameter) annotation), null,
- null, m));
+ result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null,
+ null, m));
} else if (delegateAnnotation != null) {
result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
- null, m));
+ null, m));
}
}
- cls = cls.getSuperclass();
}
return result;
}
public WrappedParameter getWrappedParameter() {
- return m_wrappedParameter;
+ return wrappedParameter;
}
public Class<?> getType() {
- if (m_method != null) {
- return m_method.getParameterTypes()[0];
+ if (method != null) {
+ return method.getParameterTypes()[0];
} else {
- return m_field.getType();
+ return field.getType();
}
}
public String getName() {
- if (m_method != null) {
- return m_method.getName();
+ if (method != null) {
+ return method.getName();
} else {
- return m_field.getName();
+ return field.getName();
}
}
public Object get(Object object) {
try {
- if (m_method != null) {
- if (m_getter == null) {
- m_getter = m_method.getDeclaringClass()
- .getMethod("g" + m_method.getName().substring(1),
- new Class[0]);
+ if (method != null) {
+ if (getter == null) {
+ getter = method.getDeclaringClass()
+ .getMethod("g" + method.getName().substring(1));
}
- return m_getter.invoke(object);
+ return getter.invoke(object);
} else {
- return m_field.get(object);
+ return field.get(object);
}
- } catch (SecurityException e) {
+ } catch (SecurityException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
throw new ParameterException(e);
} catch (NoSuchMethodException e) {
// Try to find a field
- String name = m_method.getName();
+ String name = method.getName();
String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4);
Object result = null;
try {
- Field field = m_method.getDeclaringClass().getDeclaredField(fieldName);
+ Field field = method.getDeclaringClass().getDeclaredField(fieldName);
if (field != null) {
- field.setAccessible(true);
+ setFieldAccessible(field);
result = field.get(object);
}
- } catch(NoSuchFieldException ex) {
- // ignore
- } catch(IllegalAccessException ex) {
+ } catch(NoSuchFieldException | IllegalAccessException ex) {
// ignore
}
return result;
- } catch (IllegalArgumentException e) {
- throw new ParameterException(e);
- } catch (IllegalAccessException e) {
- throw new ParameterException(e);
- } catch (InvocationTargetException e) {
- throw new ParameterException(e);
}
}
@@ -146,8 +196,8 @@ public class Parameterized {
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((m_field == null) ? 0 : m_field.hashCode());
- result = prime * result + ((m_method == null) ? 0 : m_method.hashCode());
+ result = prime * result + ((field == null) ? 0 : field.hashCode());
+ result = prime * result + ((method == null) ? 0 : method.hashCode());
return result;
}
@@ -160,73 +210,84 @@ public class Parameterized {
if (getClass() != obj.getClass())
return false;
Parameterized other = (Parameterized) obj;
- if (m_field == null) {
- if (other.m_field != null)
+ if (field == null) {
+ if (other.field != null)
return false;
- } else if (!m_field.equals(other.m_field))
+ } else if (!field.equals(other.field))
return false;
- if (m_method == null) {
- if (other.m_method != null)
+ if (method == null) {
+ if (other.method != null)
return false;
- } else if (!m_method.equals(other.m_method))
+ } else if (!method.equals(other.method))
return false;
return true;
}
public boolean isDynamicParameter(Field field) {
- if (m_method != null) {
- return m_method.getAnnotation(DynamicParameter.class) != null;
+ if (method != null) {
+ return method.getAnnotation(DynamicParameter.class) != null;
} else {
- return m_field.getAnnotation(DynamicParameter.class) != null;
+ return this.field.getAnnotation(DynamicParameter.class) != null;
+ }
+ }
+
+ private static void setFieldAccessible(Field f) {
+ if (Modifier.isFinal(f.getModifiers())) {
+ throw new ParameterException(
+ "Cannot use final field " + f.getDeclaringClass().getName() + "#" + f.getName() + " as a parameter;"
+ + " compile-time constant inlining may hide new values written to it.");
}
+ f.setAccessible(true);
+ }
+
+ private static String errorMessage(Method m, Exception ex) {
+ return "Could not invoke " + m + "\n Reason: " + ex.getMessage();
}
public void set(Object object, Object value) {
try {
- if (m_method != null) {
- m_method.invoke(object, value);
+ if (method != null) {
+ method.invoke(object, value);
} else {
- m_field.set(object, value);
+ field.set(object, value);
}
- } catch (IllegalArgumentException ex) {
- throw new ParameterException(ex);
- } catch (IllegalAccessException ex) {
- throw new ParameterException(ex);
+ } catch (IllegalAccessException | IllegalArgumentException ex) {
+ throw new ParameterException(errorMessage(method, ex));
} catch (InvocationTargetException ex) {
// If a ParameterException was thrown, don't wrap it into another one
if (ex.getTargetException() instanceof ParameterException) {
throw (ParameterException) ex.getTargetException();
} else {
- throw new ParameterException(ex);
+ throw new ParameterException(errorMessage(method, ex));
}
}
}
public ParametersDelegate getDelegateAnnotation() {
- return m_parametersDelegate;
+ return parametersDelegate;
}
public Type getGenericType() {
- if (m_method != null) {
- return m_method.getGenericParameterTypes()[0];
+ if (method != null) {
+ return method.getGenericParameterTypes()[0];
} else {
- return m_field.getGenericType();
+ return field.getGenericType();
}
}
public Parameter getParameter() {
- return m_wrappedParameter.getParameter();
+ return wrappedParameter.getParameter();
}
/**
* @return the generic type of the collection for this field, or null if not applicable.
*/
public Type findFieldGenericType() {
- if (m_method != null) {
+ if (method != null) {
return null;
} else {
- if (m_field.getGenericType() instanceof ParameterizedType) {
- ParameterizedType p = (ParameterizedType) m_field.getGenericType();
+ if (field.getGenericType() instanceof ParameterizedType) {
+ ParameterizedType p = (ParameterizedType) field.getGenericType();
Type cls = p.getActualTypeArguments()[0];
if (cls instanceof Class) {
return cls;
@@ -238,7 +299,7 @@ public class Parameterized {
}
public boolean isDynamicParameter() {
- return m_wrappedParameter.getDynamicParameter() != null;
+ return wrappedParameter.getDynamicParameter() != null;
}
}
diff --git a/src/main/java/com/beust/jcommander/Parameters.java b/src/main/java/com/beust/jcommander/Parameters.java
index f2e8c76..edab036 100644
--- a/src/main/java/com/beust/jcommander/Parameters.java
+++ b/src/main/java/com/beust/jcommander/Parameters.java
@@ -34,8 +34,6 @@ import static java.lang.annotation.ElementType.TYPE;
@Inherited
public @interface Parameters {
- public static final String DEFAULT_OPTION_PREFIXES = "-";
-
/**
* The name of the resource bundle to use for this class.
*/
@@ -47,11 +45,6 @@ public @interface Parameters {
String separators() default " ";
/**
- * What characters an option starts with.
- */
- String optionPrefixes() default DEFAULT_OPTION_PREFIXES;
-
- /**
* If the annotated class was added to {@link JCommander} as a command with
* {@link JCommander#addCommand}, then this string will be displayed in the
* description when @{link JCommander#usage} is invoked.
diff --git a/src/main/java/com/beust/jcommander/StringKey.java b/src/main/java/com/beust/jcommander/StringKey.java
index 09d1149..11a7d18 100644
--- a/src/main/java/com/beust/jcommander/StringKey.java
+++ b/src/main/java/com/beust/jcommander/StringKey.java
@@ -4,27 +4,27 @@ import com.beust.jcommander.FuzzyMap.IKey;
public class StringKey implements IKey {
- private String m_name;
+ private String name;
public StringKey(String name) {
- m_name = name;
+ this.name = name;
}
@Override
public String getName() {
- return m_name;
+ return name;
}
@Override
public String toString() {
- return m_name;
+ return name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@@ -37,10 +37,10 @@ public class StringKey implements IKey {
if (getClass() != obj.getClass())
return false;
StringKey other = (StringKey) obj;
- if (m_name == null) {
- if (other.m_name != null)
+ if (name == null) {
+ if (other.name != null)
return false;
- } else if (!m_name.equals(other.m_name))
+ } else if (!name.equals(other.name))
return false;
return true;
}
diff --git a/src/main/java/com/beust/jcommander/SubParameter.java b/src/main/java/com/beust/jcommander/SubParameter.java
new file mode 100644
index 0000000..22adfc4
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/SubParameter.java
@@ -0,0 +1,17 @@
+package com.beust.jcommander;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+
+/**
+ * @author Cedric Beust <cedric@refresh.io>
+ * @since 02 12, 2017
+ */
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({ FIELD, METHOD })
+public @interface SubParameter {
+ int order() default -1;
+}
diff --git a/src/main/java/com/beust/jcommander/WrappedParameter.java b/src/main/java/com/beust/jcommander/WrappedParameter.java
index f4e7d56..e49245a 100644
--- a/src/main/java/com/beust/jcommander/WrappedParameter.java
+++ b/src/main/java/com/beust/jcommander/WrappedParameter.java
@@ -1,5 +1,6 @@
package com.beust.jcommander;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -7,68 +8,81 @@ import java.lang.reflect.Method;
* Encapsulates the operations common to @Parameter and @DynamicParameter
*/
public class WrappedParameter {
- private Parameter m_parameter;
- private DynamicParameter m_dynamicParameter;
+ private Parameter parameter;
+ private DynamicParameter dynamicParameter;
public WrappedParameter(Parameter p) {
- m_parameter = p;
+ parameter = p;
}
public WrappedParameter(DynamicParameter p) {
- m_dynamicParameter = p;
+ dynamicParameter = p;
}
public Parameter getParameter() {
- return m_parameter;
+ return parameter;
}
public DynamicParameter getDynamicParameter() {
- return m_dynamicParameter;
+ return dynamicParameter;
}
public int arity() {
- return m_parameter != null ? m_parameter.arity() : 1;
+ return parameter != null ? parameter.arity() : 1;
}
public boolean hidden() {
- return m_parameter != null ? m_parameter.hidden() : m_dynamicParameter.hidden();
+ return parameter != null ? parameter.hidden() : dynamicParameter.hidden();
}
public boolean required() {
- return m_parameter != null ? m_parameter.required() : m_dynamicParameter.required();
+ return parameter != null ? parameter.required() : dynamicParameter.required();
}
public boolean password() {
- return m_parameter != null ? m_parameter.password() : false;
+ return parameter != null ? parameter.password() : false;
}
public String[] names() {
- return m_parameter != null ? m_parameter.names() : m_dynamicParameter.names();
+ return parameter != null ? parameter.names() : dynamicParameter.names();
}
public boolean variableArity() {
- return m_parameter != null ? m_parameter.variableArity() : false;
+ return parameter != null ? parameter.variableArity() : false;
}
- public Class<? extends IParameterValidator> validateWith() {
- return m_parameter != null ? m_parameter.validateWith() : m_dynamicParameter.validateWith();
+ public Class<? extends IParameterValidator>[] validateWith() {
+ return parameter != null ? parameter.validateWith() : dynamicParameter.validateWith();
}
- public Class<? extends IValueValidator> validateValueWith() {
- return m_parameter != null
- ? m_parameter.validateValueWith()
- : m_dynamicParameter.validateValueWith();
+ public Class<? extends IValueValidator>[] validateValueWith() {
+ return parameter != null
+ ? parameter.validateValueWith()
+ : dynamicParameter.validateValueWith();
}
public boolean echoInput() {
- return m_parameter != null ? m_parameter.echoInput() : false;
+ return parameter != null ? parameter.echoInput() : false;
}
public void addValue(Parameterized parameterized, Object object, Object value) {
- if (m_parameter != null) {
- parameterized.set(object, value);
+ try {
+ addValue(parameterized, object, value, null);
+ } catch (IllegalAccessException e) {
+ throw new ParameterException("Couldn't set " + object + " to " + value, e);
+ }
+ }
+
+ public void addValue(Parameterized parameterized, Object object, Object value, Field field)
+ throws IllegalAccessException {
+ if (parameter != null) {
+ if (field != null) {
+ field.set(object, value);
+ } else {
+ parameterized.set(object, value);
+ }
} else {
- String a = m_dynamicParameter.assignment();
+ String a = dynamicParameter.assignment();
String sv = value.toString();
int aInd = sv.indexOf(a);
@@ -86,13 +100,7 @@ public class WrappedParameter {
Method m;
m = findPut(parameterized.getType());
m.invoke(parameterized.get(object), key, value);
- } catch (SecurityException e) {
- e.printStackTrace();
- } catch(IllegalAccessException e) {
- e.printStackTrace();
- } catch(InvocationTargetException e) {
- e.printStackTrace();
- } catch (NoSuchMethodException e) {
+ } catch (SecurityException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
@@ -102,14 +110,14 @@ public class WrappedParameter {
}
public String getAssignment() {
- return m_dynamicParameter != null ? m_dynamicParameter.assignment() : "";
+ return dynamicParameter != null ? dynamicParameter.assignment() : "";
}
public boolean isHelp() {
- return m_parameter != null && m_parameter.help();
+ return parameter != null && parameter.help();
}
public boolean isNonOverwritableForced() {
- return m_parameter != null && m_parameter.forceNonOverwritable();
+ return parameter != null && parameter.forceNonOverwritable();
}
}
diff --git a/src/main/java/com/beust/jcommander/converters/BaseConverter.java b/src/main/java/com/beust/jcommander/converters/BaseConverter.java
index 4287163..02e94b8 100644
--- a/src/main/java/com/beust/jcommander/converters/BaseConverter.java
+++ b/src/main/java/com/beust/jcommander/converters/BaseConverter.java
@@ -27,14 +27,14 @@ import com.beust.jcommander.IStringConverter;
*/
abstract public class BaseConverter<T> implements IStringConverter<T> {
- private String m_optionName;
+ private String optionName;
public BaseConverter(String optionName) {
- m_optionName = optionName;
+ this.optionName = optionName;
}
public String getOptionName() {
- return m_optionName;
+ return optionName;
}
protected String getErrorString(String value, String to) {
diff --git a/src/main/java/com/beust/jcommander/converters/CharArrayConverter.java b/src/main/java/com/beust/jcommander/converters/CharArrayConverter.java
new file mode 100644
index 0000000..5252f63
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/CharArrayConverter.java
@@ -0,0 +1,33 @@
+/**
+ * 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.converters;
+
+import com.beust.jcommander.IStringConverter;
+
+/**
+ * Converts a String to a char[].
+ *
+ * @author Gary Gregory
+ */
+public class CharArrayConverter implements IStringConverter<char[]> {
+
+ public char[] convert(final String value) {
+ return value.toCharArray();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/beust/jcommander/converters/DefaultListConverter.java b/src/main/java/com/beust/jcommander/converters/DefaultListConverter.java
new file mode 100644
index 0000000..00bf9ac
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/DefaultListConverter.java
@@ -0,0 +1,36 @@
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+import com.beust.jcommander.internal.Lists;
+
+import java.util.List;
+
+/**
+ * A converter to obtain a list of elements.
+ * @param <T> the element type
+ * @author simon04
+ */
+public class DefaultListConverter<T> implements IStringConverter<List<T>> {
+
+ private final IParameterSplitter splitter;
+ private final IStringConverter<T> converter;
+
+ /**
+ * Constructs a new converter.
+ * @param splitter to split value into list of arguments
+ * @param converter to convert list of arguments to target element type
+ */
+ public DefaultListConverter(IParameterSplitter splitter, IStringConverter<T> converter) {
+ this.splitter = splitter;
+ this.converter = converter;
+ }
+
+ @Override
+ public List<T> convert(String value) {
+ List<T> result = Lists.newArrayList();
+ for (String param : splitter.split(value)) {
+ result.add(converter.convert(param));
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/com/beust/jcommander/converters/EnumConverter.java b/src/main/java/com/beust/jcommander/converters/EnumConverter.java
new file mode 100644
index 0000000..3e850bb
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/EnumConverter.java
@@ -0,0 +1,42 @@
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+import com.beust.jcommander.ParameterException;
+
+import java.util.EnumSet;
+
+/**
+ * A converter to parse enums
+ * @param <T> the enum type
+ * @author simon04
+ */
+public class EnumConverter<T extends Enum<T>> implements IStringConverter<T> {
+
+ private final String optionName;
+ private final Class<T> clazz;
+
+ /**
+ * Constructs a new converter.
+ * @param optionName the option name for error reporting
+ * @param clazz the enum class
+ */
+ public EnumConverter(String optionName, Class<T> clazz) {
+ this.optionName = optionName;
+ this.clazz = clazz;
+ }
+
+ @Override
+ public T convert(String value) {
+ try {
+ try {
+ return Enum.valueOf(clazz, value);
+ } catch (IllegalArgumentException e) {
+ return Enum.valueOf(clazz, value.toUpperCase());
+ }
+ } catch (Exception e) {
+ throw new ParameterException("Invalid value for " + optionName + " parameter. Allowed values:" +
+ EnumSet.allOf(clazz));
+
+ }
+ }
+}
diff --git a/src/main/java/com/beust/jcommander/converters/InetAddressConverter.java b/src/main/java/com/beust/jcommander/converters/InetAddressConverter.java
new file mode 100644
index 0000000..b6f391a
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/InetAddressConverter.java
@@ -0,0 +1,40 @@
+/**
+ * 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.converters;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import com.beust.jcommander.IStringConverter;
+
+/**
+ * Converts {@code String}s to {@code InetAddress}'.
+ */
+public class InetAddressConverter implements IStringConverter<InetAddress> {
+
+ @Override
+ public InetAddress convert(String host) {
+ try {
+ return InetAddress.getByName(host);
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException(host, e);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java b/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java
index d5401a1..ac66df6 100644
--- a/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java
+++ b/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java
@@ -32,7 +32,7 @@ import java.util.Properties;
*/
public class PropertyFileDefaultProvider implements IDefaultProvider {
public static final String DEFAULT_FILE_NAME = "jcommander.properties";
- private Properties m_properties;
+ private Properties properties;
public PropertyFileDefaultProvider() {
init(DEFAULT_FILE_NAME);
@@ -44,10 +44,10 @@ public class PropertyFileDefaultProvider implements IDefaultProvider {
private void init(String fileName) {
try {
- m_properties = new Properties();
+ properties = new Properties();
URL url = ClassLoader.getSystemResource(fileName);
if (url != null) {
- m_properties.load(url.openStream());
+ properties.load(url.openStream());
} else {
throw new ParameterException("Could not find property file: " + fileName
+ " on the class path");
@@ -64,7 +64,7 @@ public class PropertyFileDefaultProvider implements IDefaultProvider {
index++;
}
String key = optionName.substring(index);
- return m_properties.getProperty(key);
+ return properties.getProperty(key);
}
}
diff --git a/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java b/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java
index 7eb5ae5..51e5d67 100644
--- a/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java
+++ b/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java
@@ -43,37 +43,38 @@ public class DefaultConverterFactory implements IStringConverterFactory {
/**
* A map of converters per class.
*/
- private static Map<Class, Class<? extends IStringConverter<?>>> m_classConverters;
+ private static Map<Class, Class<? extends IStringConverter<?>>> classConverters;
static {
- m_classConverters = Maps.newHashMap();
- m_classConverters.put(String.class, StringConverter.class);
- m_classConverters.put(Integer.class, IntegerConverter.class);
- m_classConverters.put(int.class, IntegerConverter.class);
- m_classConverters.put(Long.class, LongConverter.class);
- m_classConverters.put(long.class, LongConverter.class);
- m_classConverters.put(Float.class, FloatConverter.class);
- m_classConverters.put(float.class, FloatConverter.class);
- m_classConverters.put(Double.class, DoubleConverter.class);
- m_classConverters.put(double.class, DoubleConverter.class);
- m_classConverters.put(Boolean.class, BooleanConverter.class);
- m_classConverters.put(boolean.class, BooleanConverter.class);
- m_classConverters.put(File.class, FileConverter.class);
- m_classConverters.put(BigDecimal.class, BigDecimalConverter.class);
- m_classConverters.put(Date.class, ISO8601DateConverter.class);
+ classConverters = Maps.newHashMap();
+ classConverters.put(String.class, StringConverter.class);
+ classConverters.put(Integer.class, IntegerConverter.class);
+ classConverters.put(int.class, IntegerConverter.class);
+ classConverters.put(Long.class, LongConverter.class);
+ classConverters.put(long.class, LongConverter.class);
+ classConverters.put(Float.class, FloatConverter.class);
+ classConverters.put(float.class, FloatConverter.class);
+ classConverters.put(Double.class, DoubleConverter.class);
+ classConverters.put(double.class, DoubleConverter.class);
+ classConverters.put(Boolean.class, BooleanConverter.class);
+ classConverters.put(boolean.class, BooleanConverter.class);
+ classConverters.put(File.class, FileConverter.class);
+ classConverters.put(BigDecimal.class, BigDecimalConverter.class);
+ classConverters.put(Date.class, ISO8601DateConverter.class);
+ classConverters.put(URI.class, URIConverter.class);
+ classConverters.put(URL.class, URLConverter.class);
+
try {
Class<?> pathClass = Class.forName("java.nio.file.Path");
Class<?> pathConverterClass = Class.forName("com.beust.jcommander.converters.PathConverter");
- m_classConverters.put(pathClass, (Class<? extends IStringConverter<?>>)pathConverterClass);
- } catch (ClassNotFoundException e) {
- // Do nothing: Android does not have java.nio.file.Path
+ classConverters.put(pathClass, (Class<? extends IStringConverter<?>>)pathConverterClass);
+ } catch (ClassNotFoundException ex) {
+ // skip if class is not present (e.g. on Android)
}
- m_classConverters.put(URI.class, URIConverter.class);
- m_classConverters.put(URL.class, URLConverter.class);
}
public Class<? extends IStringConverter<?>> getConverter(Class forType) {
- return m_classConverters.get(forType);
+ return classConverters.get(forType);
}
}
diff --git a/src/main/java/com/beust/jcommander/internal/JDK6Console.java b/src/main/java/com/beust/jcommander/internal/JDK6Console.java
index 70cb186..507a575 100644
--- a/src/main/java/com/beust/jcommander/internal/JDK6Console.java
+++ b/src/main/java/com/beust/jcommander/internal/JDK6Console.java
@@ -13,8 +13,8 @@ public class JDK6Console implements Console {
public JDK6Console(Object console) throws Exception {
this.console = console;
- Method writerMethod = console.getClass().getDeclaredMethod("writer", new Class<?>[0]);
- writer = (PrintWriter) writerMethod.invoke(console, new Object[0]);
+ Method writerMethod = console.getClass().getDeclaredMethod("writer");
+ writer = (PrintWriter) writerMethod.invoke(console);
}
public void print(String msg) {
@@ -30,11 +30,11 @@ public class JDK6Console implements Console {
writer.flush();
Method method;
if (echoInput) {
- method = console.getClass().getDeclaredMethod("readLine", new Class<?>[0]);
- return ((String) method.invoke(console, new Object[0])).toCharArray();
+ method = console.getClass().getDeclaredMethod("readLine");
+ return ((String) method.invoke(console)).toCharArray();
} else {
- method = console.getClass().getDeclaredMethod("readPassword", new Class<?>[0]);
- return (char[]) method.invoke(console, new Object[0]);
+ method = console.getClass().getDeclaredMethod("readPassword");
+ return (char[]) method.invoke(console);
}
}
catch (Exception e) {
diff --git a/src/main/java/com/beust/jcommander/internal/Lists.java b/src/main/java/com/beust/jcommander/internal/Lists.java
index fdbee55..f18e6cc 100644
--- a/src/main/java/com/beust/jcommander/internal/Lists.java
+++ b/src/main/java/com/beust/jcommander/internal/Lists.java
@@ -27,27 +27,27 @@ import java.util.List;
public class Lists {
public static <K> List<K> newArrayList() {
- return new ArrayList<K>();
+ return new ArrayList<>();
}
public static <K> List<K> newArrayList(Collection<K> c) {
- return new ArrayList<K>(c);
+ return new ArrayList<>(c);
}
public static <K> List<K> newArrayList(K... c) {
- return new ArrayList<K>(Arrays.asList(c));
+ return new ArrayList<>(Arrays.asList(c));
}
public static <K> List<K> newArrayList(int size) {
- return new ArrayList<K>(size);
+ return new ArrayList<>(size);
}
public static <K> LinkedList<K> newLinkedList() {
- return new LinkedList<K>();
+ return new LinkedList<>();
}
public static <K> LinkedList<K> newLinkedList(Collection<K> c) {
- return new LinkedList<K>(c);
+ return new LinkedList<>(c);
}
diff --git a/src/main/java/com/beust/jcommander/internal/Maps.java b/src/main/java/com/beust/jcommander/internal/Maps.java
index e272122..2f45010 100644
--- a/src/main/java/com/beust/jcommander/internal/Maps.java
+++ b/src/main/java/com/beust/jcommander/internal/Maps.java
@@ -25,11 +25,11 @@ import java.util.Map;
public class Maps {
public static <K, V> Map<K,V> newHashMap() {
- return new HashMap<K, V>();
+ return new HashMap<>();
}
public static <K, V> Map<K,V> newLinkedHashMap() {
- return new LinkedHashMap<K, V>();
+ return new LinkedHashMap<>();
}
public static <T> Map<T, T> newHashMap(T... parameters) {
diff --git a/src/main/java/com/beust/jcommander/internal/Sets.java b/src/main/java/com/beust/jcommander/internal/Sets.java
index 77949c3..168c55f 100644
--- a/src/main/java/com/beust/jcommander/internal/Sets.java
+++ b/src/main/java/com/beust/jcommander/internal/Sets.java
@@ -25,11 +25,11 @@ import java.util.Set;
public class Sets {
public static <K> Set<K> newHashSet() {
- return new HashSet<K>();
+ return new HashSet<>();
}
public static <K> Set<K> newLinkedHashSet() {
- return new LinkedHashSet<K>();
+ return new LinkedHashSet<>();
}
}
diff --git a/src/test/java/com/beust/jcommander/ArgMultiNameValidator.java b/src/test/java/com/beust/jcommander/ArgMultiNameValidator.java
new file mode 100644
index 0000000..92a79bc
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/ArgMultiNameValidator.java
@@ -0,0 +1,19 @@
+package com.beust.jcommander;
+
+/**
+ * Created by jeremysolarz on 12/15/16.
+ */
+public class ArgMultiNameValidator {
+
+ public static class MultiNameValidator implements IValueValidator<String> {
+
+ public static String parsedName;
+
+ public void validate(String name, String value) throws ParameterException {
+ parsedName = name;
+ }
+ }
+
+ @Parameter(names = { "-name1", "-name2" }, description = "Names of parameter", validateValueWith = MultiNameValidator.class, required = true)
+ private String parameter;
+}
diff --git a/src/test/java/com/beust/jcommander/CmdTest.java b/src/test/java/com/beust/jcommander/CmdTest.java
index 6601193..229517d 100644
--- a/src/test/java/com/beust/jcommander/CmdTest.java
+++ b/src/test/java/com/beust/jcommander/CmdTest.java
@@ -17,7 +17,7 @@ public class CmdTest {
@Parameters(commandNames = "--cmd-two")
class CmdTwo {
@Parameter
- List<String> params = new java.util.LinkedList<String>();
+ List<String> params = new java.util.LinkedList<>();
}
public String parseArgs(boolean withDefault, String[] args) {
@@ -35,7 +35,7 @@ public class CmdTest {
// is named "WithoutValidation".
jc.parseWithoutValidation(args);
if (jc.getParsedCommand() == null) {
- LinkedList<String> newArgs = new LinkedList<String>();
+ LinkedList<String> newArgs = new LinkedList<>();
newArgs.add("--cmd-two");
newArgs.addAll(Arrays.asList(args));
jc.parse(newArgs.toArray(new String[0]));
@@ -70,7 +70,12 @@ public class CmdTest {
public void testArgsWithoutDefaultCmdFail(String expected,
boolean requireDefault, String[] args) {
if (requireDefault) {
- parseArgs(false, args);
+ try {
+ parseArgs(false, args);
+ } catch (MissingCommandException e) {
+ Assert.assertEquals(e.getUnknownCommand(), args[0]);
+ throw e;
+ }
} else {
throw new MissingCommandException("irrelevant test case");
}
@@ -83,4 +88,20 @@ public class CmdTest {
Assert.assertEquals(parseArgs(true, args), expected);
}
-} \ No newline at end of file
+ @Test
+ public void testIssue244() throws Exception {
+ class P1 {}
+ class P2 {
+ @Parameter(names = "--hello")
+ private int test;
+ }
+ P1 p1 = new P1();
+ P2 p2 = new P2();
+ JCommander j = new JCommander(p1);
+ j.addCommand("wonderful", p2);
+ j.setAllowAbbreviatedOptions(true);
+ j.parse("wond", "--he", "47");
+ Assert.assertEquals("wonderful", j.getParsedCommand());
+ Assert.assertEquals(47, p2.test);
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/ConverterFactoryTest.java b/src/test/java/com/beust/jcommander/ConverterFactoryTest.java
index e02166e..4b8b923 100644
--- a/src/test/java/com/beust/jcommander/ConverterFactoryTest.java
+++ b/src/test/java/com/beust/jcommander/ConverterFactoryTest.java
@@ -62,9 +62,10 @@ public class ConverterFactoryTest {
* Test that main parameters can be used with string converters,
* either with a factory or from the annotation.
*/
- private void mainWithHostPortParameters(IStringConverterFactory f, IHostPorts a) {
+ private void mainWithHostPortParameters(IStringConverterFactory f, IStringConverterInstanceFactory f2, IHostPorts a) {
JCommander jc = new JCommander(a);
if (f != null) jc.addConverterFactory(f);
+ if (f2 != null) jc.addConverterInstanceFactory(f2);
jc.parse("a.com:10", "b.com:20");
Assert.assertEquals(a.getHostPorts().get(0).host, "a.com");
Assert.assertEquals(a.getHostPorts().get(0).port.intValue(), 10);
@@ -74,12 +75,27 @@ public class ConverterFactoryTest {
@Test
public void mainWithoutFactory() {
- mainWithHostPortParameters(null, new ArgsMainParameter1());
+ mainWithHostPortParameters(null, null, new ArgsMainParameter2());
+ }
+
+ @Test(expectedExceptions = RuntimeException.class)
+ public void mainWithoutConverterWithoutFactory() {
+ mainWithHostPortParameters(null, null, new ArgsMainParameter1());
}
@Test
public void mainWithFactory() {
- mainWithHostPortParameters(CONVERTER_FACTORY, new ArgsMainParameter2());
+ mainWithHostPortParameters(CONVERTER_FACTORY, null, new ArgsMainParameter1());
+ }
+
+ @Test
+ public void mainWithInstanceFactory() {
+ mainWithHostPortParameters(null, new IStringConverterInstanceFactory() {
+ @Override
+ public IStringConverter<?> getConverterInstance(Parameter parameter, Class<?> forType, String optionName) {
+ return HostPort.class.equals(forType) ? new HostPortConverter() : null;
+ }
+ }, new ArgsMainParameter1());
}
}
diff --git a/src/test/java/com/beust/jcommander/DefaultProviderTest.java b/src/test/java/com/beust/jcommander/DefaultProviderTest.java
index 45ab6b6..45fad38 100644
--- a/src/test/java/com/beust/jcommander/DefaultProviderTest.java
+++ b/src/test/java/com/beust/jcommander/DefaultProviderTest.java
@@ -117,4 +117,24 @@ public class DefaultProviderTest {
Assert.assertEquals(a.log.intValue(), 19);
}
+ @Test
+ public void missingRequiredParameterWithDefaultValueProviderShouldNotRaiseParameterException() {
+ class ArgsRequired {
+ @Parameter(names = "-log", description = "Level of verbosity", required = true)
+ public Integer log;
+ }
+
+ IDefaultProvider defaultProvider = new IDefaultProvider() {
+ public String getDefaultValueFor(String optionName) {
+ return "-log".equals(optionName) ? "1" : "";
+ }
+ };
+
+ ArgsRequired a = new ArgsRequired();
+ JCommander jc = new JCommander(a);
+ jc.setDefaultProvider(defaultProvider);
+ jc.parse();
+
+ Assert.assertEquals(a.log.intValue(), 1);
+ }
}
diff --git a/src/test/java/com/beust/jcommander/DefaultValueTest.java b/src/test/java/com/beust/jcommander/DefaultValueTest.java
index 3b1f29c..403ecfd 100644
--- a/src/test/java/com/beust/jcommander/DefaultValueTest.java
+++ b/src/test/java/com/beust/jcommander/DefaultValueTest.java
@@ -36,7 +36,7 @@ public class DefaultValueTest {
public void emptyDefaultValueForListParameterStaysEmptyIfNotAssignedOrIsSetOtherwise() {
MyOptsWithEmptyDefaults opts = new MyOptsWithEmptyDefaults();
JCommander cmd = new JCommander(opts);
- cmd.parse(new String[]{"-a", "anotherValue"});
+ cmd.parse("-a", "anotherValue");
Assert.assertEquals(opts.list.size(), 1);
Assert.assertEquals(opts.list.get(0), "anotherValue");
Assert.assertEquals(opts.set.size(), 0);
@@ -46,7 +46,7 @@ public class DefaultValueTest {
public void defaultValueForListParametersGetsOverwrittenWithSpecifiedValueOrStaysAsDefaultOtherwise() {
MyOptsWithDefaultValues opts = new MyOptsWithDefaultValues();
JCommander cmd = new JCommander(opts);
- cmd.parse(new String[]{"-a", "anotherValue"});
+ cmd.parse("-a", "anotherValue");
Assert.assertEquals(opts.list.size(), 1);
Assert.assertEquals(opts.list.get(0), "anotherValue");
Assert.assertEquals(opts.set.size(), 1);
@@ -67,8 +67,8 @@ public class DefaultValueTest {
private void testSettingMultipleValuesToListTypeParameters(MyOpts opts) {
JCommander cmd = new JCommander(opts);
- cmd.parse(new String[]{"-a", "anotherValue", "-a", "anotherValue2",
- "-b", "anotherValue3", "-b", "anotherValue4"});
+ cmd.parse("-a", "anotherValue", "-a", "anotherValue2",
+ "-b", "anotherValue3", "-b", "anotherValue4");
Assert.assertEquals(opts.list.size(), 2);
Assert.assertEquals(opts.list.get(0), "anotherValue");
Assert.assertEquals(opts.list.get(1), "anotherValue2");
diff --git a/src/test/java/com/beust/jcommander/FinderTest.java b/src/test/java/com/beust/jcommander/FinderTest.java
index 94bf812..d4adda9 100644
--- a/src/test/java/com/beust/jcommander/FinderTest.java
+++ b/src/test/java/com/beust/jcommander/FinderTest.java
@@ -17,7 +17,7 @@ public class FinderTest {
Arg a = new Arg();
JCommander jc = new JCommander(a);
jc.setCaseSensitiveOptions(false);
- jc.parse(new String[] { "--PARAM", "foo" });
+ jc.parse("--PARAM", "foo");
Assert.assertEquals(a.param, "foo");
}
@@ -27,7 +27,6 @@ public class FinderTest {
JCommander jc = new JCommander(a);
jc.addCommand(conf);
jc.setCaseSensitiveOptions(false);
-// jc.setCaseSensitiveCommands(false);
jc.parse("--CONFIGURE");
String command = jc.getParsedCommand();
Assert.assertEquals(command, "--configure");
@@ -41,7 +40,7 @@ public class FinderTest {
Arg a = new Arg();
JCommander jc = new JCommander(a);
jc.setAllowAbbreviatedOptions(true);
- jc.parse(new String[] { "--par", "foo" });
+ jc.parse("--par", "foo");
Assert.assertEquals(a.param, "foo");
}
@@ -54,7 +53,7 @@ public class FinderTest {
JCommander jc = new JCommander(a);
jc.setCaseSensitiveOptions(false);
jc.setAllowAbbreviatedOptions(true);
- jc.parse(new String[] { "--PAR", "foo" });
+ jc.parse("--PAR", "foo");
Assert.assertEquals(a.param, "foo");
}
@@ -69,7 +68,7 @@ public class FinderTest {
Arg a = new Arg();
JCommander jc = new JCommander(a);
jc.setAllowAbbreviatedOptions(true);
- jc.parse(new String[] { "--par", "foo" });
+ jc.parse("--par", "foo");
Assert.assertEquals(a.param, "foo");
}
@@ -85,7 +84,7 @@ public class FinderTest {
JCommander jc = new JCommander(a);
jc.setCaseSensitiveOptions(false);
jc.setAllowAbbreviatedOptions(true);
- jc.parse(new String[] { "--PAR", "foo" });
+ jc.parse("--PAR", "foo");
Assert.assertEquals(a.param, "foo");
}
diff --git a/src/test/java/com/beust/jcommander/HiddenConverter.java b/src/test/java/com/beust/jcommander/HiddenConverter.java
new file mode 100644
index 0000000..cd36b85
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/HiddenConverter.java
@@ -0,0 +1,29 @@
+/**
+ * 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;
+
+public class HiddenConverter implements IStringConverter<String> {
+ private HiddenConverter() {
+ }
+
+ @Override
+ public String convert(String value) {
+ return value;
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/HiddenParameterSplitter.java b/src/test/java/com/beust/jcommander/HiddenParameterSplitter.java
new file mode 100644
index 0000000..a40f36f
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/HiddenParameterSplitter.java
@@ -0,0 +1,31 @@
+/**
+ * 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 java.util.Arrays;
+import java.util.List;
+
+import com.beust.jcommander.converters.IParameterSplitter;
+
+public class HiddenParameterSplitter implements IParameterSplitter {
+ @Override
+ public List<String> split(String value) {
+ return Arrays.asList(value.split(";"));
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/JCommanderTest.java b/src/test/java/com/beust/jcommander/JCommanderTest.java
index ad2c5e8..e967ef7 100644
--- a/src/test/java/com/beust/jcommander/JCommanderTest.java
+++ b/src/test/java/com/beust/jcommander/JCommanderTest.java
@@ -2,13 +2,13 @@
* Copyright (C) 2010 the original author or authors.
* See the notice.md file distributed with this work for additional
* information regarding copyright ownership.
- *
+ * <p>
* 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
- *
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
* 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.
@@ -18,1064 +18,1516 @@
package com.beust.jcommander;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigDecimal;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.ResourceBundle;
-import java.util.TreeSet;
-
-import org.testng.Assert;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import com.beust.jcommander.args.AlternateNamesForListArgs;
-import com.beust.jcommander.args.Args1;
-import com.beust.jcommander.args.Args1Setter;
-import com.beust.jcommander.args.Args2;
-import com.beust.jcommander.args.ArgsArityString;
-import com.beust.jcommander.args.ArgsBooleanArity;
-import com.beust.jcommander.args.ArgsBooleanArity0;
-import com.beust.jcommander.args.ArgsConverter;
-import com.beust.jcommander.args.ArgsEnum;
+import com.beust.jcommander.args.*;
import com.beust.jcommander.args.ArgsEnum.ChoiceType;
-import com.beust.jcommander.args.ArgsEquals;
-import com.beust.jcommander.args.ArgsHelp;
-import com.beust.jcommander.args.ArgsI18N1;
-import com.beust.jcommander.args.ArgsI18N2;
-import com.beust.jcommander.args.ArgsI18N2New;
-import com.beust.jcommander.args.ArgsInherited;
-import com.beust.jcommander.args.ArgsList;
-import com.beust.jcommander.args.ArgsMainParameter1;
-import com.beust.jcommander.args.ArgsMaster;
-import com.beust.jcommander.args.ArgsMultipleUnparsed;
-import com.beust.jcommander.args.ArgsOutOfMemory;
-import com.beust.jcommander.args.ArgsPrivate;
-import com.beust.jcommander.args.ArgsRequired;
-import com.beust.jcommander.args.ArgsSlave;
-import com.beust.jcommander.args.ArgsSlaveBogus;
-import com.beust.jcommander.args.ArgsValidate1;
-import com.beust.jcommander.args.ArgsWithSet;
-import com.beust.jcommander.args.Arity1;
-import com.beust.jcommander.args.SeparatorColon;
-import com.beust.jcommander.args.SeparatorEqual;
-import com.beust.jcommander.args.SeparatorMixed;
-import com.beust.jcommander.args.SlashSeparator;
-import com.beust.jcommander.args.VariableArity;
import com.beust.jcommander.command.CommandAdd;
import com.beust.jcommander.command.CommandCommit;
import com.beust.jcommander.command.CommandMain;
+import com.beust.jcommander.converters.FileConverter;
import com.beust.jcommander.internal.Lists;
import com.beust.jcommander.internal.Maps;
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.ResourceBundle;
@Test
public class JCommanderTest {
- public void simpleArgs() throws ParseException {
- Args1 args = new Args1();
- String[] argv = { "-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
- "-date", "2011-10-26", "-groups", "unit", "a", "b", "c" };
- new JCommander(args, argv);
-
- Assert.assertTrue(args.debug);
- Assert.assertEquals(args.verbose.intValue(), 2);
- Assert.assertEquals(args.groups, "unit");
- Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
- Assert.assertEquals(args.floa, 1.2f, 0.1f);
- Assert.assertEquals(args.doub, 1.3f, 0.1f);
- Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
- Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
- }
-
- @DataProvider
- public Object[][] alternateNamesListArgs() {
- return new Object[][] {
- new String[][] {new String[] {"--servers", "1", "-s", "2", "--servers", "3"}},
- new String[][] {new String[] {"-s", "1", "-s", "2", "--servers", "3"}},
- new String[][] {new String[] {"--servers", "1", "--servers", "2", "-s", "3"}},
- new String[][] {new String[] {"-s", "1", "--servers", "2", "-s", "3"}},
- new String[][] {new String[] {"-s", "1", "-s", "2", "--servers", "3"}},
- };
- }
-
- /**
- * Confirm that List<?> parameters with alternate names return the correct
- * List regardless of how the arguments are specified
- */
-
- @Test(dataProvider = "alternateNamesListArgs")
- public void testAlternateNamesForListArguments(String[] argv) {
- AlternateNamesForListArgs args = new AlternateNamesForListArgs();
-
- new JCommander(args, argv);
-
- Assert.assertEquals(args.serverNames.size(), 3);
- Assert.assertEquals(args.serverNames.get(0), argv[1]);
- Assert.assertEquals(args.serverNames.get(1), argv[3]);
- Assert.assertEquals(args.serverNames.get(2), argv[5]);
- }
-
-
- /**
- * Make sure that if there are args with multiple names (e.g. "-log" and "-verbose"),
- * the usage will only display it once.
- */
- public void repeatedArgs() {
- Args1 args = new Args1();
- String[] argv = { "-log", "2" };
- JCommander jc = new JCommander(args, argv);
- Assert.assertEquals(jc.getParameters().size(), 8);
- }
-
- /**
- * Not specifying a required option should throw an exception.
- */
- @Test(expectedExceptions = ParameterException.class)
- public void requiredFields1Fail() {
- Args1 args = new Args1();
- String[] argv = { "-debug" };
- new JCommander(args, argv);
- }
-
- /**
- * Getting the description of a nonexistent command should throw an exception.
- */
- @Test(expectedExceptions = ParameterException.class)
- public void nonexistentCommandShouldThrow() {
- String[] argv = { };
- JCommander jc = new JCommander(new Object(), argv);
- jc.getCommandDescription("foo");
- }
-
- /**
- * Required options with multiple names should work with all names.
- */
- private void multipleNames(String option) {
- Args1 args = new Args1();
- String[] argv = { option, "2" };
- new JCommander(args, argv);
- Assert.assertEquals(args.verbose.intValue(), 2);
- }
-
- public void multipleNames1() {
- multipleNames("-log");
- }
-
- public void multipleNames2() {
- multipleNames("-verbose");
- }
-
- private void i18n1(String bundleName, Locale locale, String expectedString) {
- ResourceBundle bundle = locale != null ? ResourceBundle.getBundle(bundleName, locale)
- : null;
-
- ArgsI18N1 i18n = new ArgsI18N1();
- String[] argv = { "-host", "localhost" };
- JCommander jc = new JCommander(i18n, bundle, argv);
-// jc.usage();
-
- ParameterDescription pd = jc.getParameters().get(0);
- Assert.assertEquals(pd.getDescription(), expectedString);
- }
-
- public void i18nNoLocale() {
- i18n1("MessageBundle", null, "Host");
- }
-
- public void i18nUsLocale() {
- i18n1("MessageBundle", new Locale("en", "US"), "Host");
- }
-
- public void i18nFrLocale() {
- i18n1("MessageBundle", new Locale("fr", "FR"), "Hôte");
- }
-
- private void i18n2(Object i18n) {
- String[] argv = { "-host", "localhost" };
- Locale.setDefault(new Locale("fr", "FR"));
- JCommander jc = new JCommander(i18n, argv);
- ParameterDescription pd = jc.getParameters().get(0);
- Assert.assertEquals(pd.getDescription(), "Hôte");
- }
-
- public void i18nWithResourceAnnotation() {
- i18n2(new ArgsI18N2());
- }
-
- public void i18nWithResourceAnnotationNew() {
- i18n2(new ArgsI18N2New());
- }
-
- public void noParseConstructor() {
- JCommander jCommander = new JCommander(new ArgsMainParameter1());
- jCommander.usage(new StringBuilder());
- // Before fix, this parse would throw an exception, because it calls createDescription, which
- // was already called by usage(), and can only be called once.
- jCommander.parse();
- }
-
- /**
- * Test a use case where there are required parameters, but you still want
- * to interrogate the options which are specified.
- */
- public void usageWithRequiredArgsAndResourceBundle() {
- ArgsHelp argsHelp = new ArgsHelp();
- JCommander jc = new JCommander(new Object[]{argsHelp, new ArgsRequired()},
- java.util.ResourceBundle.getBundle("MessageBundle"));
- // Should be able to display usage without triggering validation
- jc.usage(new StringBuilder());
- try {
- jc.parse("-h");
- Assert.fail("Should have thrown a required parameter exception");
- } catch (ParameterException e) {
- Assert.assertTrue(e.getMessage().contains("are required"));
- }
- Assert.assertTrue(argsHelp.help);
- }
-
- public void multiObjects() {
- ArgsMaster m = new ArgsMaster();
- ArgsSlave s = new ArgsSlave();
- String[] argv = { "-master", "master", "-slave", "slave" };
- new JCommander(new Object[] { m , s }, argv);
-
- Assert.assertEquals(m.master, "master");
- Assert.assertEquals(s.slave, "slave");
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void multiObjectsWithDuplicatesFail() {
- ArgsMaster m = new ArgsMaster();
- ArgsSlave s = new ArgsSlaveBogus();
- String[] argv = { "-master", "master", "-slave", "slave" };
- new JCommander(new Object[] { m , s }, argv);
- }
-
- public void arityString() {
- ArgsArityString args = new ArgsArityString();
- String[] argv = { "-pairs", "pair0", "pair1", "rest" };
- new JCommander(args, argv);
-
- Assert.assertEquals(args.pairs.size(), 2);
- Assert.assertEquals(args.pairs.get(0), "pair0");
- Assert.assertEquals(args.pairs.get(1), "pair1");
- Assert.assertEquals(args.rest.size(), 1);
- Assert.assertEquals(args.rest.get(0), "rest");
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void arity2Fail() {
- ArgsArityString args = new ArgsArityString();
- String[] argv = { "-pairs", "pair0" };
- new JCommander(args, argv);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void multipleUnparsedFail() {
- ArgsMultipleUnparsed args = new ArgsMultipleUnparsed();
- String[] argv = { };
- new JCommander(args, argv);
- }
-
- public void privateArgs() {
- ArgsPrivate args = new ArgsPrivate();
- new JCommander(args, "-verbose", "3");
- Assert.assertEquals(args.getVerbose().intValue(), 3);
- }
-
- public void converterArgs() {
- ArgsConverter args = new ArgsConverter();
- String fileName = "a";
- new JCommander(args, "-file", "/tmp/" + fileName,
- "-listStrings", "Tuesday,Thursday",
- "-listInts", "-1,8",
- "-listBigDecimals", "-11.52,100.12");
- Assert.assertEquals(args.file.getName(), fileName);
- Assert.assertEquals(args.listStrings.size(), 2);
- Assert.assertEquals(args.listStrings.get(0), "Tuesday");
- Assert.assertEquals(args.listStrings.get(1), "Thursday");
- Assert.assertEquals(args.listInts.size(), 2);
- Assert.assertEquals(args.listInts.get(0).intValue(), -1);
- Assert.assertEquals(args.listInts.get(1).intValue(), 8);
- Assert.assertEquals(args.listBigDecimals.size(), 2);
- Assert.assertEquals(args.listBigDecimals.get(0), new BigDecimal("-11.52"));
- Assert.assertEquals(args.listBigDecimals.get(1), new BigDecimal("100.12"));
- }
-
- private void argsBoolean1(String[] params, Boolean expected) {
- ArgsBooleanArity args = new ArgsBooleanArity();
- new JCommander(args, params);
- Assert.assertEquals(args.debug, expected);
- }
-
- private void argsBoolean0(String[] params, Boolean expected) {
- ArgsBooleanArity0 args = new ArgsBooleanArity0();
- new JCommander(args, params);
- Assert.assertEquals(args.debug, expected);
- }
-
- public void booleanArity1() {
- argsBoolean1(new String[] {}, Boolean.FALSE);
- argsBoolean1(new String[] { "-debug", "true" }, Boolean.TRUE);
- }
-
- public void booleanArity0() {
- argsBoolean0(new String[] {}, Boolean.FALSE);
- argsBoolean0(new String[] { "-debug"}, Boolean.TRUE);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void badParameterShouldThrowParameter1Exception() {
- Args1 args = new Args1();
- String[] argv = { "-log", "foo" };
- new JCommander(args, argv);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void badParameterShouldThrowParameter2Exception() {
- Args1 args = new Args1();
- String[] argv = { "-long", "foo" };
- new JCommander(args, argv);
- }
-
- public void listParameters() {
- Args2 a = new Args2();
- String[] argv = {"-log", "2", "-groups", "unit", "a", "b", "c", "-host", "host2"};
- new JCommander(a, argv);
- Assert.assertEquals(a.verbose.intValue(), 2);
- Assert.assertEquals(a.groups, "unit");
- Assert.assertEquals(a.hosts, Arrays.asList("host2"));
- Assert.assertEquals(a.parameters, Arrays.asList("a", "b", "c"));
- }
-
- public void separatorEqual() {
- SeparatorEqual s = new SeparatorEqual();
- String[] argv = { "-log=3", "--longoption=10" };
- new JCommander(s, argv);
- Assert.assertEquals(s.log.intValue(), 3);
- Assert.assertEquals(s.longOption.intValue(), 10);
- }
-
- public void separatorColon() {
- SeparatorColon s = new SeparatorColon();
- String[] argv = { "-verbose:true" };
- new JCommander(s, argv);
- Assert.assertTrue(s.verbose);
- }
-
- public void separatorBoth() {
- SeparatorColon s = new SeparatorColon();
- SeparatorEqual s2 = new SeparatorEqual();
- String[] argv = { "-verbose:true", "-log=3" };
- new JCommander(new Object[] { s, s2 }, argv);
- Assert.assertTrue(s.verbose);
- Assert.assertEquals(s2.log.intValue(), 3);
- }
-
- public void separatorMixed1() {
- SeparatorMixed s = new SeparatorMixed();
- String[] argv = { "-long:1", "-level=42" };
- new JCommander(s, argv);
- Assert.assertEquals(s.l.longValue(), 1l);
- Assert.assertEquals(s.level.intValue(), 42);
- }
-
- public void slashParameters() {
- SlashSeparator a = new SlashSeparator();
- String[] argv = { "/verbose", "/file", "/tmp/a" };
- new JCommander(a, argv);
- Assert.assertTrue(a.verbose);
- Assert.assertEquals(a.file, "/tmp/a");
- }
-
- public void inheritance() {
- ArgsInherited args = new ArgsInherited();
- String[] argv = { "-log", "3", "-child", "2" };
- new JCommander(args, argv);
- Assert.assertEquals(args.child.intValue(), 2);
- Assert.assertEquals(args.log.intValue(), 3);
- }
-
- public void negativeNumber() {
- Args1 a = new Args1();
- String[] argv = { "-verbose", "-3" };
- new JCommander(a, argv);
- Assert.assertEquals(a.verbose.intValue(), -3);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void requiredMainParameters() {
- ArgsRequired a = new ArgsRequired();
- String[] argv = {};
- new JCommander(a, argv);
- }
-
- public void usageShouldNotChange() {
- JCommander jc = new JCommander(new Args1(), new String[]{"-log", "1"});
- StringBuilder sb = new StringBuilder();
- jc.usage(sb);
- String expected = sb.toString();
- jc = new JCommander(new Args1(), new String[]{"-debug", "-log", "2", "-long", "5"});
- sb = new StringBuilder();
- jc.usage(sb);
- String actual = sb.toString();
- Assert.assertEquals(actual, expected);
- }
-
- private void verifyCommandOrdering(String[] commandNames, Object[] commands) {
- CommandMain cm = new CommandMain();
- JCommander jc = new JCommander(cm);
-
- for (int i = 0; i < commands.length; i++) {
- jc.addCommand(commandNames[i], commands[i]);
- }
-
- Map<String, JCommander> c = jc.getCommands();
- Assert.assertEquals(c.size(), commands.length);
-
- Iterator<String> it = c.keySet().iterator();
- for (int i = 0; i < commands.length; i++) {
- Assert.assertEquals(it.next(), commandNames[i]);
- }
- }
-
- public void commandsShouldBeShownInOrderOfInsertion() {
- verifyCommandOrdering(new String[] { "add", "commit" },
- new Object[] { new CommandAdd(), new CommandCommit() });
- verifyCommandOrdering(new String[] { "commit", "add" },
- new Object[] { new CommandCommit(), new CommandAdd() });
- }
-
- @DataProvider
- public static Object[][] f() {
- return new Integer[][] {
- new Integer[] { 3, 5, 1 },
- new Integer[] { 3, 8, 1 },
- new Integer[] { 3, 12, 2 },
- new Integer[] { 8, 12, 2 },
- new Integer[] { 9, 10, 1 },
- };
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void arity1Fail() {
- final Arity1 arguments = new Arity1();
- final JCommander jCommander = new JCommander(arguments);
- final String[] commands = {
- "-inspect"
- };
- jCommander.parse(commands);
- }
-
- public void arity1Success1() {
- final Arity1 arguments = new Arity1();
- final JCommander jCommander = new JCommander(arguments);
- final String[] commands = {
- "-inspect", "true"
- };
- jCommander.parse(commands);
- Assert.assertTrue(arguments.inspect);
- }
-
- public void arity1Success2() {
- final Arity1 arguments = new Arity1();
- final JCommander jCommander = new JCommander(arguments);
- final String[] commands = {
- "-inspect", "false"
- };
- jCommander.parse(commands);
- Assert.assertFalse(arguments.inspect);
- }
-
- @Parameters(commandDescription = "Help for the given commands.")
- public static class Help {
- public static final String NAME = "help";
-
- @Parameter(description = "List of commands.")
- public List<String> commands=new ArrayList<String>();
- }
-
- @Test(expectedExceptions = ParameterException.class,
- description = "Verify that the main parameter's type is checked to be a List")
- public void wrongMainTypeShouldThrow() {
- JCommander jc = new JCommander(new ArgsRequiredWrongMain());
- jc.parse(new String[] { "f1", "f2" });
- }
-
- @Test(description = "This used to run out of memory")
- public void oom() {
- JCommander jc = new JCommander(new ArgsOutOfMemory());
- jc.usage(new StringBuilder());
- }
-
- @Test
- public void getParametersShouldNotNpe() {
- JCommander jc = new JCommander(new Args1());
- List<ParameterDescription> parameters = jc.getParameters();
- }
-
- public void validationShouldWork1() {
- ArgsValidate1 a = new ArgsValidate1();
- JCommander jc = new JCommander(a);
- jc.parse(new String[] { "-age", "2 "});
- Assert.assertEquals(a.age, new Integer(2));
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void validationShouldWorkWithDefaultValues() {
- ArgsValidate2 a = new ArgsValidate2();
- new JCommander(a);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void validationShouldWork2() {
- ArgsValidate1 a = new ArgsValidate1();
- JCommander jc = new JCommander(a);
- jc.parse(new String[] { "-age", "-2 "});
- }
-
- public void atFileCanContainEmptyLines() throws IOException {
- File f = File.createTempFile("JCommander", null);
- f.deleteOnExit();
- FileWriter fw = new FileWriter(f);
- fw.write("-log\n");
- fw.write("\n");
- fw.write("2\n");
- fw.close();
- new JCommander(new Args1(), "@" + f.getAbsolutePath());
- }
-
- public void handleEqualSigns() {
- ArgsEquals a = new ArgsEquals();
- JCommander jc = new JCommander(a);
- jc.parse(new String[] { "-args=a=b,b=c" });
- Assert.assertEquals(a.args, "a=b,b=c");
- }
-
- @SuppressWarnings("serial")
- public void handleSets() {
- ArgsWithSet a = new ArgsWithSet();
- new JCommander(a, new String[] { "-s", "3,1,2" });
- Assert.assertEquals(a.set, new TreeSet<Integer>() {{ add(1); add(2); add(3); }});
- }
-
- private static final List<String> V = Arrays.asList("a", "b", "c", "d");
-
- @DataProvider
- public Object[][] variable() {
- return new Object[][] {
- new Object[] { 0, V.subList(0, 0), V },
- new Object[] { 1, V.subList(0, 1), V.subList(1, 4) },
- new Object[] { 2, V.subList(0, 2), V.subList(2, 4) },
- new Object[] { 3, V.subList(0, 3), V.subList(3, 4) },
- new Object[] { 4, V.subList(0, 4), V.subList(4, 4) },
- };
- }
-
- @Test(dataProvider = "variable")
- public void variableArity(int count, List<String> var, List<String> main) {
- VariableArity va = new VariableArity(count);
- new JCommander(va).parse("-variable", "a", "b", "c", "d");
- Assert.assertEquals(var, va.var);
- Assert.assertEquals(main, va.main);
- }
-
- public void enumArgs() {
- ArgsEnum args = new ArgsEnum();
- String[] argv = { "-choice", "ONE", "-choices", "ONE", "Two" };
- JCommander jc = new JCommander(args, argv);
-
- Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
-
- List<ChoiceType> expected = Arrays.asList(ChoiceType.ONE, ChoiceType.Two);
- Assert.assertEquals(expected, args.choices);
- Assert.assertEquals(jc.getParameters().get(0).getDescription(),
- "Options: " + EnumSet.allOf((Class<? extends Enum>) ArgsEnum.ChoiceType.class));
-
- }
-
- public void enumArgsCaseInsensitive() {
- ArgsEnum args = new ArgsEnum();
- String[] argv = { "-choice", "one"};
- JCommander jc = new JCommander(args, argv);
-
- Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void enumArgsFail() {
- ArgsEnum args = new ArgsEnum();
- String[] argv = { "-choice", "A" };
- new JCommander(args, argv);
- }
-
- public void testListAndSplitters() {
- ArgsList al = new ArgsList();
- JCommander j = new JCommander(al);
- j.parse("-groups", "a,b", "-ints", "41,42", "-hp", "localhost:1000;example.com:1001",
- "-hp2", "localhost:1000,example.com:1001", "-uppercase", "ab,cd");
- Assert.assertEquals(al.groups.get(0), "a");
- Assert.assertEquals(al.groups.get(1), "b");
- Assert.assertEquals(al.ints.get(0).intValue(), 41);
- Assert.assertEquals(al.ints.get(1).intValue(), 42);
- Assert.assertEquals(al.hostPorts.get(0).host, "localhost");
- Assert.assertEquals(al.hostPorts.get(0).port.intValue(), 1000);
- Assert.assertEquals(al.hostPorts.get(1).host, "example.com");
- Assert.assertEquals(al.hostPorts.get(1).port.intValue(), 1001);
- Assert.assertEquals(al.hp2.get(1).host, "example.com");
- Assert.assertEquals(al.hp2.get(1).port.intValue(), 1001);
- Assert.assertEquals(al.uppercase.get(0), "AB");
- Assert.assertEquals(al.uppercase.get(1), "CD");
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void shouldThrowIfUnknownOption() {
- class A {
- @Parameter(names = "-long")
- public long l;
- }
- A a = new A();
- new JCommander(a).parse("-lon", "32");
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void mainParameterShouldBeValidate() {
- class V implements IParameterValidator {
-
- @Override
- public void validate(String name, String value) throws ParameterException {
- Assert.assertEquals("a", value);
- }
- }
-
- class A {
- @Parameter(validateWith = V.class)
- public List<String> m;
- }
-
- A a = new A();
- new JCommander(a).parse("b");
- }
-
- @Parameters(commandNames = { "--configure" })
- public static class ConfigureArgs {
- }
-
- public static class BaseArgs {
- @Parameter(names = { "-h", "--help" }, description = "Show this help screen")
- private boolean help = false;
-
- @Parameter(names = { "--version", "-version" }, description = "Show the program version")
- private boolean version;
- }
-
- public void commandsWithSamePrefixAsOptionsShouldWork() {
- BaseArgs a = new BaseArgs();
- ConfigureArgs conf = new ConfigureArgs();
- JCommander jc = new JCommander(a);
- jc.addCommand(conf);
- jc.parse("--configure");
- }
-
- // Tests:
- // required unparsed parameter
- @Test(enabled = false,
- description = "For some reason, this test still asks the password on stdin")
- public void askedRequiredPassword() {
- class A {
- @Parameter(names = { "--password", "-p" }, description = "Private key password",
- password = true, required = true)
- public String password;
-
- @Parameter(names = { "--port", "-o" }, description = "Port to bind server to",
- required = true)
- public int port;
- }
- A a = new A();
- InputStream stdin = System.in;
- try {
- System.setIn(new ByteArrayInputStream("password".getBytes()));
- new JCommander(a,new String[]{"--port", "7","--password"});
- Assert.assertEquals(a.port, 7);
- Assert.assertEquals(a.password, "password");
- } finally {
- System.setIn(stdin);
- }
- }
-
- public void dynamicParameters() {
- class Command {
- @DynamicParameter(names = {"-P"}, description = "Additional command parameters")
- private Map<String, String> params = Maps.newHashMap();
- }
- JCommander commander = new JCommander();
- Command c = new Command();
- commander.addCommand("command", c);
- commander.parse(new String[] { "command", "-Pparam='name=value'" });
- Assert.assertEquals(c.params.get("param"), "'name=value'");
- }
-
- public void exeParser() {
- class Params {
- @Parameter( names= "-i")
- private String inputFile;
- }
-
- String args[] = { "-i", "" };
- Params p = new Params();
- new JCommander(p, args);
- }
-
- public void multiVariableArityList() {
- class Params {
- @Parameter(names = "-paramA", description = "ParamA", variableArity = true)
- private List<String> paramA = Lists.newArrayList();
-
- @Parameter(names = "-paramB", description = "ParamB", variableArity = true)
- private List<String> paramB = Lists.newArrayList();
- }
-
- {
- String args[] = { "-paramA", "a1", "a2", "-paramB", "b1", "b2", "b3" };
- Params p = new Params();
- new JCommander(p, args).parse();
- Assert.assertEquals(p.paramA, Arrays.asList(new String[] { "a1", "a2" }));
- Assert.assertEquals(p.paramB, Arrays.asList(new String[] { "b1", "b2", "b3" }));
- }
-
- {
- String args[] = { "-paramA", "a1", "a2", "-paramB", "b1", "-paramA", "a3" };
- Params p = new Params();
- new JCommander(p, args).parse();
- Assert.assertEquals(p.paramA, Arrays.asList(new String[] { "a1", "a2", "a3" }));
- Assert.assertEquals(p.paramB, Arrays.asList(new String[] { "b1" }));
- }
- }
-
- @Test(enabled = false,
- description = "Need to double check that the command description is i18n'ed in the usage")
- public void commandKey() {
- @Parameters(resourceBundle = "MessageBundle", commandDescriptionKey = "command")
- class Args {
- @Parameter(names="-myoption", descriptionKey="myoption")
- private boolean option;
- }
- JCommander j = new JCommander();
- Args a = new Args();
- j.addCommand("comm", a);
- j.usage();
- }
-
- public void tmp() {
- class A {
- @Parameter(names = "-b")
- public String b;
- }
- new JCommander(new A()).parse("");
- }
-
- public void unknownOptionWithDifferentPrefix() {
- @Parameters(optionPrefixes = "/")
- class SlashSeparator {
-
- @Parameter(names = "/verbose")
- public boolean verbose = false;
-
- @Parameter(names = "/file")
- public String file;
- }
- SlashSeparator ss = new SlashSeparator();
- try {
- new JCommander(ss).parse("/notAParam");
- } catch (ParameterException ex) {
- boolean result = ex.getMessage().contains("Unknown option");
- Assert.assertTrue(result);
- }
- }
-
- public void equalSeparator() {
- @Parameters(separators = "=", commandDescription = "My command")
- class MyClass {
-
- @Parameter(names = { "-p", "--param" }, required = true, description = "param desc...")
- private String param;
- }
- MyClass c = new MyClass();
- String expected = "\"hello\"world";
- new JCommander(c).parse("--param=" + expected);
- Assert.assertEquals(expected, c.param);
- }
-
- public void simpleArgsSetter() throws ParseException {
- Args1Setter args = new Args1Setter();
- String[] argv = { "-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
- "-date", "2011-10-26", "-groups", "unit", "a", "b", "c" };
- new JCommander(args, argv);
-
- Assert.assertTrue(args.debug);
- Assert.assertEquals(args.verbose.intValue(), 2);
- Assert.assertEquals(args.groups, "unit");
- Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
- Assert.assertEquals(args.floa, 1.2f, 0.1f);
- Assert.assertEquals(args.doub, 1.3f, 0.1f);
- Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
- Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
- }
-
- public void verifyHelp() {
- class Arg {
- @Parameter(names = "--help", help = true)
- public boolean help = false;
-
- @Parameter(names = "file", required = true)
- public String file;
- }
- Arg arg = new Arg();
- String[] argv = { "--help" };
- new JCommander(arg, argv);
-
- Assert.assertTrue(arg.help);
- }
-
- public void helpTest() {
- class Arg {
- @Parameter(names = { "?", "-help", "--help" }, description = "Shows help", help = true)
- private boolean help = false;
- }
- Arg arg = new Arg();
- JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-help" });
-// System.out.println("helpTest:" + arg.help);
- }
-
- @Test(enabled = false, description = "Should only be enable once multiple parameters are allowed")
- public void duplicateParameterNames() {
- class ArgBase {
- @Parameter(names = { "-host" })
- protected String host;
- }
-
- class Arg1 extends ArgBase {}
- Arg1 arg1 = new Arg1();
-
- class Arg2 extends ArgBase {}
- Arg2 arg2 = new Arg2();
-
- JCommander jc = new JCommander(new Object[] { arg1, arg2});
- jc.parse(new String[] { "-host", "foo" });
- Assert.assertEquals(arg1.host, "foo");
- Assert.assertEquals(arg2.host, "foo");
- }
-
- public void parameterWithOneDoubleQuote() {
- @Parameters(separators = "=")
- class Arg {
- @Parameter(names = { "-p", "--param" })
- private String param;
- }
- JCommander jc = new JCommander(new MyClass());
- jc.parse("-p=\"");
- }
-
- public void emptyStringAsDefault() {
- class Arg {
- @Parameter(names = "-x")
- String s = "";
- }
- Arg a = new Arg();
- StringBuilder sb = new StringBuilder();
- new JCommander(a).usage(sb);
- Assert.assertTrue(sb.toString().contains("Default: <empty string>"));
- }
-
- public void spaces() {
- class Arg {
- @Parameter(names = "-rule", description = "rule")
- private List<String> rules = new ArrayList<String>();
- }
- Arg a = new Arg();
- new JCommander(a, "-rule", "some test");
- Assert.assertEquals(a.rules, Arrays.asList("some test"));
- }
-
- static class V2 implements IParameterValidator2 {
- final static List<String> names = Lists.newArrayList();
- static boolean validateCalled = false;
-
- @Override
- public void validate(String name, String value) throws ParameterException {
- validateCalled = true;
- }
-
- @Override
- public void validate(String name, String value, ParameterDescription pd)
- throws ParameterException {
- names.addAll(Arrays.asList(pd.getParameter().names()));
- }
- }
-
- public void validator2() {
- class Arg {
- @Parameter(names = { "-h", "--host" }, validateWith = V2.class)
- String host;
- }
- Arg a = new Arg();
- V2.names.clear();
- V2.validateCalled = false;
- JCommander jc = new JCommander(a, "--host", "h");
- jc.setAcceptUnknownOptions(true);
- Assert.assertEquals(V2.names, Arrays.asList(new String[] { "-h", "--host" }));
- Assert.assertTrue(V2.validateCalled);
- }
-
- public void usageCommandsUnderUsage() {
- class Arg {
- }
- @Parameters(commandDescription = "command a")
- class ArgCommandA {
- @Parameter(description = "command a parameters")
- List<String> parameters;
- }
- @Parameters(commandDescription = "command b")
- class ArgCommandB {
- @Parameter(description = "command b parameters")
- List<String> parameters;
- }
-
- Arg a = new Arg();
-
- JCommander c = new JCommander(a);
- c.addCommand("a", new ArgCommandA());
- c.addCommand("b", new ArgCommandB());
-
- StringBuilder sb = new StringBuilder();
- c.usage(sb);
- Assert.assertTrue(sb.toString().contains("[command options]\n Commands:"));
- }
-
- public void usageWithEmpytLine() {
- class Arg {
- }
- @Parameters(commandDescription = "command a")
- class ArgCommandA {
- @Parameter(description = "command a parameters")
- List<String> parameters;
- }
- @Parameters(commandDescription = "command b")
- class ArgCommandB {
- @Parameter(description = "command b parameters")
- List<String> parameters;
- }
-
- Arg a = new Arg();
-
- JCommander c = new JCommander(a);
- c.addCommand("a", new ArgCommandA());
- c.addCommand("b", new ArgCommandB());
-
- StringBuilder sb = new StringBuilder();
- c.usage(sb);
- Assert.assertTrue(sb.toString().contains("command a parameters\n\n b"));
- }
-
- public void partialValidation() {
- class Arg {
- @Parameter(names = { "-h", "--host" })
- String host;
- }
- Arg a = new Arg();
- JCommander jc = new JCommander();
- jc.setAcceptUnknownOptions(true);
- jc.addObject(a);
- jc.parse("-a", "foo", "-h", "host");
- Assert.assertEquals(a.host, "host");
- Assert.assertEquals(jc.getUnknownOptions(), Lists.newArrayList("-a", "foo"));
- }
-
- /**
- * GITHUB-137.
- */
- public void listArgShouldBeCleared() {
- class Args {
- @Parameter(description = "[endpoint]")
- public List<String> endpoint = Lists.newArrayList("prod");
- }
- Args a = new Args();
- new JCommander(a, new String[] { "dev" });
- Assert.assertEquals(a.endpoint, Lists.newArrayList("dev"));
- }
-
- public void dashDashParameter() {
- class Arguments {
- @Parameter(names = { "-name" })
- public String name;
- @Parameter
- public List<String> mainParameters;
- }
-
- Arguments a = new Arguments();
- new JCommander(a, new String[] {
- "-name", "theName", "--", "param1", "param2"}
- );
- Assert.assertEquals(a.name, "theName");
- Assert.assertEquals(a.mainParameters.size(), 2);
- Assert.assertEquals(a.mainParameters.get(0), "param1");
- Assert.assertEquals(a.mainParameters.get(1), "param2");
- }
-
- public void dashDashParameter2() {
- class Arguments {
- @Parameter(names = { "-name" })
- public String name;
+
+ @Test
+ public void testLongMainParameterDescription() {
+ //setup
+ JCommander jc = new JCommander(new ArgsLongMainParameterDescription());
+ StringBuilder sb = new StringBuilder();
+
+ //action
+ jc.usage(sb);
+
+ //verify
+ for (String line : sb.toString().split("\n")) {
+ Assert.assertTrue(line.length() <= jc.getColumnSize(), "line length < column size");
+ }
+ }
+
+ @Test
+ public void testLongCommandDescription() throws Exception {
+ //setup
+ JCommander jc = new JCommander();
+ jc.addCommand(new ArgsLongCommandDescription());
+ StringBuilder sb = new StringBuilder();
+
+ //action
+ jc.usage(sb);
+
+ //verify
+ for (String line : sb.toString().split("\n")) {
+ Assert.assertTrue(line.length() <= jc.getColumnSize(), "line length < column size");
+ }
+ }
+
+ @Test
+ public void testDescriptionWrappingLongWord() {
+ //setup
+ StringBuilder sb = new StringBuilder();
+ final JCommander jc = new JCommander(new ArgsLongDescription());
+
+ //action
+ jc.usage(sb);
+
+ //verify
+ for (String line : sb.toString().split("\n")) {
+ Assert.assertTrue(line.length() <= jc.getColumnSize(), "line length < column size");
+ }
+ }
+
+ public void simpleArgs() throws ParseException {
+ Args1 args = new Args1();
+ String[] argv = {"-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
+ "-date", "2011-10-26", "-groups", "unit", "a", "b", "c"};
+ new JCommander(args, argv);
+
+ Assert.assertTrue(args.debug);
+ Assert.assertEquals(args.verbose.intValue(), 2);
+ Assert.assertEquals(args.groups, "unit");
+ Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
+ Assert.assertEquals(args.floa, 1.2f, 0.1f);
+ Assert.assertEquals(args.doub, 1.3f, 0.1f);
+ Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
+ Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
+ }
+
+ @DataProvider
+ public Object[][] alternateNamesListArgs() {
+ return new Object[][]{
+ new String[][]{new String[]{"--servers", "1", "-s", "2", "--servers", "3"}},
+ new String[][]{new String[]{"-s", "1", "-s", "2", "--servers", "3"}},
+ new String[][]{new String[]{"--servers", "1", "--servers", "2", "-s", "3"}},
+ new String[][]{new String[]{"-s", "1", "--servers", "2", "-s", "3"}},
+ new String[][]{new String[]{"-s", "1", "-s", "2", "--servers", "3"}},
+ };
+ }
+
+ /**
+ * Confirm that List<?> parameters with alternate names return the correct
+ * List regardless of how the arguments are specified
+ */
+
+ @Test(dataProvider = "alternateNamesListArgs")
+ public void testAlternateNamesForListParameters(String[] argv) {
+ AlternateNamesForListArgs args = new AlternateNamesForListArgs();
+
+ new JCommander(args, argv);
+
+ Assert.assertEquals(args.serverNames.size(), 3);
+ Assert.assertEquals(args.serverNames.get(0), argv[1]);
+ Assert.assertEquals(args.serverNames.get(1), argv[3]);
+ Assert.assertEquals(args.serverNames.get(2), argv[5]);
+ }
+
+
+ /**
+ * Make sure that if there are args with multiple names (e.g. "-log" and "-verbose"),
+ * the usage will only display it once.
+ */
+ public void repeatedArgs() {
+ Args1 args = new Args1();
+ String[] argv = {"-log", "2"};
+ JCommander jc = new JCommander(args, argv);
+ Assert.assertEquals(jc.getParameters().size(), 8);
+ }
+
+ /**
+ * Not specifying a required option should throw an exception.
+ */
+ @Test(expectedExceptions = ParameterException.class)
+ public void requiredFields1Fail() {
+ Args1 args = new Args1();
+ String[] argv = {"-debug"};
+ new JCommander(args, argv);
+ }
+
+ /**
+ * Getting the description of a nonexistent command should throw an exception.
+ */
+ @Test(expectedExceptions = ParameterException.class)
+ public void nonexistentCommandShouldThrow() {
+ String[] argv = {};
+ JCommander jc = new JCommander(new Object(), argv);
+ jc.getCommandDescription("foo");
+ }
+
+ /**
+ * Required options with multiple names should work with all names.
+ */
+ private void multipleNames(String option) {
+ Args1 args = new Args1();
+ String[] argv = {option, "2"};
+ new JCommander(args, argv);
+ Assert.assertEquals(args.verbose.intValue(), 2);
+ }
+
+ public void multipleNames1() {
+ multipleNames("-log");
+ }
+
+ public void multipleNames2() {
+ multipleNames("-verbose");
+ }
+
+ private void i18n1(String bundleName, Locale locale, String expectedString) {
+ ResourceBundle bundle = locale != null ? ResourceBundle.getBundle(bundleName, locale)
+ : null;
+
+ ArgsI18N1 i18n = new ArgsI18N1();
+ String[] argv = {"-host", "localhost"};
+ JCommander jc = new JCommander(i18n, bundle, argv);
+
+ ParameterDescription pd = jc.getParameters().get(0);
+ Assert.assertEquals(pd.getDescription(), expectedString);
+ }
+
+ public void i18nNoLocale() {
+ i18n1("MessageBundle", null, "Host");
+ }
+
+ public void i18nUsLocale() {
+ i18n1("MessageBundle", new Locale("en", "US"), "Host");
+ }
+
+ public void i18nFrLocale() {
+ i18n1("MessageBundle", new Locale("fr", "FR"), "Hôte");
+ }
+
+ private void i18n2(Object i18n) {
+ String[] argv = {"-host", "localhost"};
+ Locale.setDefault(new Locale("fr", "FR"));
+ JCommander jc = new JCommander(i18n, argv);
+ ParameterDescription pd = jc.getParameters().get(0);
+ Assert.assertEquals(pd.getDescription(), "Hôte");
+ }
+
+ public void i18nWithResourceAnnotation() {
+ i18n2(new ArgsI18N2());
+ }
+
+ public void i18nWithResourceAnnotationNew() {
+ i18n2(new ArgsI18N2New());
+ }
+
+ public void i18MissingKeyForCommand() {
+ ResourceBundle bundle = ResourceBundle.getBundle("MessageBundle", new Locale("en", "US"));
+ JCommander jc = new JCommander(new ArgsHelp(), bundle);
+ jc.addCommand(new ArgsLongCommandDescription());
+ StringBuilder sb = new StringBuilder();
+ jc.usage(sb);
+ String usage = sb.toString();
+ Assert.assertTrue(usage.contains("text"));
+ }
+
+ public void noParseConstructor() {
+ JCommander jCommander = new JCommander(new ArgsMainParameter1());
+ jCommander.usage(new StringBuilder());
+ // Before fix, this parse would throw an exception, because it calls createDescription, which
+ // was already called by usage(), and can only be called once.
+ jCommander.parse();
+ }
+
+ /**
+ * Test a use case where there are required parameters, but you still want
+ * to interrogate the options which are specified.
+ */
+ public void usageWithRequiredArgsAndResourceBundle() {
+ ArgsHelp argsHelp = new ArgsHelp();
+ JCommander jc = new JCommander(new Object[]{argsHelp, new ArgsRequired()},
+ java.util.ResourceBundle.getBundle("MessageBundle"));
+ // Should be able to display usage without triggering validation
+ jc.usage(new StringBuilder());
+ try {
+ jc.parse("-h");
+ Assert.fail("Should have thrown a required parameter exception");
+ } catch (ParameterException e) {
+ Assert.assertTrue(e.getMessage().contains("are required"));
+ }
+ Assert.assertTrue(argsHelp.help);
+ }
+
+ public void multiObjects() {
+ ArgsMaster m = new ArgsMaster();
+ ArgsSlave s = new ArgsSlave();
+ String[] argv = {"-master", "master", "-slave", "slave"};
+ new JCommander(new Object[]{m, s}, argv);
+
+ Assert.assertEquals(m.master, "master");
+ Assert.assertEquals(s.slave, "slave");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void multiObjectsWithDuplicatesFail() {
+ ArgsMaster m = new ArgsMaster();
+ ArgsSlave s = new ArgsSlaveBogus();
+ String[] argv = {"-master", "master", "-slave", "slave"};
+ new JCommander(new Object[]{m, s}, argv);
+ }
+
+ public void arityString() {
+ ArgsArityString args = new ArgsArityString();
+ String[] argv = {"-pairs", "pair0", "pair1", "rest"};
+ new JCommander(args, argv);
+
+ Assert.assertEquals(args.pairs.size(), 2);
+ Assert.assertEquals(args.pairs.get(0), "pair0");
+ Assert.assertEquals(args.pairs.get(1), "pair1");
+ Assert.assertEquals(args.rest.size(), 1);
+ Assert.assertEquals(args.rest.get(0), "rest");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void arity2Fail() {
+ ArgsArityString args = new ArgsArityString();
+ String[] argv = {"-pairs", "pair0"};
+ new JCommander(args, argv);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void multipleUnparsedFail() {
+ ArgsMultipleUnparsed args = new ArgsMultipleUnparsed();
+ String[] argv = {};
+ new JCommander(args, argv);
+ }
+
+ public void privateArgs() {
+ ArgsPrivate args = new ArgsPrivate();
+ new JCommander(args, "-verbose", "3");
+ Assert.assertEquals(args.getVerbose().intValue(), 3);
+ }
+
+ @Test(
+ expectedExceptions = ParameterException.class,
+ expectedExceptionsMessageRegExp = "Cannot use final field .*#_foo as a parameter;"
+ + " compile-time constant inlining may hide new values written to it.")
+ public void finalArgs() {
+ Object args = new Object() {
+ @Parameter(names = "-foo")
+ final int _foo = 0;
+ };
+ new JCommander(args).usage();
+ }
+
+ public void converterArgs() {
+ ArgsConverter args = new ArgsConverter();
+ String fileName = "a";
+ new JCommander(args, "-file", "/tmp/" + fileName,
+ "-listStrings", "Tuesday,Thursday",
+ "-listInts", "-1,8",
+ "-listBigDecimals", "-11.52,100.12");
+ Assert.assertEquals(args.file.getName(), fileName);
+ Assert.assertEquals(args.listStrings.size(), 2);
+ Assert.assertEquals(args.listStrings.get(0), "Tuesday");
+ Assert.assertEquals(args.listStrings.get(1), "Thursday");
+ Assert.assertEquals(args.listInts.size(), 2);
+ Assert.assertEquals(args.listInts.get(0).intValue(), -1);
+ Assert.assertEquals(args.listInts.get(1).intValue(), 8);
+ Assert.assertEquals(args.listBigDecimals.size(), 2);
+ Assert.assertEquals(args.listBigDecimals.get(0), new BigDecimal("-11.52"));
+ Assert.assertEquals(args.listBigDecimals.get(1), new BigDecimal("100.12"));
+ }
+
+ public void hiddenConverter() {
+ class Args {
+ @Parameter(names = "--path", converter = HiddenConverter.class)
+ public String path;
+ }
+
+ new JCommander(new Args(), "--path", "/tmp/a");
+ }
+
+ public void hiddenArgs() {
+ new JCommander(new HiddenArgs(), "--input", "/tmp/a", "--output", "/tmp/b");
+ }
+
+ public void hiddenSplitter() {
+ class Args {
+ @Parameter(names = "--extensions", splitter = HiddenParameterSplitter.class)
+ public List<String> extensions;
+ }
+ if (HiddenParameterSplitter.class.getConstructors().length == 0) {
+ return; // Compiler has optimised away the private constructor
+ }
+
+ Args args = new Args();
+ new JCommander(args, "--extensions", ".txt;.md");
+ Assert.assertEquals(Arrays.asList(".txt", ".md"), args.extensions);
+ }
+
+ private void argsBoolean1(String[] params, Boolean expected) {
+ ArgsBooleanArity args = new ArgsBooleanArity();
+ new JCommander(args, params);
+ Assert.assertEquals(args.debug, expected);
+ }
+
+ private void argsBoolean0(String[] params, Boolean expected) {
+ ArgsBooleanArity0 args = new ArgsBooleanArity0();
+ new JCommander(args, params);
+ Assert.assertEquals(args.debug, expected);
+ }
+
+ public void booleanArity1() {
+ argsBoolean1(new String[]{}, Boolean.FALSE);
+ argsBoolean1(new String[]{"-debug", "true"}, Boolean.TRUE);
+ }
+
+ public void booleanArity0() {
+ argsBoolean0(new String[]{}, Boolean.FALSE);
+ argsBoolean0(new String[]{"-debug"}, Boolean.TRUE);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void badParameterShouldThrowParameter1Exception() {
+ Args1 args = new Args1();
+ String[] argv = {"-log", "foo"};
+ new JCommander(args, argv);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void badParameterShouldThrowParameter2Exception() {
+ Args1 args = new Args1();
+ String[] argv = {"-long", "foo"};
+ new JCommander(args, argv);
+ }
+
+ public void listParameters() {
+ Args2 a = new Args2();
+ String[] argv = {"-log", "2", "-groups", "unit", "a", "b", "c", "-host", "host2"};
+ new JCommander(a, argv);
+ Assert.assertEquals(a.verbose.intValue(), 2);
+ Assert.assertEquals(a.groups, "unit");
+ Assert.assertEquals(a.hosts, Arrays.asList("host2"));
+ Assert.assertEquals(a.parameters, Arrays.asList("a", "b", "c"));
+ }
+
+ public void separatorEqual() {
+ SeparatorEqual s = new SeparatorEqual();
+ String[] argv = {"-log=3", "--longoption=10"};
+ new JCommander(s, argv);
+ Assert.assertEquals(s.log.intValue(), 3);
+ Assert.assertEquals(s.longOption.intValue(), 10);
+ }
+
+ public void separatorColon() {
+ SeparatorColon s = new SeparatorColon();
+ String[] argv = {"-verbose:true"};
+ new JCommander(s, argv);
+ Assert.assertTrue(s.verbose);
+ }
+
+ public void separatorBoth() {
+ SeparatorColon s = new SeparatorColon();
+ SeparatorEqual s2 = new SeparatorEqual();
+ String[] argv = {"-verbose:true", "-log=3"};
+ new JCommander(new Object[]{s, s2}, argv);
+ Assert.assertTrue(s.verbose);
+ Assert.assertEquals(s2.log.intValue(), 3);
+ }
+
+ public void separatorMixed1() {
+ SeparatorMixed s = new SeparatorMixed();
+ String[] argv = {"-long:1", "-level=42"};
+ new JCommander(s, argv);
+ Assert.assertEquals(s.l.longValue(), 1l);
+ Assert.assertEquals(s.level.intValue(), 42);
+ }
+
+ public void slashParameters() {
+ SlashSeparator a = new SlashSeparator();
+ String[] argv = {"/verbose", "/file", "/tmp/a"};
+ new JCommander(a, argv);
+ Assert.assertTrue(a.verbose);
+ Assert.assertEquals(a.file, "/tmp/a");
+ }
+
+ public void inheritance() {
+ ArgsInherited args = new ArgsInherited();
+ String[] argv = {"-log", "3", "-child", "2"};
+ new JCommander(args, argv);
+ Assert.assertEquals(args.child.intValue(), 2);
+ Assert.assertEquals(args.log.intValue(), 3);
+ }
+
+ public void negativeNumber() {
+ Args1 a = new Args1();
+ String[] argv = {"-verbose", "-3"};
+ new JCommander(a, argv);
+ Assert.assertEquals(a.verbose.intValue(), -3);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void requiredMainParameters() {
+ ArgsRequired a = new ArgsRequired();
+ String[] argv = {};
+ new JCommander(a, argv);
+ }
+
+ public void usageShouldNotChange() {
+ JCommander jc = new JCommander(new Args1(), "-log", "1");
+ StringBuilder sb = new StringBuilder();
+ jc.usage(sb);
+ String expected = sb.toString();
+ jc = new JCommander(new Args1(), "-debug", "-log", "2", "-long", "5");
+ sb = new StringBuilder();
+ jc.usage(sb);
+ String actual = sb.toString();
+ Assert.assertEquals(actual, expected);
+ }
+
+ private void verifyCommandOrdering(String[] commandNames, Object[] commands) {
+ CommandMain cm = new CommandMain();
+ JCommander jc = new JCommander(cm);
+
+ for (int i = 0; i < commands.length; i++) {
+ jc.addCommand(commandNames[i], commands[i]);
+ }
+
+ Map<String, JCommander> c = jc.getCommands();
+ Assert.assertEquals(c.size(), commands.length);
+
+ Iterator<String> it = c.keySet().iterator();
+ for (int i = 0; i < commands.length; i++) {
+ Assert.assertEquals(it.next(), commandNames[i]);
+ }
+ }
+
+ public void commandsShouldBeShownInOrderOfInsertion() {
+ verifyCommandOrdering(new String[]{"add", "commit"},
+ new Object[]{new CommandAdd(), new CommandCommit()});
+ verifyCommandOrdering(new String[]{"commit", "add"},
+ new Object[]{new CommandCommit(), new CommandAdd()});
+ }
+
+ @DataProvider
+ public static Object[][] f() {
+ return new Integer[][]{
+ new Integer[]{3, 5, 1},
+ new Integer[]{3, 8, 1},
+ new Integer[]{3, 12, 2},
+ new Integer[]{8, 12, 2},
+ new Integer[]{9, 10, 1},
+ };
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void arity1Fail() {
+ final Arity1 arguments = new Arity1();
+ final JCommander jCommander = new JCommander(arguments);
+ final String[] commands = {
+ "-inspect"
+ };
+ jCommander.parse(commands);
+ }
+
+ public void arity1Success1() {
+ final Arity1 arguments = new Arity1();
+ final JCommander jCommander = new JCommander(arguments);
+ final String[] commands = {
+ "-inspect", "true"
+ };
+ jCommander.parse(commands);
+ Assert.assertTrue(arguments.inspect);
+ }
+
+ public void arity1Success2() {
+ final Arity1 arguments = new Arity1();
+ final JCommander jCommander = new JCommander(arguments);
+ final String[] commands = {
+ "-inspect", "false"
+ };
+ jCommander.parse(commands);
+ Assert.assertFalse(arguments.inspect);
+ }
+
+ @Parameters(commandDescription = "Help for the given commands.")
+ public static class Help {
+ public static final String NAME = "help";
+
+ @Parameter(description = "List of commands.")
+ public List<String> commands = new ArrayList<>();
+ }
+
+ @Test(expectedExceptions = ParameterException.class,
+ description = "Verify that the main parameter's type is checked to be a List")
+ public void wrongMainTypeShouldThrow() {
+ JCommander jc = new JCommander(new ArgsRequiredWrongMain());
+ jc.parse("f1", "f2");
+ }
+
+ @Test(description = "This used to run out of memory")
+ public void oom() {
+ JCommander jc = new JCommander(new ArgsOutOfMemory());
+ jc.usage(new StringBuilder());
+ }
+
+ @Test
+ public void getParametersShouldNotNpe() {
+ JCommander jc = new JCommander(new Args1());
+ List<ParameterDescription> parameters = jc.getParameters();
+ }
+
+ public void validationShouldWork1() {
+ ArgsValidate1 a = new ArgsValidate1();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", "2 ");
+ Assert.assertEquals(a.age, new Integer(2));
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void validationShouldWorkWithDefaultValues() {
+ ArgsValidate2 a = new ArgsValidate2();
+ new JCommander(a).usage();
+ }
+
+ @Test
+ public void multipleValidators() {
+ for (int i = 1; i < 100; i += 2) {
+ ArgsMultiValidate a = new ArgsMultiValidate();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", String.valueOf(i));
+ }
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void multipleValidatorsFails1() {
+ ArgsMultiValidate a = new ArgsMultiValidate();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", "131");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void multipleValidatorsFails2() {
+ ArgsMultiValidate a = new ArgsMultiValidate();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", "0");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void validationShouldWork2() {
+ ArgsValidate1 a = new ArgsValidate1();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", "-2 ");
+ }
+
+ @Test
+ public void validationShouldReceiveRightParameterName() {
+ ArgMultiNameValidator validator = new ArgMultiNameValidator();
+ JCommander jc = new JCommander(validator);
+ String paramName = "-name2";
+ jc.parse(paramName, "param1");
+ Assert.assertEquals(ArgMultiNameValidator.MultiNameValidator.parsedName, paramName);
+ }
+
+ public void atFileCanContainEmptyLines() throws IOException {
+ File f = File.createTempFile("JCommander", null);
+ f.deleteOnExit();
+ FileWriter fw = new FileWriter(f);
+ fw.write("-log\n");
+ fw.write("\n");
+ fw.write("2\n");
+ fw.close();
+ new JCommander(new Args1(), "@" + f.getAbsolutePath());
+ }
+
+ public void atFileWithInNonDefaultCharset() throws IOException {
+ final Charset utf32 = Charset.forName("UTF-32");
+ final File f = File.createTempFile("JCommander", null);
+ f.deleteOnExit();
+ try (OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(f), utf32)) {
+ fw.write("-log\n");
+ fw.write("2\n");
+ fw.write("-groups\n");
+ fw.write("\u9731\n");
+ }
+ final Args1 args1 = new Args1();
+ final JCommander jc = new JCommander(args1);
+ try {
+ jc.parse("@" + f.getAbsolutePath());
+ throw new IllegalStateException("Expected exception to be thrown");
+ } catch (ParameterException expected) {
+ Assert.assertTrue(expected.getMessage().startsWith("Could not read file"));
+ }
+ jc.setAtFileCharset(utf32);
+ jc.parse("@" + f.getAbsolutePath());
+ Assert.assertEquals("\u9731", args1.groups);
+ }
+
+ public void handleEqualSigns() {
+ ArgsEquals a = new ArgsEquals();
+ JCommander jc = new JCommander(a);
+ jc.parse("-args=a=b,b=c");
+ Assert.assertEquals(a.args, "a=b,b=c");
+ }
+
+ @SuppressWarnings("serial")
+ public void handleSets() {
+ ArgsWithSet a = new ArgsWithSet();
+ new JCommander(a, "-s", "3,1,2");
+ Assert.assertEquals(a.set, new TreeSet<Integer>() {{
+ add(1);
+ add(2);
+ add(3);
+ }});
+ }
+
+ private static final List<String> V = Arrays.asList("a", "b", "c", "d");
+
+ @DataProvider
+ public Object[][] variable() {
+ return new Object[][]{
+ //new Object[]{0, V.subList(0, 0), V},
+ new Object[]{1, V.subList(0, 1), V.subList(1, 4)},
+ new Object[]{2, V.subList(0, 2), V.subList(2, 4)},
+ new Object[]{3, V.subList(0, 3), V.subList(3, 4)},
+ new Object[]{4, V.subList(0, 4), V.subList(4, 4)},
+ };
+ }
+
+ @Test(dataProvider = "variable")
+ public void variableArity(int count, List<String> var, List<String> main) {
+ VariableArity va = new VariableArity(count);
+ new JCommander(va).parse("-variable", "a", "b", "c", "d");
+ Assert.assertEquals(var, va.var);
+ Assert.assertEquals(main, va.main);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void variableArityZeroNonBoolean() {
+ VariableArity va = new VariableArity(0);
+ new JCommander(va).parse("-variable", "a", "b", "c", "d");
+ }
+
+ public void enumArgs() {
+ ArgsEnum args = new ArgsEnum();
+ String[] argv = {"-choice", "ONE", "-choices", "ONE", "Two"};
+ JCommander jc = new JCommander(args, argv);
+
+ Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
+
+ List<ChoiceType> expected = Arrays.asList(ChoiceType.ONE, ChoiceType.Two);
+ Assert.assertEquals(expected, args.choices);
+
+ for (ParameterDescription param : jc.getParameters()) {
+ // order can vary depending on JDK version
+ if (param.getLongestName().equals("-choice")) {
+ Assert.assertEquals(param.getDescription(),
+ "Options: " + EnumSet.allOf((Class<? extends Enum>) ArgsEnum.ChoiceType.class));
+ return;
+ }
+ }
+ Assert.fail("Could not find -choice parameter.");
+ }
+
+ public void enumArgs2() {
+ // issue #266
+ ArgsEnum args = new ArgsEnum();
+ new JCommander(args, "-choices", "ONE,Two");
+ Assert.assertEquals(Arrays.asList(ChoiceType.ONE, ChoiceType.Two), args.choices);
+ }
+
+ public void enumArgsCaseInsensitive() {
+ ArgsEnum args = new ArgsEnum();
+ String[] argv = {"-choice", "one"};
+ JCommander jc = new JCommander(args, argv);
+
+ Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void enumArgsFail() {
+ ArgsEnum args = new ArgsEnum();
+ String[] argv = {"-choice", "A"};
+ new JCommander(args, argv);
+ }
+
+ @Test
+ public void testDefaultListConverterForString() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-groups", "a,b");
+ Assert.assertEquals(al.groups.get(0), "a");
+ Assert.assertEquals(al.groups.get(1), "b");
+ }
+
+ @Test
+ public void testDefaultListConverterForStandardType() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-ints", "41,42");
+ Assert.assertEquals(al.ints.get(0).intValue(), 41);
+ Assert.assertEquals(al.ints.get(1).intValue(), 42);
+ }
+
+ @Test
+ public void testDefaultListConverterWithCustomConverterAndSplitter() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-hp", "localhost:1000;example.com:1001");
+ Assert.assertEquals(al.hostPorts.get(0).host, "localhost");
+ Assert.assertEquals(al.hostPorts.get(0).port.intValue(), 1000);
+ Assert.assertEquals(al.hostPorts.get(1).host, "example.com");
+ Assert.assertEquals(al.hostPorts.get(1).port.intValue(), 1001);
+ }
+
+ @Test
+ public void testDefaultListConverterWithCustomConverterAndDefaultSplitter() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-hp2", "localhost:1000,example.com:1001");
+ Assert.assertEquals(al.hp2.get(1).host, "example.com");
+ Assert.assertEquals(al.hp2.get(1).port.intValue(), 1001);
+ }
+
+ @Test
+ public void testCustomListConverter() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-uppercase", "ab,cd");
+ Assert.assertEquals(al.uppercase.get(0), "AB");
+ Assert.assertEquals(al.uppercase.get(1), "CD");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void shouldThrowIfUnknownOption() {
+ class A {
+ @Parameter(names = "-long")
+ public long l;
+ }
+ A a = new A();
+ new JCommander(a).parse("-lon", "32");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void mainParameterShouldBeValidate() {
+ class V implements IParameterValidator {
+
+ @Override
+ public void validate(String name, String value) throws ParameterException {
+ Assert.assertEquals("a", value);
+ }
+ }
+
+ class A {
+ @Parameter(validateWith = V.class)
+ public List<String> m;
+ }
+
+ A a = new A();
+ new JCommander(a).parse("b");
+ }
+
+ @Parameters(commandNames = {"--configure"})
+ public static class ConfigureArgs {
+ }
+
+ public static class BaseArgs {
+ @Parameter(names = {"-h", "--help"}, description = "Show this help screen")
+ private boolean help = false;
+
+ @Parameter(names = {"--version", "-version"}, description = "Show the program version")
+ private boolean version;
+ }
+
+ public void commandsWithSamePrefixAsOptionsShouldWork() {
+ BaseArgs a = new BaseArgs();
+ ConfigureArgs conf = new ConfigureArgs();
+ JCommander jc = new JCommander(a);
+ jc.addCommand(conf);
+ jc.parse("--configure");
+ }
+
+ public void dynamicParameters() {
+ class Command {
+ @DynamicParameter(names = {"-P"}, description = "Additional command parameters")
+ private Map<String, String> params = Maps.newHashMap();
+ }
+ JCommander commander = new JCommander();
+ Command c = new Command();
+ commander.addCommand("command", c);
+ commander.parse("command", "-Pparam='name=value'");
+ Assert.assertEquals(c.params.get("param"), "'name=value'");
+ }
+
+ public void exeParser() {
+ class Params {
+ @Parameter(names = "-i")
+ private String inputFile;
+ }
+
+ String args[] = {"-i", ""};
+ Params p = new Params();
+ new JCommander(p, args);
+ }
+
+ public void multiVariableArityList() {
+ class Params {
+ @Parameter(names = "-paramA", description = "ParamA", variableArity = true)
+ private List<String> paramA = Lists.newArrayList();
+
+ @Parameter(names = "-paramB", description = "ParamB", variableArity = true)
+ private List<String> paramB = Lists.newArrayList();
+ }
+
+ {
+ String args[] = {"-paramA", "a1", "a2", "-paramB", "b1", "b2", "b3"};
+ Params p = new Params();
+ new JCommander(p, args).parse();
+ Assert.assertEquals(p.paramA, Arrays.asList("a1", "a2"));
+ Assert.assertEquals(p.paramB, Arrays.asList("b1", "b2", "b3"));
+ }
+
+ {
+ String args[] = {"-paramA", "a1", "a2", "-paramB", "b1", "-paramA", "a3"};
+ Params p = new Params();
+ new JCommander(p, args).parse();
+ Assert.assertEquals(p.paramA, Arrays.asList("a1", "a2", "a3"));
+ Assert.assertEquals(p.paramB, Arrays.asList("b1"));
+ }
+ }
+
+ @Test(enabled = false,
+ description = "Need to double check that the command description is i18n'ed in the usage")
+ public void commandKey() {
+ @Parameters(resourceBundle = "MessageBundle", commandDescriptionKey = "command")
+ class Args {
+ @Parameter(names = "-myoption", descriptionKey = "myoption")
+ private boolean option;
+ }
+ JCommander j = new JCommander();
+ Args a = new Args();
+ j.addCommand("comm", a);
+ j.usage();
+ }
+
+ @Test(expectedExceptions = ParameterException.class,
+ expectedExceptionsMessageRegExp = "Was passed main parameter '' but no main parameter was defined.*")
+ public void tmp() {
+ class A {
+ @Parameter(names = "-b")
+ public String b;
+ }
+ new JCommander(new A()).parse("");
+ }
+
+ @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = "\"--b\": couldn't convert \"ThisIsATest\" to an integer")
+ public void multipleParameterNames() {
+ class MultipleParameterNames {
+ @Parameter(names = {"-b", "--b"})
+ public Integer b;
+ }
+ new JCommander(new MultipleParameterNames()).parse("--b", "ThisIsATest");
+ }
+
+ public void unknownOptionWithDifferentPrefix() {
+ @Parameters
+ class SlashSeparator {
+
+ @Parameter(names = "/verbose")
+ public boolean verbose = false;
+
+ @Parameter(names = "/file")
+ public String file;
+ }
+ SlashSeparator ss = new SlashSeparator();
+ try {
+ new JCommander(ss).parse("/notAParam");
+ } catch (ParameterException ex) {
+ boolean result = ex.getMessage().contains("in your arg class");
+ Assert.assertTrue(result);
+ }
+ }
+
+ public void equalSeparator() {
+ @Parameters(separators = "=", commandDescription = "My command")
+ class MyClass {
+
+ @Parameter(names = {"-p", "--param"}, required = true, description = "param desc...")
+ private String param;
+ }
+ MyClass c = new MyClass();
+ String expected = "\"hello\"world";
+ new JCommander(c).parse("--param=" + expected);
+ Assert.assertEquals(expected, c.param);
+ }
+
+ public void simpleArgsSetter() throws ParseException {
+ Args1Setter args = new Args1Setter();
+ String[] argv = {"-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
+ "-date", "2011-10-26", "-groups", "unit", "a", "b", "c"};
+ new JCommander(args, argv);
+
+ Assert.assertTrue(args.debug);
+ Assert.assertEquals(args.verbose.intValue(), 2);
+ Assert.assertEquals(args.groups, "unit");
+ Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
+ Assert.assertEquals(args.floa, 1.2f, 0.1f);
+ Assert.assertEquals(args.doub, 1.3f, 0.1f);
+ Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
+ Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
+ }
+
+ public void verifyHelp() {
+ class Arg {
+ @Parameter(names = "--help", help = true)
+ public boolean help = false;
+
+ @Parameter(names = "file", required = true)
+ public String file;
+ }
+ Arg arg = new Arg();
+ String[] argv = {"--help"};
+ new JCommander(arg, argv);
+
+ Assert.assertTrue(arg.help);
+ }
+
+ public void helpTest() {
+ class Arg {
+ @Parameter(names = {"?", "-help", "--help"}, description = "Shows help", help = true)
+ private boolean help = false;
+ }
+ Arg arg = new Arg();
+ JCommander jc = new JCommander(arg);
+ jc.parse("-help");
+ }
+
+ @Test
+ public void doNotDisplayHelpDefaultValue() {
+ class Arg {
+ @Parameter(names = "--help", help = true)
+ public boolean help = false;
+ }
+ Arg arg = new Arg();
+ String[] argv = {"--help"};
+ JCommander jc = new JCommander(arg, argv);
+
+ StringBuilder sb = new StringBuilder();
+
+ jc.usage(sb);
+
+ Assert.assertFalse(sb.toString().contains("Default"));
+ }
+
+ @Test(enabled = false, description = "Should only be enable once multiple parameters are allowed")
+ public void duplicateParameterNames() {
+ class ArgBase {
+ @Parameter(names = {"-host"})
+ protected String host;
+ }
+
+ class Arg1 extends ArgBase {
+ }
+ Arg1 arg1 = new Arg1();
+
+ class Arg2 extends ArgBase {
+ }
+ Arg2 arg2 = new Arg2();
+
+ JCommander jc = new JCommander(new Object[]{arg1, arg2});
+ jc.parse("-host", "foo");
+ Assert.assertEquals(arg1.host, "foo");
+ Assert.assertEquals(arg2.host, "foo");
+ }
+
+ @Test(enabled = true, description = "Disable top-level @/ampersand file expansion")
+ public void disabledAtSignExpansionTest() {
+ class Params {
+ @Parameter(names = {"-username"})
+ protected String username;
+ }
+
+ Params params = new Params();
+
+ JCommander jc = new JCommander(params);
+ jc.setExpandAtSign(false);
+ jc.parse("-username", "@tzellman");
+ Assert.assertEquals(params.username, "@tzellman");
+ }
+
+ @Test(enabled = true, description = "Enable top-level @/ampersand file expansion, which should throw in this case",
+ expectedExceptions = ParameterException.class)
+ public void enabledAtSignExpansionTest() {
+ class Params {
+ @Parameter(names = {"-username"})
+ protected String username;
+ }
+
+ Params params = new Params();
+
+ JCommander jc = new JCommander(params);
+ jc.parse("-username", "@tzellman");
+ Assert.assertEquals(params.username, "@tzellman");
+ }
+
+ public void parameterWithOneDoubleQuote() {
+ @Parameters(separators = "=")
+ class Arg {
+ @Parameter(names = {"-p", "--param"})
+ private String param;
+ }
+ JCommander jc = new JCommander(new MyClass());
+ jc.parse("-p=\"");
+ }
+
+ public void emptyStringAsDefault() {
+ class Arg {
+ @Parameter(names = "-x")
+ String s = "";
+ }
+ Arg a = new Arg();
+ StringBuilder sb = new StringBuilder();
+ new JCommander(a).usage(sb);
+ Assert.assertTrue(sb.toString().contains("Default: <empty string>"));
+ }
+
+ @Test
+ public void emptyStringShouldBeConsideredAsParameter() {
+ class Arg {
+ @Parameter(description = "parameters")
+ List<String> params;
+ }
+
+ Arg a = new Arg();
+ String[] args = {""};
+
+ new JCommander(a).parse(args);
+ Assert.assertEquals(a.params.size(), 1);
+// Assert.assertEquals();
+ }
+
+ @Test
+ public void doubleQuotedStringShouldBeConsideredAsParameter() {
+ class Arg {
+ @Parameter(description = "parameters")
+ List<String> params;
+ }
+
+ Arg a = new Arg();
+ String[] args = {"\"\""};
+
+ new JCommander(a).parse(args);
+ Assert.assertEquals(a.params.size(), 1);
+// Assert.assertEquals();
+ }
+
+ public void spaces() {
+ class Arg {
+ @Parameter(names = "-rule", description = "rule")
+ private List<String> rules = new ArrayList<>();
+ }
+ Arg a = new Arg();
+ new JCommander(a, "-rule", "some test");
+ Assert.assertEquals(a.rules, Arrays.asList("some test"));
+ }
+
+ static class V2 implements IParameterValidator2 {
+ final static List<String> names = Lists.newArrayList();
+ static boolean validateCalled = false;
+
+ @Override
+ public void validate(String name, String value) throws ParameterException {
+ validateCalled = true;
+ }
+
+ @Override
+ public void validate(String name, String value, ParameterDescription pd)
+ throws ParameterException {
+ names.addAll(Arrays.asList(pd.getParameter().names()));
+ }
+ }
+
+ public void validator2() {
+ class Arg {
+ @Parameter(names = {"-h", "--host"}, validateWith = V2.class)
+ String host;
+ }
+ Arg a = new Arg();
+ V2.names.clear();
+ V2.validateCalled = false;
+ JCommander jc = new JCommander(a, "--host", "h");
+ jc.setAcceptUnknownOptions(true);
+ Assert.assertEquals(V2.names, Arrays.asList("-h", "--host"));
+ Assert.assertTrue(V2.validateCalled);
+ }
+
+ public void usageCommandsUnderUsage() {
+ class Arg {
+ }
+ @Parameters(commandDescription = "command a")
+ class ArgCommandA {
+ @Parameter(description = "command a parameters")
+ List<String> parameters;
+ }
+ @Parameters(commandDescription = "command b")
+ class ArgCommandB {
+ @Parameter(description = "command b parameters")
+ List<String> parameters;
+ }
+
+ Arg a = new Arg();
+
+ JCommander c = new JCommander(a);
+ c.addCommand("a", new ArgCommandA());
+ c.addCommand("b", new ArgCommandB());
+
+ StringBuilder sb = new StringBuilder();
+ c.usage(sb);
+ Assert.assertTrue(sb.toString().contains("[command options]\n Commands:"));
+ }
+
+ public void usageWithEmpytLine() {
+ class Arg {
+ }
+ @Parameters(commandDescription = "command a")
+ class ArgCommandA {
+ @Parameter(description = "command a parameters")
+ List<String> parameters;
+ }
+ @Parameters(commandDescription = "command b")
+ class ArgCommandB {
+ @Parameter(description = "command b parameters")
+ List<String> parameters;
+ }
+
+ Arg a = new Arg();
+
+ JCommander c = new JCommander(a);
+ c.addCommand("a", new ArgCommandA());
+ c.addCommand("b", new ArgCommandB());
+
+ StringBuilder sb = new StringBuilder();
+ c.usage(sb);
+ Assert.assertTrue(sb.toString().contains("command a parameters\n\n b"));
+ }
+
+ public void usageWithSubCommands() {
+ class Arg {
+ }
+ @Parameters(commandDescription = "command a")
+ class ArgCommandA {
+ @Parameter(description = "command a parameters")
+ List<String> parameters;
+ }
+ @Parameters(commandDescription = "command b")
+ class ArgCommandB {
+ @Parameter(description = "command b parameters")
+ List<String> parameters;
+ }
+
+ Arg a = new Arg();
+
+ JCommander c = new JCommander(a);
+ c.setColumnSize(100);
+ c.addCommand("a", new ArgCommandA());
+
+ // b is a sub-command of a
+ JCommander aCommand = c.getCommands().get("a");
+ aCommand.addCommand("b", new ArgCommandB());
+
+ StringBuilder sb = new StringBuilder();
+ c.usage(sb);
+ Assert.assertTrue(sb.toString().contains("command a parameters\n Commands:"));
+ Assert.assertTrue(sb.toString().contains("command b\n Usage:"));
+ }
+
+ public void partialValidation() {
+ class Arg {
+ @Parameter(names = {"-h", "--host"})
+ String host;
+ }
+ Arg a = new Arg();
+ JCommander jc = new JCommander();
+ jc.setAcceptUnknownOptions(true);
+ jc.addObject(a);
+ jc.parse("-a", "foo", "-h", "host");
+ Assert.assertEquals(a.host, "host");
+ Assert.assertEquals(jc.getUnknownOptions(), Lists.newArrayList("-a", "foo"));
+ }
+
+ /**
+ * GITHUB-137.
+ */
+ public void listArgShouldBeCleared() {
+ class Args {
+ @Parameter(description = "[endpoint]")
+ public List<String> endpoint = Lists.newArrayList("prod");
+ }
+ Args a = new Args();
+ new JCommander(a, "dev");
+ Assert.assertEquals(a.endpoint, Lists.newArrayList("dev"));
+ }
+
+ @Test
+ public void dashDashEmpty() {
+ class Parameters {
+ @Parameter
+ public List<String> mainParameters = new ArrayList<>();
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "--");
+ Assert.assertTrue(a.mainParameters.isEmpty());
+ }
+
+ @Test
+ public void dashDashDashDash() {
+ class Parameters {
+ @Parameter
+ public List<String> mainParameters = new ArrayList<>();
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "--", "--");
+ Assert.assertEquals(a.mainParameters.size(), 1);
+ Assert.assertEquals(a.mainParameters.get(0), "--");
+ }
+
+ public void dashDashParameter() {
+ class Parameters {
+ @Parameter(names = {"-name"})
+ public String name;
+ @Parameter
+ public List<String> mainParameters;
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "-name", "theName", "--", "param1", "param2");
+ Assert.assertEquals(a.name, "theName");
+ Assert.assertEquals(a.mainParameters.size(), 2);
+ Assert.assertEquals(a.mainParameters.get(0), "param1");
+ Assert.assertEquals(a.mainParameters.get(1), "param2");
+ }
+
+ public void dashDashParameter2() {
+ class Parameters {
+ @Parameter(names = {"-name"})
+ public String name;
+ @Parameter
+ public List<String> mainParameters;
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "param1", "param2", "--", "param3", "-name", "theName");
+ Assert.assertNull(a.name);
+ Assert.assertEquals(a.mainParameters.size(), 5);
+ Assert.assertEquals(a.mainParameters.get(0), "param1");
+ Assert.assertEquals(a.mainParameters.get(1), "param2");
+ Assert.assertEquals(a.mainParameters.get(2), "param3");
+ Assert.assertEquals(a.mainParameters.get(3), "-name");
+ Assert.assertEquals(a.mainParameters.get(4), "theName");
+ }
+
+ public void access() {
+ class Parameters {
+ private int bar;
+
+ @Parameter(names = {"-bar", "-foo"})
+ private void setBar(int value) {
+ bar = value;
+ }
+
+ @Parameter(names = "-otherName")
+ private String otherName;
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "-bar", "1");
+ Assert.assertEquals(a.bar, 1);
+ }
+
+ public void noDash() {
+ class Parameters {
+ private int bar;
+
+ @Parameter(names = {"bar", "foo"})
+ private void setBar(int value) {
+ bar = value;
+ }
+
+ @Parameter(names = "otherName")
+ private String otherName;
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "bar", "1");
+ Assert.assertEquals(a.bar, 1);
+ }
+
+ public void commitTest() {
+ CommandCommit cc = new CommandCommit();
+ new JCommander(cc, "--author=cedric");
+ Assert.assertEquals(cc.author, "cedric");
+ }
+
+ static class CommandTemplate {
@Parameter
- public List<String> mainParameters;
- }
-
- Arguments a = new Arguments();
- new JCommander(a, new String[] {
- "param1", "param2", "--", "param3", "-name", "theName"}
- );
- Assert.assertNull(a.name);
- Assert.assertEquals(a.mainParameters.size(), 5);
- Assert.assertEquals(a.mainParameters.get(0), "param1");
- Assert.assertEquals(a.mainParameters.get(1), "param2");
- Assert.assertEquals(a.mainParameters.get(2), "param3");
- Assert.assertEquals(a.mainParameters.get(3), "-name");
- Assert.assertEquals(a.mainParameters.get(4), "theName");
- }
-
- @Test(enabled = false)
- public static void main(String[] args) throws Exception {
- new JCommanderTest().enumArgsFail();
-// class A {
-// @Parameter(names = "-short", required = true)
-// List<String> parameters;
-//
-// @Parameter(names = "-long", required = true)
-// public long l;
-// }
-// A a = new A();
-// new JCommander(a).parse();
-// System.out.println(a.l);
-// System.out.println(a.parameters);
-// ArgsList al = new ArgsList();
-// JCommander j = new JCommander(al);
-// j.setColumnSize(40);
-// j.usage();
-// new JCommanderTest().testListAndSplitters();
-// new JCommanderTest().converterArgs();
- }
-
- // Tests:
- // required unparsed parameter
+ private List<String> parameters = new ArrayList<>();
+
+ @Parameter(names = "help", help = true)
+ private boolean help;
+ }
+
+ public void noDashCommand() {
+ class P1 {
+ @Parameter(names = "hello")
+ private int test;
+ }
+ P1 p1 = new P1();
+ JCommander j = new JCommander();
+ j.addCommand("p1", p1);
+ j.parse("p1", "hello", "47");
+ Assert.assertEquals(p1.test, 47);
+ }
+
+ static class ValuesValidator implements IValueValidator<List<Integer>> {
+ @Override
+ public void validate(String name, List<Integer> values) throws ParameterException {
+ int previous = Integer.MIN_VALUE;
+ for (Integer i : values) {
+ if (i <= previous) {
+ throw new ParameterException("Invalid: values should be strictly increasing.");
+ }
+ previous = i;
+ }
+ }
+ }
+
+ @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = ".*strictly.*")
+ public void issue() {
+ class Parameters {
+ @Parameter(names = {"-v", "--values"},
+ required = true,
+ variableArity = true,
+ validateValueWith = ValuesValidator.class)
+ private List<Integer> values;
+ }
+
+ String[] commands = "-v 1 5 2".split("\\s+");
+ Parameters arg = new Parameters();
+ new JCommander(arg, commands);
+ }
+
+ static class MvParameters {
+ @SubParameter(order = 0)
+ String from;
+ @SubParameter(order = 1)
+ String to;
+ }
+
+ @Test
+ public void arity() {
+ class Parameters {
+ @Parameter(names = {"--mv"}, arity = 2)
+ private MvParameters mvParameters;
+ }
+
+ Parameters args = new Parameters();
+ JCommander.newBuilder()
+ .addObject(args)
+ .args(new String[]{"--mv", "from", "to"})
+ .build();
+
+ Assert.assertNotNull(args.mvParameters);
+ Assert.assertEquals(args.mvParameters.from, "from");
+ Assert.assertEquals(args.mvParameters.to, "to");
+ }
+
+ public void programName() {
+ JCommander jcommander = new JCommander();
+ String programName = "main";
+ jcommander.setProgramName(programName);
+ StringBuilder sb = new StringBuilder();
+ jcommander.usage(sb);
+
+ Assert.assertTrue(sb.toString().contains(programName));
+ Assert.assertEquals(jcommander.getProgramName(), programName);
+ }
+
+ public void dontShowOptionUsageIfThereAreNoOptions() {
+ class CommandTemplate {
+ @Parameter
+ List<String> parameters = new ArrayList<>();
+ }
+
+ CommandTemplate template = new CommandTemplate();
+ JCommander jcommander = new JCommander(template);
+ jcommander.setProgramName("main");
+ StringBuilder sb = new StringBuilder();
+ jcommander.usage(sb);
+ Assert.assertEquals(sb.toString().indexOf("options"), -1);
+ }
+
+ @Test
+ public void annotationsAndDynamicParameters() {
+ class DSimple {
+ @DynamicParameter(names = "-D", description = "Dynamic parameters go here")
+ public Map<String, String> params = Maps.newHashMap();
+
+ @DynamicParameter(names = "-A", assignment = "@")
+ public Map<String, String> params2 = Maps.newHashMap();
+ }
+
+ new JCommander(new DSimple()).usage(new StringBuilder());
+ }
+
+ @Test
+ public void twoCommandsSameOption() {
+ class GenerateOption {
+ @Parameter(names = {"--config"}, required = true, converter = FileConverter.class)
+ public File configFile;
+ }
+
+ class RegenerateOption {
+ @Parameter(names = {"--config"}, required = true, converter = FileConverter.class)
+ public File configFile;
+ }
+
+ GenerateOption generateOption = new GenerateOption();
+ RegenerateOption regenerateOption = new RegenerateOption();
+ JCommander.newBuilder()
+ .addCommand("--generate", generateOption)
+ .addCommand("--regenerate", regenerateOption)
+ .args(new String[]{"--generate", "--config", "foo.txt"})
+ .build();
+ Assert.assertEquals(generateOption.configFile.getName(), "foo.txt");
+ }
+
+ @Test
+ public void invertedBoolean() {
+ class Args {
+ @Parameter(names = {"--f"})
+ private boolean f = true;
+ }
+ Args args = new Args();
+ JCommander.newBuilder()
+ .addObject(args)
+ .args(new String[]{"--f"})
+ .build();
+ Assert.assertEquals(args.f, false);
+ }
+
+ @Test(enabled = false)
+ public static void main(String[] args) {
+
+ CommandTemplate template = new CommandTemplate();
+ JCommander jcommander = new JCommander(template);
+ jcommander.setProgramName("prog");
+ jcommander.parse("help");
+
+ if (template.help) {
+ jcommander.usage();
+ }
+ }
}
diff --git a/src/test/java/com/beust/jcommander/MethodSetterTest.java b/src/test/java/com/beust/jcommander/MethodSetterTest.java
index f995ad6..49bb42c 100644
--- a/src/test/java/com/beust/jcommander/MethodSetterTest.java
+++ b/src/test/java/com/beust/jcommander/MethodSetterTest.java
@@ -26,9 +26,6 @@ public class MethodSetterTest {
public void setRest(List<String> rest) {
this.rest = rest;
}
-// public List<String> getRest() {
-// return this.rest;
-// }
public List<String> rest;
}
ArgsArityStringSetter args = new ArgsArityStringSetter();
@@ -51,7 +48,7 @@ public class MethodSetterTest {
}
boolean passed = false;
try {
- new JCommander(new Arg(), new String[] { "--host", "host" });
+ new JCommander(new Arg(), "--host", "host");
} catch(ParameterException ex) {
Assert.assertEquals(ex.getCause(), null);
passed = true;
@@ -73,7 +70,7 @@ public class MethodSetterTest {
}
}
Arg arg = new Arg();
- new JCommander(arg, new String[] { "--port", "42" });
+ new JCommander(arg, "--port", "42");
Assert.assertEquals(arg.port, new Integer(42));
}
@@ -88,7 +85,7 @@ public class MethodSetterTest {
}
}
Arg arg = new Arg();
- JCommander jc = new JCommander(arg, new String[] { "--port", "42" });
+ JCommander jc = new JCommander(arg, "--port", "42");
ParameterDescription pd = jc.getParameters().get(0);
Assert.assertEquals(pd.getDefault(), 43);
}
diff --git a/src/test/java/com/beust/jcommander/ParametersDelegateTest.java b/src/test/java/com/beust/jcommander/ParametersDelegateTest.java
index 46c7c6a..1f9f9e0 100644
--- a/src/test/java/com/beust/jcommander/ParametersDelegateTest.java
+++ b/src/test/java/com/beust/jcommander/ParametersDelegateTest.java
@@ -143,7 +143,7 @@ public class ParametersDelegateTest {
public void mainParametersTest() {
class Delegate {
@Parameter
- public List<String> mainParams = new ArrayList<String>();
+ public List<String> mainParams = new ArrayList<>();
}
class Command {
@ParametersDelegate
@@ -200,11 +200,11 @@ public class ParametersDelegateTest {
public void duplicateMainParametersAreNotAllowed() {
class Delegate1 {
@Parameter
- public List<String> mainParams1 = new ArrayList<String>();
+ public List<String> mainParams1 = new ArrayList<>();
}
class Delegate2 {
@Parameter
- public List<String> mainParams2 = new ArrayList<String>();
+ public List<String> mainParams2 = new ArrayList<>();
}
class Command {
@ParametersDelegate
diff --git a/src/test/java/com/beust/jcommander/ParametersNotEmptyTest.java b/src/test/java/com/beust/jcommander/ParametersNotEmptyTest.java
new file mode 100644
index 0000000..7ebfc24
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/ParametersNotEmptyTest.java
@@ -0,0 +1,37 @@
+package com.beust.jcommander;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+@Test
+public class ParametersNotEmptyTest {
+
+ public class Args1 {
+ @Parameter(names = "-debug", description = "Debug mode")
+ public boolean debug = false;
+
+ @Parameter(names = "-date", description = "An ISO 8601 formatted date.")
+ public Date date;
+ }
+
+ @Test
+ public void testParameters() throws Exception {
+ JCommander jc = new JCommander(new Args1());
+ List<String> parameters = new ArrayList<>();
+ for (ParameterDescription pd : jc.getParameters()) {
+ parameters.add(pd.getNames());
+ }
+ Collections.sort(parameters);
+
+ Assert.assertEquals(parameters, new ArrayList<String>() {{
+ add("-date");
+ add("-debug");
+ }}
+ );
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/PasswordTest.java b/src/test/java/com/beust/jcommander/PasswordTest.java
new file mode 100644
index 0000000..ff1792e
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/PasswordTest.java
@@ -0,0 +1,111 @@
+package com.beust.jcommander;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class PasswordTest {
+
+ @DataProvider(name = "args")
+ public Object[][] createArgs() {
+ return new Object[][] {
+ { new PasswordTestingArgs() },
+ { new OptionalPasswordTestingArgs() },
+ };
+ }
+
+ public interface Args {
+
+ String getPassword();
+
+ int getPort();
+
+ }
+
+ public static class PasswordTestingArgs implements PasswordTest.Args {
+ @Parameter(names = {"--password", "-p"}, description = "Private key password",
+ password = true, required = true)
+ public String password;
+
+ @Parameter(names = {"--port", "-o"}, description = "Port to bind server to",
+ required = true)
+ public int port;
+
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+ }
+
+ @Test(dataProvider = "args")
+ public void passwordNotAsked(Args a) {
+ String expectedPassword = "somepassword";
+ int expectedPort = 7;
+ new JCommander(a, "--password", expectedPassword, "--port", String.valueOf(7));
+ Assert.assertEquals(a.getPort(), expectedPort);
+ Assert.assertEquals(a.getPassword(), expectedPassword);
+ }
+
+ @Test(dataProvider = "args", expectedExceptions = ParameterException.class)
+ public void passwordWithExcessiveArity(Args a) {
+ new JCommander(a, "--password", "somepassword", "someotherarg", "--port", String.valueOf(7));
+ }
+
+ @Test(dataProvider = "args")
+ public void passwordAsked(Args a) {
+ InputStream stdin = System.in;
+ String password = "password";
+ int port = 7;
+ try {
+ System.setIn(new ByteArrayInputStream(password.getBytes()));
+ new JCommander(a, "--port", String.valueOf(port), "--password");
+ Assert.assertEquals(a.getPort(), port);
+ Assert.assertEquals(a.getPassword(), password);
+ } finally {
+ System.setIn(stdin);
+ }
+ }
+
+ public static class OptionalPasswordTestingArgs implements PasswordTest.Args {
+ @Parameter(names = {"--password", "-p"}, description = "Private key password",
+ password = true)
+ public String password;
+
+ @Parameter(names = {"--port", "-o"}, description = "Port to bind server to",
+ required = true)
+ public int port;
+
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+ }
+
+ @Test
+ public void passwordOptionalNotProvided() {
+ Args a = new OptionalPasswordTestingArgs();
+ new JCommander(a, "--port", "7");
+ Assert.assertEquals(a.getPort(), 7);
+ Assert.assertEquals(a.getPassword(), null);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void passwordRequredNotProvided() {
+ Args a = new PasswordTestingArgs();
+ new JCommander(a, "--port", "7");
+ }
+
+}
diff --git a/src/test/java/com/beust/jcommander/PositiveIntegerTest.java b/src/test/java/com/beust/jcommander/PositiveIntegerTest.java
index ec7d273..81d9b37 100644
--- a/src/test/java/com/beust/jcommander/PositiveIntegerTest.java
+++ b/src/test/java/com/beust/jcommander/PositiveIntegerTest.java
@@ -14,7 +14,7 @@ public class PositiveIntegerTest {
}
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-p", "8080" });
+ jc.parse("-p", "8080");
}
@@ -26,7 +26,7 @@ public class PositiveIntegerTest {
}
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-p", "" });
+ jc.parse("-p", "");
}
@Test(expectedExceptions = ParameterException.class)
@@ -37,7 +37,7 @@ public class PositiveIntegerTest {
}
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-p", "-1" });
+ jc.parse("-p", "-1");
}
@Test(expectedExceptions = ParameterException.class)
@@ -48,7 +48,7 @@ public class PositiveIntegerTest {
}
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-p", "abc" });
+ jc.parse("-p", "abc");
}
@Test(expectedExceptions = ParameterException.class)
@@ -60,6 +60,6 @@ public class PositiveIntegerTest {
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "--port", " " });
+ jc.parse("--port", " ");
}
} \ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/SetConverter.java b/src/test/java/com/beust/jcommander/SetConverter.java
index c19df11..580fd98 100644
--- a/src/test/java/com/beust/jcommander/SetConverter.java
+++ b/src/test/java/com/beust/jcommander/SetConverter.java
@@ -6,7 +6,7 @@ import java.util.TreeSet;
public class SetConverter implements IStringConverter<SortedSet<Integer>> {
public SortedSet<Integer> convert(String value) {
- SortedSet<Integer> set = new TreeSet<Integer>();
+ SortedSet<Integer> set = new TreeSet<>();
String[] values = value.split(",");
for (String num : values) {
set.add(Integer.parseInt(num));
diff --git a/src/test/java/com/beust/jcommander/SimpleExample.java b/src/test/java/com/beust/jcommander/SimpleExample.java
new file mode 100644
index 0000000..b8000ca
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/SimpleExample.java
@@ -0,0 +1,50 @@
+package com.beust.jcommander;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.testng.Assert.assertEquals;
+
+class Main {
+ static PrintWriter out;
+ @Parameter(names={"--length", "-l"})
+ int length;
+ @Parameter(names={"--pattern", "-p"})
+ int pattern;
+
+ public static void main(String ... args) {
+ Main main = new Main();
+ new JCommander(main, args);
+ main.run();
+ }
+
+ public void run() {
+ out.printf("%d %d", length, pattern);
+ }
+}
+
+public class SimpleExample {
+ StringWriter out;
+
+ @BeforeMethod
+ public void setupMain(){
+ out=new StringWriter();
+ Main.out=new PrintWriter(out);
+ }
+
+ @Test
+ public void testLongArgs() {
+ Main.main("--length", "512", "--pattern", "2");
+ assertEquals("512 2", out.toString());
+ }
+
+ @Test
+ public void testShortArgs() {
+ Main.main("-l", "256", "-p", "171");
+ assertEquals("256 171", out.toString());
+ }
+
+}
diff --git a/src/test/java/com/beust/jcommander/TypeHierarchyTest.java b/src/test/java/com/beust/jcommander/TypeHierarchyTest.java
new file mode 100644
index 0000000..1ae3771
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/TypeHierarchyTest.java
@@ -0,0 +1,111 @@
+package com.beust.jcommander;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * <p>Test that parent classes and interfaces are used correctly</p>
+ */
+public class TypeHierarchyTest {
+
+ public interface Marker {
+ @Parameter(names = {"--available"})
+ void setAvailable(boolean available);
+ }
+
+ public class Base implements Marker {
+ boolean available = false;
+
+ public boolean isAvailable() {
+ return available;
+ }
+
+ @Override
+ public void setAvailable(boolean available) {
+ this.available = true;
+ }
+ }
+
+ public interface IMiddle {
+ @Parameter(names = {"--count"})
+ void setCount(int count);
+ }
+
+ public interface IMiddleMiddle extends IMiddle {
+ @Parameter(names = {"--again"})
+ void setCountAgain(int count);
+ }
+
+ public class Middle extends Base implements IMiddleMiddle {
+
+ private int count;
+ private int again;
+
+ public int getCount() {
+ return count;
+ }
+
+ @Override
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public int getCountAgain() {
+ return again;
+ }
+
+ @Override
+ public void setCountAgain(int again) {
+ this.again = again;
+ }
+ }
+
+ // trying to trip it up and get it to go from Child -> Composite -> Visitor
+ public interface Composite extends Visitor {
+ @Parameter(names = {"-n", "--name"})
+ void setName(String validate);
+ }
+
+ public interface Visitor {
+ @Parameter(names = {"--validate"})
+ void setValidate(boolean validate);
+ }
+
+ public class Child extends Middle implements Composite, Visitor{
+
+ private String name;
+ private boolean validate = false;
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isValidate() {
+ return validate;
+ }
+
+ @Override
+ public void setValidate(boolean validate) {
+ this.validate = validate;
+ }
+ }
+
+ @Test
+ public void testTypeHierarchy() {
+ final Child child = new Child();
+ JCommander commander = new JCommander(child);
+ commander.parse("--validate","-n","child-one","--count","14","--again","22","--available");
+
+ // test values through entire hierarchy
+ Assert.assertTrue(child.isValidate());
+ Assert.assertTrue(child.isAvailable());
+ Assert.assertEquals(14, child.getCount());
+ Assert.assertEquals(22, child.getCountAgain());
+ Assert.assertEquals("child-one", child.getName());
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java b/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java
index 6a3a98f..e237d73 100644
--- a/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java
+++ b/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java
@@ -10,9 +10,8 @@ public class ValidatePropertiesWhenParsingTest {
JCommander cmd = new JCommander();
cmd.addCommand("a", new A());
-// cmd.addCommand("b", new B());
- cmd.parse(new String[] { "a", "-path", "myPathToHappiness" });
+ cmd.parse("a", "-path", "myPathToHappiness");
}
public static class MyPathValidator implements IParameterValidator {
diff --git a/src/test/java/com/beust/jcommander/VariableArityTest.java b/src/test/java/com/beust/jcommander/VariableArityTest.java
index a90392f..c2d86b8 100644
--- a/src/test/java/com/beust/jcommander/VariableArityTest.java
+++ b/src/test/java/com/beust/jcommander/VariableArityTest.java
@@ -13,15 +13,15 @@ public class VariableArityTest {
@Parameter(names = { "-m", "--matrixData" }, variableArity = true,
description = "File containing a list of instances and their runtimes on various configurations", required = false)
- public List<String> modelMatrixFile = new LinkedList<String>();
+ public List<String> modelMatrixFile = new LinkedList<>();
@Parameter(names = { "-f", "--featureData" }, variableArity = true,
description = "File containing a list of instances and their corresponding features", required = true)
- public List<String> featureFile = new LinkedList<String>();
+ public List<String> featureFile = new LinkedList<>();
@Parameter(names = { "-c", "--configData" }, variableArity = true,
description = "File containing a list of configuration parameter values")
- public List<String> configFile = new LinkedList<String>();
+ public List<String> configFile = new LinkedList<>();
@Parameter(names = { "-o", "--outputFile" },
description = "File to output the resulting data to. Defaults to ./matrix-generation.zip", required = false)
@@ -52,10 +52,9 @@ public class VariableArityTest {
com.parse(split);
-// config.print();
Assert.assertNotEquals(config.seed, 0);
- Assert.assertEquals(config.modelMatrixFile, Arrays.asList(new String[] { "foo" }));
- Assert.assertEquals(config.featureFile, Arrays.asList(new String[] { "foo" }));
+ Assert.assertEquals(config.modelMatrixFile, Arrays.asList("foo"));
+ Assert.assertEquals(config.featureFile, Arrays.asList("foo"));
Assert.assertEquals(config.seed, 1024);
Assert.assertEquals(config.outputFile, "foo");
}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsEnum.java b/src/test/java/com/beust/jcommander/args/ArgsEnum.java
index bef663b..ba0628d 100644
--- a/src/test/java/com/beust/jcommander/args/ArgsEnum.java
+++ b/src/test/java/com/beust/jcommander/args/ArgsEnum.java
@@ -34,12 +34,12 @@ import com.beust.jcommander.Parameter;
*/
public class ArgsEnum {
- public enum ChoiceType { ONE, Two, THREE };
+ public enum ChoiceType { ONE, Two, THREE }
@Parameter(names = "-choice")
public ChoiceType choice = ChoiceType.ONE;
@Parameter(names = "-choices", variableArity = true)
- public List<ChoiceType> choices = new ArrayList<ChoiceType>();
+ public List<ChoiceType> choices = new ArrayList<>();
public static void main(String[] args1) {
ArgsEnum args = new ArgsEnum();
diff --git a/src/test/java/com/beust/jcommander/args/ArgsLongCommandDescription.java b/src/test/java/com/beust/jcommander/args/ArgsLongCommandDescription.java
new file mode 100644
index 0000000..398e514
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsLongCommandDescription.java
@@ -0,0 +1,12 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+@Parameters(commandNames = {"command"}, commandDescription = "text text text text text " +
+ "text text text text text text text text text text text text text text text " +
+ "really-really-really-long-word-or-url text text text text text text text.")
+public class ArgsLongCommandDescription {
+ @Parameter(names = {"-b"}, description = "boolean parameter")
+ public boolean var;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsLongMainParameterDescription.java b/src/test/java/com/beust/jcommander/args/ArgsLongMainParameterDescription.java
new file mode 100644
index 0000000..18bb06c
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsLongMainParameterDescription.java
@@ -0,0 +1,17 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ArgsLongMainParameterDescription {
+
+ @Parameter(description = "[text] [text] text text text text text text text text " +
+ "text text text text text text text text " +
+ "really-really-really-long-word-or-url text text text text text text text.")
+ public List<String> main = new ArrayList<>();
+
+ @Parameter(names = {"-b"}, description = "boolean parameter")
+ public boolean var;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsMultiValidate.java b/src/test/java/com/beust/jcommander/args/ArgsMultiValidate.java
new file mode 100644
index 0000000..1a913ff
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsMultiValidate.java
@@ -0,0 +1,36 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.IParameterValidator;
+import com.beust.jcommander.IValueValidator;
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.ParameterException;
+import com.beust.jcommander.validators.PositiveInteger;
+
+public class ArgsMultiValidate {
+
+ public static class OddIntegerParameterValidator implements IParameterValidator {
+ @Override
+ public void validate(String name, String value) throws ParameterException {
+ if(Integer.parseInt(value) %2 != 1) throw new ParameterException("param "+name+"="+value+" is not odd");
+ }
+ }
+
+ public static class LowerThan100ValueValidator implements IValueValidator<Integer> {
+ @Override
+ public void validate(String name, Integer value) throws ParameterException {
+ if(value >= 100) throw new ParameterException("param "+name+"="+value+" is greater than 100");
+ }
+ }
+
+ public static class GreaterTha0ValueValidator implements IValueValidator<Integer> {
+ @Override
+ public void validate(String name, Integer value) throws ParameterException {
+ if(value <= 0) throw new ParameterException("param "+name+"="+value+" is lower than 1");
+ }
+ }
+
+ @Parameter(names = "-age",
+ validateWith = {PositiveInteger.class,OddIntegerParameterValidator.class},
+ validateValueWith={GreaterTha0ValueValidator.class,LowerThan100ValueValidator.class})
+ public int age=29;
+}
diff --git a/src/test/java/com/beust/jcommander/args/HiddenArgs.java b/src/test/java/com/beust/jcommander/args/HiddenArgs.java
new file mode 100644
index 0000000..1b89334
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/HiddenArgs.java
@@ -0,0 +1,36 @@
+/**
+ * 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.args;
+
+import com.beust.jcommander.Parameter;
+
+public class HiddenArgs {
+ public HiddenArgs() {
+ }
+
+ @Parameter(names = "--input")
+ private String input;
+
+ private String output;
+
+ @Parameter(names = "--output")
+ private void setOutput(String output) {
+ this.output = output;
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/args/SlashSeparator.java b/src/test/java/com/beust/jcommander/args/SlashSeparator.java
index 64d3930..06b38e5 100644
--- a/src/test/java/com/beust/jcommander/args/SlashSeparator.java
+++ b/src/test/java/com/beust/jcommander/args/SlashSeparator.java
@@ -21,7 +21,7 @@ package com.beust.jcommander.args;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
-@Parameters(optionPrefixes = "/")
+@Parameters
public class SlashSeparator {
@Parameter(names = "/verbose")
diff --git a/src/test/java/com/beust/jcommander/args/VariableArity.java b/src/test/java/com/beust/jcommander/args/VariableArity.java
index 21a861d..ec4130e 100644
--- a/src/test/java/com/beust/jcommander/args/VariableArity.java
+++ b/src/test/java/com/beust/jcommander/args/VariableArity.java
@@ -8,19 +8,19 @@ import java.util.List;
public class VariableArity implements IVariableArity {
- private int m_count;
+ private int count;
public VariableArity(int count) {
- m_count = count;
+ this.count = count;
}
@Parameter
- public List<String> main = new ArrayList<String>();
+ public List<String> main = new ArrayList<>();
@Parameter(names = "-variable", variableArity = true)
- public List<String> var = new ArrayList<String>();
+ public List<String> var = new ArrayList<>();
public int processVariableArity(String optionName, String[] options) {
- return m_count;
+ return count;
}
}
diff --git a/src/test/java/com/beust/jcommander/command/CommandNoParametersAnnotation.java b/src/test/java/com/beust/jcommander/command/CommandNoParametersAnnotation.java
new file mode 100644
index 0000000..8b5c577
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/command/CommandNoParametersAnnotation.java
@@ -0,0 +1,9 @@
+package com.beust.jcommander.command;
+
+import com.beust.jcommander.Parameter;
+import java.util.List;
+
+public class CommandNoParametersAnnotation {
+ @Parameter(description = "Patterns of files to be added")
+ public List<String> patterns;
+}
diff --git a/src/test/java/com/beust/jcommander/command/CommandTest.java b/src/test/java/com/beust/jcommander/command/CommandTest.java
index cf921bd..5cb7c34 100644
--- a/src/test/java/com/beust/jcommander/command/CommandTest.java
+++ b/src/test/java/com/beust/jcommander/command/CommandTest.java
@@ -18,12 +18,13 @@
package com.beust.jcommander.command;
+import com.beust.jcommander.ArgsValidate2;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
-
import org.testng.Assert;
import org.testng.annotations.Test;
+import java.io.File;
import java.util.Arrays;
public class CommandTest {
@@ -75,11 +76,6 @@ public class CommandTest {
jc.addCommand("commit", commit);
jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java");
-// jc.setProgramName("TestCommander");
-// jc.usage();
-// jc.usage("add");
-// jc.usage("commit");
-
Assert.assertTrue(cm.verbose);
Assert.assertEquals(jc.getParsedCommand(), "commit");
Assert.assertTrue(commit.amend);
@@ -109,6 +105,55 @@ public class CommandTest {
Assert.assertFalse(out.toString().contains("hidden Hidden command to add file contents to the index"));
}
+ @Test
+ public void noParametersAnnotationOnCommandTest() {
+ CommandMain cm = new CommandMain();
+ JCommander jc = new JCommander(cm);
+ CommandNoParametersAnnotation noParametersAnnotation = new CommandNoParametersAnnotation();
+ jc.addCommand("no-annotation", noParametersAnnotation);
+
+ jc.setProgramName("TestCommander");
+ StringBuilder out = new StringBuilder();
+ jc.usage(out);
+
+ Assert.assertTrue(out.toString().contains("no-annotation"));
+ }
+
+ @Test
+ public void noTrailingSpaceInUsageTest() {
+ CommandMain cm = new CommandMain();
+ JCommander jc = new JCommander(cm);
+ CommandAdd add = new CommandAdd();
+ jc.addCommand("add", add);
+ CommandCommit commit = new CommandCommit();
+ jc.addCommand("commit", commit);
+ jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java");
+ StringBuilder out = new StringBuilder();
+ jc.usage(out);
+ String firstLine = out.toString().split("\n")[0];
+ Assert.assertFalse(firstLine.endsWith(" "), "Usage should not have trailing spaces");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void validateSubCommand() throws Exception {
+ JCommander jc = new JCommander(new CommandMain());
+ final ArgsValidate2 sub = new ArgsValidate2();
+ sub.template = null;
+ jc.addCommand("sub", sub);
+ jc.parse("sub", "-template", "foo");
+ }
+
+ @Test
+ public void doNotValidateSubCommand() throws Exception {
+ JCommander jc = new JCommander(new CommandMain());
+ final ArgsValidate2 sub = new ArgsValidate2();
+ sub.template = null;
+ jc.addCommand("sub", sub);
+ jc.parseWithoutValidation("sub", "-template", "foo");
+ Assert.assertEquals(sub.template, new File("foo"));
+
+ }
+
public static void main(String[] args) {
new CommandTest().shouldComplainIfNoAnnotations();
}
diff --git a/src/test/java/com/beust/jcommander/converters/CharArrayConverterTest.java b/src/test/java/com/beust/jcommander/converters/CharArrayConverterTest.java
new file mode 100644
index 0000000..596c806
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/converters/CharArrayConverterTest.java
@@ -0,0 +1,37 @@
+/**
+ * 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.converters;
+
+import java.net.UnknownHostException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@Test
+public class CharArrayConverterTest {
+
+ private static final String FIXTURE = "Hello World";
+ private static final CharArrayConverter CONVERTER = new CharArrayConverter();
+
+ @Test
+ public void testString() throws UnknownHostException {
+ Assert.assertEquals(CONVERTER.convert(FIXTURE), FIXTURE.toCharArray());
+ }
+
+}
diff --git a/src/test/java/com/beust/jcommander/converters/InetAddressConverterTest.java b/src/test/java/com/beust/jcommander/converters/InetAddressConverterTest.java
new file mode 100644
index 0000000..d8cf8b4
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/converters/InetAddressConverterTest.java
@@ -0,0 +1,62 @@
+/**
+ * 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.converters;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@Test
+public class InetAddressConverterTest {
+
+ private static final InetAddressConverter INET_ADDRESS_CONVERTER = new InetAddressConverter();
+ private static final InetAddress LOOPBACK_ADDRESS = InetAddress.getLoopbackAddress();
+
+ @Test
+ public void testLocalhost() throws UnknownHostException {
+ test("localhost");
+ }
+
+ @Test
+ public void testLocalhostAddress() throws UnknownHostException {
+ test("127.0.0.1");
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testGargabeInput() throws UnknownHostException {
+ test("!@#$%");
+ }
+
+ @Test
+ public void testEmptyInput() throws UnknownHostException {
+ test("");
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testBlankInput() throws UnknownHostException {
+ test(" ");
+ }
+
+ private void test(String string) throws UnknownHostException {
+ Assert.assertEquals(INET_ADDRESS_CONVERTER.convert(string), LOOPBACK_ADDRESS);
+
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java b/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java
index 98327bd..73e3c9e 100644
--- a/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java
+++ b/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java
@@ -51,10 +51,5 @@ public class DynamicParameterTest {
public static void main(String[] args) {
DynamicParameterTest dpt = new DynamicParameterTest();
dpt.simpleWithSpaces();
-// dpt.nonMapShouldThrow();
-// dpt.wrongSeparatorShouldThrow();
-// dpt.differentAssignment();
-// dpt.arity0();
-// dpt.usage();
}
}
diff --git a/src/test/java/test/QuotedMainTest.java b/src/test/java/test/QuotedMainTest.java
index abb97c0..68126ea 100644
--- a/src/test/java/test/QuotedMainTest.java
+++ b/src/test/java/test/QuotedMainTest.java
@@ -11,7 +11,7 @@ import com.beust.jcommander.Parameter;
public class QuotedMainTest {
@Parameter
- List<String> args = new ArrayList<String>();
+ List<String> args = new ArrayList<>();
String quoted = "\" \"";