diff options
Diffstat (limited to 'src')
125 files changed, 8557 insertions, 0 deletions
diff --git a/src/main/java/com/beust/jcommander/DynamicParameter.java b/src/main/java/com/beust/jcommander/DynamicParameter.java new file mode 100644 index 0000000..2159c1f --- /dev/null +++ b/src/main/java/com/beust/jcommander/DynamicParameter.java @@ -0,0 +1,50 @@ +package com.beust.jcommander; + +import static java.lang.annotation.ElementType.FIELD; + +import com.beust.jcommander.validators.NoValidator; +import com.beust.jcommander.validators.NoValueValidator; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target({ FIELD }) +public @interface DynamicParameter { + /** + * An array of allowed command line parameters (e.g. "-D", "--define", etc...). + */ + String[] names() default {}; + + /** + * Whether this option is required. + */ + boolean required() default false; + + /** + * A description of this option. + */ + String description() default ""; + + /** + * The key used to find the string in the message bundle. + */ + String descriptionKey() default ""; + + /** + * If true, this parameter won't appear in the usage(). + */ + boolean hidden() default false; + + /** + * The validation class to use. + */ + 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; +} diff --git a/src/main/java/com/beust/jcommander/FuzzyMap.java b/src/main/java/com/beust/jcommander/FuzzyMap.java new file mode 100644 index 0000000..5f3939b --- /dev/null +++ b/src/main/java/com/beust/jcommander/FuzzyMap.java @@ -0,0 +1,61 @@ +package com.beust.jcommander; + +import com.beust.jcommander.internal.Maps; + +import java.util.Map; + +/** + * Helper class to perform fuzzy key look ups: looking up case insensitive or + * abbreviated keys. + */ +public class FuzzyMap { + interface IKey { + String getName(); + } + + public static <V> V findInMap(Map<? extends IKey, V> map, IKey name, + boolean caseSensitive, boolean allowAbbreviations) { + if (allowAbbreviations) { + return findAbbreviatedValue(map, name, caseSensitive); + } else { + if (caseSensitive) { + return map.get(name); + } else { + for (IKey c : map.keySet()) { + if (c.getName().equalsIgnoreCase(name.getName())) { + return map.get(c); + } + } + } + } + return null; + } + + private static <V> V findAbbreviatedValue(Map<? extends IKey, V> map, IKey name, + boolean caseSensitive) { + String string = name.getName(); + Map<String, V> results = Maps.newHashMap(); + for (IKey c : map.keySet()) { + String n = c.getName(); + boolean match = (caseSensitive && n.startsWith(string)) + || ((! caseSensitive) && n.toLowerCase().startsWith(string.toLowerCase())); + if (match) { + results.put(n, map.get(c)); + } + } + + V result; + if (results.size() > 1) { + throw new ParameterException("Ambiguous option: " + name + + " matches " + results.keySet()); + } else if (results.size() == 1) { + result = results.values().iterator().next(); + } else { + result = null; + } + + return result; + } + + +} diff --git a/src/main/java/com/beust/jcommander/IDefaultProvider.java b/src/main/java/com/beust/jcommander/IDefaultProvider.java new file mode 100644 index 0000000..0353928 --- /dev/null +++ b/src/main/java/com/beust/jcommander/IDefaultProvider.java @@ -0,0 +1,35 @@ +/** + * 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; + +/** + * Allows the specification of default values. + * + * @author cbeust + */ +public interface IDefaultProvider { + + /** + * @param optionName The name of the option as specified in the names() attribute + * of the @Parameter option (e.g. "-file"). + * + * @return the default value for this option. + */ + String getDefaultValueFor(String optionName); +} diff --git a/src/main/java/com/beust/jcommander/IParameterValidator.java b/src/main/java/com/beust/jcommander/IParameterValidator.java new file mode 100644 index 0000000..19fee0d --- /dev/null +++ b/src/main/java/com/beust/jcommander/IParameterValidator.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2011 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; + +/** + * The class used to validate parameters. + * + * @author Cedric Beust <cedric@beust.com> + */ +public interface IParameterValidator { + + /** + * Validate the parameter. + * + * @param name The name of the parameter (e.g. "-host"). + * @param value The value of the parameter that we need to validate + * + * @throws ParameterException Thrown if the value of the parameter is invalid. + */ + void validate(String name, String value) throws ParameterException; + +} diff --git a/src/main/java/com/beust/jcommander/IParameterValidator2.java b/src/main/java/com/beust/jcommander/IParameterValidator2.java new file mode 100644 index 0000000..77e7dd3 --- /dev/null +++ b/src/main/java/com/beust/jcommander/IParameterValidator2.java @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2011 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 interface IParameterValidator2 extends IParameterValidator { + + /** + * Validate the parameter. + * + * @param name The name of the parameter (e.g. "-host"). + * @param value The value of the parameter that we need to validate + * @param pd The description of this parameter + * + * @throws ParameterException Thrown if the value of the parameter is invalid. + */ + void validate(String name, String value, ParameterDescription pd) throws ParameterException; + +} diff --git a/src/main/java/com/beust/jcommander/IStringConverter.java b/src/main/java/com/beust/jcommander/IStringConverter.java new file mode 100644 index 0000000..fb51a79 --- /dev/null +++ b/src/main/java/com/beust/jcommander/IStringConverter.java @@ -0,0 +1,39 @@ +/** + * 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; + +/** + * An interface that converts strings to any arbitrary type. + * + * If your class implements a constructor that takes a String, this + * constructor will be used to instantiate your converter and the + * parameter will receive the name of the option that's being parsed, + * which can be useful to issue a more useful error message if the + * conversion fails. + * + * You can also extend BaseConverter to make your life easier. + * + * @author cbeust + */ +public interface IStringConverter<T> { + /** + * @return an object of type <T> created from the parameter value. + */ + T convert(String value); +} diff --git a/src/main/java/com/beust/jcommander/IStringConverterFactory.java b/src/main/java/com/beust/jcommander/IStringConverterFactory.java new file mode 100644 index 0000000..0e53ca0 --- /dev/null +++ b/src/main/java/com/beust/jcommander/IStringConverterFactory.java @@ -0,0 +1,30 @@ +/** + * 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; + +/** + * A factory for IStringConverter. This interface lets you specify your + * converters in one place instead of having them repeated all over + * your argument classes. + * + * @author cbeust + */ +public interface IStringConverterFactory { + <T> Class<? extends IStringConverter<T>> getConverter(Class<T> forType); +} diff --git a/src/main/java/com/beust/jcommander/IValueValidator.java b/src/main/java/com/beust/jcommander/IValueValidator.java new file mode 100644 index 0000000..feed25d --- /dev/null +++ b/src/main/java/com/beust/jcommander/IValueValidator.java @@ -0,0 +1,14 @@ +package com.beust.jcommander; + +public interface IValueValidator<T> { + /** + * Validate the parameter. + * + * @param name The name of the parameter (e.g. "-host"). + * @param value The value of the parameter that we need to validate + * + * @throws ParameterException Thrown if the value of the parameter is invalid. + */ + void validate(String name, T value) throws ParameterException; + +} diff --git a/src/main/java/com/beust/jcommander/IVariableArity.java b/src/main/java/com/beust/jcommander/IVariableArity.java new file mode 100644 index 0000000..e8a40ba --- /dev/null +++ b/src/main/java/com/beust/jcommander/IVariableArity.java @@ -0,0 +1,16 @@ +package com.beust.jcommander; + +/** + * Must be implemented by argument classes that contain at least one + * \@Parameter with "variableArity = true". + */ +public interface IVariableArity { + + /** + * @param optionName the name of the option to process. + * @param options the entire list of options. + * + * @return how many options were processed. + */ + int processVariableArity(String optionName, String[] options); +} diff --git a/src/main/java/com/beust/jcommander/JCommander.java b/src/main/java/com/beust/jcommander/JCommander.java new file mode 100644 index 0000000..2e049a1 --- /dev/null +++ b/src/main/java/com/beust/jcommander/JCommander.java @@ -0,0 +1,1599 @@ +/** + * 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.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.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; + +/** + * The main class for JCommander. It's responsible for parsing the object that contains + * all the annotated fields, parse the command line and assign the fields with the correct + * values and a few other helper methods, such as usage(). + * + * The object(s) you pass in the constructor are expected to have one or more + * \@Parameter annotations on them. You can pass either a single object, an array of objects + * or an instance of Iterable. In the case of an array or Iterable, JCommander will collect + * the \@Parameter annotations from all the objects passed in parameter. + * + * @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); + } + } else { + vResult2.add(arg); + } + } else { + vResult2.add(arg); + } + } + + return vResult2.toArray(new String[vResult2.size()]); + } + + 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())); + } + } + } + } + + return Arrays.asList(arg); + } + + private boolean isOption(String[] args, String arg) { + String prefixes = getOptionPrefixes(args, arg); + return arg.length() > 0 && prefixes.indexOf(arg.charAt(0)) >= 0; + } + + private ParameterDescription getPrefixDescriptionFor(String arg) { + for (Map.Entry<IKey, ParameterDescription> es : m_descriptions.entrySet()) { + if (arg.startsWith(es.getKey().getName())) return es.getValue(); + } + + return null; + } + + /** + * 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; + + for (String a : args) { + ParameterDescription pd = getPrefixDescriptionFor(arg); + if (pd != null) result = pd; + if (a.equals(arg)) return result; + } + + throw new ParameterException("Unknown parameter: " + arg); + } + + private String getSeparatorFor(String[] args, String arg) { + ParameterDescription pd = getDescriptionFor(args, arg); + + // 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 " "; + } + + private String getOptionPrefixes(String[] args, String arg) { + ParameterDescription pd = getDescriptionFor(args, arg); + + // 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(); + } + 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()); + } + } + + if (! Strings.isStringEmpty(sb.toString())) { + result = sb.toString(); + } + + return result; + } + + /** + * 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(); + + try { + BufferedReader bufRead = new BufferedReader(new FileReader(fileName)); + + 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); + } + } + + bufRead.close(); + } + catch (IOException e) { + throw new ParameterException("Could not read file " + fileName + ": " + e); + } + + return result; + } + + /** + * 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; + } + + /** + * Create the ParameterDescriptions for all the \@Parameter found. + */ + private void createDescriptions() { + m_descriptions = Maps.newHashMap(); + + for (Object object : m_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 (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); + } 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"); + } + p("Adding description for " + name); + m_fields.put(parameterized, pd); + m_descriptions.put(new StringKey(name), pd); + + if (p.required()) m_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 (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) { + // + // Option + // + ParameterDescription pd = findParameterDescription(a); + + 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; + } + } + } + } else { + if (m_acceptUnknownOptions) { + m_unknownArgs.add(arg); + i++; + while (i < args.length && ! isOption(args, args[i])) { + m_unknownArgs.add(args[i++]); + } + increment = 0; + } else { + throw new ParameterException("Unknown option: " + arg); + } + } + } + else { + // + // Main parameter + // + 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); + } + } + + 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; + } + } + } + } + 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); + } + } + + } + + private class DefaultVariableArity implements IVariableArity { + + @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()) { + 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)); + } + } 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); + + /* + * 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; + } + + /* + * Important: ProgramName#toString() is used by longestName(Collection) function + * to format usage output. + */ + @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; +// } +} + diff --git a/src/main/java/com/beust/jcommander/MissingCommandException.java b/src/main/java/com/beust/jcommander/MissingCommandException.java new file mode 100644 index 0000000..1d572ab --- /dev/null +++ b/src/main/java/com/beust/jcommander/MissingCommandException.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; + +/** + * Thrown when a command was expected. + * + * @author Cedric Beust <cedric@beust.com> + */ +@SuppressWarnings("serial") +public class MissingCommandException extends ParameterException { + + public MissingCommandException(String string) { + super(string); + } + + public MissingCommandException(Throwable t) { + super(t); + } +} diff --git a/src/main/java/com/beust/jcommander/Parameter.java b/src/main/java/com/beust/jcommander/Parameter.java new file mode 100644 index 0000000..d8cf87d --- /dev/null +++ b/src/main/java/com/beust/jcommander/Parameter.java @@ -0,0 +1,130 @@ +/** + * 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 static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; + +import com.beust.jcommander.converters.CommaParameterSplitter; +import com.beust.jcommander.converters.IParameterSplitter; +import com.beust.jcommander.converters.NoConverter; +import com.beust.jcommander.validators.NoValidator; +import com.beust.jcommander.validators.NoValueValidator; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target({ FIELD, METHOD }) +public @interface Parameter { + + /** + * An array of allowed command line parameters (e.g. "-d", "--outputdir", etc...). + * If this attribute is omitted, the field it's annotating will receive all the + * unparsed options. There can only be at most one such annotation. + */ + String[] names() default {}; + + /** + * A description of this option. + */ + String description() default ""; + + /** + * Whether this option is required. + */ + boolean required() default false; + + /** + * The key used to find the string in the message bundle. + */ + String descriptionKey() default ""; + + /** + * How many parameter values this parameter will consume. For example, + * an arity of 2 will allow "-pair value1 value2". + */ + int arity() default -1; + + /** + * If true, this parameter is a password and it will be prompted on the console + * (if available). + */ + boolean password() default false; + + /** + * The string converter to use for this field. If the field is of type <tt>List</tt> + * and not <tt>listConverter</tt> attribute was specified, JCommander will split + * the input in individual values and convert each of them separately. + */ + Class<? extends IStringConverter<?>> converter() default NoConverter.class; + + /** + * The list string converter to use for this field. If it's specified, the + * field has to be of type <tt>List</tt> and the converter needs to return + * a List that's compatible with that type. + */ + Class<? extends IStringConverter<?>> listConverter() default NoConverter.class; + + /** + * If true, this parameter won't appear in the usage(). + */ + boolean hidden() default false; + + /** + * Validate the parameter found on the command line. + */ + Class<? extends IParameterValidator> validateWith() default NoValidator.class; + + /** + * Validate the value for this parameter. + */ + Class<? extends IValueValidator> validateValueWith() default NoValueValidator.class; + + /** + * @return true if this parameter has a variable arity. See @{IVariableArity} + */ + boolean variableArity() default false; + + /** + * What splitter to use (applicable only on fields of type <tt>List</tt>). By default, + * a comma separated splitter will be used. + */ + Class<? extends IParameterSplitter> splitter() default CommaParameterSplitter.class; + + /** + * If true, console will not echo typed input + * Used in conjunction with password = true + */ + boolean echoInput() default false; + + /** + * If true, this parameter is for help. If such a parameter is specified, + * required parameters are no longer checked for their presence. + */ + boolean help() default false; + + /** + * If true, this parameter can be overwritten through a file or another appearance of the parameter + * @return + */ + boolean forceNonOverwritable() default false; + + +} diff --git a/src/main/java/com/beust/jcommander/ParameterDescription.java b/src/main/java/com/beust/jcommander/ParameterDescription.java new file mode 100644 index 0000000..2ef2d5f --- /dev/null +++ b/src/main/java/com/beust/jcommander/ParameterDescription.java @@ -0,0 +1,364 @@ +/** + * Copyright (C) 2010 the original author or authors. + * See the notice.md file distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.beust.jcommander; + +import com.beust.jcommander.validators.NoValidator; +import com.beust.jcommander.validators.NoValueValidator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +public class ParameterDescription { + private Object m_object; + + private WrappedParameter m_wrappedParameter; + private Parameter m_parameterAnnotation; + private DynamicParameter m_dynamicParameterAnnotation; + + /** The field/method */ + private Parameterized m_parameterized; + /** Keep track of whether a value was added to flag an error */ + private boolean m_assigned = false; + private ResourceBundle m_bundle; + private String m_description; + private JCommander m_jCommander; + private Object m_default; + /** Longest of the names(), used to present usage() alphabetically */ + private String m_longestName = ""; + + public ParameterDescription(Object object, DynamicParameter annotation, + Parameterized parameterized, + ResourceBundle bundle, JCommander jc) { + if (! Map.class.isAssignableFrom(parameterized.getType())) { + throw new ParameterException("@DynamicParameter " + parameterized.getName() + + " should be of type " + + "Map but is " + parameterized.getType().getName()); + } + + m_dynamicParameterAnnotation = annotation; + m_wrappedParameter = new WrappedParameter(m_dynamicParameterAnnotation); + init(object, parameterized, bundle, jc); + } + + public ParameterDescription(Object object, Parameter annotation, Parameterized parameterized, + ResourceBundle bundle, JCommander jc) { + m_parameterAnnotation = annotation; + m_wrappedParameter = new WrappedParameter(m_parameterAnnotation); + init(object, parameterized, bundle, jc); + } + + /** + * Find the resource bundle in the annotations. + * @return + */ + @SuppressWarnings("deprecation") + private ResourceBundle findResourceBundle(Object o) { + ResourceBundle result = null; + + Parameters p = o.getClass().getAnnotation(Parameters.class); + if (p != null && ! isEmpty(p.resourceBundle())) { + result = ResourceBundle.getBundle(p.resourceBundle(), Locale.getDefault()); + } else { + com.beust.jcommander.ResourceBundle a = o.getClass().getAnnotation( + com.beust.jcommander.ResourceBundle.class); + if (a != null && ! isEmpty(a.value())) { + result = ResourceBundle.getBundle(a.value(), Locale.getDefault()); + } + } + + return result; + } + + private boolean isEmpty(String s) { + return s == null || "".equals(s); + } + + private void initDescription(String description, String descriptionKey, String[] names) { + m_description = description; + if (! "".equals(descriptionKey)) { + if (m_bundle != null) { + m_description = m_bundle.getString(descriptionKey); + } else { +// JCommander.getConsole().println("Warning: field " + object.getClass() + "." + field.getName() +// + " has a descriptionKey but no bundle was defined with @ResourceBundle, using " + +// "default description:'" + m_description + "'"); + } + } + + for (String name : names) { + if (name.length() > m_longestName.length()) m_longestName = name; + } + } + + @SuppressWarnings("unchecked") + private void init(Object object, Parameterized parameterized, ResourceBundle bundle, + JCommander jCommander) { + m_object = object; + m_parameterized = parameterized; + m_bundle = bundle; + if (m_bundle == null) { + m_bundle = findResourceBundle(object); + } + m_jCommander = jCommander; + + if (m_parameterAnnotation != null) { + String description; + if (Enum.class.isAssignableFrom(parameterized.getType()) + && m_parameterAnnotation.description().isEmpty()) { + description = "Options: " + EnumSet.allOf((Class<? extends Enum>) parameterized.getType()); + }else { + description = m_parameterAnnotation.description(); + } + initDescription(description, m_parameterAnnotation.descriptionKey(), + m_parameterAnnotation.names()); + } else if (m_dynamicParameterAnnotation != null) { + initDescription(m_dynamicParameterAnnotation.description(), + m_dynamicParameterAnnotation.descriptionKey(), + m_dynamicParameterAnnotation.names()); + } else { + throw new AssertionError("Shound never happen"); + } + + try { + m_default = parameterized.get(object); + } catch (Exception e) { + } + + // + // Validate default values, if any and if applicable + // + if (m_default != null) { + if (m_parameterAnnotation != null) { + validateDefaultValues(m_parameterAnnotation.names()); + } + } + } + + private void validateDefaultValues(String[] names) { + String name = names.length > 0 ? names[0] : ""; + validateValueParameter(name, m_default); + } + + public String getLongestName() { + return m_longestName; + } + + public Object getDefault() { + return m_default; + } + + public String getDescription() { + return m_description; + } + + public Object getObject() { + return m_object; + } + + public String getNames() { + StringBuilder sb = new StringBuilder(); + String[] names = m_wrappedParameter.names(); + for (int i = 0; i < names.length; i++) { + if (i > 0) sb.append(", "); + sb.append(names[i]); + } + return sb.toString(); + } + + public WrappedParameter getParameter() { + return m_wrappedParameter; + } + + public Parameterized getParameterized() { + return m_parameterized; + } + + private boolean isMultiOption() { + Class<?> fieldType = m_parameterized.getType(); + return fieldType.equals(List.class) || fieldType.equals(Set.class) + || m_parameterized.isDynamicParameter(); + } + + public void addValue(String value) { + addValue(value, false /* not default */); + } + + /** + * @return true if this parameter received a value during the parsing phase. + */ + public boolean isAssigned() { + return m_assigned; + } + + + public void setAssigned(boolean b) { + m_assigned = b; + } + + /** + * Add the specified value to the field. First, validate the value if a + * validator was specified. Then look up any field converter, then any type + * converter, and if we can't find any, throw an exception. + */ + public void addValue(String value, boolean isDefault) { + p("Adding " + (isDefault ? "default " : "") + "value:" + value + + " to parameter:" + m_parameterized.getName()); + String name = m_wrappedParameter.names()[0]; + if (m_assigned && ! isMultiOption() && !m_jCommander.isParameterOverwritingAllowed() || isNonOverwritableForced()) { + throw new ParameterException("Can only specify option " + name + " once."); + } + + validateParameter(name, value); + + Class<?> type = m_parameterized.getType(); + + Object convertedValue = m_jCommander.convertValue(this, value); + validateValueParameter(name, convertedValue); + boolean isCollection = Collection.class.isAssignableFrom(type); + + if (isCollection) { + @SuppressWarnings("unchecked") + Collection<Object> l = (Collection<Object>) m_parameterized.get(m_object); + if (l == null || fieldIsSetForTheFirstTime(isDefault)) { + l = newCollection(type); + m_parameterized.set(m_object, l); + } + if (convertedValue instanceof Collection) { + l.addAll((Collection) convertedValue); + } else { // if (isMainParameter || m_parameterAnnotation.arity() > 1) { + l.add(convertedValue); +// } else { +// l. + } + } else { + m_wrappedParameter.addValue(m_parameterized, m_object, convertedValue); + } + if (! isDefault) m_assigned = true; + } + + private void validateParameter(String name, String value) { + Class<? extends IParameterValidator> validator = m_wrappedParameter.validateWith(); + if (validator != null) { + validateParameter(this, validator, name, value); + } + } + + private void validateValueParameter(String name, Object value) { + Class<? extends IValueValidator> validator = m_wrappedParameter.validateValueWith(); + if (validator != null) { + validateValueParameter(validator, name, value); + } + } + + public static void validateValueParameter(Class<? extends IValueValidator> validator, + String name, Object value) { + try { + if (validator != NoValueValidator.class) { + p("Validating value parameter:" + name + " value:" + value + " validator:" + validator); + } + validator.newInstance().validate(name, value); + } catch (InstantiationException e) { + throw new ParameterException("Can't instantiate validator:" + e); + } catch (IllegalAccessException e) { + throw new ParameterException("Can't instantiate validator:" + e); + } + } + + public static void validateParameter(ParameterDescription pd, + Class<? extends IParameterValidator> validator, + String name, String value) { + try { + if (validator != NoValidator.class) { + p("Validating parameter:" + name + " value:" + value + " validator:" + validator); + } + validator.newInstance().validate(name, value); + if (IParameterValidator2.class.isAssignableFrom(validator)) { + IParameterValidator2 instance = (IParameterValidator2) validator.newInstance(); + instance.validate(name, value, pd); + } + } catch (InstantiationException e) { + throw new ParameterException("Can't instantiate validator:" + e); + } catch (IllegalAccessException e) { + throw new ParameterException("Can't instantiate validator:" + e); + } catch(ParameterException ex) { + throw ex; + } catch(Exception ex) { + throw new ParameterException(ex); + } + } + + /* + * Creates a new collection for the field's type. + * + * Currently only List and Set are supported. Support for + * Queues and Stacks could be useful. + */ + @SuppressWarnings("unchecked") + private Collection<Object> newCollection(Class<?> type) { + if (SortedSet.class.isAssignableFrom(type)) return new TreeSet(); + else if (LinkedHashSet.class.isAssignableFrom(type)) return new LinkedHashSet(); + else if (Set.class.isAssignableFrom(type)) return new HashSet(); + else if (List.class.isAssignableFrom(type)) return new ArrayList(); + else { + throw new ParameterException("Parameters of Collection type '" + type.getSimpleName() + + "' are not supported. Please use List or Set instead."); + } + } + + /* + * Tests if its the first time a non-default value is + * being added to the field. + */ + private boolean fieldIsSetForTheFirstTime(boolean isDefault) { + return (!isDefault && !m_assigned); + } + + private static void p(String string) { + if (System.getProperty(JCommander.DEBUG_PROPERTY) != null) { + JCommander.getConsole().println("[ParameterDescription] " + string); + } + } + + @Override + public String toString() { + return "[ParameterDescription " + m_parameterized.getName() + "]"; + } + + public boolean isDynamicParameter() { + return m_dynamicParameterAnnotation != null; + } + + public boolean isHelp() { + return m_wrappedParameter.isHelp(); + } + + public boolean isNonOverwritableForced() { + return m_wrappedParameter.isNonOverwritableForced(); + } +} diff --git a/src/main/java/com/beust/jcommander/ParameterException.java b/src/main/java/com/beust/jcommander/ParameterException.java new file mode 100644 index 0000000..2bba7d1 --- /dev/null +++ b/src/main/java/com/beust/jcommander/ParameterException.java @@ -0,0 +1,42 @@ +/** + * 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; + +/** + * The main exception that JCommand will throw when something goes wrong while + * parsing parameters. + * + * @author Cedric Beust <cedric@beust.com> + */ +@SuppressWarnings("serial") +public class ParameterException extends RuntimeException { + + public ParameterException(Throwable t) { + super(t); + } + + public ParameterException(String string) { + super(string); + } + + public ParameterException(String string, Throwable t) { + super(string, t); + } + +} diff --git a/src/main/java/com/beust/jcommander/Parameterized.java b/src/main/java/com/beust/jcommander/Parameterized.java new file mode 100644 index 0000000..ff8753b --- /dev/null +++ b/src/main/java/com/beust/jcommander/Parameterized.java @@ -0,0 +1,244 @@ +package com.beust.jcommander; + +import com.beust.jcommander.internal.Lists; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; + +/** + * Encapsulate a field or a method annotated with @Parameter or @DynamicParameter + */ +public class Parameterized { + + // Either a method or a field + private Field m_field; + private Method m_method; + private Method m_getter; + + // Either of these two + private WrappedParameter m_wrappedParameter; + private ParametersDelegate m_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); + } + m_parametersDelegate = pd; + } + + public static List<Parameterized> parseArg(Object arg) { + List<Parameterized> result = Lists.newArrayList(); + + Class<? extends Object> cls = arg.getClass(); + while (!Object.class.equals(cls)) { + 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)); + } else if (dynamicParameter != null) { + result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null, + f, null)); + } else if (delegateAnnotation != null) { + result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation, + f, null)); + } + } + cls = cls.getSuperclass(); + } + + // Reassigning + cls = arg.getClass(); + while (!Object.class.equals(cls)) { + for (Method m : cls.getDeclaredMethods()) { + 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)); + } else if (dynamicParameter != null) { + result.add(new Parameterized(new WrappedParameter((DynamicParameter) annotation), null, + null, m)); + } else if (delegateAnnotation != null) { + result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation, + null, m)); + } + } + cls = cls.getSuperclass(); + } + + return result; + } + + public WrappedParameter getWrappedParameter() { + return m_wrappedParameter; + } + + public Class<?> getType() { + if (m_method != null) { + return m_method.getParameterTypes()[0]; + } else { + return m_field.getType(); + } + } + + public String getName() { + if (m_method != null) { + return m_method.getName(); + } else { + return m_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]); + } + return m_getter.invoke(object); + } else { + return m_field.get(object); + } + } catch (SecurityException e) { + throw new ParameterException(e); + } catch (NoSuchMethodException e) { + // Try to find a field + String name = m_method.getName(); + String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4); + Object result = null; + try { + Field field = m_method.getDeclaringClass().getDeclaredField(fieldName); + if (field != null) { + field.setAccessible(true); + result = field.get(object); + } + } catch(NoSuchFieldException ex) { + // ignore + } catch(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); + } + } + + @Override + 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()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Parameterized other = (Parameterized) obj; + if (m_field == null) { + if (other.m_field != null) + return false; + } else if (!m_field.equals(other.m_field)) + return false; + if (m_method == null) { + if (other.m_method != null) + return false; + } else if (!m_method.equals(other.m_method)) + return false; + return true; + } + + public boolean isDynamicParameter(Field field) { + if (m_method != null) { + return m_method.getAnnotation(DynamicParameter.class) != null; + } else { + return m_field.getAnnotation(DynamicParameter.class) != null; + } + } + + public void set(Object object, Object value) { + try { + if (m_method != null) { + m_method.invoke(object, value); + } else { + m_field.set(object, value); + } + } catch (IllegalArgumentException ex) { + throw new ParameterException(ex); + } catch (IllegalAccessException ex) { + throw new ParameterException(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); + } + } + } + + public ParametersDelegate getDelegateAnnotation() { + return m_parametersDelegate; + } + + public Type getGenericType() { + if (m_method != null) { + return m_method.getGenericParameterTypes()[0]; + } else { + return m_field.getGenericType(); + } + } + + public Parameter getParameter() { + return m_wrappedParameter.getParameter(); + } + + /** + * @return the generic type of the collection for this field, or null if not applicable. + */ + public Type findFieldGenericType() { + if (m_method != null) { + return null; + } else { + if (m_field.getGenericType() instanceof ParameterizedType) { + ParameterizedType p = (ParameterizedType) m_field.getGenericType(); + Type cls = p.getActualTypeArguments()[0]; + if (cls instanceof Class) { + return cls; + } + } + } + + return null; + } + + public boolean isDynamicParameter() { + return m_wrappedParameter.getDynamicParameter() != null; + } + +} diff --git a/src/main/java/com/beust/jcommander/Parameters.java b/src/main/java/com/beust/jcommander/Parameters.java new file mode 100644 index 0000000..f2e8c76 --- /dev/null +++ b/src/main/java/com/beust/jcommander/Parameters.java @@ -0,0 +1,75 @@ +/** + * 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.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; + +/** + * An annotation used to specify settings for parameter parsing. + * + * @author cbeust + */ +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target({ TYPE }) +@Inherited +public @interface Parameters { + + public static final String DEFAULT_OPTION_PREFIXES = "-"; + + /** + * The name of the resource bundle to use for this class. + */ + String resourceBundle() default ""; + + /** + * The character(s) that separate options. + */ + 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. + */ + String commandDescription() default ""; + + /** + * @return the key used to find the command description in the resource bundle. + */ + String commandDescriptionKey() default ""; + + /** + * An array of allowed command names. + */ + String[] commandNames() default {}; + + /** + * If true, this command won't appear in the usage(). + */ + boolean hidden() default false; +} diff --git a/src/main/java/com/beust/jcommander/ParametersDelegate.java b/src/main/java/com/beust/jcommander/ParametersDelegate.java new file mode 100644 index 0000000..5a06f8e --- /dev/null +++ b/src/main/java/com/beust/jcommander/ParametersDelegate.java @@ -0,0 +1,44 @@ +/** + * 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.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; + +/** + * <p>When applied to a field all of its child fields annotated + * with {@link Parameter} will be included during arguments + * parsing.</p> + * + * <p>Mainly useful when creating complex command based CLI interfaces, + * where several commands can share a set of arguments, but using + * object inheritance is not enough, due to no-multiple-inheritance + * restriction. Using {@link ParametersDelegate} any number of + * command sets can be shared by using composition pattern.</p> + * + * <p>Delegations can be chained (nested).</p> + * + * @author rodionmoiseev + */ +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target({ FIELD }) +public @interface ParametersDelegate { +} diff --git a/src/main/java/com/beust/jcommander/ResourceBundle.java b/src/main/java/com/beust/jcommander/ResourceBundle.java new file mode 100644 index 0000000..806ade8 --- /dev/null +++ b/src/main/java/com/beust/jcommander/ResourceBundle.java @@ -0,0 +1,38 @@ +/** + * 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 static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @deprecated use @Parameters + * + * @author Cedric Beust <cedric@beust.com> + */ +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target({ TYPE }) +public @interface ResourceBundle { + /** + * The name of the resource bundle to use for this class. + */ + String value(); +} diff --git a/src/main/java/com/beust/jcommander/StringKey.java b/src/main/java/com/beust/jcommander/StringKey.java new file mode 100644 index 0000000..09d1149 --- /dev/null +++ b/src/main/java/com/beust/jcommander/StringKey.java @@ -0,0 +1,48 @@ +package com.beust.jcommander; + +import com.beust.jcommander.FuzzyMap.IKey; + +public class StringKey implements IKey { + + private String m_name; + + public StringKey(String name) { + m_name = name; + } + + @Override + public String getName() { + return m_name; + } + + @Override + public String toString() { + return m_name; + } + + @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; + StringKey other = (StringKey) obj; + if (m_name == null) { + if (other.m_name != null) + return false; + } else if (!m_name.equals(other.m_name)) + return false; + return true; + } + +} diff --git a/src/main/java/com/beust/jcommander/Strings.java b/src/main/java/com/beust/jcommander/Strings.java new file mode 100644 index 0000000..591a38a --- /dev/null +++ b/src/main/java/com/beust/jcommander/Strings.java @@ -0,0 +1,7 @@ +package com.beust.jcommander; + +public class Strings { + public static boolean isStringEmpty(String s) { + return s == null || "".equals(s); + } +} diff --git a/src/main/java/com/beust/jcommander/WrappedParameter.java b/src/main/java/com/beust/jcommander/WrappedParameter.java new file mode 100644 index 0000000..f4e7d56 --- /dev/null +++ b/src/main/java/com/beust/jcommander/WrappedParameter.java @@ -0,0 +1,115 @@ +package com.beust.jcommander; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Encapsulates the operations common to @Parameter and @DynamicParameter + */ +public class WrappedParameter { + private Parameter m_parameter; + private DynamicParameter m_dynamicParameter; + + public WrappedParameter(Parameter p) { + m_parameter = p; + } + + public WrappedParameter(DynamicParameter p) { + m_dynamicParameter = p; + } + + public Parameter getParameter() { + return m_parameter; + } + + public DynamicParameter getDynamicParameter() { + return m_dynamicParameter; + } + + public int arity() { + return m_parameter != null ? m_parameter.arity() : 1; + } + + public boolean hidden() { + return m_parameter != null ? m_parameter.hidden() : m_dynamicParameter.hidden(); + } + + public boolean required() { + return m_parameter != null ? m_parameter.required() : m_dynamicParameter.required(); + } + + public boolean password() { + return m_parameter != null ? m_parameter.password() : false; + } + + public String[] names() { + return m_parameter != null ? m_parameter.names() : m_dynamicParameter.names(); + } + + public boolean variableArity() { + return m_parameter != null ? m_parameter.variableArity() : false; + } + + public Class<? extends IParameterValidator> validateWith() { + return m_parameter != null ? m_parameter.validateWith() : m_dynamicParameter.validateWith(); + } + + public Class<? extends IValueValidator> validateValueWith() { + return m_parameter != null + ? m_parameter.validateValueWith() + : m_dynamicParameter.validateValueWith(); + } + + public boolean echoInput() { + return m_parameter != null ? m_parameter.echoInput() : false; + } + + public void addValue(Parameterized parameterized, Object object, Object value) { + if (m_parameter != null) { + parameterized.set(object, value); + } else { + String a = m_dynamicParameter.assignment(); + String sv = value.toString(); + + int aInd = sv.indexOf(a); + if (aInd == -1) { + throw new ParameterException( + "Dynamic parameter expected a value of the form a" + a + "b" + + " but got:" + sv); + } + callPut(object, parameterized, sv.substring(0, aInd), sv.substring(aInd + 1)); + } + } + + private void callPut(Object object, Parameterized parameterized, String key, String value) { + try { + 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) { + e.printStackTrace(); + } + } + + private Method findPut(Class<?> cls) throws SecurityException, NoSuchMethodException { + return cls.getMethod("put", Object.class, Object.class); + } + + public String getAssignment() { + return m_dynamicParameter != null ? m_dynamicParameter.assignment() : ""; + } + + public boolean isHelp() { + return m_parameter != null && m_parameter.help(); + } + + public boolean isNonOverwritableForced() { + return m_parameter != null && m_parameter.forceNonOverwritable(); + } +} diff --git a/src/main/java/com/beust/jcommander/converters/BaseConverter.java b/src/main/java/com/beust/jcommander/converters/BaseConverter.java new file mode 100644 index 0000000..4287163 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/BaseConverter.java @@ -0,0 +1,44 @@ +/** + * 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; + +/** + * Base class for converters that stores the name of the option. + * + * @author cbeust + */ +abstract public class BaseConverter<T> implements IStringConverter<T> { + + private String m_optionName; + + public BaseConverter(String optionName) { + m_optionName = optionName; + } + + public String getOptionName() { + return m_optionName; + } + + protected String getErrorString(String value, String to) { + return "\"" + getOptionName() + "\": couldn't convert \"" + value + "\" to " + to; + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/BigDecimalConverter.java b/src/main/java/com/beust/jcommander/converters/BigDecimalConverter.java new file mode 100644 index 0000000..dfbba34 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/BigDecimalConverter.java @@ -0,0 +1,43 @@ +/** + * 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.ParameterException; + +import java.math.BigDecimal; + +/** + * Converts a String to a BigDecimal. + * + * @author Angus Smithson + */ +public class BigDecimalConverter extends BaseConverter<BigDecimal> { + + public BigDecimalConverter(String optionName) { + super(optionName); + } + + public BigDecimal convert(String value) { + try { + return new BigDecimal(value); + } catch (NumberFormatException nfe) { + throw new ParameterException(getErrorString(value, "a BigDecimal")); + } + } +} diff --git a/src/main/java/com/beust/jcommander/converters/BooleanConverter.java b/src/main/java/com/beust/jcommander/converters/BooleanConverter.java new file mode 100644 index 0000000..5126d22 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/BooleanConverter.java @@ -0,0 +1,42 @@ +/** + * 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.ParameterException; + +/** + * Converts a string to a boolean. + * + * @author cbeust + */ +public class BooleanConverter extends BaseConverter<Boolean> { + + public BooleanConverter(String optionName) { + super(optionName); + } + + public Boolean convert(String value) { + if ("false".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value)) { + return Boolean.parseBoolean(value); + } else { + throw new ParameterException(getErrorString(value, "a boolean")); + } + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/CommaParameterSplitter.java b/src/main/java/com/beust/jcommander/converters/CommaParameterSplitter.java new file mode 100644 index 0000000..0e3bb18 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/CommaParameterSplitter.java @@ -0,0 +1,12 @@ +package com.beust.jcommander.converters; + +import java.util.Arrays; +import java.util.List; + +public class CommaParameterSplitter implements IParameterSplitter { + + public List<String> split(String value) { + return Arrays.asList(value.split(",")); + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/DoubleConverter.java b/src/main/java/com/beust/jcommander/converters/DoubleConverter.java new file mode 100644 index 0000000..0c36c68 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/DoubleConverter.java @@ -0,0 +1,42 @@ +/** + * 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.ParameterException; + +/** + * Convert a string to a double. + * + * @author acornejo + */ +public class DoubleConverter extends BaseConverter<Double> { + + public DoubleConverter(String optionName) { + super(optionName); + } + + public Double convert(String value) { + try { + return Double.parseDouble(value); + } catch(NumberFormatException ex) { + throw new ParameterException(getErrorString(value, "a double")); + } + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/FileConverter.java b/src/main/java/com/beust/jcommander/converters/FileConverter.java new file mode 100644 index 0000000..c18b575 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/FileConverter.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.converters; + +import com.beust.jcommander.IStringConverter; + +import java.io.File; + +/** + * Convert a string into a file. + * + * @author cbeust + */ +public class FileConverter implements IStringConverter<File> { + + public File convert(String value) { + return new File(value); + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/FloatConverter.java b/src/main/java/com/beust/jcommander/converters/FloatConverter.java new file mode 100644 index 0000000..2e2eff8 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/FloatConverter.java @@ -0,0 +1,42 @@ +/** + * 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.ParameterException; + +/** + * Convert a string to a float. + * + * @author acornejo + */ +public class FloatConverter extends BaseConverter<Float> { + + public FloatConverter(String optionName) { + super(optionName); + } + + public Float convert(String value) { + try { + return Float.parseFloat(value); + } catch(NumberFormatException ex) { + throw new ParameterException(getErrorString(value, "a float")); + } + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/IParameterSplitter.java b/src/main/java/com/beust/jcommander/converters/IParameterSplitter.java new file mode 100644 index 0000000..5859f4a --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/IParameterSplitter.java @@ -0,0 +1,11 @@ +package com.beust.jcommander.converters; + +import java.util.List; + +/** + * Convert a string representing several parameters (e.g. "a,b,c" or "d/e/f") into a + * list of arguments ([a,b,c] and [d,e,f]). + */ +public interface IParameterSplitter { + List<String> split(String value); +} diff --git a/src/main/java/com/beust/jcommander/converters/ISO8601DateConverter.java b/src/main/java/com/beust/jcommander/converters/ISO8601DateConverter.java new file mode 100644 index 0000000..f024f5c --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/ISO8601DateConverter.java @@ -0,0 +1,48 @@ +/** + * 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.ParameterException; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Converts a String to a Date. + * TODO Modify to work with all valid ISO 8601 date formats (currently only works with yyyy-MM-dd). + * + * @author Angus Smithson + */ +public class ISO8601DateConverter extends BaseConverter<Date> { + + private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); + + public ISO8601DateConverter(String optionName) { + super(optionName); + } + + public Date convert(String value) { + try { + return DATE_FORMAT.parse(value); + } catch (ParseException pe) { + throw new ParameterException(getErrorString(value, String.format("an ISO-8601 formatted date (%s)", DATE_FORMAT.toPattern()))); + } + } +} diff --git a/src/main/java/com/beust/jcommander/converters/IntegerConverter.java b/src/main/java/com/beust/jcommander/converters/IntegerConverter.java new file mode 100644 index 0000000..53d1119 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/IntegerConverter.java @@ -0,0 +1,42 @@ +/** + * 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.ParameterException; + +/** + * Convert a string to an integer. + * + * @author cbeust + */ +public class IntegerConverter extends BaseConverter<Integer> { + + public IntegerConverter(String optionName) { + super(optionName); + } + + public Integer convert(String value) { + try { + return Integer.parseInt(value); + } catch(NumberFormatException ex) { + throw new ParameterException(getErrorString(value, "an integer")); + } + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/LongConverter.java b/src/main/java/com/beust/jcommander/converters/LongConverter.java new file mode 100644 index 0000000..863956b --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/LongConverter.java @@ -0,0 +1,42 @@ +/** + * 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.ParameterException; + +/** + * Convert a string to a long. + * + * @author cbeust + */ +public class LongConverter extends BaseConverter<Long> { + + public LongConverter(String optionName) { + super(optionName); + } + + public Long convert(String value) { + try { + return Long.parseLong(value); + } catch(NumberFormatException ex) { + throw new ParameterException(getErrorString(value, "a long")); + } + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/NoConverter.java b/src/main/java/com/beust/jcommander/converters/NoConverter.java new file mode 100644 index 0000000..618daf9 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/NoConverter.java @@ -0,0 +1,34 @@ +/** + * 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; + +/** + * Default value for a converter when none is specified. + * + * @author cbeust + */ +public class NoConverter implements IStringConverter<String> { + + public String convert(String value) { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/PathConverter.java b/src/main/java/com/beust/jcommander/converters/PathConverter.java new file mode 100644 index 0000000..b7fdafd --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/PathConverter.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 com.beust.jcommander.IStringConverter; + +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Convert a string into a path. + * + * @author samvv + */ +public class PathConverter implements IStringConverter<Path> { + + public Path convert(String value) { + return Paths.get(value); + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/StringConverter.java b/src/main/java/com/beust/jcommander/converters/StringConverter.java new file mode 100644 index 0000000..ea1ae38 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/StringConverter.java @@ -0,0 +1,34 @@ +/** + * 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; + +/** + * Default converter for strings. + * + * @author cbeust + */ +public class StringConverter implements IStringConverter<String> { + + public String convert(String value) { + return value; + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/URIConverter.java b/src/main/java/com/beust/jcommander/converters/URIConverter.java new file mode 100644 index 0000000..3473bf0 --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/URIConverter.java @@ -0,0 +1,45 @@ +/** + * 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.ParameterException; + +import java.net.URI; +import java.net.URISyntaxException; + +/** + * Convert a string into a URI. + * + * @author samvv + */ +public class URIConverter extends BaseConverter<URI> { + + public URIConverter(String optionName) { + super(optionName); + } + + public URI convert(String value) { + try { + return new URI(value); + } catch (URISyntaxException e) { + throw new ParameterException(getErrorString(value, "a RFC 2396 and RFC 2732 compliant URI")); + } + } + +} diff --git a/src/main/java/com/beust/jcommander/converters/URLConverter.java b/src/main/java/com/beust/jcommander/converters/URLConverter.java new file mode 100644 index 0000000..1f3734b --- /dev/null +++ b/src/main/java/com/beust/jcommander/converters/URLConverter.java @@ -0,0 +1,46 @@ +/** + * 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.ParameterException; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Convert a string into a URI. + * + * @author samvv + */ +public class URLConverter extends BaseConverter<URL> { + + public URLConverter(String optionName) { + super(optionName); + } + + public URL convert(String value) { + try { + return new URL(value); + } catch (MalformedURLException e) { + throw new ParameterException( + getErrorString(value, "a RFC 2396 and RFC 2732 compliant URL")); + + } + } +}
\ 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 new file mode 100644 index 0000000..d5401a1 --- /dev/null +++ b/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java @@ -0,0 +1,70 @@ +/** + * 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.defaultprovider; + +import com.beust.jcommander.IDefaultProvider; +import com.beust.jcommander.ParameterException; + +import java.io.IOException; +import java.net.URL; +import java.util.Properties; + +/** + * A default provider that reads its default values from a property file. + * + * @author cbeust + */ +public class PropertyFileDefaultProvider implements IDefaultProvider { + public static final String DEFAULT_FILE_NAME = "jcommander.properties"; + private Properties m_properties; + + public PropertyFileDefaultProvider() { + init(DEFAULT_FILE_NAME); + } + + public PropertyFileDefaultProvider(String fileName) { + init(fileName); + } + + private void init(String fileName) { + try { + m_properties = new Properties(); + URL url = ClassLoader.getSystemResource(fileName); + if (url != null) { + m_properties.load(url.openStream()); + } else { + throw new ParameterException("Could not find property file: " + fileName + + " on the class path"); + } + } + catch (IOException e) { + throw new ParameterException("Could not open property file: " + fileName); + } + } + + public String getDefaultValueFor(String optionName) { + int index = 0; + while (index < optionName.length() && ! Character.isLetterOrDigit(optionName.charAt(index))) { + index++; + } + String key = optionName.substring(index); + return m_properties.getProperty(key); + } + +} diff --git a/src/main/java/com/beust/jcommander/internal/Console.java b/src/main/java/com/beust/jcommander/internal/Console.java new file mode 100644 index 0000000..95eafe1 --- /dev/null +++ b/src/main/java/com/beust/jcommander/internal/Console.java @@ -0,0 +1,10 @@ +package com.beust.jcommander.internal; + +public interface Console { + + void print(String msg); + + void println(String msg); + + char[] readPassword(boolean echoInput); +} diff --git a/src/main/java/com/beust/jcommander/internal/DefaultConsole.java b/src/main/java/com/beust/jcommander/internal/DefaultConsole.java new file mode 100644 index 0000000..8fd7d6d --- /dev/null +++ b/src/main/java/com/beust/jcommander/internal/DefaultConsole.java @@ -0,0 +1,32 @@ +package com.beust.jcommander.internal; + +import com.beust.jcommander.ParameterException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class DefaultConsole implements Console { + + public void print(String msg) { + System.out.print(msg); + } + + public void println(String msg) { + System.out.println(msg); + } + + public char[] readPassword(boolean echoInput) { + try { + // Do not close the readers since System.in should not be closed + InputStreamReader isr = new InputStreamReader(System.in); + BufferedReader in = new BufferedReader(isr); + String result = in.readLine(); + return result.toCharArray(); + } + catch (IOException e) { + throw new ParameterException(e); + } + } + +} diff --git a/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java b/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java new file mode 100644 index 0000000..2b8a10c --- /dev/null +++ b/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java @@ -0,0 +1,75 @@ +/** + * 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.internal; + +import com.beust.jcommander.IStringConverter; +import com.beust.jcommander.IStringConverterFactory; +import com.beust.jcommander.converters.BigDecimalConverter; +import com.beust.jcommander.converters.BooleanConverter; +import com.beust.jcommander.converters.DoubleConverter; +import com.beust.jcommander.converters.FileConverter; +import com.beust.jcommander.converters.FloatConverter; +import com.beust.jcommander.converters.ISO8601DateConverter; +import com.beust.jcommander.converters.IntegerConverter; +import com.beust.jcommander.converters.LongConverter; +import com.beust.jcommander.converters.StringConverter; +import com.beust.jcommander.converters.PathConverter; +import com.beust.jcommander.converters.URIConverter; +import com.beust.jcommander.converters.URLConverter; + +import java.io.File; +import java.math.BigDecimal; +import java.util.Date; +import java.net.URI; +import java.net.URL; +import java.nio.file.Path; +import java.util.Map; + +public class DefaultConverterFactory implements IStringConverterFactory { + /** + * A map of converters per class. + */ + private static Map<Class, Class<? extends IStringConverter<?>>> m_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); + m_classConverters.put(Path.class, PathConverter.class); + 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); + } + +} diff --git a/src/main/java/com/beust/jcommander/internal/JDK6Console.java b/src/main/java/com/beust/jcommander/internal/JDK6Console.java new file mode 100644 index 0000000..70cb186 --- /dev/null +++ b/src/main/java/com/beust/jcommander/internal/JDK6Console.java @@ -0,0 +1,45 @@ +package com.beust.jcommander.internal; + +import com.beust.jcommander.ParameterException; + +import java.io.PrintWriter; +import java.lang.reflect.Method; + +public class JDK6Console implements Console { + + private Object console; + + private PrintWriter writer; + + 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]); + } + + public void print(String msg) { + writer.print(msg); + } + + public void println(String msg) { + writer.println(msg); + } + + public char[] readPassword(boolean echoInput) { + try { + writer.flush(); + Method method; + if (echoInput) { + method = console.getClass().getDeclaredMethod("readLine", new Class<?>[0]); + return ((String) method.invoke(console, new Object[0])).toCharArray(); + } else { + method = console.getClass().getDeclaredMethod("readPassword", new Class<?>[0]); + return (char[]) method.invoke(console, new Object[0]); + } + } + catch (Exception e) { + throw new ParameterException(e); + } + } + +}
\ No newline at end of file diff --git a/src/main/java/com/beust/jcommander/internal/Lists.java b/src/main/java/com/beust/jcommander/internal/Lists.java new file mode 100644 index 0000000..fdbee55 --- /dev/null +++ b/src/main/java/com/beust/jcommander/internal/Lists.java @@ -0,0 +1,54 @@ +/** + * 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.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +public class Lists { + + public static <K> List<K> newArrayList() { + return new ArrayList<K>(); + } + + public static <K> List<K> newArrayList(Collection<K> c) { + return new ArrayList<K>(c); + } + + public static <K> List<K> newArrayList(K... c) { + return new ArrayList<K>(Arrays.asList(c)); + } + + public static <K> List<K> newArrayList(int size) { + return new ArrayList<K>(size); + } + + public static <K> LinkedList<K> newLinkedList() { + return new LinkedList<K>(); + } + + public static <K> LinkedList<K> newLinkedList(Collection<K> c) { + return new LinkedList<K>(c); + } + + +} diff --git a/src/main/java/com/beust/jcommander/internal/Maps.java b/src/main/java/com/beust/jcommander/internal/Maps.java new file mode 100644 index 0000000..e272122 --- /dev/null +++ b/src/main/java/com/beust/jcommander/internal/Maps.java @@ -0,0 +1,43 @@ +/** + * 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.internal; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +public class Maps { + + public static <K, V> Map<K,V> newHashMap() { + return new HashMap<K, V>(); + } + + public static <K, V> Map<K,V> newLinkedHashMap() { + return new LinkedHashMap<K, V>(); + } + + public static <T> Map<T, T> newHashMap(T... parameters) { + Map<T, T> result = Maps.newHashMap(); + for (int i = 0; i < parameters.length; i += 2) { + result.put(parameters[i], parameters[i + 1]); + } + return result; + } + +} diff --git a/src/main/java/com/beust/jcommander/internal/Nullable.java b/src/main/java/com/beust/jcommander/internal/Nullable.java new file mode 100644 index 0000000..b988373 --- /dev/null +++ b/src/main/java/com/beust/jcommander/internal/Nullable.java @@ -0,0 +1,12 @@ +package com.beust.jcommander.internal; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.PARAMETER; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target({FIELD, PARAMETER}) +public @interface Nullable { +} diff --git a/src/main/java/com/beust/jcommander/internal/Sets.java b/src/main/java/com/beust/jcommander/internal/Sets.java new file mode 100644 index 0000000..77949c3 --- /dev/null +++ b/src/main/java/com/beust/jcommander/internal/Sets.java @@ -0,0 +1,35 @@ +/** + * 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.internal; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +public class Sets { + + public static <K> Set<K> newHashSet() { + return new HashSet<K>(); + } + + public static <K> Set<K> newLinkedHashSet() { + return new LinkedHashSet<K>(); + } + +} diff --git a/src/main/java/com/beust/jcommander/validators/NoValidator.java b/src/main/java/com/beust/jcommander/validators/NoValidator.java new file mode 100644 index 0000000..f1b4df2 --- /dev/null +++ b/src/main/java/com/beust/jcommander/validators/NoValidator.java @@ -0,0 +1,35 @@ +/** + * Copyright (C) 2011 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.validators; + +import com.beust.jcommander.IParameterValidator; +import com.beust.jcommander.ParameterException; + +/** + * This is the default value of the validateWith attribute. + * + * @author Cedric Beust <cedric@beust.com> + */ +public class NoValidator implements IParameterValidator { + + public void validate(String parameterName, String parameterValue) + throws ParameterException { + } + +} diff --git a/src/main/java/com/beust/jcommander/validators/NoValueValidator.java b/src/main/java/com/beust/jcommander/validators/NoValueValidator.java new file mode 100644 index 0000000..21fa820 --- /dev/null +++ b/src/main/java/com/beust/jcommander/validators/NoValueValidator.java @@ -0,0 +1,35 @@ +/** + * Copyright (C) 2011 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.validators; + +import com.beust.jcommander.IValueValidator; +import com.beust.jcommander.ParameterException; + +/** + * This is the default value of the validateValueWith attribute. + * + * @author Cedric Beust <cedric@beust.com> + */ +public class NoValueValidator<T> implements IValueValidator<T> { + + public void validate(String parameterName, T parameterValue) + throws ParameterException { + } + +} diff --git a/src/main/java/com/beust/jcommander/validators/PositiveInteger.java b/src/main/java/com/beust/jcommander/validators/PositiveInteger.java new file mode 100644 index 0000000..7b5c1b7 --- /dev/null +++ b/src/main/java/com/beust/jcommander/validators/PositiveInteger.java @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2011 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.validators; + +import com.beust.jcommander.IParameterValidator; +import com.beust.jcommander.ParameterException; + +/** + * A validator that makes sure the value of the parameter is a positive integer. + * + * @author Cedric Beust <cedric@beust.com> + */ +public class PositiveInteger implements IParameterValidator { + + public void validate(String name, String value) + throws ParameterException { + int n = Integer.parseInt(value); + if (n < 0) { + throw new ParameterException("Parameter " + name + + " should be positive (found " + value +")"); + } + } + +} diff --git a/src/main/license/license-header.txt b/src/main/license/license-header.txt new file mode 100644 index 0000000..4cbe379 --- /dev/null +++ b/src/main/license/license-header.txt @@ -0,0 +1,15 @@ +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.
diff --git a/src/test/java/com/beust/jcommander/ArgsRequiredWrongMain.java b/src/test/java/com/beust/jcommander/ArgsRequiredWrongMain.java new file mode 100644 index 0000000..c2124d9 --- /dev/null +++ b/src/test/java/com/beust/jcommander/ArgsRequiredWrongMain.java @@ -0,0 +1,6 @@ +package com.beust.jcommander; + +public class ArgsRequiredWrongMain { + @Parameter(required = true) + public String[] file; +}
\ No newline at end of file diff --git a/src/test/java/com/beust/jcommander/ArgsValidate2.java b/src/test/java/com/beust/jcommander/ArgsValidate2.java new file mode 100644 index 0000000..2b8f07b --- /dev/null +++ b/src/test/java/com/beust/jcommander/ArgsValidate2.java @@ -0,0 +1,24 @@ +package com.beust.jcommander; + +import com.beust.jcommander.converters.FileConverter; + +import java.io.File; + +public class ArgsValidate2 { + public static class FailingValidator implements IValueValidator<File> { + + public void validate(String name, File value) throws ParameterException { + throw new ParameterException("Validation will always fail:" + name + " " + value); + } + + } + + public static final String POSSIBLE_TEMPLATE_FILE = "mayOrMayNotExist.template"; + + @Parameter(names = { "-template"}, + description = "The default file may or may not exist", + converter = FileConverter.class, + validateValueWith = FailingValidator.class + ) + public File template = new File(POSSIBLE_TEMPLATE_FILE); +} diff --git a/src/test/java/com/beust/jcommander/CmdTest.java b/src/test/java/com/beust/jcommander/CmdTest.java new file mode 100644 index 0000000..6601193 --- /dev/null +++ b/src/test/java/com/beust/jcommander/CmdTest.java @@ -0,0 +1,86 @@ +package com.beust.jcommander; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class CmdTest { + + @Parameters(commandNames = "--cmd-one") + public static class CmdOne { + } + + @Parameters(commandNames = "--cmd-two") + class CmdTwo { + @Parameter + List<String> params = new java.util.LinkedList<String>(); + } + + public String parseArgs(boolean withDefault, String[] args) { + JCommander jc = new JCommander(); + jc.addCommand(new CmdOne()); + jc.addCommand(new CmdTwo()); + + if (withDefault) { + // First check if a command was given, when not prepend default + // command (--cmd-two") + // In version up to 1.23 JCommander throws an Exception in this + // line, + // which might be incorrect, at least its not reasonable if the + // method + // is named "WithoutValidation". + jc.parseWithoutValidation(args); + if (jc.getParsedCommand() == null) { + LinkedList<String> newArgs = new LinkedList<String>(); + newArgs.add("--cmd-two"); + newArgs.addAll(Arrays.asList(args)); + jc.parse(newArgs.toArray(new String[0])); + } + } else { + jc.parse(args); + } + return jc.getParsedCommand(); + } + + @DataProvider + public Object[][] testData() { + return new Object[][] { + new Object[] { "--cmd-one", false, new String[] { "--cmd-one" } }, + new Object[] { "--cmd-two", false, new String[] { "--cmd-two" } }, + new Object[] { "--cmd-two", false, + new String[] { "--cmd-two", "param1", "param2" } }, + // This is the relevant test case to test default commands + new Object[] { "--cmd-two", true, + new String[] { "param1", "param2" } } }; + } + + @Test(dataProvider = "testData") + public void testArgsWithoutDefaultCmd(String expected, + boolean requireDefault, String[] args) { + if (!requireDefault) { + Assert.assertEquals(parseArgs(false, args), expected); + } + } + + @Test(dataProvider = "testData", expectedExceptions = MissingCommandException.class) + public void testArgsWithoutDefaultCmdFail(String expected, + boolean requireDefault, String[] args) { + if (requireDefault) { + parseArgs(false, args); + } else { + throw new MissingCommandException("irrelevant test case"); + } + } + + // We do not expect a MissingCommandException! + @Test(dataProvider = "testData") + public void testArgsWithDefaultCmd(String expected, boolean requireDefault, + String[] args) { + Assert.assertEquals(parseArgs(true, args), expected); + } + +}
\ No newline at end of file diff --git a/src/test/java/com/beust/jcommander/ConverterFactoryTest.java b/src/test/java/com/beust/jcommander/ConverterFactoryTest.java new file mode 100644 index 0000000..e02166e --- /dev/null +++ b/src/test/java/com/beust/jcommander/ConverterFactoryTest.java @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2010 the original author or authors. + * See the notice.md file distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.beust.jcommander; + +import com.beust.jcommander.args.ArgsConverterFactory; +import com.beust.jcommander.args.ArgsMainParameter1; +import com.beust.jcommander.args.ArgsMainParameter2; +import com.beust.jcommander.args.IHostPorts; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * Test the converter factory feature. + * + * @author cbeust + */ +public class ConverterFactoryTest { + private static final Map<Class, Class<? extends IStringConverter<?>>> MAP = new HashMap() {{ + put(HostPort.class, HostPortConverter.class); + }}; + + private static final IStringConverterFactory CONVERTER_FACTORY = new IStringConverterFactory() { + + public Class<? extends IStringConverter<?>> getConverter(Class forType) { + return MAP.get(forType); + } + + }; + + @Test + public void parameterWithHostPortParameters() { + ArgsConverterFactory a = new ArgsConverterFactory(); + JCommander jc = new JCommander(a); + jc.addConverterFactory(CONVERTER_FACTORY); + jc.parse("-hostport", "example.com:8080"); + + Assert.assertEquals(a.hostPort.host, "example.com"); + Assert.assertEquals(a.hostPort.port.intValue(), 8080); + } + + /** + * 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) { + JCommander jc = new JCommander(a); + if (f != null) jc.addConverterFactory(f); + 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); + Assert.assertEquals(a.getHostPorts().get(1).host, "b.com"); + Assert.assertEquals(a.getHostPorts().get(1).port.intValue(), 20); + } + + @Test + public void mainWithoutFactory() { + mainWithHostPortParameters(null, new ArgsMainParameter1()); + } + + @Test + public void mainWithFactory() { + mainWithHostPortParameters(CONVERTER_FACTORY, new ArgsMainParameter2()); + } + +} + diff --git a/src/test/java/com/beust/jcommander/DefaultProviderTest.java b/src/test/java/com/beust/jcommander/DefaultProviderTest.java new file mode 100644 index 0000000..45ab6b6 --- /dev/null +++ b/src/test/java/com/beust/jcommander/DefaultProviderTest.java @@ -0,0 +1,120 @@ +/** + * Copyright (C) 2010 the original author or authors. + * See the notice.md file distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.beust.jcommander; + +import com.beust.jcommander.args.ArgsDefault; +import com.beust.jcommander.defaultprovider.PropertyFileDefaultProvider; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class DefaultProviderTest { + private static final IDefaultProvider DEFAULT_PROVIDER = new IDefaultProvider() { + + public String getDefaultValueFor(String optionName) { + return "-debug".equals(optionName) ? "false" : "42"; + } + + }; + + private ArgsDefault defaultProvider(IDefaultProvider provider, String... args) { + ArgsDefault a = new ArgsDefault(); + JCommander jc = new JCommander(a); + jc.setDefaultProvider(provider); + + jc.parse(args); + return a; + } + + @Test + public void defaultProvider1() { + ArgsDefault a = defaultProvider(DEFAULT_PROVIDER, "f"); + + Assert.assertEquals(a.groups, "42"); + Assert.assertEquals(a.level, 42); + Assert.assertEquals(a.log.intValue(), 42); + } + + @Test + public void defaultProvider2() { + ArgsDefault a = defaultProvider(DEFAULT_PROVIDER, "-groups", "foo", "f"); + + Assert.assertEquals(a.groups, "foo"); + Assert.assertEquals(a.level, 42); + Assert.assertEquals(a.log.intValue(), 42); + } + + @Test + public void defaultProvider3() { + ArgsDefault a = defaultProvider(DEFAULT_PROVIDER, "-groups", "foo", "-level", "13", "f"); + + Assert.assertEquals(a.groups, "foo"); + Assert.assertEquals(a.level, 13); + Assert.assertEquals(a.log.intValue(), 42); + } + + @Test + public void defaultProvider4() { + ArgsDefault a = defaultProvider(DEFAULT_PROVIDER, + "-log", "19", "-groups", "foo", "-level", "13", "f"); + + Assert.assertEquals(a.groups, "foo"); + Assert.assertEquals(a.level, 13); + Assert.assertEquals(a.log.intValue(), 19); + } + + @Test + public void propertyFileDefaultProvider1() { + ArgsDefault a = defaultProvider(new PropertyFileDefaultProvider(), "f"); + + Assert.assertEquals(a.groups, "unit"); + Assert.assertEquals(a.level, 17); + Assert.assertEquals(a.log.intValue(), 18); + } + + @Test + public void propertyFileDefaultProvider2() { + ArgsDefault a = defaultProvider(new PropertyFileDefaultProvider(), "-groups", "foo", "f"); + + Assert.assertEquals(a.groups, "foo"); + Assert.assertEquals(a.level, 17); + Assert.assertEquals(a.log.intValue(), 18); + } + + @Test + public void propertyFileDefaultProvider3() { + ArgsDefault a = defaultProvider(new PropertyFileDefaultProvider(), + "-groups", "foo", "-level", "13", "f"); + + Assert.assertEquals(a.groups, "foo"); + Assert.assertEquals(a.level, 13); + Assert.assertEquals(a.log.intValue(), 18); + } + + @Test + public void propertyFileDefaultProvider4() { + ArgsDefault a = defaultProvider(new PropertyFileDefaultProvider(), + "-log", "19", "-groups", "foo", "-level", "13", "f"); + + Assert.assertEquals(a.groups, "foo"); + Assert.assertEquals(a.level, 13); + Assert.assertEquals(a.log.intValue(), 19); + } + +} diff --git a/src/test/java/com/beust/jcommander/DefaultValueTest.java b/src/test/java/com/beust/jcommander/DefaultValueTest.java new file mode 100644 index 0000000..3b1f29c --- /dev/null +++ b/src/test/java/com/beust/jcommander/DefaultValueTest.java @@ -0,0 +1,113 @@ +/** + * Copyright (C) 2010 the original author or authors. + * See the notice.md file distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.beust.jcommander; + +import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Sets; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Test behaviour of default parameter values + * @author rodionmoiseev + */ +public class DefaultValueTest { + @Test + public void emptyDefaultValueForListParameterStaysEmptyIfNotAssignedOrIsSetOtherwise() { + MyOptsWithEmptyDefaults opts = new MyOptsWithEmptyDefaults(); + JCommander cmd = new JCommander(opts); + cmd.parse(new String[]{"-a", "anotherValue"}); + Assert.assertEquals(opts.list.size(), 1); + Assert.assertEquals(opts.list.get(0), "anotherValue"); + Assert.assertEquals(opts.set.size(), 0); + } + + @Test + public void defaultValueForListParametersGetsOverwrittenWithSpecifiedValueOrStaysAsDefaultOtherwise() { + MyOptsWithDefaultValues opts = new MyOptsWithDefaultValues(); + JCommander cmd = new JCommander(opts); + cmd.parse(new String[]{"-a", "anotherValue"}); + Assert.assertEquals(opts.list.size(), 1); + Assert.assertEquals(opts.list.get(0), "anotherValue"); + Assert.assertEquals(opts.set.size(), 1); + Assert.assertEquals(opts.set.iterator().next(), "defaultValue"); + } + + @Test + public void anyNumberOfValuesCanBeSetToListParameters_ForEmptyDefaults(){ + MyOptsWithEmptyDefaults opts = new MyOptsWithEmptyDefaults(); + testSettingMultipleValuesToListTypeParameters(opts); + } + + @Test + public void anyNumberOfValuesCanBeSetToListParameters_ForNonEmptyDefaults(){ + MyOptsWithDefaultValues opts = new MyOptsWithDefaultValues(); + testSettingMultipleValuesToListTypeParameters(opts); + } + + private void testSettingMultipleValuesToListTypeParameters(MyOpts opts) { + JCommander cmd = new JCommander(opts); + cmd.parse(new String[]{"-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"); + Assert.assertEquals(opts.set.size(), 2); + Iterator<String> arg2it = opts.set.iterator(); + Assert.assertEquals(arg2it.next(), "anotherValue3"); + Assert.assertEquals(arg2it.next(), "anotherValue4"); + } + + public static class MyOpts { + @Parameter(names = "-a") + public List<String> list; + @Parameter(names = "-b") + public Set<String> set; + } + + public static final class MyOptsWithDefaultValues extends MyOpts { + public MyOptsWithDefaultValues(){ + this.list = singletonList("defaultValue"); + this.set = singletonSet("defaultValue"); + } + } + + public static final class MyOptsWithEmptyDefaults extends MyOpts { + public MyOptsWithEmptyDefaults(){ + this.list = Lists.newArrayList(); + this.set = Sets.newLinkedHashSet(); + } + } + + public static final List<String> singletonList(String value) { + List<String> list = Lists.newArrayList(); + list.add(value); + return list; + } + + public static final Set<String> singletonSet(String value){ + Set<String> set = Sets.newLinkedHashSet(); + set.add(value); + return set; + } +} diff --git a/src/test/java/com/beust/jcommander/FinderTest.java b/src/test/java/com/beust/jcommander/FinderTest.java new file mode 100644 index 0000000..94bf812 --- /dev/null +++ b/src/test/java/com/beust/jcommander/FinderTest.java @@ -0,0 +1,97 @@ +package com.beust.jcommander; + +import com.beust.jcommander.JCommanderTest.BaseArgs; +import com.beust.jcommander.JCommanderTest.ConfigureArgs; + +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class FinderTest { + public void caseInsensitiveOption() { + class Arg { + + @Parameter(names = { "-p", "--param" }) + private String param; + } + Arg a = new Arg(); + JCommander jc = new JCommander(a); + jc.setCaseSensitiveOptions(false); + jc.parse(new String[] { "--PARAM", "foo" }); + Assert.assertEquals(a.param, "foo"); + } + + public void caseInsensitiveCommand() { + BaseArgs a = new BaseArgs(); + ConfigureArgs conf = new ConfigureArgs(); + 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"); + } + + public void abbreviatedOptions() { + class Arg { + @Parameter(names = { "-p", "--param" }) + private String param; + } + Arg a = new Arg(); + JCommander jc = new JCommander(a); + jc.setAllowAbbreviatedOptions(true); + jc.parse(new String[] { "--par", "foo" }); + Assert.assertEquals(a.param, "foo"); + } + + public void abbreviatedOptionsCaseInsensitive() { + class Arg { + @Parameter(names = { "-p", "--param" }) + private String param; + } + Arg a = new Arg(); + JCommander jc = new JCommander(a); + jc.setCaseSensitiveOptions(false); + jc.setAllowAbbreviatedOptions(true); + jc.parse(new String[] { "--PAR", "foo" }); + Assert.assertEquals(a.param, "foo"); + } + + @Test(expectedExceptions = ParameterException.class) + public void ambiguousAbbreviatedOptions() { + class Arg { + @Parameter(names = { "--param" }) + private String param; + @Parameter(names = { "--parb" }) + private String parb; + } + Arg a = new Arg(); + JCommander jc = new JCommander(a); + jc.setAllowAbbreviatedOptions(true); + jc.parse(new String[] { "--par", "foo" }); + Assert.assertEquals(a.param, "foo"); + } + + @Test(expectedExceptions = ParameterException.class) + public void ambiguousAbbreviatedOptionsCaseInsensitive() { + class Arg { + @Parameter(names = { "--param" }) + private String param; + @Parameter(names = { "--parb" }) + private String parb; + } + Arg a = new Arg(); + JCommander jc = new JCommander(a); + jc.setCaseSensitiveOptions(false); + jc.setAllowAbbreviatedOptions(true); + jc.parse(new String[] { "--PAR", "foo" }); + Assert.assertEquals(a.param, "foo"); + } + + @Test(enabled = false) + public static void main(String[] args) throws Exception { + new FinderTest().ambiguousAbbreviatedOptionsCaseInsensitive(); + } + +} diff --git a/src/test/java/com/beust/jcommander/HostPort.java b/src/test/java/com/beust/jcommander/HostPort.java new file mode 100644 index 0000000..a18018e --- /dev/null +++ b/src/test/java/com/beust/jcommander/HostPort.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 HostPort { + public String host; + public Integer port; + @Override + + public String toString() { + return "[Host:" + host + " port:" + port + "]"; + } +} diff --git a/src/test/java/com/beust/jcommander/HostPortConverter.java b/src/test/java/com/beust/jcommander/HostPortConverter.java new file mode 100644 index 0000000..f45e3ba --- /dev/null +++ b/src/test/java/com/beust/jcommander/HostPortConverter.java @@ -0,0 +1,32 @@ +/** + * 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 HostPortConverter implements IStringConverter<HostPort> { + + public HostPort convert(String value) { + HostPort result = new HostPort(); + String[] s = value.split(":"); + result.host = s[0]; + result.port = Integer.parseInt(s[1]); + + return result; + } + +}
\ No newline at end of file diff --git a/src/test/java/com/beust/jcommander/JCommanderTest.java b/src/test/java/com/beust/jcommander/JCommanderTest.java new file mode 100644 index 0000000..ad2c5e8 --- /dev/null +++ b/src/test/java/com/beust/jcommander/JCommanderTest.java @@ -0,0 +1,1081 @@ +/** + * 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.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.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.internal.Lists; +import com.beust.jcommander.internal.Maps; + +@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; + @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 +} diff --git a/src/test/java/com/beust/jcommander/MethodSetterTest.java b/src/test/java/com/beust/jcommander/MethodSetterTest.java new file mode 100644 index 0000000..f995ad6 --- /dev/null +++ b/src/test/java/com/beust/jcommander/MethodSetterTest.java @@ -0,0 +1,100 @@ +package com.beust.jcommander; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.List; + +/** + * Tests for @Parameter on top of methods. + */ +@Test +public class MethodSetterTest { + public void arityStringsSetter() { + class ArgsArityStringSetter { + + @Parameter(names = "-pairs", arity = 2, description = "Pairs") + public void setPairs(List<String> pairs) { + this.pairs = pairs; + } + public List<String> getPairs() { + return this.pairs; + } + public List<String> pairs; + + @Parameter(description = "Rest") + public void setRest(List<String> rest) { + this.rest = rest; + } +// public List<String> getRest() { +// return this.rest; +// } + public List<String> rest; + } + ArgsArityStringSetter args = new ArgsArityStringSetter(); + 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"); + } + + public void setterThatThrows() { + class Arg { + @Parameter(names = "--host") + public void setHost(String host) { + throw new ParameterException("Illegal host"); + } + } + boolean passed = false; + try { + new JCommander(new Arg(), new String[] { "--host", "host" }); + } catch(ParameterException ex) { + Assert.assertEquals(ex.getCause(), null); + passed = true; + } + Assert.assertTrue(passed, "Should have thrown an exception"); + } + + public void getterReturningNonString() { + class Arg { + private Integer port; + + @Parameter(names = "--port") + public void setPort(String port) { + this.port = Integer.parseInt(port); + } + + public Integer getPort() { + return port; + } + } + Arg arg = new Arg(); + new JCommander(arg, new String[] { "--port", "42" }); + + Assert.assertEquals(arg.port, new Integer(42)); + } + + public void noGetterButWithField() { + class Arg { + private Integer port = 43; + + @Parameter(names = "--port") + public void setPort(String port) { + this.port = Integer.parseInt(port); + } + } + Arg arg = new Arg(); + JCommander jc = new JCommander(arg, new String[] { "--port", "42" }); + ParameterDescription pd = jc.getParameters().get(0); + Assert.assertEquals(pd.getDefault(), 43); + } + + @Test(enabled = false) + public static void main(String[] args) throws Exception { + new MethodSetterTest().noGetterButWithField(); + } +} diff --git a/src/test/java/com/beust/jcommander/MyClass.java b/src/test/java/com/beust/jcommander/MyClass.java new file mode 100644 index 0000000..c2d3371 --- /dev/null +++ b/src/test/java/com/beust/jcommander/MyClass.java @@ -0,0 +1,24 @@ +package com.beust.jcommander; + +import org.testng.Assert; + + +@Parameters(separators = "=") +public class MyClass { + + @Parameter(names = { "-p", "--param" }, validateWith = MyValidator.class) + private String param; + + public static void main(String[] args) { + JCommander jCommander = new JCommander(new MyClass()); + jCommander.parse("--param=value"); + } + + public static class MyValidator implements IParameterValidator { + @Override + public void validate(String name, String value) throws ParameterException { + Assert.assertEquals(value, "\""); + } + } + +}
\ No newline at end of file diff --git a/src/test/java/com/beust/jcommander/ParametersDelegateTest.java b/src/test/java/com/beust/jcommander/ParametersDelegateTest.java new file mode 100644 index 0000000..46c7c6a --- /dev/null +++ b/src/test/java/com/beust/jcommander/ParametersDelegateTest.java @@ -0,0 +1,227 @@ +package com.beust.jcommander; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author rodionmoiseev + */ +public class ParametersDelegateTest { + + @Test + public void delegatingEmptyClassHasNoEffect() { + class EmptyDelegate { + public String nonParamString = "a"; + } + class MainParams { + @Parameter(names = "-a") + public boolean isA; + @Parameter(names = {"-b", "--long-b"}) + public String bValue = ""; + @ParametersDelegate + public EmptyDelegate delegate = new EmptyDelegate(); + } + + MainParams p = new MainParams(); + JCommander cmd = new JCommander(p); + cmd.parse("-a", "-b", "someValue"); + Assert.assertTrue(p.isA); + Assert.assertEquals(p.bValue, "someValue"); + Assert.assertEquals(p.delegate.nonParamString, "a"); + } + + @Test + public void delegatingSetsFieldsOnBothMainParamsAndTheDelegatedParams() { + class ComplexDelegate { + @Parameter(names = "-c") + public boolean isC; + @Parameter(names = {"-d", "--long-d"}) + public Integer d; + } + class MainParams { + @Parameter(names = "-a") + public boolean isA; + @Parameter(names = {"-b", "--long-b"}) + public String bValue = ""; + @ParametersDelegate + public ComplexDelegate delegate = new ComplexDelegate(); + } + + MainParams p = new MainParams(); + JCommander cmd = new JCommander(p); + cmd.parse("-c", "--long-d", "123", "--long-b", "bValue"); + Assert.assertFalse(p.isA); + Assert.assertEquals(p.bValue, "bValue"); + Assert.assertTrue(p.delegate.isC); + Assert.assertEquals(p.delegate.d, Integer.valueOf(123)); + } + + @Test + public void combinedAndNestedDelegates() { + abstract class LeafAbstractDelegate { + abstract float getFloat(); + } + class LeafDelegate { + @Parameter(names = "--list") + public List<String> list = new ArrayList<String>() {{ + add("value1"); + add("value2"); + }}; + @Parameter(names = "--bool") + public boolean bool; + } + class NestedDelegate1 { + @ParametersDelegate + public LeafDelegate leafDelegate = new LeafDelegate(); + @Parameter(names = {"-d", "--long-d"}) + public Integer d; + } + class NestedDelegate2 { + @Parameter(names = "-c") + public boolean isC; + @ParametersDelegate + public NestedDelegate1 nestedDelegate1 = new NestedDelegate1(); + @ParametersDelegate + public LeafAbstractDelegate anonymousDelegate = new LeafAbstractDelegate() { + @Parameter(names = "--anon-float") + public float anon = 999f; + + @Override + float getFloat() { + return anon; + } + }; + } + class MainParams { + @Parameter(names = "-a") + public boolean isA; + @Parameter(names = {"-b", "--long-b"}) + public String bValue = ""; + @ParametersDelegate + public NestedDelegate2 nestedDelegate2 = new NestedDelegate2(); + } + + MainParams p = new MainParams(); + JCommander cmd = new JCommander(p); + cmd.parse("--anon-float 1.2 -d 234 --list a --list b -a".split(" ")); + Assert.assertEquals(p.nestedDelegate2.anonymousDelegate.getFloat(), 1.2f); + Assert.assertEquals(p.nestedDelegate2.nestedDelegate1.leafDelegate.list, new ArrayList<String>() {{ + add("a"); + add("b"); + }}); + Assert.assertFalse(p.nestedDelegate2.nestedDelegate1.leafDelegate.bool); + Assert.assertEquals(p.nestedDelegate2.nestedDelegate1.d, Integer.valueOf(234)); + Assert.assertFalse(p.nestedDelegate2.isC); + Assert.assertTrue(p.isA); + Assert.assertEquals(p.bValue, ""); + } + + @Test + public void commandTest() { + class Delegate { + @Parameter(names = "-a") + public String a = "b"; + } + class Command { + @ParametersDelegate + public Delegate delegate = new Delegate(); + } + + Command c = new Command(); + + JCommander cmd = new JCommander(); + cmd.addCommand("command", c); + + cmd.parse("command -a a".split(" ")); + Assert.assertEquals(c.delegate.a, "a"); + } + + @Test + public void mainParametersTest() { + class Delegate { + @Parameter + public List<String> mainParams = new ArrayList<String>(); + } + class Command { + @ParametersDelegate + public Delegate delegate = new Delegate(); + } + + Command c = new Command(); + + JCommander cmd = new JCommander(); + cmd.addCommand("command", c); + + cmd.parse("command main params".split(" ")); + Assert.assertEquals(c.delegate.mainParams, new ArrayList<String>() {{ + add("main"); + add("params"); + }}); + } + + @Test(expectedExceptions = ParameterException.class, + expectedExceptionsMessageRegExp = ".*delegate.*null.*") + public void nullDelegatesAreProhibited() { + class ComplexDelegate { + } + class MainParams { + @ParametersDelegate + public ComplexDelegate delegate; + } + + MainParams p = new MainParams(); + JCommander cmd = new JCommander(p); + cmd.parse(); + } + + @Test(expectedExceptions = ParameterException.class, + expectedExceptionsMessageRegExp = ".*-a.*") + public void duplicateDelegateThrowDuplicateOptionException() { + class Delegate { + @Parameter(names = "-a") + public String a; + } + class MainParams { + @ParametersDelegate + public Delegate d1 = new Delegate(); + @ParametersDelegate + public Delegate d2 = new Delegate(); + } + + MainParams p = new MainParams(); + JCommander cmd = new JCommander(p); + cmd.parse("-a value".split(" ")); + } + + @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = "Only one.*is allowed.*") + public void duplicateMainParametersAreNotAllowed() { + class Delegate1 { + @Parameter + public List<String> mainParams1 = new ArrayList<String>(); + } + class Delegate2 { + @Parameter + public List<String> mainParams2 = new ArrayList<String>(); + } + class Command { + @ParametersDelegate + public Delegate1 delegate1 = new Delegate1(); + @ParametersDelegate + public Delegate2 delegate2 = new Delegate2(); + } + + Command c = new Command(); + + JCommander cmd = new JCommander(); + cmd.addCommand("command", c); + + cmd.parse("command main params".split(" ")); + } + + public static void main(String[] args) { + new ParametersDelegateTest().commandTest(); + } +} diff --git a/src/test/java/com/beust/jcommander/PositiveIntegerTest.java b/src/test/java/com/beust/jcommander/PositiveIntegerTest.java new file mode 100644 index 0000000..ec7d273 --- /dev/null +++ b/src/test/java/com/beust/jcommander/PositiveIntegerTest.java @@ -0,0 +1,65 @@ +package com.beust.jcommander; + +import com.beust.jcommander.validators.PositiveInteger; + +import org.testng.annotations.Test; + +public class PositiveIntegerTest { + + @Test + public void validateTest() { + class Arg { + @Parameter(names = { "-p", "--port" }, description = "Shows help", validateWith = PositiveInteger.class) + private int port = 0; + } + Arg arg = new Arg(); + JCommander jc = new JCommander(arg); + jc.parse(new String[] { "-p", "8080" }); + + } + + @Test(expectedExceptions = ParameterException.class) + public void validateTest2() { + class Arg { + @Parameter(names = { "-p", "--port" }, description = "Shows help", validateWith = PositiveInteger.class) + private int port = 0; + } + Arg arg = new Arg(); + JCommander jc = new JCommander(arg); + jc.parse(new String[] { "-p", "" }); + } + + @Test(expectedExceptions = ParameterException.class) + public void validateTest3() { + class Arg { + @Parameter(names = { "-p", "--port" }, description = "Shows help", validateWith = PositiveInteger.class) + private int port = 0; + } + Arg arg = new Arg(); + JCommander jc = new JCommander(arg); + jc.parse(new String[] { "-p", "-1" }); + } + + @Test(expectedExceptions = ParameterException.class) + public void validateTest4() { + class Arg { + @Parameter(names = { "-p", "--port" }, description = "Port Number", validateWith = PositiveInteger.class) + private int port = 0; + } + Arg arg = new Arg(); + JCommander jc = new JCommander(arg); + jc.parse(new String[] { "-p", "abc" }); + } + + @Test(expectedExceptions = ParameterException.class) + public void validateTest5() { + class Arg { + @Parameter(names = { "-p", "--port" }, description = "Port Number", validateWith = PositiveInteger.class) + private int port = 0; + } + + Arg arg = new Arg(); + JCommander jc = new JCommander(arg); + jc.parse(new String[] { "--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 new file mode 100644 index 0000000..c19df11 --- /dev/null +++ b/src/test/java/com/beust/jcommander/SetConverter.java @@ -0,0 +1,16 @@ +package com.beust.jcommander; + +import java.util.SortedSet; +import java.util.TreeSet; + +public class SetConverter implements IStringConverter<SortedSet<Integer>> { + + public SortedSet<Integer> convert(String value) { + SortedSet<Integer> set = new TreeSet<Integer>(); + String[] values = value.split(","); + for (String num : values) { + set.add(Integer.parseInt(num)); + } + return set; + } +} diff --git a/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java b/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java new file mode 100644 index 0000000..6a3a98f --- /dev/null +++ b/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java @@ -0,0 +1,42 @@ +package com.beust.jcommander; + +import org.testng.annotations.Test; + +public class ValidatePropertiesWhenParsingTest { + @Test + public void f() + throws Exception { + + JCommander cmd = new JCommander(); + + cmd.addCommand("a", new A()); +// cmd.addCommand("b", new B()); + + cmd.parse(new String[] { "a", "-path", "myPathToHappiness" }); + } + + public static class MyPathValidator implements IParameterValidator { + + public void validate(String name, String value) throws ParameterException { + throw new RuntimeException("I shouldn't be called for command A!"); + } + } + + @Parameters + public static class A { + + @Parameter(names = "-path") + private String path = "W"; + } + + @Parameters + public static class B { + + @Parameter(names = "-path", validateWith = MyPathValidator.class) + private String path = "W"; + } + + public static void main(String[] args) throws Exception { + new ValidatePropertiesWhenParsingTest().f(); + } +}
\ No newline at end of file diff --git a/src/test/java/com/beust/jcommander/VariableArityTest.java b/src/test/java/com/beust/jcommander/VariableArityTest.java new file mode 100644 index 0000000..a90392f --- /dev/null +++ b/src/test/java/com/beust/jcommander/VariableArityTest.java @@ -0,0 +1,66 @@ +package com.beust.jcommander; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class VariableArityTest { + + public static class ModelGenerationConfig { + + @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>(); + + @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>(); + + @Parameter(names = { "-c", "--configData" }, variableArity = true, + description = "File containing a list of configuration parameter values") + public List<String> configFile = new LinkedList<String>(); + + @Parameter(names = { "-o", "--outputFile" }, + description = "File to output the resulting data to. Defaults to ./matrix-generation.zip", required = false) + public String outputFile = "matrix-generation.zip"; + + @Parameter(names = { "--seed" }, description = "Seed used for PRNG [0 means don't use a Seed]") + public long seed = 0; + + public void print() { + System.out.println("modelMatrixFile: " + modelMatrixFile); + System.out.println("featureData: " + featureFile); + System.out.println("configFile: " + configFile); + System.out.println("output: " + outputFile); + System.out.println("seed: " + seed); + + } + } + + @Test + public void verifyVariableArity() { + String input = "-m foo --seed 1024 -c foo -o foo -f foo "; + + String[] split = input.split("\\s+"); + + ModelGenerationConfig config = new ModelGenerationConfig(); + JCommander com = new JCommander(config); + com.setProgramName("modelgen"); + + 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.seed, 1024); + Assert.assertEquals(config.outputFile, "foo"); + } + + public static void main(String[] args) { + new VariableArityTest().verifyVariableArity(); + } +}
\ No newline at end of file diff --git a/src/test/java/com/beust/jcommander/args/AlternateNamesForListArgs.java b/src/test/java/com/beust/jcommander/args/AlternateNamesForListArgs.java new file mode 100644 index 0000000..18a1655 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/AlternateNamesForListArgs.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 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; +import com.beust.jcommander.internal.Lists; +import java.util.List; + +/** + * + * @author Andy Law <andy.law@roslin.ed.ac.uk> + */ +public class AlternateNamesForListArgs { + + @Parameter(names = {"-s", "--servers"}, description = "blah") + public List<String> serverNames = Lists.newLinkedList(); +} diff --git a/src/test/java/com/beust/jcommander/args/Args1.java b/src/test/java/com/beust/jcommander/args/Args1.java new file mode 100644 index 0000000..093abec --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/Args1.java @@ -0,0 +1,56 @@ +/** + * 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 java.math.BigDecimal; +import java.util.List; +import java.util.Date; + +import org.testng.collections.Lists; + +import com.beust.jcommander.Parameter; + +public class Args1 { + @Parameter + public List<String> parameters = Lists.newArrayList(); + + @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity", required = true) + public Integer verbose = 1; + + @Parameter(names = "-groups", description = "Comma-separated list of group names to be run") + public String groups; + + @Parameter(names = "-debug", description = "Debug mode") + public boolean debug = false; + + @Parameter(names = "-long", description = "A long number") + public long l; + + @Parameter(names = "-double", description = "A double number") + public double doub; + + @Parameter(names = "-float", description = "A float number") + public float floa; + + @Parameter(names = "-bigdecimal", description = "A BigDecimal number") + public BigDecimal bigd; + + @Parameter(names = "-date", description = "An ISO 8601 formatted date.") + public Date date; +} diff --git a/src/test/java/com/beust/jcommander/args/Args1Setter.java b/src/test/java/com/beust/jcommander/args/Args1Setter.java new file mode 100644 index 0000000..10f9ef3 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/Args1Setter.java @@ -0,0 +1,93 @@ +/** + * 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; +import com.beust.jcommander.internal.Lists; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +public class Args1Setter { + @Parameter + public void setParameters(List<String> p) { + parameters = p; + } + + public List<String> getParameters() { + return this.parameters; + } + public List<String> parameters = Lists.newArrayList(); + + @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity", required = true) + public void setVerbose(Integer v) { + verbose = v; + } + public Integer verbose = 1; + + @Parameter(names = "-groups", description = "Comma-separated list of group names to be run") + public void setGroups(String g) { + groups = g; + } + + public String groups; + + @Parameter(names = "-debug", description = "Debug mode") + public void setDebug(boolean d) { + debug = d; + } + + public boolean debug = false; + + @Parameter(names = "-long", description = "A long number") + public void setLong(long ll) { + l = ll; + } + + public long l; + + @Parameter(names = "-double", description = "A double number") + public void setDouble(double d) { + doub = d; + } + + public double doub; + + @Parameter(names = "-float", description = "A float number") + public void setFloat(float f) { + floa = f; + } + + public float floa; + + @Parameter(names = "-bigdecimal", description = "A BigDecimal number") + public void setBigDecimal(BigDecimal bd) { + bigd = bd; + } + + public BigDecimal bigd; + + @Parameter(names = "-date", description = "An ISO 8601 formatted date.") + public void setDate(Date d) { + date = d; + } + + public Date date; +} diff --git a/src/test/java/com/beust/jcommander/args/Args2.java b/src/test/java/com/beust/jcommander/args/Args2.java new file mode 100644 index 0000000..89f622b --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/Args2.java @@ -0,0 +1,42 @@ +/** + * 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; +import com.beust.jcommander.internal.Lists; + +import java.util.ArrayList; +import java.util.List; + +public class Args2 { + @Parameter(description = "List of parameters") + public List parameters = Lists.newArrayList(); + + @Parameter(names = {"-log", "-verbose"}, description = "Level of verbosity") + public Integer verbose = 1; + + @Parameter(names = "-groups", description = "Comma-separated list of group names to be run") + public String groups; + + @Parameter(names = "-debug", description = "Debug mode") + public boolean debug = false; + + @Parameter(names = "-host", description = "The host") + public List hosts = new ArrayList(); +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsArityInteger.java b/src/test/java/com/beust/jcommander/args/ArgsArityInteger.java new file mode 100644 index 0000000..9c1611f --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsArityInteger.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.args; + +import com.beust.jcommander.Parameter; + +import java.util.List; + +/** + * Test parameter arity. + * + * @author cbeust + */ +public class ArgsArityInteger { + + @Parameter(names = "-pairs", arity = 2, description = "Pairs") + public List<Integer> pairs; + + @Parameter(description = "Rest") + public List<String> rest; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsArityString.java b/src/test/java/com/beust/jcommander/args/ArgsArityString.java new file mode 100644 index 0000000..056ae85 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsArityString.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.args; + +import com.beust.jcommander.Parameter; + +import java.util.List; + +/** + * Test parameter arity. + * + * @author cbeust + */ +public class ArgsArityString { + + @Parameter(names = "-pairs", arity = 2, description = "Pairs") + public List<String> pairs; + + @Parameter(description = "Rest") + public List<String> rest; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsBooleanArity.java b/src/test/java/com/beust/jcommander/args/ArgsBooleanArity.java new file mode 100644 index 0000000..242e347 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsBooleanArity.java @@ -0,0 +1,26 @@ +/** + * 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 ArgsBooleanArity { + @Parameter(names = "-debug", arity = 1) + public Boolean debug = false; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsBooleanArity0.java b/src/test/java/com/beust/jcommander/args/ArgsBooleanArity0.java new file mode 100644 index 0000000..62895ec --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsBooleanArity0.java @@ -0,0 +1,26 @@ +/** + * 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 ArgsBooleanArity0 { + @Parameter(names = "-debug", arity = 0) + public Boolean debug = false; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsConverter.java b/src/test/java/com/beust/jcommander/args/ArgsConverter.java new file mode 100644 index 0000000..677cf9b --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsConverter.java @@ -0,0 +1,57 @@ +/** + * 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; +import com.beust.jcommander.converters.FileConverter; +import com.beust.jcommander.converters.PathConverter; +import com.beust.jcommander.converters.URIConverter; +import com.beust.jcommander.converters.URLConverter; + +import java.io.File; +import java.math.BigDecimal; +import java.net.URI; +import java.net.URL; +import java.nio.file.Path; +import java.util.List; + +public class ArgsConverter { + + @Parameter(names = "-file", converter = FileConverter.class) + public File file; + + @Parameter(names = "-url", converter = URLConverter.class) + public URL url; + + @Parameter(names = "-uri", converter = URIConverter.class) + public URI uri; + + @Parameter(names = "-path", converter = PathConverter.class) + public Path path; + + @Parameter(names = "-listStrings") + public List<String> listStrings; + + @Parameter(names = "-listInts") + public List<Integer> listInts; + + @Parameter(names = "-listBigDecimals") + public List<BigDecimal> listBigDecimals; + +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsConverterFactory.java b/src/test/java/com/beust/jcommander/args/ArgsConverterFactory.java new file mode 100644 index 0000000..f8463a0 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsConverterFactory.java @@ -0,0 +1,28 @@ +/** + * 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.HostPort; +import com.beust.jcommander.Parameter; + +public class ArgsConverterFactory { + + @Parameter(names = "-hostport") + public HostPort hostPort; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsDefault.java b/src/test/java/com/beust/jcommander/args/ArgsDefault.java new file mode 100644 index 0000000..1adcd73 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsDefault.java @@ -0,0 +1,43 @@ +/** + * 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; + +import org.testng.collections.Lists; + +import java.util.List; + +public class ArgsDefault { + @Parameter + public List<String> parameters = Lists.newArrayList(); + + @Parameter(names = "-log", description = "Level of verbosity") + public Integer log = 1; + + @Parameter(names = "-groups", description = "Comma-separated list of group names to be run") + public String groups; + + @Parameter(names = "-debug", description = "Debug mode") + public boolean debug = false; + + @Parameter(names = "-level", description = "A long number") + public long level; + +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsEnum.java b/src/test/java/com/beust/jcommander/args/ArgsEnum.java new file mode 100644 index 0000000..bef663b --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsEnum.java @@ -0,0 +1,55 @@ +/** + * 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 java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +import org.testng.Assert; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; + +/** + * Test enums. + * + * @author Adrian Muraru + */ +public class ArgsEnum { + + 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 static void main(String[] args1) { + ArgsEnum args = new ArgsEnum(); + String[] argv = { "-choice", "ONE"}; + JCommander jc = new JCommander(args, argv); + jc.usage(); + Assert.assertEquals(jc.getParameters().get(0).getDescription(), + "Options: " + EnumSet.allOf((Class<? extends Enum>) ArgsEnum.ChoiceType.class)); + } + +} + + diff --git a/src/test/java/com/beust/jcommander/args/ArgsEquals.java b/src/test/java/com/beust/jcommander/args/ArgsEquals.java new file mode 100644 index 0000000..016166d --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsEquals.java @@ -0,0 +1,11 @@ +package com.beust.jcommander.args; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; + +@Parameters(separators = "=") +public class ArgsEquals { + + @Parameter(names = "-args") + public String args; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsHelp.java b/src/test/java/com/beust/jcommander/args/ArgsHelp.java new file mode 100644 index 0000000..4baaddd --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsHelp.java @@ -0,0 +1,30 @@ +/** + * 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; + +/** + * Test a help option which overrides other options and option validations + */ +public class ArgsHelp { + + @Parameter(names = "-h", description = "Display help") + public boolean help; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsI18N1.java b/src/test/java/com/beust/jcommander/args/ArgsI18N1.java new file mode 100644 index 0000000..80e29e2 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsI18N1.java @@ -0,0 +1,27 @@ +/** + * 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 ArgsI18N1 { + + @Parameter(names = "-host", description = "Host", descriptionKey = "host") + String hostName; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsI18N2.java b/src/test/java/com/beust/jcommander/args/ArgsI18N2.java new file mode 100644 index 0000000..1ccfd43 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsI18N2.java @@ -0,0 +1,30 @@ +/** + * 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; +import com.beust.jcommander.ResourceBundle; + +@SuppressWarnings("deprecation") +@ResourceBundle("MessageBundle") +public class ArgsI18N2 { + + @Parameter(names = "-host", description = "Host", descriptionKey = "host") + String hostName; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsI18N2New.java b/src/test/java/com/beust/jcommander/args/ArgsI18N2New.java new file mode 100644 index 0000000..c760973 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsI18N2New.java @@ -0,0 +1,34 @@ +/** + * 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; +import com.beust.jcommander.Parameters; + +import java.util.List; + +@Parameters(resourceBundle = "MessageBundle") +public class ArgsI18N2New { + + @Parameter(names = "-host", description = "Host", descriptionKey = "host") + String hostName; + + @Parameter(descriptionKey = "files") + List<String> files; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsInherited.java b/src/test/java/com/beust/jcommander/args/ArgsInherited.java new file mode 100644 index 0000000..abc0e82 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsInherited.java @@ -0,0 +1,28 @@ +/** + * 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 ArgsInherited extends ArgsDefault { + + @Parameter(names = "-child", description = "Child parameter") + public Integer child = 1; + +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsList.java b/src/test/java/com/beust/jcommander/args/ArgsList.java new file mode 100644 index 0000000..e827773 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsList.java @@ -0,0 +1,49 @@ +package com.beust.jcommander.args; + +import com.beust.jcommander.HostPort; +import com.beust.jcommander.HostPortConverter; +import com.beust.jcommander.IStringConverter; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.converters.IParameterSplitter; + +import org.testng.collections.Lists; + +import java.util.Arrays; +import java.util.List; + +public class ArgsList { + @Parameter(names = "-groups", description = "Comma-separated list of group names to be run") + public List<String> groups; + + @Parameter(names = "-ints") + public List<Integer> ints; + + @Parameter(names = "-hp", converter = HostPortConverter.class, splitter = SemiColonSplitter.class) + public List<HostPort> hostPorts; + + @Parameter(names = "-hp2", converter = HostPortConverter.class) + public List<HostPort> hp2; + + @Parameter(names = "-uppercase", listConverter = UppercaseConverter.class) + public List<String> uppercase; + + public static class UppercaseConverter implements IStringConverter<List<String>> { + public List<String> convert(String value) { + List<String> result = Lists.newArrayList(); + String[] s = value.split(","); + for (String p : s) { + result.add(p.toUpperCase()); + } + return result; + } + } + + public static class SemiColonSplitter implements IParameterSplitter { + + public List<String> split(String value) { + return Arrays.asList(value.split(";")); + } + + } + +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsLongDescription.java b/src/test/java/com/beust/jcommander/args/ArgsLongDescription.java new file mode 100644 index 0000000..e961016 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsLongDescription.java @@ -0,0 +1,25 @@ +package com.beust.jcommander.args; + +import com.beust.jcommander.Parameter; + +public class ArgsLongDescription { + + @Parameter(names = "--classpath", description = "The classpath. This is a very long " + + "description in order to test the line wrapping. Let's see how this works." + + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor" + + " incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis " + + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.") + public String classpath = "/tmp"; + + @Parameter(names = { "-c", "--convention" }, description = "The convention", required = true) + public String convention = "Java"; + + @Parameter(names = { "-d", "--destination" }, description = "The destination to go to") + public String destination; + + @Parameter(names = "--configure", description = "How to configure") + public String configure; + + @Parameter(names = "--filespec") + public String filespec; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsMainParameter1.java b/src/test/java/com/beust/jcommander/args/ArgsMainParameter1.java new file mode 100644 index 0000000..cd5c52d --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsMainParameter1.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.args; + +import com.beust.jcommander.HostPort; +import com.beust.jcommander.Parameter; + +import org.testng.collections.Lists; + +import java.util.List; + +/** + * A class with main parameter that is not a List<String> + * + * @author cbeust + */ +public class ArgsMainParameter1 implements IHostPorts { + @Parameter + public List<HostPort> parameters = Lists.newArrayList(); + + public List<HostPort> getHostPorts() { + return parameters; + } +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsMainParameter2.java b/src/test/java/com/beust/jcommander/args/ArgsMainParameter2.java new file mode 100644 index 0000000..57422e3 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsMainParameter2.java @@ -0,0 +1,41 @@ +/** + * 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.HostPort; +import com.beust.jcommander.HostPortConverter; +import com.beust.jcommander.Parameter; + +import org.testng.collections.Lists; + +import java.util.List; + +/** + * A class with main parameter that is not a List<String> + * + * @author cbeust + */ +public class ArgsMainParameter2 implements IHostPorts { + @Parameter(converter = HostPortConverter.class) + public List<HostPort> parameters = Lists.newArrayList(); + + public List<HostPort> getHostPorts() { + return parameters; + } +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsMaster.java b/src/test/java/com/beust/jcommander/args/ArgsMaster.java new file mode 100644 index 0000000..ebcba67 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsMaster.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.args; + +import com.beust.jcommander.Parameter; + +/** + * Test multi-object parsing, along with ArgsSlave. + * + * @author cbeust + */ +public class ArgsMaster { + @Parameter(names = "-master") + public String master; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsMultipleUnparsed.java b/src/test/java/com/beust/jcommander/args/ArgsMultipleUnparsed.java new file mode 100644 index 0000000..f7b0628 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsMultipleUnparsed.java @@ -0,0 +1,35 @@ +/** + * 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; + +/** + * Error case if multiple unparsed (without a names attribute) arguments are defined. + * + * @author cbeust + */ +public class ArgsMultipleUnparsed { + + @Parameter(description = "Bogus1") + public String unparsed1; + + @Parameter(description = "Bogus2") + public String unparsed2; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsOutOfMemory.java b/src/test/java/com/beust/jcommander/args/ArgsOutOfMemory.java new file mode 100644 index 0000000..f059ebb --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsOutOfMemory.java @@ -0,0 +1,13 @@ +package com.beust.jcommander.args; + +import com.beust.jcommander.Parameter; + +public class ArgsOutOfMemory +{ + @Parameter(names = { "-p", "--pattern" }, + description = "pattern used by 'tail'. See http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout and http://logback.qos.ch/manual/layouts.html#AccessPatternLayout") + public String pattern; + + @Parameter(names = "-q", description = "Filler arg") + public String filler; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsPassword.java b/src/test/java/com/beust/jcommander/args/ArgsPassword.java new file mode 100644 index 0000000..06c468e --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsPassword.java @@ -0,0 +1,26 @@ +/** + * 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 ArgsPassword { + @Parameter(names = "-password", description = "Connection password", password = true) + public String password; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsPrivate.java b/src/test/java/com/beust/jcommander/args/ArgsPrivate.java new file mode 100644 index 0000000..16e4b4e --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsPrivate.java @@ -0,0 +1,30 @@ +/** + * 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 ArgsPrivate { + @Parameter(names = "-verbose") + private Integer verbose = 1; + + public Integer getVerbose() { + return verbose; + } +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsRequired.java b/src/test/java/com/beust/jcommander/args/ArgsRequired.java new file mode 100644 index 0000000..69cb814 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsRequired.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.args; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.internal.Lists; + +import java.util.List; + +public class ArgsRequired { + + @Parameter(description = "List of files", required = true) + public List<String> parameters = Lists.newArrayList(); + +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsSlave.java b/src/test/java/com/beust/jcommander/args/ArgsSlave.java new file mode 100644 index 0000000..dabeb57 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsSlave.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.args; + +import com.beust.jcommander.Parameter; + +/** + * Test multi-object parsing, along with ArgsSlave. + * + * @author cbeust + */ +public class ArgsSlave { + @Parameter(names = "-slave") + public String slave; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsSlaveBogus.java b/src/test/java/com/beust/jcommander/args/ArgsSlaveBogus.java new file mode 100644 index 0000000..1abdf1f --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsSlaveBogus.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.args; + +import com.beust.jcommander.Parameter; + +/** + * Same as ArgsMaster class, should cause an error. + * + * @author cbeust + */ +public class ArgsSlaveBogus extends ArgsSlave { + @Parameter(names = "-master") + public String master; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsValidate1.java b/src/test/java/com/beust/jcommander/args/ArgsValidate1.java new file mode 100644 index 0000000..5274220 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsValidate1.java @@ -0,0 +1,10 @@ +package com.beust.jcommander.args; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.validators.PositiveInteger; + +public class ArgsValidate1 { + + @Parameter(names = "-age", validateWith = PositiveInteger.class) + public Integer age; +} diff --git a/src/test/java/com/beust/jcommander/args/ArgsWithSet.java b/src/test/java/com/beust/jcommander/args/ArgsWithSet.java new file mode 100644 index 0000000..1e41cd3 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/ArgsWithSet.java @@ -0,0 +1,11 @@ +package com.beust.jcommander.args; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.SetConverter; + +import java.util.SortedSet; + +public class ArgsWithSet { + @Parameter(names = "-s", converter = SetConverter.class) + public SortedSet<Integer> set; +}
\ No newline at end of file diff --git a/src/test/java/com/beust/jcommander/args/Arity1.java b/src/test/java/com/beust/jcommander/args/Arity1.java new file mode 100644 index 0000000..8cbf086 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/Arity1.java @@ -0,0 +1,9 @@ +package com.beust.jcommander.args; + +import com.beust.jcommander.Parameter; + +public class Arity1 +{ + @Parameter(arity = 1, names = "-inspect", description = "", required = false) + public boolean inspect; +} diff --git a/src/test/java/com/beust/jcommander/args/CommandLineArgs.java b/src/test/java/com/beust/jcommander/args/CommandLineArgs.java new file mode 100644 index 0000000..7c8414f --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/CommandLineArgs.java @@ -0,0 +1,120 @@ +/** + * 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; +import com.beust.jcommander.internal.Lists; + +import java.util.List; + +public class CommandLineArgs { + + @Parameter(description = "The XML suite files to run") + public List<String> suiteFiles = Lists.newArrayList(); + + @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity") + public Integer verbose; + + @Parameter(names = "-groups", description = "Comma-separated list of group names to be run") + public String groups; + + @Parameter(names = "-excludedgroups", description ="Comma-separated list of group names to be " + + "run") + public String excludedGroups; + + @Parameter(names = "-d", description ="Output directory") + public String outputDirectory; + + @Parameter(names = "-junit", description ="JUnit mode") + public Boolean junit = Boolean.FALSE; + + @Parameter(names = "-listener", description = "List of .class files or list of class names" + + " implementing ITestListener or ISuiteListener") + public String listener; + + @Parameter(names = "-methodselectors", description = "List of .class files or list of class " + + "names implementing IMethodSelector") + public String methodSelectors; + + @Parameter(names = "-objectfactory", description = "List of .class files or list of class " + + "names implementing ITestRunnerFactory") + public String objectFactory; + + @Parameter(names = "-parallel", description = "Parallel mode (methods, tests or classes)") + public String parallelMode; + + @Parameter(names = "-configfailurepolicy", description = "Configuration failure policy (skip or continue)") + public String configFailurePolicy; + + @Parameter(names = "-threadcount", description = "Number of threads to use when running tests " + + "in parallel") + public Integer threadCount; + + @Parameter(names = "-dataproviderthreadcount", description = "Number of threads to use when " + + "running data providers") + public Integer dataProviderThreadCount; + + @Parameter(names = "-suitename", description = "Default name of test suite, if not specified " + + "in suite definition file or source code") + public String suiteName; + + @Parameter(names = "-testname", description = "Default name of test, if not specified in suite" + + "definition file or source code") + public String testName; + + @Parameter(names = "-reporter", description = "Extended configuration for custom report listener") + public String reporter; + + /** + * Used as map key for the complete list of report listeners provided with the above argument + */ + @Parameter(names = "-reporterslist") + public String reportersList; + + @Parameter(names = "-usedefaultlisteners", description = "Whether to use the default listeners") + public String useDefaultListeners = "true"; + + @Parameter(names = "-skipfailedinvocationcounts") + public Boolean skipFailedInvocationCounts; + + @Parameter(names = "-testclass", description = "The list of test classes") + public String testClass; + + @Parameter(names = "-testnames", description = "The list of test names to run") + public String testNames; + + @Parameter(names = "-testjar", description = "") + public String testJar; + + @Parameter(names = "-testRunFactory", description = "") + public String testRunFactory; + + @Parameter(names = "-port", description = "The port") + public Integer port; + + @Parameter(names = "-host", description = "The host") + public String host; + + @Parameter(names = "-master", description ="Host where the master is") + public String master; + + @Parameter(names = "-slave", description ="Host where the slave is") + public String slave; + +} diff --git a/src/test/java/com/beust/jcommander/args/CommandLineArgs2.java b/src/test/java/com/beust/jcommander/args/CommandLineArgs2.java new file mode 100644 index 0000000..ac62792 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/CommandLineArgs2.java @@ -0,0 +1,72 @@ +/** + * 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; +import com.beust.jcommander.converters.FileConverter; + +import java.io.File; +import java.util.List; + +public class CommandLineArgs2 { + @Parameter(description = "list of files") + List<String> list; + + @Parameter(names = { "-v", "--verbose" }, description = "print verbose log messages.", arity = 1) + public boolean verbose = false; + + @Parameter(names = { "-h", "--help" }, description = "show this help.") + public boolean showHelp = false; + + @Parameter(names = { "-F", "--flush-preferences" }, description = "flush gui preferences.") + public boolean flushPreferences = false; + + @Parameter(names = { "-L", "--flush-licensed" }, description = "flush licensed.") + public boolean flushLicensed = false; + + @Parameter(names = { "-I", "--index-file" }, description = "indexes the given file.") + public Long indexFile; + + @Parameter(names = { "-b", "--bonjour" }, description = "enable Bonjour.") + public boolean enableBonjour = false; + + @Parameter(names = { "-m", "--md5" }, description = "create an MD5 checksum for the given file.", converter = FileConverter.class) + public File md5File; + + @Parameter(names = { "-c", "--cat" }, description = "'cat' the given Lilith logfile.", converter = FileConverter.class) + public File catFile; + + @Parameter(names = { "-t", "--tail" }, description = "'tail' the given Lilith logfile.", converter = FileConverter.class) + public File tailFile; + + @Parameter(names = { "-p", "--pattern" }, description = "pattern used by 'cat' or 'tail'.") + public String pattern; + + @Parameter(names = { "-f", "--keep-running" }, description = "keep tailing the given Lilith logfile.") + public boolean keepRunning = false; + + @Parameter(names = { "-n", "--number-of-lines" }, description = "number of entries printed by cat or tail") + public Integer numberOfLines = -1; + + @Parameter(names = { "-e", "--export-preferences" }, description = "export preferences into the given file.") + public String exportPreferencesFile; + + @Parameter(names = { "-i", "--import-preferences" }, description = "import preferences from the given file.") + public String importPreferencesFile; +} diff --git a/src/test/java/com/beust/jcommander/args/IHostPorts.java b/src/test/java/com/beust/jcommander/args/IHostPorts.java new file mode 100644 index 0000000..2020c77 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/IHostPorts.java @@ -0,0 +1,27 @@ +/** + * 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.HostPort; + +import java.util.List; + +public interface IHostPorts { + List<HostPort> getHostPorts(); +} diff --git a/src/test/java/com/beust/jcommander/args/SeparatorColon.java b/src/test/java/com/beust/jcommander/args/SeparatorColon.java new file mode 100644 index 0000000..ab454fc --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/SeparatorColon.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.args; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; + +@Parameters(separators = ":") +public class SeparatorColon { + + @Parameter(names = "-verbose", arity = 1) + public boolean verbose = false; +} diff --git a/src/test/java/com/beust/jcommander/args/SeparatorEqual.java b/src/test/java/com/beust/jcommander/args/SeparatorEqual.java new file mode 100644 index 0000000..a135207 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/SeparatorEqual.java @@ -0,0 +1,32 @@ +/** + * 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; +import com.beust.jcommander.Parameters; + +@Parameters(separators = "=") +public class SeparatorEqual { + + @Parameter(names = "-log") + public Integer log = 2; + + @Parameter(names = "--longoption") + public Integer longOption; +} diff --git a/src/test/java/com/beust/jcommander/args/SeparatorMixed.java b/src/test/java/com/beust/jcommander/args/SeparatorMixed.java new file mode 100644 index 0000000..aa2641f --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/SeparatorMixed.java @@ -0,0 +1,32 @@ +/** + * 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; +import com.beust.jcommander.Parameters; + +@Parameters(separators = ":=") +public class SeparatorMixed { + + @Parameter(names = "-level") + public Integer level = 0; + + @Parameter(names = "-long") + public Long l = 0l; +} diff --git a/src/test/java/com/beust/jcommander/args/SlashSeparator.java b/src/test/java/com/beust/jcommander/args/SlashSeparator.java new file mode 100644 index 0000000..64d3930 --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/SlashSeparator.java @@ -0,0 +1,32 @@ +/** + * 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; +import com.beust.jcommander.Parameters; + +@Parameters(optionPrefixes = "/") +public class SlashSeparator { + + @Parameter(names = "/verbose") + public boolean verbose = false; + + @Parameter(names = "/file") + public String file; +} diff --git a/src/test/java/com/beust/jcommander/args/VariableArity.java b/src/test/java/com/beust/jcommander/args/VariableArity.java new file mode 100644 index 0000000..21a861d --- /dev/null +++ b/src/test/java/com/beust/jcommander/args/VariableArity.java @@ -0,0 +1,26 @@ +package com.beust.jcommander.args; + +import com.beust.jcommander.IVariableArity; +import com.beust.jcommander.Parameter; + +import java.util.ArrayList; +import java.util.List; + +public class VariableArity implements IVariableArity { + + private int m_count; + + public VariableArity(int count) { + m_count = count; + } + + @Parameter + public List<String> main = new ArrayList<String>(); + + @Parameter(names = "-variable", variableArity = true) + public List<String> var = new ArrayList<String>(); + + public int processVariableArity(String optionName, String[] options) { + return m_count; + } +} diff --git a/src/test/java/com/beust/jcommander/command/CommandAdd.java b/src/test/java/com/beust/jcommander/command/CommandAdd.java new file mode 100644 index 0000000..733e490 --- /dev/null +++ b/src/test/java/com/beust/jcommander/command/CommandAdd.java @@ -0,0 +1,35 @@ +/** + * 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.command; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; + +import java.util.List; + +@Parameters(commandDescription = "Add file contents to the index") +public class CommandAdd { + + @Parameter(description = "Patterns of files to be added") + public List<String> patterns; + + @Parameter(names = "-i") + public Boolean interactive = false; + +} diff --git a/src/test/java/com/beust/jcommander/command/CommandAliasTest.java b/src/test/java/com/beust/jcommander/command/CommandAliasTest.java new file mode 100644 index 0000000..4eee7f2 --- /dev/null +++ b/src/test/java/com/beust/jcommander/command/CommandAliasTest.java @@ -0,0 +1,175 @@ +/** + * 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.command; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.ParameterException; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Tests command alias functionality + * + * @author rodionmoiseev + */ +public class CommandAliasTest { + @Test + public void oneCommandWithSingleAlias() { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + CommandAdd add = new CommandAdd(); + jc.addCommand("add", add, "a"); + jc.parse("a", "-i", "A.java"); + + Assert.assertEquals(jc.getParsedCommand(), "add"); + Assert.assertEquals(jc.getParsedAlias(), "a"); + Assert.assertEquals(add.interactive.booleanValue(), true); + Assert.assertEquals(add.patterns, Arrays.asList("A.java")); + } + + @Test + public void oneCommandWithMultipleAliases_commit_ci() { + testCommitWithAlias("ci"); + } + + @Test + public void oneCommandWithMultipleAliases_commit_cmt() { + testCommitWithAlias("cmt"); + } + + private void testCommitWithAlias(String alias) { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + CommandCommit commit = new CommandCommit(); + jc.addCommand("commit", commit, "ci", "cmt"); + jc.parse(alias, "--amend", "--author", "jack", "file1.txt"); + + Assert.assertEquals(jc.getParsedCommand(), "commit"); + Assert.assertEquals(jc.getParsedAlias(), alias); + Assert.assertEquals(commit.amend.booleanValue(), true); + Assert.assertEquals(commit.author, "jack"); + Assert.assertEquals(commit.files, Arrays.asList("file1.txt")); + } + + @Test + public void twoCommandsWithAliases() { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + CommandAdd add = new CommandAdd(); + jc.addCommand("add", add, "a"); + CommandCommit commit = new CommandCommit(); + jc.addCommand("commit", commit, "ci", "cmt"); + jc.parse("a", "-i", "A.java"); + + Assert.assertEquals(jc.getParsedCommand(), "add"); + Assert.assertEquals(add.interactive.booleanValue(), true); + Assert.assertEquals(add.patterns, Arrays.asList("A.java")); + } + + @Test + public void clashingAliasesAreNotAllowed() { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + CommandAdd add = new CommandAdd(); + jc.addCommand("add", add, "xx"); + CommandCommit commit = new CommandCommit(); + try { + jc.addCommand("commit", commit, "ci", "xx"); + Assert.fail("Should not be able to register clashing alias 'xx'"); + } catch (ParameterException pe) { + //Make sure the message mentions that "xx" aliases is already + //defined for "add" command + Assert.assertTrue(pe.getMessage().contains("xx")); + Assert.assertTrue(pe.getMessage().contains("add")); + } + } + + @Test + public void mainCommandReturnsNullsForGetCommandAndGetParsedAlias() { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + Assert.assertNull(jc.getParsedCommand()); + Assert.assertNull(jc.getParsedAlias()); + } + + @Test + public void usageCanBeRetrievedWithBothCommandAndAlias() { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + CommandCommit commit = new CommandCommit(); + jc.addCommand("commit", commit, "ci", "cmt"); + StringBuilder out = new StringBuilder(); + jc.usage("commit", out); + patternMatchesTimes("commit\\(ci,cmt\\)", out.toString(), 1); + + out = new StringBuilder(); + jc.usage("ci", out); + patternMatchesTimes("commit\\(ci,cmt\\)", out.toString(), 1); + + out = new StringBuilder(); + jc.usage("cmt", out); + patternMatchesTimes("commit\\(ci,cmt\\)", out.toString(), 1); + } + + @Test + public void usageDisplaysCommandWithAliasesOnlyOnce() { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + CommandCommit commit = new CommandCommit(); + jc.addCommand("commit", commit, "ci", "cmt"); + StringBuilder out = new StringBuilder(); + jc.usage(out); + // The usage should display this string twice: one as the command name + // and one after Usage: + patternMatchesTimes("commit\\(ci,cmt\\)", out.toString(), 2); + } + + /** + * Visually test the formatting for "prettiness" + */ + @Test(enabled = false, description = "TODO: test the output instead of displaying it") + public void formattingLooksNice(){ + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + CommandAdd add = new CommandAdd(); + jc.addCommand("add", add, "a"); + CommandCommit commit = new CommandCommit(); + jc.addCommand("commit", commit, "ci", "cmt"); + StringBuilder sb = new StringBuilder(); + jc.usage(sb); + System.out.println("--- usage() formatting ---"); + System.out.println(sb.toString()); + + sb = new StringBuilder(); + jc.usage("commit", sb); + System.out.println("--- usage('commit') formatting ---"); + System.out.println(sb.toString()); + } + + private void patternMatchesTimes(String pattern, String input, int times) { + Matcher m = Pattern.compile(pattern).matcher(input); + int matches = 0; + while (m.find()) matches++; + Assert.assertEquals(matches, times); + } +} diff --git a/src/test/java/com/beust/jcommander/command/CommandCommit.java b/src/test/java/com/beust/jcommander/command/CommandCommit.java new file mode 100644 index 0000000..2934a89 --- /dev/null +++ b/src/test/java/com/beust/jcommander/command/CommandCommit.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.command; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; + +import java.util.List; + +@Parameters(separators = "=", commandDescription = "Record changes to the repository") +public class CommandCommit { + + @Parameter(description = "List of files") + public List<String> files; + + @Parameter(names = "--amend", description = "Amend") + public Boolean amend = false; + + @Parameter(names = "--author") + public String author; +} diff --git a/src/test/java/com/beust/jcommander/command/CommandHidden.java b/src/test/java/com/beust/jcommander/command/CommandHidden.java new file mode 100644 index 0000000..a3fc4fa --- /dev/null +++ b/src/test/java/com/beust/jcommander/command/CommandHidden.java @@ -0,0 +1,17 @@ +package com.beust.jcommander.command; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; + +import java.util.List; + +@Parameters(commandNames = "add", commandDescription = "Hidden command to add file contents to the index", hidden = true) +public class CommandHidden { + + @Parameter(description = "Patterns of files to be added") + public List<String> patterns; + + @Parameter(names = "-i") + public Boolean interactive = false; + +} diff --git a/src/test/java/com/beust/jcommander/command/CommandMain.java b/src/test/java/com/beust/jcommander/command/CommandMain.java new file mode 100644 index 0000000..c205116 --- /dev/null +++ b/src/test/java/com/beust/jcommander/command/CommandMain.java @@ -0,0 +1,27 @@ +/** + * 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.command; + +import com.beust.jcommander.Parameter; + +public class CommandMain { + + @Parameter(names = "-v", description = "Verbose mode") + public Boolean verbose = false; +} diff --git a/src/test/java/com/beust/jcommander/command/CommandTest.java b/src/test/java/com/beust/jcommander/command/CommandTest.java new file mode 100644 index 0000000..cf921bd --- /dev/null +++ b/src/test/java/com/beust/jcommander/command/CommandTest.java @@ -0,0 +1,115 @@ +/** + * 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.command; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.ParameterException; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Arrays; + +public class CommandTest { + @Test + public void namedCommandTest1() { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + NamedCommandAdd add = new NamedCommandAdd(); + jc.addCommand(add); + CommandCommit commit = new CommandCommit(); + jc.addCommand("commit", commit); + jc.parse("add", "-i", "A.java"); + + Assert.assertEquals(jc.getParsedCommand(), "add"); + Assert.assertEquals(add.interactive.booleanValue(), true); + Assert.assertEquals(add.patterns, Arrays.asList("A.java")); + } + + @Test(expectedExceptions = ParameterException.class) + public void shouldComplainIfNoAnnotations() { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + CommandAdd add = new CommandAdd(); + jc.addCommand(add); + } + + @Test + public void commandTest1() { + 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("add", "-i", "A.java"); + + Assert.assertEquals(jc.getParsedCommand(), "add"); + Assert.assertEquals(add.interactive.booleanValue(), true); + Assert.assertEquals(add.patterns, Arrays.asList("A.java")); + } + + @Test + public void commandTest2() { + 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"); + +// jc.setProgramName("TestCommander"); +// jc.usage(); +// jc.usage("add"); +// jc.usage("commit"); + + Assert.assertTrue(cm.verbose); + Assert.assertEquals(jc.getParsedCommand(), "commit"); + Assert.assertTrue(commit.amend); + Assert.assertEquals(commit.author, "cbeust"); + Assert.assertEquals(commit.files, Arrays.asList("A.java", "B.java")); + } + + @Test + public void hiddenCommandTest() { + CommandMain cm = new CommandMain(); + JCommander jc = new JCommander(cm); + CommandAdd add = new CommandAdd(); + jc.addCommand("add", add); + CommandHidden hidden = new CommandHidden(); + jc.addCommand("hidden", hidden); + jc.parse("hidden", "-i", "A.java"); + + Assert.assertEquals(jc.getParsedCommand(), "hidden"); + Assert.assertEquals(hidden.interactive.booleanValue(), true); + Assert.assertEquals(hidden.patterns, Arrays.asList("A.java")); + + jc.setProgramName("TestCommander"); + StringBuilder out = new StringBuilder(); + jc.usage(out); + + Assert.assertTrue(out.toString().contains("add Add file contents to the index")); + Assert.assertFalse(out.toString().contains("hidden Hidden command to add file contents to the index")); + } + + public static void main(String[] args) { + new CommandTest().shouldComplainIfNoAnnotations(); + } +} diff --git a/src/test/java/com/beust/jcommander/command/NamedCommandAdd.java b/src/test/java/com/beust/jcommander/command/NamedCommandAdd.java new file mode 100644 index 0000000..0773e18 --- /dev/null +++ b/src/test/java/com/beust/jcommander/command/NamedCommandAdd.java @@ -0,0 +1,17 @@ +package com.beust.jcommander.command; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; + +import java.util.List; + +@Parameters(commandNames = "add", commandDescription = "Add file contents to the index") +public class NamedCommandAdd { + + @Parameter(description = "Patterns of files to be added") + public List<String> patterns; + + @Parameter(names = "-i") + public Boolean interactive = false; + +} diff --git a/src/test/java/com/beust/jcommander/dynamic/DSimple.java b/src/test/java/com/beust/jcommander/dynamic/DSimple.java new file mode 100644 index 0000000..deb5a2b --- /dev/null +++ b/src/test/java/com/beust/jcommander/dynamic/DSimple.java @@ -0,0 +1,16 @@ +package com.beust.jcommander.dynamic; + +import com.beust.jcommander.DynamicParameter; + +import org.testng.collections.Maps; + +import java.util.Map; + +public 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(); +} diff --git a/src/test/java/com/beust/jcommander/dynamic/DSimpleBad.java b/src/test/java/com/beust/jcommander/dynamic/DSimpleBad.java new file mode 100644 index 0000000..f859029 --- /dev/null +++ b/src/test/java/com/beust/jcommander/dynamic/DSimpleBad.java @@ -0,0 +1,11 @@ +package com.beust.jcommander.dynamic; + +import com.beust.jcommander.DynamicParameter; + +import java.util.List; + +public class DSimpleBad { + + @DynamicParameter(names = "-D") + public List<String> params; +} diff --git a/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java b/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java new file mode 100644 index 0000000..98327bd --- /dev/null +++ b/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java @@ -0,0 +1,60 @@ +package com.beust.jcommander.dynamic; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.internal.Maps; + +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class DynamicParameterTest { + + @Test(expectedExceptions = ParameterException.class) + public void nonMapShouldThrow() { + new JCommander(new DSimpleBad()).parse("-D", "a=b", "-D", "c=d"); + } + + @Test(expectedExceptions = ParameterException.class) + public void wrongSeparatorShouldThrow() { + DSimple ds = new DSimple(); + new JCommander(ds).parse("-D", "a:b", "-D", "c=d"); + } + + private void simple(String... parameters) { + DSimple ds = new DSimple(); + new JCommander(ds).parse(parameters); + Assert.assertEquals(ds.params, Maps.newHashMap("a", "b", "c", "d")); + } + + public void simpleWithSpaces() { + simple("-D", "a=b", "-D", "c=d"); + } + + public void simpleWithoutSpaces() { + simple("-Da=b", "-Dc=d"); + } + + public void usage() { + DSimple ds = new DSimple(); + new JCommander(ds).usage(new StringBuilder()); + } + + public void differentAssignment() { + DSimple ds = new DSimple(); + new JCommander(ds).parse("-D", "a=b", "-A", "c@d"); + Assert.assertEquals(ds.params, Maps.newHashMap("a", "b")); + Assert.assertEquals(ds.params2, Maps.newHashMap("c", "d")); + } + + @Test(enabled = false) + 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/com/beust/jcommander/internal/DefaultConsoleTest.java b/src/test/java/com/beust/jcommander/internal/DefaultConsoleTest.java new file mode 100644 index 0000000..e101439 --- /dev/null +++ b/src/test/java/com/beust/jcommander/internal/DefaultConsoleTest.java @@ -0,0 +1,64 @@ +package com.beust.jcommander.internal; + +import java.io.IOException; +import java.io.InputStream; + +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class DefaultConsoleTest { + public void readPasswordCanBeCalledMultipleTimes() { + final InputStream inBackup = System.in; + try { + final StringInputStream in = new StringInputStream(); + System.setIn(in); + final Console console = new DefaultConsole(); + + in.setData("password1\n"); + char[] password = console.readPassword(false); + Assert.assertEquals(password, "password1".toCharArray()); + Assert.assertFalse(in.isClosedCalled(), "System.in stream shouldn't be closed"); + + in.setData("password2\n"); + password = console.readPassword(false); + Assert.assertEquals(password, "password2".toCharArray()); + Assert.assertFalse(in.isClosedCalled(), "System.in stream shouldn't be closed"); + } finally { + System.setIn(inBackup); + } + } + + private static class StringInputStream extends InputStream { + private byte[] data = new byte[0]; + private int offset = 0; + private boolean closedCalled; + + StringInputStream() { + super(); + } + + void setData(final String strData) { + data = strData.getBytes(); + offset = 0; + } + + boolean isClosedCalled() { + return closedCalled; + } + + @Override + public int read() throws IOException { + if (offset >= data.length) { + return -1; + } + return 0xFFFF & data[offset++]; + } + + @Override + public void close() throws IOException { + closedCalled = true; + super.close(); + } + } +} diff --git a/src/test/java/test/QuotedMainTest.java b/src/test/java/test/QuotedMainTest.java new file mode 100644 index 0000000..abb97c0 --- /dev/null +++ b/src/test/java/test/QuotedMainTest.java @@ -0,0 +1,29 @@ +package test; + +import java.util.ArrayList; +import java.util.List; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; + +public class QuotedMainTest { + @Parameter + List<String> args = new ArrayList<String>(); + + String quoted = "\" \""; + + @Test + public void testMain() { + JCommander jc = new JCommander(this); + jc.parse(quoted); + Assert.assertEquals(args.size(), 1); + Assert.assertEquals(args.get(0), " "); + } + + public static void main(String[] args) { + new QuotedMainTest().testMain(); + } +} diff --git a/src/test/resources/MessageBundle_en_US.properties b/src/test/resources/MessageBundle_en_US.properties new file mode 100644 index 0000000..11e19e4 --- /dev/null +++ b/src/test/resources/MessageBundle_en_US.properties @@ -0,0 +1,21 @@ +# +# 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. +# + +host = Host +command = Command from the bundle +myoption = My option
\ No newline at end of file diff --git a/src/test/resources/MessageBundle_fr_FR.properties b/src/test/resources/MessageBundle_fr_FR.properties new file mode 100644 index 0000000..4ae83a9 --- /dev/null +++ b/src/test/resources/MessageBundle_fr_FR.properties @@ -0,0 +1,20 @@ +# +# 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. +# + +host = Hôte +files = Les fichiers diff --git a/src/test/resources/jcommander.properties b/src/test/resources/jcommander.properties new file mode 100644 index 0000000..3cea84b --- /dev/null +++ b/src/test/resources/jcommander.properties @@ -0,0 +1,21 @@ +# +# 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. +# + +groups=unit +level=17 +log=18
\ No newline at end of file diff --git a/src/test/resources/testng-single.xml b/src/test/resources/testng-single.xml new file mode 100644 index 0000000..cc0602c --- /dev/null +++ b/src/test/resources/testng-single.xml @@ -0,0 +1,19 @@ +<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > + +<suite name="JCommander" verbose="2"> + + <test name="JCommander tests"> + <classes> +<!-- + <class name="com.beust.jcommander.FinderTest" /> +--> + <class name="com.beust.jcommander.JCommanderTest"> + <methods> + <include name="partialValidation"></include> + </methods> + </class> + </classes> + </test> + +</suite> + diff --git a/src/test/resources/testng.xml b/src/test/resources/testng.xml new file mode 100644 index 0000000..f804418 --- /dev/null +++ b/src/test/resources/testng.xml @@ -0,0 +1,24 @@ +<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > + +<suite name="JCommander"> + + <test name="JCommander tests"> + <classes> + <class name="com.beust.jcommander.command.CommandTest" /> + <class name="com.beust.jcommander.command.CommandAliasTest" /> + <class name="com.beust.jcommander.ConverterFactoryTest" /> + <class name="com.beust.jcommander.DefaultProviderTest" /> + <class name="com.beust.jcommander.DefaultValueTest" /> + <class name="com.beust.jcommander.JCommanderTest" /> + <class name="com.beust.jcommander.ParametersDelegateTest" /> + <class name="com.beust.jcommander.dynamic.DynamicParameterTest" /> + <class name="com.beust.jcommander.VariableArityTest" /> + <class name="com.beust.jcommander.MethodSetterTest" /> + <class name="com.beust.jcommander.PositiveIntegerTest" /> + <class name="com.beust.jcommander.FinderTest" /> + <class name="com.beust.jcommander.CmdTest" /> + </classes> + </test> + +</suite> + |