summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaibo Huang <hhb@google.com>2020-04-09 23:15:15 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-04-09 23:15:15 +0000
commitff4f16dfe7979d8bb8d13d1fec15f4062a974b6c (patch)
treeb88ceec0eaf4b7c3e0e213f45cbfd2d678bfbb7c
parentc4d97985b3787276d6b391510250246c9ca94541 (diff)
parent833231bb26846c62c77ad5f21cb9f1fb80115ec0 (diff)
downloadjcommander-ff4f16dfe7979d8bb8d13d1fec15f4062a974b6c.tar.gz
Upgrade JCommander to '1.71' am: cdfd109c35 am: 1581a8da5e am: 833231bb26
Change-Id: I8c3c5d63953a7e14eaaf653c22962e07a3a931a6
-rw-r--r--.gitignore17
-rw-r--r--.travis.yml11
-rw-r--r--CHANGELOG139
-rw-r--r--CHANGELOG.md209
l---------LICENSE1
-rw-r--r--METADATA8
-rwxr-xr-xNOTICE203
-rw-r--r--README.markdown11
-rw-r--r--README.version4
-rwxr-xr-xbuild-with-maven16
-rw-r--r--build.gradle44
-rw-r--r--doc/colony.css698
-rwxr-xr-xdoc/generate-doc.sh1
-rw-r--r--doc/golo.css702
-rw-r--r--doc/index.adoc1065
-rw-r--r--doc/index.html2463
-rw-r--r--doc/old-index.html938
-rw-r--r--gradle/publishing.gradle63
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 51106 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xgradlew164
-rw-r--r--kobalt/src/Build.kt59
-rw-r--r--kobalt/wrapper/kobalt-wrapper.jarbin0 -> 11296 bytes
-rw-r--r--kobalt/wrapper/kobalt-wrapper.properties1
-rwxr-xr-xkobaltw2
-rw-r--r--lib/kotlin-reflect.jarbin0 -> 2852051 bytes
-rw-r--r--lib/kotlin-runtime-sources.jarbin0 -> 1026940 bytes
-rw-r--r--lib/kotlin-runtime.jarbin0 -> 871805 bytes
-rw-r--r--misc.xml4
-rw-r--r--pom.xml283
-rw-r--r--src/main/java/com/beust/jcommander/DynamicParameter.java6
-rw-r--r--src/main/java/com/beust/jcommander/IStringConverterFactory.java1
-rw-r--r--src/main/java/com/beust/jcommander/IStringConverterInstanceFactory.java20
-rw-r--r--src/main/java/com/beust/jcommander/JCommander.java3052
-rw-r--r--src/main/java/com/beust/jcommander/MissingCommandException.java19
-rw-r--r--src/main/java/com/beust/jcommander/Parameter.java11
-rw-r--r--src/main/java/com/beust/jcommander/ParameterDescription.java265
-rw-r--r--src/main/java/com/beust/jcommander/ParameterException.java18
-rw-r--r--src/main/java/com/beust/jcommander/Parameterized.java223
-rw-r--r--src/main/java/com/beust/jcommander/Parameters.java7
-rw-r--r--src/main/java/com/beust/jcommander/StringKey.java16
-rw-r--r--src/main/java/com/beust/jcommander/SubParameter.java17
-rw-r--r--src/main/java/com/beust/jcommander/WrappedParameter.java72
-rw-r--r--src/main/java/com/beust/jcommander/converters/BaseConverter.java6
-rw-r--r--src/main/java/com/beust/jcommander/converters/CharArrayConverter.java33
-rw-r--r--src/main/java/com/beust/jcommander/converters/DefaultListConverter.java36
-rw-r--r--src/main/java/com/beust/jcommander/converters/EnumConverter.java42
-rw-r--r--src/main/java/com/beust/jcommander/converters/InetAddressConverter.java40
-rw-r--r--src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java8
-rw-r--r--src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java45
-rw-r--r--src/main/java/com/beust/jcommander/internal/JDK6Console.java12
-rw-r--r--src/main/java/com/beust/jcommander/internal/Lists.java12
-rw-r--r--src/main/java/com/beust/jcommander/internal/Maps.java4
-rw-r--r--src/main/java/com/beust/jcommander/internal/Sets.java4
-rw-r--r--src/test/java/com/beust/jcommander/ArgMultiNameValidator.java19
-rw-r--r--src/test/java/com/beust/jcommander/CmdTest.java29
-rw-r--r--src/test/java/com/beust/jcommander/ConverterFactoryTest.java22
-rw-r--r--src/test/java/com/beust/jcommander/DefaultProviderTest.java20
-rw-r--r--src/test/java/com/beust/jcommander/DefaultValueTest.java8
-rw-r--r--src/test/java/com/beust/jcommander/FinderTest.java11
-rw-r--r--src/test/java/com/beust/jcommander/HiddenConverter.java29
-rw-r--r--src/test/java/com/beust/jcommander/HiddenParameterSplitter.java31
-rw-r--r--src/test/java/com/beust/jcommander/JCommanderTest.java2560
-rw-r--r--src/test/java/com/beust/jcommander/MethodSetterTest.java9
-rw-r--r--src/test/java/com/beust/jcommander/ParametersDelegateTest.java6
-rw-r--r--src/test/java/com/beust/jcommander/ParametersNotEmptyTest.java37
-rw-r--r--src/test/java/com/beust/jcommander/PasswordTest.java111
-rw-r--r--src/test/java/com/beust/jcommander/PositiveIntegerTest.java10
-rw-r--r--src/test/java/com/beust/jcommander/SetConverter.java2
-rw-r--r--src/test/java/com/beust/jcommander/SimpleExample.java50
-rw-r--r--src/test/java/com/beust/jcommander/TypeHierarchyTest.java111
-rw-r--r--src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java3
-rw-r--r--src/test/java/com/beust/jcommander/VariableArityTest.java11
-rw-r--r--src/test/java/com/beust/jcommander/args/ArgsEnum.java4
-rw-r--r--src/test/java/com/beust/jcommander/args/ArgsLongCommandDescription.java12
-rw-r--r--src/test/java/com/beust/jcommander/args/ArgsLongMainParameterDescription.java17
-rw-r--r--src/test/java/com/beust/jcommander/args/ArgsMultiValidate.java36
-rw-r--r--src/test/java/com/beust/jcommander/args/HiddenArgs.java36
-rw-r--r--src/test/java/com/beust/jcommander/args/SlashSeparator.java2
-rw-r--r--src/test/java/com/beust/jcommander/args/VariableArity.java10
-rw-r--r--src/test/java/com/beust/jcommander/command/CommandNoParametersAnnotation.java9
-rw-r--r--src/test/java/com/beust/jcommander/command/CommandTest.java57
-rw-r--r--src/test/java/com/beust/jcommander/converters/CharArrayConverterTest.java37
-rw-r--r--src/test/java/com/beust/jcommander/converters/InetAddressConverterTest.java62
-rw-r--r--src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java5
-rw-r--r--src/test/java/test/QuotedMainTest.java2
86 files changed, 10294 insertions, 4155 deletions
diff --git a/.gitignore b/.gitignore
index 5812af2..4af1799 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,22 @@ target
.classpath
.project
.settings
+bin
test-output
src/test/java/com/beust/jcommander/ignore
+build
+.idea
+*.iml
+*.ipr
+*.iws
+lib/kotlin-r*jar
+.gradle
+local.properties
+kobaltBuild
+.kobalt
+
+*~
+.DS_Store
+out
+update-doc.sh
+classes
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..46dc775
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: java
+jdk:
+ - oraclejdk8
+
+before_install:
+ - touch local.properties
+
+install: true
+
+script: ./kobaltw assemble test
+
diff --git a/CHANGELOG b/CHANGELOG
deleted file mode 100644
index 05bb809..0000000
--- a/CHANGELOG
+++ /dev/null
@@ -1,139 +0,0 @@
-Current
-
-1.30
-2012/10/27
-
-Added: JCommander#acceptUnknownOption and JCommander#getUnknownArgs
-Added: JCommander#allowAbbreviatedOptions (default: false)
-Added: JCommander#setCaseSensitiveOptions (default: true)
-Added: Support for enums (Scott M Stark)
-Fixed: Missing new lines in usage (styurin)
-Fixed: The description of commands is now displayed on the next line and indented.
-
-1.29
-2012/07/28
-
-Fixed: Empty string defaults now displayed as "<empty string>" in the usage
-Fixed: Bugs with the PositiveInteger validator
-Fixed: Parameters with a single double quote were not working properly
-
-1.27
-2012/07/05
-
-Added: IValueValidator to validate parameter values (typed) as opposed to IParameterValidator which validates strings
-Added: echoInput, used when password=true to echo the characters (Jason Wheeler)
-Added: @Parameter(help = true)
-Fixed: wasn't handling parameters that start with " but don't end with one correctly
-Fixed: if using a different option prefix, unknown option are mistakenly reported as "no main parameter defined" (kurmasz)
-Fixed: 113: getCommandDescription() returns the description of the main parameter instead of that of the command
-Fixed: bug with several multiple arity parameters (VariableArityTest)
-Fixed: variable arities not working when same parameter appears multiple times.
-
-1.25
-2012/04/26
-
-Added: Default passwords are no longer displayed in the usage (Paul Mendelson)
-Added: Variable arities now work magically, no need for IVariableArity any more
-Fixed: Commands using @Parameters(resourceBundle) were not i18n'ed properly in the usage()
-Fixed: StringIndexOutOfBoundsException if passing an empty parameter (bomanz)
-Fixed: GITHUB-105: If no description is given for an enum, use that enum's value (Adrian Muraru)
-Fixed: GITHUB-108: Dynamic parameters with "=" in them are not parsed correctly (szhem)
-Fixed: Commands with same prefix as options were not working properly.
-Fixed: GITHUB-97: Required password always complains that it is not specified (timoteoponce)
-
-1.23
-2012/01/12
-
-Added: @DynamicParameter
-Fixed: Use JDK 6 Console() when available to improve support of non ascii chars (Julien Henry)
-
-1.20
-2011/11/24
-
-Added: Support for delegating parameter definitions to child classes (rodionmoiseev)
-Added: @Parameter(commandNames) so that command names can be specified with annotations
-Added: Support for enums (Adrian Muraru)
-Fixed: Throw if an unknown option is found
-Fixed: Main parameters are now validated as well (Connor Mullen)
-
-1.19
-2011/10/10
-
-Added: commandDescriptionKey to @Parameters, to allow internationalized command descriptions
-Added: JCommander#setParameterDescriptionComparator for better control over usage()
-Fixed: Fields of type Set (HashSet and SortedSet) are now supported
-Fixed: defaults for commands were not properly applied (Stevo Slavic)
-Fixed: "-args=a=b,b=c" was not being parsed correctly (Michael Lancaster)
-Fixed: GITHUB-73: descriptionKey was being ignored on main parameters
-
-1.18
-2011/07/20
-
-Added: Default converter factories can be overridden (Scott Clasen)
-Added: IParameterValidator
-Added: Don't display "Options:" if none were defined
-Added: Enforce that the type of the main parameter is a List
-Added: usage() now displays the options for each command as well
-Fixed: Default values with a validator were being validate at parse() time instead of creation time.
-Fixed: Exception when using an @ file with empty lines between options
-Fixed: OOM when parsing certain descriptions with long URL's in them
-
-1.15
-2011/01/24
-
-Added: Added a constructor that takes a Bundle only, https://github.com/cbeust/jcommander/pull/47 (Russell Egan)
-Fixed: NPE with calling getCommandDescription() of an unknown command
-
-1.13
-2010/12/15
-
-Added: Boolean parameters with arity 0 (e.g. "foo -debug")
-Fixed: JCommander would sometimes just print a stack trace and continue, now rethrowing.
-
-1.7
-2010/09/06
-
-Added: Command usages are now shown in the order they were added to the JCommander object
-Fixed: JCommander now compatible with Java 5
-Fixed: Minor bug in the command display (Marc Ende)
-
-1.6
-2010/08/28
-
-Added: @Parameters(commandDescription = "command description")
-Added: now throwing an exception if required main parameters are not supplied
-Fixed: usage() was changing default values after two runs (jstrachan)
-
-1.5
-2010/08/15
-
-Added: overloaded versions of usage() with StringBuilders
-Added: inheritance support (Guillaume Sauthier)
-Added: support for commands (e.g. "main add --author=cbeust Foo.java")
-Added: support for converters for main parameters (e.g. List<HostPort>).
-
-1.4
-2010/07/28
-
-Added: string converter factories
-Added: IDefaultProvider
-Added: PropertyFileDefaultProvider
-Added: Usage is now showing required parameters and default value
-Added: Support for values that look like parameters ("-integer -3", "/file /tmp/a")
-Added: @Parameters(optionPrefixes) to allow for different prefixes than "-"
-
-1.2
-2010/07/25
-
-Usage is now aligned and alphabetically sorted
-Added the hidden attribute
-Added support for different separators than " " (e.g. "=").
-Deprecated @ResourceBundle, replaced with @Parameters
-
-1.1
-2010/08/15
-
-Better internationalization
-Password support
-Type converters
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..3fcc135
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,209 @@
+## Changelog
+
+### Current
+2017-03-05
+
+* Fixed: '--' handling, #296
+* Added: Add `getProgramName` to `JCommander`, #247
+* Added: Documentation for `listConverter` and `splitter`, #253, (@jeremysolarz)
+* Fixed: Return right parameter name in exception, #227, (@jeremysolarz)
+* Fixed: `JCommander#getParameters` returning nothing, #315, (@simon04)
+* Fixed: Allow empty string (e.g. `java -jar jcommander-program.jar param1 ""`) as part of main parameter, #306 (@jeremysolarz)
+* Fixed: Default value for `@Parameter(help=true)` parameter is not displayed in output of `JCommander.usage()`, #305 (@jeremysolarz)
+* Fixed: When providing two names in `@Parameter` always first name is given to `IValueValidator`, #309 (@jeremysolarz)
+
+### 1.58
+2016-09-29
+
+* Added: `IStringConverterInstanceFactory` to create converter instances, #279 (@simon04)
+* Added: Allow to specify the `@file` charset, #286 (@simon04)
+* Added: Converters `InetAddressConverter` (#288), `CharArrayConverter` (#289; @garydgregory)
+* Fixed: When using `parseWithoutValidation()`, JCommander uses 'parse()' on child commanders, #267 (@simon04)
+* Fixed: Share all options (such as column size, allow abbreviated options, case sensitivity) with sub commands, see `JCommander.Options` class, #280 (fixes #155, #244, #261, #274; @simon04)
+* Fixed: Thread-safe and non-shared converter factories, #284 (@simon04)
+* Fixed: Skip `Path` converter when class is not available (Android), #287 (@JesusFreke)
+* Added: JCommander now requires Java 8
+
+### 1.56
+2016-08-05
+
+* Added: Allow user to retrieve unknown command, #275
+
+### 1.55
+2016-02-28
+
+* Added: Support for disabling the `@file` expansion, #156
+* Fixed: Wrap usage for commands and main parameters, #258
+* Added: Read parameters from interfaces, #252
+* Added: Refuse to write to final fields, #243
+* Added: Access private fields/methods, #236
+* Fixed: Fix description line wrapping, #239
+* Fixed: Prioritize registered converters for enums over generic enum conversion, #179
+* Added: Travis CI support, https://travis-ci.org/cbeust/jcommander
+* Added: Gradle build support
+* Fixed: Better error message when there's a visibility problem.
+* Require Java 7
+
+### 1.48
+2015-04-11
+
+* Added: Added support for URL, URI, Java NIO paths parameters, #189, #219
+* Fixed: Incorrect usage formatting with single long options, #200
+
+### 1.37
+2014-10-05
+
+* Added: Support for `hidden` commands (`Parameters`), #191
+* Added: parameter overwriting (and even disallowing it for certain parameters)
+* Added: `#` mark comments in a `@file`, #199
+* Added: Support for "--"
+* Fixed: Bug in enum parsing, #184
+
+### 1.34
+2014-02-22
+
+* Fixed problem whereby Parameters returning Lists and with alternate names were being reset on the first use of an alternate name, #182
+
+### 1.32
+2013-09-09
+
+* Fixed: Main parameters with a default value should be overridden if a main parameter is specified, #137
+* Fixed: Allow enum values without converting them to uppercase, #107
+
+### 1.30
+2012/10/27
+
+* Added: JCommander#acceptUnknownOption and JCommander#getUnknownArgs
+* Added: JCommander#allowAbbreviatedOptions (default: false)
+* Added: JCommander#setCaseSensitiveOptions (default: true)
+* Added: Support for enums (Scott M Stark)
+* Fixed: Missing new lines in usage (styurin)
+* Fixed: The description of commands is now displayed on the next line and indented.
+
+### 1.29
+2012/07/28
+
+* Fixed: Empty string defaults now displayed as "<empty string>" in the usage
+* Fixed: Bugs with the PositiveInteger validator
+* Fixed: Parameters with a single double quote were not working properly
+
+### 1.27
+2012/07/05
+
+* Added: IValueValidator to validate parameter values (typed) as opposed to IParameterValidator which validates strings
+* Added: echoInput, used when password=true to echo the characters (Jason Wheeler)
+* Added: @Parameter(help = true)
+* Fixed: wasn't handling parameters that start with " but don't end with one correctly
+* Fixed: if using a different option prefix, unknown option are mistakenly reported as "no main parameter defined" (kurmasz)
+* Fixed: 113: getCommandDescription() returns the description of the main parameter instead of that of the command
+* Fixed: bug with several multiple arity parameters (VariableArityTest)
+* Fixed: variable arities not working when same parameter appears multiple times.
+
+### 1.25
+2012/04/26
+
+* Added: Default passwords are no longer displayed in the usage (Paul Mendelson)
+* Added: Variable arities now work magically, no need for IVariableArity any more
+* Fixed: Commands using @Parameters(resourceBundle) were not i18n'ed properly in the usage()
+* Fixed: StringIndexOutOfBoundsException if passing an empty parameter (bomanz)
+* Fixed: #105: If no description is given for an enum, use that enum's value (Adrian Muraru)
+* Fixed: #108: Dynamic parameters with "=" in them are not parsed correctly (szhem)
+* Fixed: Commands with same prefix as options were not working properly.
+* Fixed: #97: Required password always complains that it is not specified (timoteoponce)
+
+### 1.23
+2012/01/12
+
+* Added: @DynamicParameter
+* Fixed: Use JDK 6 Console() when available to improve support of non ascii chars (Julien Henry)
+
+### 1.20
+2011/11/24
+
+* Added: Support for delegating parameter definitions to child classes (rodionmoiseev)
+* Added: @Parameter(commandNames) so that command names can be specified with annotations
+* Added: Support for enums (Adrian Muraru)
+* Fixed: Throw if an unknown option is found
+* Fixed: Main parameters are now validated as well (Connor Mullen)
+
+### 1.19
+2011/10/10
+
+* Added: commandDescriptionKey to @Parameters, to allow internationalized command descriptions
+* Added: JCommander#setParameterDescriptionComparator for better control over usage()
+* Fixed: Fields of type Set (HashSet and SortedSet) are now supported
+* Fixed: defaults for commands were not properly applied (Stevo Slavic)
+* Fixed: "-args=a=b,b=c" was not being parsed correctly (Michael Lancaster)
+* Fixed: #73: descriptionKey was being ignored on main parameters
+
+### 1.18
+2011/07/20
+
+* Added: Default converter factories can be overridden (Scott Clasen)
+* Added: IParameterValidator
+* Added: Don't display "Options:" if none were defined
+* Added: Enforce that the type of the main parameter is a List
+* Added: usage() now displays the options for each command as well
+* Fixed: Default values with a validator were being validate at parse() time instead of creation time.
+* Fixed: Exception when using an @ file with empty lines between options
+* Fixed: OOM when parsing certain descriptions with long URL's in them
+
+### 1.15
+2011/01/24
+
+* Added: Added a constructor that takes a Bundle only, #47 (Russell Egan)
+* Fixed: NPE with calling getCommandDescription() of an unknown command
+
+### 1.13
+2010/12/15
+
+* Added: Boolean parameters with arity 0 (e.g. "foo -debug")
+* Fixed: JCommander would sometimes just print a stack trace and continue, now rethrowing.
+
+### 1.7
+2010/09/06
+
+* Added: Command usages are now shown in the order they were added to the JCommander object
+* Fixed: JCommander now compatible with Java 5
+* Fixed: Minor bug in the command display (Marc Ende)
+
+### 1.6
+2010/08/28
+
+* Added: @Parameters(commandDescription = "command description")
+* Added: now throwing an exception if required main parameters are not supplied
+* Fixed: usage() was changing default values after two runs (jstrachan)
+
+### 1.5
+2010/08/15
+
+* Added: overloaded versions of usage() with StringBuilders
+* Added: inheritance support (Guillaume Sauthier)
+* Added: support for commands (e.g. "main add --author=cbeust Foo.java")
+* Added: support for converters for main parameters (e.g. List<HostPort>).
+
+### 1.4
+2010/07/28
+
+* Added: string converter factories
+* Added: IDefaultProvider
+* Added: PropertyFileDefaultProvider
+* Added: Usage is now showing required parameters and default value
+* Added: Support for values that look like parameters ("-integer -3", "/file /tmp/a")
+* Added: @Parameters(optionPrefixes) to allow for different prefixes than "-"
+
+### 1.2
+2010/07/25
+
+* Usage is now aligned and alphabetically sorted
+* Added the hidden attribute
+* Added support for different separators than " " (e.g. "=").
+* Deprecated @ResourceBundle, replaced with @Parameters
+
+### 1.1
+2010/08/15
+
+* Better internationalization
+* Password support
+* Type converters
+
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..cd301a4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+license.txt \ No newline at end of file
diff --git a/METADATA b/METADATA
index 2608f15..20a7c39 100644
--- a/METADATA
+++ b/METADATA
@@ -9,10 +9,10 @@ third_party {
type: GIT
value: "https://github.com/cbeust/jcommander.git"
}
- version: "14fbe2bc5a2c402b456ed68578a5d5dc2c343fa2"
+ version: "1.66"
last_upgrade_date {
- year: 2016
- month: 3
- day: 16
+ year: 2020
+ month: 4
+ day: 8
}
}
diff --git a/NOTICE b/NOTICE
deleted file mode 100755
index d0c18cf..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,203 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright 2012, Cedric Beust
-
- 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/README.markdown b/README.markdown
index 0aecbf5..b29263d 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,7 +1,7 @@
JCommander
==========
-This is an annotation based parameter parsing framework for Java.
+This is an annotation based parameter parsing framework for Java 8.
Here is a quick example:
@@ -40,4 +40,11 @@ Assert.assertEquals("value", jct.dynamicParams.get("option"));
Assert.assertEquals(Arrays.asList("a", "b", "c"), jct.parameters);
```
-The full doc is available at http://beust.com/jcommander
+The full doc is available at [http://jcommander.org](http://jcommander.org).
+
+## Building JCommander
+
+```
+./kobaltw assemble
+```
+
diff --git a/README.version b/README.version
deleted file mode 100644
index 9de0411..0000000
--- a/README.version
+++ /dev/null
@@ -1,4 +0,0 @@
-URL: https://github.com/cbeust/jcommander
-Version: 1.48 (14fbe2bc5a2c402b456ed68578a5d5dc2c343fa2)
-BugComponent: 99142
-Owners: iam
diff --git a/build-with-maven b/build-with-maven
deleted file mode 100755
index 0e2de5a..0000000
--- a/build-with-maven
+++ /dev/null
@@ -1,16 +0,0 @@
-mvn -B clean source:jar javadoc:jar repository:bundle-create -P sign
-
-
-#v=6.5.2beta
-export TESTNG=`echo ../testng/target/testng-6.8.13.jar`
-
-run="java -classpath \"target/classes;target/test-classes;${TESTNG};$CLASSPATH\" org.testng.TestNG src/test/resources/testng.xml"
-echo "Launching tests: ${run}"
-$run
-#java -classpath target/classes:target/test-classes:${TESTNG}:$CLASSPATH org.testng.TestNG src/test/resources/testng.xml
-
-echo "To deploy to the snapshot repository: mvn deploy"
-echo "To deploy to the release directory: mvn -DskipTests=true release:clean release:prepare release:perform"
-echo "Nexus UI: https://oss.sonatype.org/index.html"
-echo "Wiki: https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide"
-
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..484d594
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,44 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven {
+ url 'http://oss.sonatype.org/content/repositories/snapshots'
+ }
+ }
+}
+
+plugins {
+ id "com.jfrog.bintray" version "1.2"
+}
+
+version = '1.58'
+
+apply plugin: 'java'
+apply plugin: 'eclipse'
+apply plugin: 'idea'
+apply from: 'gradle/publishing.gradle'
+
+repositories {
+ jcenter()
+}
+
+dependencies {
+ testCompile 'org.testng:testng:6.9.13'
+}
+
+task sourceJar(type: Jar) {
+ group 'Build'
+ description 'An archive of the source code'
+ classifier 'sources'
+ from sourceSets.main.allSource
+}
+
+artifacts {
+ sourceJar
+}
+
+test {
+ // enable TestNG support (default is JUnit)
+ useTestNG()
+}
diff --git a/doc/colony.css b/doc/colony.css
new file mode 100644
index 0000000..ab74c89
--- /dev/null
+++ b/doc/colony.css
@@ -0,0 +1,698 @@
+/*! normalize.css v2.1.2 | MIT License | git.io/normalize */
+/* ========================================================================== HTML5 display definitions ========================================================================== */
+/** Correct `block` display not defined in IE 8/9. */
+article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; }
+
+/** Correct `inline-block` display not defined in IE 8/9. */
+audio, canvas, video { display: inline-block; }
+
+/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */
+audio:not([controls]) { display: none; height: 0; }
+
+/** Address `[hidden]` styling not present in IE 8/9. Hide the `template` element in IE, Safari, and Firefox < 22. */
+[hidden], template { display: none; }
+
+script { display: none !important; }
+
+/* ========================================================================== Base ========================================================================== */
+/** 1. Set default font family to sans-serif. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */
+html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ }
+
+/** Remove default margin. */
+body { margin: 0; }
+
+/* ========================================================================== Links ========================================================================== */
+/** Remove the gray background color from active links in IE 10. */
+a { background: transparent; }
+
+/** Address `outline` inconsistency between Chrome and other browsers. */
+a:focus { outline: thin dotted; }
+
+/** Improve readability when focused and also mouse hovered in all browsers. */
+a:active, a:hover { outline: 0; }
+
+/* ========================================================================== Typography ========================================================================== */
+/** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */
+h1 { font-size: 2em; margin: 0.67em 0; }
+
+/** Address styling not present in IE 8/9, Safari 5, and Chrome. */
+abbr[title] { border-bottom: 1px dotted; }
+
+/** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */
+b, strong { font-weight: bold; }
+
+/** Address styling not present in Safari 5 and Chrome. */
+dfn { font-style: italic; }
+
+/** Address differences between Firefox and other browsers. */
+hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; }
+
+/** Address styling not present in IE 8/9. */
+mark { background: #ff0; color: #000; }
+
+/** Correct font family set oddly in Safari 5 and Chrome. */
+code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; }
+
+/** Improve readability of pre-formatted text in all browsers. */
+pre { white-space: pre-wrap; }
+
+/** Set consistent quote types. */
+q { quotes: "\201C" "\201D" "\2018" "\2019"; }
+
+/** Address inconsistent and variable font size in all browsers. */
+small { font-size: 80%; }
+
+/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */
+sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
+
+sup { top: -0.5em; }
+
+sub { bottom: -0.25em; }
+
+/* ========================================================================== Embedded content ========================================================================== */
+/** Remove border when inside `a` element in IE 8/9. */
+img { border: 0; }
+
+/** Correct overflow displayed oddly in IE 9. */
+svg:not(:root) { overflow: hidden; }
+
+/* ========================================================================== Figures ========================================================================== */
+/** Address margin not present in IE 8/9 and Safari 5. */
+figure { margin: 0; }
+
+/* ========================================================================== Forms ========================================================================== */
+/** Define consistent border, margin, and padding. */
+fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
+
+/** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */
+legend { border: 0; /* 1 */ padding: 0; /* 2 */ }
+
+/** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */
+button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ }
+
+/** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */
+button, input { line-height: normal; }
+
+/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */
+button, select { text-transform: none; }
+
+/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */
+button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ }
+
+/** Re-set default cursor for disabled elements. */
+button[disabled], html input[disabled] { cursor: default; }
+
+/** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */
+input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ }
+
+/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */
+input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; }
+
+/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */
+input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
+
+/** Remove inner padding and border in Firefox 4+. */
+button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
+
+/** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */
+textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ }
+
+/* ========================================================================== Tables ========================================================================== */
+/** Remove most spacing between table cells. */
+table { border-collapse: collapse; border-spacing: 0; }
+
+meta.foundation-mq-small { font-family: "only screen and (min-width: 768px)"; width: 768px; }
+
+meta.foundation-mq-medium { font-family: "only screen and (min-width:1280px)"; width: 1280px; }
+
+meta.foundation-mq-large { font-family: "only screen and (min-width:1440px)"; width: 1440px; }
+
+*, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
+
+html, body { font-size: 100%; }
+
+body { background: white; color: #222222; padding: 0; margin: 0; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; }
+
+a:hover { cursor: pointer; }
+
+img, object, embed { max-width: 100%; height: auto; }
+
+object, embed { height: 100%; }
+
+img { -ms-interpolation-mode: bicubic; }
+
+#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; }
+
+.left { float: left !important; }
+
+.right { float: right !important; }
+
+.text-left { text-align: left !important; }
+
+.text-right { text-align: right !important; }
+
+.text-center { text-align: center !important; }
+
+.text-justify { text-align: justify !important; }
+
+.hide { display: none; }
+
+.antialiased { -webkit-font-smoothing: antialiased; }
+
+img { display: inline-block; vertical-align: middle; }
+
+textarea { height: auto; min-height: 50px; }
+
+select { width: 100%; }
+
+object, svg { display: inline-block; vertical-align: middle; }
+
+.center { margin-left: auto; margin-right: auto; }
+
+.spread { width: 100%; }
+
+p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; }
+
+.subheader, .admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { line-height: 1.4; color: #003b6b; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; }
+
+/* Typography resets */
+div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; }
+
+/* Default Link Styles */
+a { color: #00579e; text-decoration: none; line-height: inherit; }
+a:hover, a:focus { color: #333333; }
+a img { border: none; }
+
+/* Default paragraph styles */
+p { font-family: Arial, sans-serif; font-weight: normal; font-size: 1em; line-height: 1.6; margin-bottom: 0.75em; text-rendering: optimizeLegibility; }
+p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; }
+
+/* Default header styles */
+h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { font-family: Arial, sans-serif; font-weight: normal; font-style: normal; color: #7b2d00; text-rendering: optimizeLegibility; margin-top: 0.5em; margin-bottom: 0.5em; line-height: 1.2125em; }
+h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { font-size: 60%; color: #ff6b15; line-height: 0; }
+
+h1 { font-size: 2.125em; }
+
+h2 { font-size: 1.6875em; }
+
+h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.375em; }
+
+h4 { font-size: 1.125em; }
+
+h5 { font-size: 1.125em; }
+
+h6 { font-size: 1em; }
+
+hr { border: solid #dddddd; border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; }
+
+/* Helpful Typography Defaults */
+em, i { font-style: italic; line-height: inherit; }
+
+strong, b { font-weight: bold; line-height: inherit; }
+
+small { font-size: 60%; line-height: inherit; }
+
+code { font-family: Consolas, "Liberation Mono", Courier, monospace; font-weight: bold; color: #003426; }
+
+/* Lists */
+ul, ol, dl { font-size: 1em; line-height: 1.6; margin-bottom: 0.75em; list-style-position: outside; font-family: Arial, sans-serif; }
+
+ul, ol { margin-left: 1.5em; }
+ul.no-bullet, ol.no-bullet { margin-left: 1.5em; }
+
+/* Unordered Lists */
+ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ }
+ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; }
+ul.square { list-style-type: square; }
+ul.circle { list-style-type: circle; }
+ul.disc { list-style-type: disc; }
+ul.no-bullet { list-style: none; }
+
+/* Ordered Lists */
+ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; }
+
+/* Definition Lists */
+dl dt { margin-bottom: 0.3em; font-weight: bold; }
+dl dd { margin-bottom: 0.75em; }
+
+/* Abbreviations */
+abbr, acronym { text-transform: uppercase; font-size: 90%; color: black; border-bottom: 1px dotted #dddddd; cursor: help; }
+
+abbr { text-transform: none; }
+
+/* Blockquotes */
+blockquote { margin: 0 0 0.75em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; }
+blockquote cite { display: block; font-size: 0.8125em; color: #e15200; }
+blockquote cite:before { content: "\2014 \0020"; }
+blockquote cite a, blockquote cite a:visited { color: #e15200; }
+
+blockquote, blockquote p { line-height: 1.6; color: #333333; }
+
+/* Microformats */
+.vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; }
+.vcard li { margin: 0; display: block; }
+.vcard .fn { font-weight: bold; font-size: 0.9375em; }
+
+.vevent .summary { font-weight: bold; }
+.vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; }
+
+@media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; }
+ h1 { font-size: 2.75em; }
+ h2 { font-size: 2.3125em; }
+ h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; }
+ h4 { font-size: 1.4375em; } }
+/* Tables */
+table { background: white; margin-bottom: 1.25em; border: solid 1px #d8d8ce; }
+table thead, table tfoot { background: -webkit-linear-gradient(top, #add386, #90b66a); font-weight: bold; }
+table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 0.5em 0.625em 0.625em; font-size: inherit; color: white; text-align: left; }
+table tr th, table tr td { padding: 0.5625em 0.625em; font-size: inherit; color: #6d6e71; }
+table tr.even, table tr.alt, table tr:nth-of-type(even) { background: #edf2f2; }
+table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.4; }
+
+body { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; tab-size: 4; }
+
+h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; }
+
+a:hover, a:focus { text-decoration: underline; }
+
+.clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; }
+.clearfix:after, .float-group:after { clear: both; }
+
+*:not(pre) > code { font-size: inherit; font-style: normal !important; letter-spacing: 0; padding: 3px 2px 1px 2px; background-color: #eeeeee; border: 1px solid #dddddd; -webkit-border-radius: 0; border-radius: 0; line-height: inherit; word-wrap: break-word; }
+*:not(pre) > code.nobreak { word-wrap: normal; }
+*:not(pre) > code.nowrap { white-space: nowrap; }
+
+pre, pre > code { line-height: 1.6; color: black; font-family: Consolas, "Liberation Mono", Courier, monospace; font-weight: normal; }
+
+em em { font-style: normal; }
+
+strong strong { font-weight: normal; }
+
+.keyseq { color: #333333; }
+
+kbd { font-family: Consolas, "Liberation Mono", Courier, monospace; display: inline-block; color: black; font-size: 0.65em; line-height: 1.45; background-color: #f7f7f7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; margin: 0 0.15em; padding: 0.2em 0.5em; vertical-align: middle; position: relative; top: -0.1em; white-space: nowrap; }
+
+.keyseq kbd:first-child { margin-left: 0; }
+
+.keyseq kbd:last-child { margin-right: 0; }
+
+.menuseq, .menu { color: black; }
+
+b.button:before, b.button:after { position: relative; top: -1px; font-weight: normal; }
+
+b.button:before { content: "["; padding: 0 3px 0 2px; }
+
+b.button:after { content: "]"; padding: 0 2px 0 3px; }
+
+#header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 1.5em; padding-right: 1.5em; }
+#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; }
+#header:after, #content:after, #footnotes:after, #footer:after { clear: both; }
+
+#content { margin-top: 1.25em; }
+
+#content:before { content: none; }
+
+#header > h1:first-child { color: #7b2d00; margin-top: 2.25rem; margin-bottom: 0; }
+#header > h1:first-child + #toc { margin-top: 8px; border-top: 1px solid #dddddd; }
+#header > h1:only-child, body.toc2 #header > h1:nth-last-child(2) { border-bottom: 1px solid #dddddd; padding-bottom: 8px; }
+#header .details { border-bottom: 1px solid #dddddd; line-height: 1.45; padding-top: 0.25em; padding-bottom: 0.25em; padding-left: 0.25em; color: #e15200; display: -ms-flexbox; display: -webkit-flex; display: flex; -ms-flex-flow: row wrap; -webkit-flex-flow: row wrap; flex-flow: row wrap; }
+#header .details span:first-child { margin-left: -0.125em; }
+#header .details span.email a { color: #333333; }
+#header .details br { display: none; }
+#header .details br + span:before { content: "\00a0\2013\00a0"; }
+#header .details br + span.author:before { content: "\00a0\22c5\00a0"; color: #333333; }
+#header .details br + span#revremark:before { content: "\00a0|\00a0"; }
+#header #revnumber { text-transform: capitalize; }
+#header #revnumber:after { content: "\00a0"; }
+
+#content > h1:first-child:not([class]) { color: #7b2d00; border-bottom: 1px solid #dddddd; padding-bottom: 8px; margin-top: 0; padding-top: 1rem; margin-bottom: 1.25rem; }
+
+#toc { border-bottom: 0 solid #dddddd; padding-bottom: 0.5em; }
+#toc > ul { margin-left: 0.125em; }
+#toc ul.sectlevel0 > li > a { font-style: italic; }
+#toc ul.sectlevel0 ul.sectlevel1 { margin: 0.5em 0; }
+#toc ul { font-family: Arial, sans-serif; list-style-type: none; }
+#toc li { line-height: 1.3334; margin-top: 0.3334em; }
+#toc a { text-decoration: none; }
+#toc a:active { text-decoration: underline; }
+
+#toctitle { color: #003b6b; font-size: 1.2em; }
+
+@media only screen and (min-width: 768px) { #toctitle { font-size: 1.375em; }
+ body.toc2 { padding-left: 15em; padding-right: 0; }
+ #toc.toc2 { margin-top: 0 !important; background-color: white; position: fixed; width: 15em; left: 0; top: 0; border-right: 1px solid #dddddd; border-top-width: 0 !important; border-bottom-width: 0 !important; z-index: 1000; padding: 1.25em 1em; height: 100%; overflow: auto; }
+ #toc.toc2 #toctitle { margin-top: 0; margin-bottom: 0.8rem; font-size: 1.2em; }
+ #toc.toc2 > ul { font-size: 0.9em; margin-bottom: 0; }
+ #toc.toc2 ul ul { margin-left: 0; padding-left: 1em; }
+ #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; }
+ body.toc2.toc-right { padding-left: 0; padding-right: 15em; }
+ body.toc2.toc-right #toc.toc2 { border-right-width: 0; border-left: 1px solid #dddddd; left: auto; right: 0; } }
+@media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; padding-right: 0; }
+ #toc.toc2 { width: 20em; }
+ #toc.toc2 #toctitle { font-size: 1.375em; }
+ #toc.toc2 > ul { font-size: 0.95em; }
+ #toc.toc2 ul ul { padding-left: 1.25em; }
+ body.toc2.toc-right { padding-left: 0; padding-right: 20em; } }
+#content #toc { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: white; -webkit-border-radius: 0; border-radius: 0; }
+#content #toc > :first-child { margin-top: 0; }
+#content #toc > :last-child { margin-bottom: 0; }
+
+#footer { max-width: 100%; background-color: none; padding: 1.25em; }
+
+#footer-text { color: black; line-height: 1.44; }
+
+.sect1 { padding-bottom: 0.625em; }
+
+@media only screen and (min-width: 768px) { .sect1 { padding-bottom: 1.25em; } }
+.sect1 + .sect1 { border-top: 0 solid #dddddd; }
+
+#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; z-index: 1001; width: 1.5ex; margin-left: -1.5ex; display: block; text-decoration: none !important; visibility: hidden; text-align: center; font-weight: normal; }
+#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: "\00A7"; font-size: 0.85em; display: block; padding-top: 0.1em; }
+#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; }
+#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #7b2d00; text-decoration: none; }
+#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #622400; }
+
+.audioblock, .imageblock, .literalblock, .listingblock, .stemblock, .videoblock { margin-bottom: 1.25em; }
+
+.admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-rendering: optimizeLegibility; text-align: left; }
+
+table.tableblock > caption.title { white-space: nowrap; overflow: visible; max-width: 0; }
+
+.paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { color: #7b2d00; }
+
+table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { font-size: inherit; }
+
+.admonitionblock > table { border-collapse: separate; border: 0; background: none; width: 100%; }
+.admonitionblock > table td.icon { text-align: center; width: 80px; }
+.admonitionblock > table td.icon img { max-width: initial; }
+.admonitionblock > table td.icon .title { font-weight: bold; font-family: Arial, sans-serif; text-transform: uppercase; }
+.admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px solid #dddddd; color: #e15200; }
+.admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; }
+
+.exampleblock > .content { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: white; -webkit-border-radius: 0; border-radius: 0; }
+.exampleblock > .content > :first-child { margin-top: 0; }
+.exampleblock > .content > :last-child { margin-bottom: 0; }
+
+.sidebarblock { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: white; -webkit-border-radius: 0; border-radius: 0; }
+.sidebarblock > :first-child { margin-top: 0; }
+.sidebarblock > :last-child { margin-bottom: 0; }
+.sidebarblock > .content > .title { color: #003b6b; margin-top: 0; }
+
+.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; }
+
+.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint { background: #eeeeee; }
+.sidebarblock .literalblock pre, .sidebarblock .listingblock pre:not(.highlight), .sidebarblock .listingblock pre[class="highlight"], .sidebarblock .listingblock pre[class^="highlight "], .sidebarblock .listingblock pre.CodeRay, .sidebarblock .listingblock pre.prettyprint { background: #f2f1f1; }
+
+.literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { border: 1px dashed #666666; -webkit-border-radius: 0; border-radius: 0; word-wrap: break-word; padding: 1.25em 1.5625em 1.125em 1.5625em; font-size: 0.8125em; }
+.literalblock pre.nowrap, .literalblock pre[class].nowrap, .listingblock pre.nowrap, .listingblock pre[class].nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; }
+@media only screen and (min-width: 768px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.90625em; } }
+@media only screen and (min-width: 1280px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 1em; } }
+
+.literalblock.output pre { color: #eeeeee; background-color: black; }
+
+.listingblock pre.highlightjs { padding: 0; }
+.listingblock pre.highlightjs > code { padding: 1.25em 1.5625em 1.125em 1.5625em; -webkit-border-radius: 0; border-radius: 0; }
+
+.listingblock > .content { position: relative; }
+
+.listingblock code[data-lang]:before { display: none; content: attr(data-lang); position: absolute; font-size: 0.75em; top: 0.425rem; right: 0.5rem; line-height: 1; text-transform: uppercase; color: #999; }
+
+.listingblock:hover code[data-lang]:before { display: block; }
+
+.listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; }
+
+.listingblock.terminal pre .command:not([data-prompt]):before { content: "$"; }
+
+table.pyhltable { border-collapse: separate; border: 0; margin-bottom: 0; background: none; }
+
+table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; line-height: 1.6; }
+
+table.pyhltable td.code { padding-left: .75em; padding-right: 0; }
+
+pre.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid #dddddd; }
+
+pre.pygments .lineno { display: inline-block; margin-right: .25em; }
+
+table.pyhltable .linenodiv { background: none !important; padding-right: 0 !important; }
+
+.quoteblock { margin: 0 1em 0.75em 1.5em; display: table; }
+.quoteblock > .title { margin-left: -1.5em; margin-bottom: 0.75em; }
+.quoteblock blockquote, .quoteblock blockquote p { color: #333333; font-size: 1.15rem; line-height: 1.75; word-spacing: 0.1em; letter-spacing: 0; font-style: italic; text-align: justify; }
+.quoteblock blockquote { margin: 0; padding: 0; border: 0; }
+.quoteblock blockquote:before { content: "\201c"; float: left; font-size: 2.75em; font-weight: bold; line-height: 0.6em; margin-left: -0.6em; color: #003b6b; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); }
+.quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; }
+.quoteblock .attribution { margin-top: 0.5em; margin-right: 0.5ex; text-align: right; }
+.quoteblock .quoteblock { margin-left: 0; margin-right: 0; padding: 0.5em 0; border-left: 3px solid #e15200; }
+.quoteblock .quoteblock blockquote { padding: 0 0 0 0.75em; }
+.quoteblock .quoteblock blockquote:before { display: none; }
+
+.verseblock { margin: 0 1em 0.75em 1em; }
+.verseblock pre { font-family: "Open Sans", "DejaVu Sans", sans; font-size: 1.15rem; color: #333333; font-weight: 300; text-rendering: optimizeLegibility; }
+.verseblock pre strong { font-weight: 400; }
+.verseblock .attribution { margin-top: 1.25rem; margin-left: 0.5ex; }
+
+.quoteblock .attribution, .verseblock .attribution { font-size: 0.8125em; line-height: 1.45; font-style: italic; }
+.quoteblock .attribution br, .verseblock .attribution br { display: none; }
+.quoteblock .attribution cite, .verseblock .attribution cite { display: block; letter-spacing: -0.025em; color: #e15200; }
+
+.quoteblock.abstract { margin: 0 0 0.75em 0; display: block; }
+.quoteblock.abstract blockquote, .quoteblock.abstract blockquote p { text-align: left; word-spacing: 0; }
+.quoteblock.abstract blockquote:before, .quoteblock.abstract blockquote p:first-of-type:before { display: none; }
+
+table.tableblock { max-width: 100%; border-collapse: separate; }
+table.tableblock td > .paragraph:last-child p > p:last-child, table.tableblock th > p:last-child, table.tableblock td > p:last-child { margin-bottom: 0; }
+
+table.tableblock, th.tableblock, td.tableblock { border: 0 solid #d8d8ce; }
+
+table.grid-all th.tableblock, table.grid-all td.tableblock { border-width: 0 1px 1px 0; }
+
+table.grid-all tfoot > tr > th.tableblock, table.grid-all tfoot > tr > td.tableblock { border-width: 1px 1px 0 0; }
+
+table.grid-cols th.tableblock, table.grid-cols td.tableblock { border-width: 0 1px 0 0; }
+
+table.grid-all * > tr > .tableblock:last-child, table.grid-cols * > tr > .tableblock:last-child { border-right-width: 0; }
+
+table.grid-rows th.tableblock, table.grid-rows td.tableblock { border-width: 0 0 1px 0; }
+
+table.grid-all tbody > tr:last-child > th.tableblock, table.grid-all tbody > tr:last-child > td.tableblock, table.grid-all thead:last-child > tr > th.tableblock, table.grid-rows tbody > tr:last-child > th.tableblock, table.grid-rows tbody > tr:last-child > td.tableblock, table.grid-rows thead:last-child > tr > th.tableblock { border-bottom-width: 0; }
+
+table.grid-rows tfoot > tr > th.tableblock, table.grid-rows tfoot > tr > td.tableblock { border-width: 1px 0 0 0; }
+
+table.frame-all { border-width: 1px; }
+
+table.frame-sides { border-width: 0 1px; }
+
+table.frame-topbot { border-width: 1px 0; }
+
+th.halign-left, td.halign-left { text-align: left; }
+
+th.halign-right, td.halign-right { text-align: right; }
+
+th.halign-center, td.halign-center { text-align: center; }
+
+th.valign-top, td.valign-top { vertical-align: top; }
+
+th.valign-bottom, td.valign-bottom { vertical-align: bottom; }
+
+th.valign-middle, td.valign-middle { vertical-align: middle; }
+
+table thead th, table tfoot th { font-weight: bold; }
+
+tbody tr th { display: table-cell; line-height: 1.4; background: -webkit-linear-gradient(top, #add386, #90b66a); }
+
+tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p { color: white; font-weight: bold; }
+
+p.tableblock > code:only-child { background: none; padding: 0; }
+
+p.tableblock { font-size: 1em; }
+
+td > div.verse { white-space: pre; }
+
+ol { margin-left: 1.75em; }
+
+ul li ol { margin-left: 1.5em; }
+
+dl dd { margin-left: 1.125em; }
+
+dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; }
+
+ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.375em; }
+
+ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; }
+
+ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; }
+
+ul.checklist li > p:first-child > .fa-square-o:first-child, ul.checklist li > p:first-child > .fa-check-square-o:first-child { width: 1em; font-size: 0.85em; }
+
+ul.checklist li > p:first-child > input[type="checkbox"]:first-child { width: 1em; position: relative; top: 1px; }
+
+ul.inline { margin: 0 auto 0.375em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; }
+ul.inline > li { list-style: none; float: left; margin-left: 1.375em; display: block; }
+ul.inline > li > * { display: block; }
+
+.unstyled dl dt { font-weight: normal; font-style: normal; }
+
+ol.arabic { list-style-type: decimal; }
+
+ol.decimal { list-style-type: decimal-leading-zero; }
+
+ol.loweralpha { list-style-type: lower-alpha; }
+
+ol.upperalpha { list-style-type: upper-alpha; }
+
+ol.lowerroman { list-style-type: lower-roman; }
+
+ol.upperroman { list-style-type: upper-roman; }
+
+ol.lowergreek { list-style-type: lower-greek; }
+
+.hdlist > table, .colist > table { border: 0; background: none; }
+.hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; }
+
+td.hdlist1, td.hdlist2 { vertical-align: top; padding: 0 0.625em; }
+
+td.hdlist1 { font-weight: bold; padding-bottom: 0.75em; }
+
+.literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; }
+
+.colist > table tr > td:first-of-type { padding: 0 0.75em; line-height: 1; }
+.colist > table tr > td:first-of-type img { max-width: initial; }
+.colist > table tr > td:last-of-type { padding: 0.25em 0; }
+
+.thumb, .th { line-height: 0; display: inline-block; border: solid 4px white; -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; }
+
+.imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; }
+.imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; }
+.imageblock > .title { margin-bottom: 0; }
+.imageblock.thumb, .imageblock.th { border-width: 6px; }
+.imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; }
+
+.image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; }
+.image.left { margin-right: 0.625em; }
+.image.right { margin-left: 0.625em; }
+
+a.image { text-decoration: none; display: inline-block; }
+a.image object { pointer-events: none; }
+
+sup.footnote, sup.footnoteref { font-size: 0.875em; position: static; vertical-align: super; }
+sup.footnote a, sup.footnoteref a { text-decoration: none; }
+sup.footnote a:active, sup.footnoteref a:active { text-decoration: underline; }
+
+#footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; }
+#footnotes hr { width: 20%; min-width: 6.25em; margin: -0.25em 0 0.75em 0; border-width: 1px 0 0 0; }
+#footnotes .footnote { padding: 0 0.375em 0 0.225em; line-height: 1.3334; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.05em; margin-bottom: 0.2em; }
+#footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; }
+#footnotes .footnote:last-of-type { margin-bottom: 0; }
+#content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; }
+
+.gist .file-data > table { border: 0; background: #fff; width: 100%; margin-bottom: 0; }
+.gist .file-data > table td.line-data { width: 99%; }
+
+div.unbreakable { page-break-inside: avoid; }
+
+.big { font-size: larger; }
+
+.small { font-size: smaller; }
+
+.underline { text-decoration: underline; }
+
+.overline { text-decoration: overline; }
+
+.line-through { text-decoration: line-through; }
+
+.aqua { color: #00bfbf; }
+
+.aqua-background { background-color: #00fafa; }
+
+.black { color: black; }
+
+.black-background { background-color: black; }
+
+.blue { color: #0000bf; }
+
+.blue-background { background-color: #0000fa; }
+
+.fuchsia { color: #bf00bf; }
+
+.fuchsia-background { background-color: #fa00fa; }
+
+.gray { color: #606060; }
+
+.gray-background { background-color: #7d7d7d; }
+
+.green { color: #006000; }
+
+.green-background { background-color: #007d00; }
+
+.lime { color: #00bf00; }
+
+.lime-background { background-color: #00fa00; }
+
+.maroon { color: #600000; }
+
+.maroon-background { background-color: #7d0000; }
+
+.navy { color: #000060; }
+
+.navy-background { background-color: #00007d; }
+
+.olive { color: #606000; }
+
+.olive-background { background-color: #7d7d00; }
+
+.purple { color: #600060; }
+
+.purple-background { background-color: #7d007d; }
+
+.red { color: #bf0000; }
+
+.red-background { background-color: #fa0000; }
+
+.silver { color: #909090; }
+
+.silver-background { background-color: #bcbcbc; }
+
+.teal { color: #006060; }
+
+.teal-background { background-color: #007d7d; }
+
+.white { color: #bfbfbf; }
+
+.white-background { background-color: #fafafa; }
+
+.yellow { color: #bfbf00; }
+
+.yellow-background { background-color: #fafa00; }
+
+span.icon > .fa { cursor: default; }
+
+.admonitionblock td.icon [class^="fa icon-"] { font-size: 2.5em; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; }
+.admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #004176; }
+.admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; }
+.admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; }
+.admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; }
+.admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; }
+
+.conum[data-value] { display: inline-block; color: #fff !important; background-color: black; -webkit-border-radius: 100px; border-radius: 100px; text-align: center; font-size: 0.75em; width: 1.67em; height: 1.67em; line-height: 1.67em; font-family: "Open Sans", "DejaVu Sans", sans-serif; font-style: normal; font-weight: bold; }
+.conum[data-value] * { color: #fff !important; }
+.conum[data-value] + b { display: none; }
+.conum[data-value]:after { content: attr(data-value); }
+pre .conum[data-value] { position: relative; top: -0.125em; }
+
+b.conum * { color: inherit !important; }
+
+.conum:not([data-value]):empty { display: none; }
+
+h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { border-bottom: 1px solid #dddddd; }
+
+.sect1 { padding-bottom: 0; }
+
+#toctitle { color: #00406F; font-weight: normal; margin-top: 1.5em; }
+
+.sidebarblock { border-color: #aaa; }
+
+code { -webkit-border-radius: 4px; border-radius: 4px; }
+
+p.tableblock.header { color: #6d6e71; }
+
+.literalblock pre, .listingblock pre { background: #eeeeee; }
diff --git a/doc/generate-doc.sh b/doc/generate-doc.sh
new file mode 100755
index 0000000..bf2bf55
--- /dev/null
+++ b/doc/generate-doc.sh
@@ -0,0 +1 @@
+asciidoctor index.adoc -o index.html -a stylesheet=golo.css
diff --git a/doc/golo.css b/doc/golo.css
new file mode 100644
index 0000000..1f109d7
--- /dev/null
+++ b/doc/golo.css
@@ -0,0 +1,702 @@
+@import url(https://fonts.googleapis.com/css?family=Varela+Round|Open+Sans:400italic,600italic,400,600|Ubuntu+Mono:400);
+/*! normalize.css v2.1.2 | MIT License | git.io/normalize */
+/* ========================================================================== HTML5 display definitions ========================================================================== */
+/** Correct `block` display not defined in IE 8/9. */
+article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; }
+
+/** Correct `inline-block` display not defined in IE 8/9. */
+audio, canvas, video { display: inline-block; }
+
+/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */
+audio:not([controls]) { display: none; height: 0; }
+
+/** Address `[hidden]` styling not present in IE 8/9. Hide the `template` element in IE, Safari, and Firefox < 22. */
+[hidden], template { display: none; }
+
+script { display: none !important; }
+
+/* ========================================================================== Base ========================================================================== */
+/** 1. Set default font family to sans-serif. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */
+html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ }
+
+/** Remove default margin. */
+body { margin: 0; }
+
+/* ========================================================================== Links ========================================================================== */
+/** Remove the gray background color from active links in IE 10. */
+a { background: transparent; }
+
+/** Address `outline` inconsistency between Chrome and other browsers. */
+a:focus { outline: thin dotted; }
+
+/** Improve readability when focused and also mouse hovered in all browsers. */
+a:active, a:hover { outline: 0; }
+
+/* ========================================================================== Typography ========================================================================== */
+/** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */
+h1 { font-size: 2em; margin: 0.67em 0; }
+
+/** Address styling not present in IE 8/9, Safari 5, and Chrome. */
+abbr[title] { border-bottom: 1px dotted; }
+
+/** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */
+b, strong { font-weight: bold; }
+
+/** Address styling not present in Safari 5 and Chrome. */
+dfn { font-style: italic; }
+
+/** Address differences between Firefox and other browsers. */
+hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; }
+
+/** Address styling not present in IE 8/9. */
+mark { background: #ff0; color: #000; }
+
+/** Correct font family set oddly in Safari 5 and Chrome. */
+code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; }
+
+/** Improve readability of pre-formatted text in all browsers. */
+pre { white-space: pre-wrap; }
+
+/** Set consistent quote types. */
+q { quotes: "\201C" "\201D" "\2018" "\2019"; }
+
+/** Address inconsistent and variable font size in all browsers. */
+small { font-size: 80%; }
+
+/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */
+sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
+
+sup { top: -0.5em; }
+
+sub { bottom: -0.25em; }
+
+/* ========================================================================== Embedded content ========================================================================== */
+/** Remove border when inside `a` element in IE 8/9. */
+img { border: 0; }
+
+/** Correct overflow displayed oddly in IE 9. */
+svg:not(:root) { overflow: hidden; }
+
+/* ========================================================================== Figures ========================================================================== */
+/** Address margin not present in IE 8/9 and Safari 5. */
+figure { margin: 0; }
+
+/* ========================================================================== Forms ========================================================================== */
+/** Define consistent border, margin, and padding. */
+fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
+
+/** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */
+legend { border: 0; /* 1 */ padding: 0; /* 2 */ }
+
+/** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */
+button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ }
+
+/** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */
+button, input { line-height: normal; }
+
+/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */
+button, select { text-transform: none; }
+
+/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */
+button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ }
+
+/** Re-set default cursor for disabled elements. */
+button[disabled], html input[disabled] { cursor: default; }
+
+/** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */
+input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ }
+
+/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */
+input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; }
+
+/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */
+input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
+
+/** Remove inner padding and border in Firefox 4+. */
+button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
+
+/** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */
+textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ }
+
+/* ========================================================================== Tables ========================================================================== */
+/** Remove most spacing between table cells. */
+table { border-collapse: collapse; border-spacing: 0; }
+
+meta.foundation-mq-small { font-family: "only screen and (min-width: 768px)"; width: 768px; }
+
+meta.foundation-mq-medium { font-family: "only screen and (min-width:1280px)"; width: 1280px; }
+
+meta.foundation-mq-large { font-family: "only screen and (min-width:1440px)"; width: 1440px; }
+
+*, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
+
+html, body { font-size: 100%; }
+
+body { background: #fefdfd; color: rgba(0, 0, 0, 0.8); padding: 0; margin: 0; font-family: "Open Sans", sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; }
+
+a:hover { cursor: pointer; }
+
+img, object, embed { max-width: 100%; height: auto; }
+
+object, embed { height: 100%; }
+
+img { -ms-interpolation-mode: bicubic; }
+
+#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; }
+
+.left { float: left !important; }
+
+.right { float: right !important; }
+
+.text-left { text-align: left !important; }
+
+.text-right { text-align: right !important; }
+
+.text-center { text-align: center !important; }
+
+.text-justify { text-align: justify !important; }
+
+.hide { display: none; }
+
+.antialiased { -webkit-font-smoothing: antialiased; }
+
+img { display: inline-block; vertical-align: middle; }
+
+textarea { height: auto; min-height: 50px; }
+
+select { width: 100%; }
+
+object, svg { display: inline-block; vertical-align: middle; }
+
+.center { margin-left: auto; margin-right: auto; }
+
+.spread { width: 100%; }
+
+p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; }
+
+.subheader, .admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { line-height: 1.25; color: #002c5e; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; }
+
+/* Typography resets */
+div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; }
+
+/* Default Link Styles */
+a { color: #005580; text-decoration: underline; line-height: inherit; }
+a:hover, a:focus { color: #078d71; }
+a img { border: none; }
+
+/* Default paragraph styles */
+p { font-family: inherit; font-weight: normal; font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; text-rendering: optimizeLegibility; }
+p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; }
+
+/* Default header styles */
+h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { font-family: "Varela Round", sans-serif; font-weight: 400; font-style: normal; color: #00326b; text-rendering: optimizeLegibility; margin-top: 0.8em; margin-bottom: 0.5em; line-height: 1.0625em; }
+h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { font-size: 60%; color: #057aff; line-height: 0; }
+
+h1 { font-size: 2.125em; }
+
+h2 { font-size: 1.6875em; }
+
+h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.375em; }
+
+h4 { font-size: 1.125em; }
+
+h5 { font-size: 1.125em; }
+
+h6 { font-size: 1em; }
+
+hr { border: solid rgba(145, 135, 84, 0.15); border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; }
+
+/* Helpful Typography Defaults */
+em, i { font-style: italic; line-height: inherit; }
+
+strong, b { font-weight: bold; line-height: inherit; }
+
+small { font-size: 60%; line-height: inherit; }
+
+code { font-family: "Ubuntu Mono", "Inconsolata", monospace; font-weight: 400; color: #331d00; }
+
+/* Lists */
+ul, ol, dl { font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; list-style-position: outside; font-family: inherit; }
+
+ul, ol { margin-left: 1.5em; }
+ul.no-bullet, ol.no-bullet { margin-left: 1.5em; }
+
+/* Unordered Lists */
+ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ }
+ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; }
+ul.square { list-style-type: square; }
+ul.circle { list-style-type: circle; }
+ul.disc { list-style-type: disc; }
+ul.no-bullet { list-style: none; }
+
+/* Ordered Lists */
+ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; }
+
+/* Definition Lists */
+dl dt { margin-bottom: 0.3125em; font-weight: bold; }
+dl dd { margin-bottom: 1.25em; }
+
+/* Abbreviations */
+abbr, acronym { text-transform: uppercase; font-size: 90%; color: rgba(0, 0, 0, 0.8); border-bottom: 1px dotted #dddddd; cursor: help; }
+
+abbr { text-transform: none; }
+
+/* Blockquotes */
+blockquote { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; }
+blockquote cite { display: block; font-size: 0.8125em; color: #666666; }
+blockquote cite:before { content: "\2014 \0020"; }
+blockquote cite a, blockquote cite a:visited { color: #666666; }
+
+blockquote, blockquote p { line-height: 1.5; color: #999999; }
+
+/* Microformats */
+.vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; }
+.vcard li { margin: 0; display: block; }
+.vcard .fn { font-weight: bold; font-size: 0.9375em; }
+
+.vevent .summary { font-weight: bold; }
+.vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; }
+
+@media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.25; }
+ h1 { font-size: 2.75em; }
+ h2 { font-size: 2.3125em; }
+ h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; }
+ h4 { font-size: 1.4375em; } }
+/* Tables */
+table { background: white; margin-bottom: 1.25em; border: solid 1px rgba(145, 135, 84, 0.15); }
+table thead, table tfoot { background: rgba(119, 84, 22, 0.1); font-weight: bold; }
+table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 0.5em 0.625em 0.625em; font-size: inherit; color: rgba(0, 0, 0, 0.8); text-align: left; }
+table tr th, table tr td { padding: 0.5625em 0.625em; font-size: inherit; color: rgba(0, 0, 0, 0.8); }
+table tr.even, table tr.alt, table tr:nth-of-type(even) { background: rgba(119, 84, 22, 0.025); }
+table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.5; }
+
+body { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; tab-size: 4; }
+
+h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.25; }
+
+.clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; }
+.clearfix:after, .float-group:after { clear: both; }
+
+*:not(pre) > code { font-size: 1.0625em; font-style: normal !important; letter-spacing: 0; padding: 0; line-height: 1.25; word-wrap: break-word; }
+*:not(pre) > code.nobreak { word-wrap: normal; }
+*:not(pre) > code.nowrap { white-space: nowrap; }
+
+pre, pre > code { line-height: 1.4; color: inherit; font-family: "Liberation Mono", "Consolas", monospace; font-weight: normal; }
+
+em em { font-style: normal; }
+
+strong strong { font-weight: normal; }
+
+.keyseq { color: rgba(51, 51, 51, 0.8); }
+
+kbd { font-family: "Ubuntu Mono", "Inconsolata", monospace; display: inline-block; color: rgba(0, 0, 0, 0.8); font-size: 0.65em; line-height: 1.45; background-color: #f7f7f7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; margin: 0 0.15em; padding: 0.2em 0.5em; vertical-align: middle; position: relative; top: -0.1em; white-space: nowrap; }
+
+.keyseq kbd:first-child { margin-left: 0; }
+
+.keyseq kbd:last-child { margin-right: 0; }
+
+.menuseq, .menu { color: rgba(0, 0, 0, 0.8); }
+
+b.button:before, b.button:after { position: relative; top: -1px; font-weight: normal; }
+
+b.button:before { content: "["; padding: 0 3px 0 2px; }
+
+b.button:after { content: "]"; padding: 0 2px 0 3px; }
+
+p a > code:hover { color: #1a0f00; }
+
+#header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; }
+#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; }
+#header:after, #content:after, #footnotes:after, #footer:after { clear: both; }
+
+#content { margin-top: 1.25em; }
+
+#content:before { content: none; }
+
+#header > h1:first-child { color: #703f1c; margin-top: 2.25rem; margin-bottom: 0; }
+#header > h1:first-child + #toc { margin-top: 8px; border-top: 1px solid rgba(145, 135, 84, 0.15); }
+#header > h1:only-child, body.toc2 #header > h1:nth-last-child(2) { border-bottom: 1px solid rgba(145, 135, 84, 0.15); padding-bottom: 8px; }
+#header .details { border-bottom: 1px solid rgba(145, 135, 84, 0.15); line-height: 1.45; padding-top: 0.25em; padding-bottom: 0.25em; padding-left: 0.25em; color: #666666; display: -ms-flexbox; display: -webkit-flex; display: flex; -ms-flex-flow: row wrap; -webkit-flex-flow: row wrap; flex-flow: row wrap; }
+#header .details span:first-child { margin-left: -0.125em; }
+#header .details span.email a { color: #999999; }
+#header .details br { display: none; }
+#header .details br + span:before { content: "\00a0\2013\00a0"; }
+#header .details br + span.author:before { content: "\00a0\22c5\00a0"; color: #999999; }
+#header .details br + span#revremark:before { content: "\00a0|\00a0"; }
+#header #revnumber { text-transform: capitalize; }
+#header #revnumber:after { content: "\00a0"; }
+
+#content > h1:first-child:not([class]) { color: #703f1c; border-bottom: 1px solid rgba(145, 135, 84, 0.15); padding-bottom: 8px; margin-top: 0; padding-top: 1rem; margin-bottom: 1.25rem; }
+
+#toc { border-bottom: 0px solid #dddddd; padding-bottom: 0.5em; }
+#toc > ul { margin-left: 0.125em; }
+#toc ul.sectlevel0 > li > a { font-style: italic; }
+#toc ul.sectlevel0 ul.sectlevel1 { margin: 0.5em 0; }
+#toc ul { font-family: "Varela Round", sans-serif; list-style-type: none; }
+#toc li { line-height: 1.3334; margin-top: 0.3334em; }
+#toc a { text-decoration: none; }
+#toc a:active { text-decoration: underline; }
+
+#toctitle { color: #002c5e; font-size: 1.2em; }
+
+@media only screen and (min-width: 768px) { #toctitle { font-size: 1.375em; }
+ body.toc2 { padding-left: 15em; padding-right: 0; }
+ #toc.toc2 { margin-top: 0 !important; background-color: #f2f2f4; position: fixed; width: 15em; left: 0; top: 0; border-right: 1px solid #dddddd; border-top-width: 0 !important; border-bottom-width: 0 !important; z-index: 1000; padding: 1.25em 1em; height: 100%; overflow: auto; }
+ #toc.toc2 #toctitle { margin-top: 0; margin-bottom: 0.8rem; font-size: 1.2em; }
+ #toc.toc2 > ul { font-size: 0.9em; margin-bottom: 0; }
+ #toc.toc2 ul ul { margin-left: 0; padding-left: 1em; }
+ #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; }
+ body.toc2.toc-right { padding-left: 0; padding-right: 15em; }
+ body.toc2.toc-right #toc.toc2 { border-right-width: 0; border-left: 1px solid #dddddd; left: auto; right: 0; } }
+@media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; padding-right: 0; }
+ #toc.toc2 { width: 20em; }
+ #toc.toc2 #toctitle { font-size: 1.375em; }
+ #toc.toc2 > ul { font-size: 0.95em; }
+ #toc.toc2 ul ul { padding-left: 1.25em; }
+ body.toc2.toc-right { padding-left: 0; padding-right: 20em; } }
+#content #toc { border-style: solid; border-width: 1px; border-color: #d6d6dd; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f4; -webkit-border-radius: 6px; border-radius: 6px; }
+#content #toc > :first-child { margin-top: 0; }
+#content #toc > :last-child { margin-bottom: 0; }
+
+#footer { max-width: 100%; background-color: #0b445a; padding: 1.25em; }
+
+#footer-text { color: #fefdfd; line-height: 1.35; }
+
+.sect1 { padding-bottom: 0.625em; }
+
+@media only screen and (min-width: 768px) { .sect1 { padding-bottom: 1.25em; } }
+.sect1 + .sect1 { border-top: 0px solid #dddddd; }
+
+#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; z-index: 1001; width: 1.5ex; margin-left: -1.5ex; display: block; text-decoration: none !important; visibility: hidden; text-align: center; font-weight: normal; }
+#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: "\00A7"; font-size: 0.85em; display: block; padding-top: 0.1em; }
+#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; }
+#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #00326b; text-decoration: none; }
+#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #002652; }
+
+.audioblock, .imageblock, .literalblock, .listingblock, .stemblock, .videoblock { margin-bottom: 1.25em; }
+
+.admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-rendering: optimizeLegibility; text-align: left; }
+
+table.tableblock > caption.title { white-space: nowrap; overflow: visible; max-width: 0; }
+
+.paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { color: #703f1c; }
+
+table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { font-size: inherit; }
+
+.admonitionblock > table { border-collapse: separate; border: 0; background: none; width: 100%; }
+.admonitionblock > table td.icon { text-align: center; width: 80px; }
+.admonitionblock > table td.icon img { max-width: initial; }
+.admonitionblock > table td.icon .title { font-weight: bold; font-family: "Varela Round", sans-serif; text-transform: uppercase; }
+.admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px solid rgba(145, 135, 84, 0.15); color: #666666; }
+.admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; }
+
+.exampleblock > .content { border-style: solid; border-width: 1px; border-color: #eddbdb; margin-bottom: 1.25em; padding: 1.25em; background: #fefdfd; -webkit-border-radius: 6px; border-radius: 6px; }
+.exampleblock > .content > :first-child { margin-top: 0; }
+.exampleblock > .content > :last-child { margin-bottom: 0; }
+
+.sidebarblock { border-style: solid; border-width: 1px; border-color: #d6d6dd; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f4; -webkit-border-radius: 6px; border-radius: 6px; }
+.sidebarblock > :first-child { margin-top: 0; }
+.sidebarblock > :last-child { margin-bottom: 0; }
+.sidebarblock > .content > .title { color: #002c5e; margin-top: 0; }
+
+.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; }
+
+.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint { background: rgba(16, 195, 196, 0.05); }
+.sidebarblock .literalblock pre, .sidebarblock .listingblock pre:not(.highlight), .sidebarblock .listingblock pre[class="highlight"], .sidebarblock .listingblock pre[class^="highlight "], .sidebarblock .listingblock pre.CodeRay, .sidebarblock .listingblock pre.prettyprint { background: #f2f1f1; }
+
+.literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { border: 1px solid rgba(16, 195, 196, 0.125); -webkit-border-radius: 6px; border-radius: 6px; word-wrap: break-word; padding: 1em; font-size: 0.8125em; }
+.literalblock pre.nowrap, .literalblock pre[class].nowrap, .listingblock pre.nowrap, .listingblock pre[class].nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; }
+@media only screen and (min-width: 768px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.90625em; } }
+@media only screen and (min-width: 1280px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 1em; } }
+
+.literalblock.output pre { color: rgba(16, 195, 196, 0.05); background-color: inherit; }
+
+.listingblock pre.highlightjs { padding: 0; }
+.listingblock pre.highlightjs > code { padding: 1em; -webkit-border-radius: 6px; border-radius: 6px; }
+
+.listingblock > .content { position: relative; }
+
+.listingblock code[data-lang]:before { display: none; content: attr(data-lang); position: absolute; font-size: 0.75em; top: 0.425rem; right: 0.5rem; line-height: 1; text-transform: uppercase; color: #999; }
+
+.listingblock:hover code[data-lang]:before { display: block; }
+
+.listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; }
+
+.listingblock.terminal pre .command:not([data-prompt]):before { content: "$"; }
+
+table.pyhltable { border-collapse: separate; border: 0; margin-bottom: 0; background: none; }
+
+table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; line-height: 1.4; }
+
+table.pyhltable td.code { padding-left: .75em; padding-right: 0; }
+
+pre.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid rgba(145, 135, 84, 0.15); }
+
+pre.pygments .lineno { display: inline-block; margin-right: .25em; }
+
+table.pyhltable .linenodiv { background: none !important; padding-right: 0 !important; }
+
+.quoteblock { margin: 0 1em 1.25em 1.5em; display: table; }
+.quoteblock > .title { margin-left: -1.5em; margin-bottom: 0.75em; }
+.quoteblock blockquote, .quoteblock blockquote p { color: #999999; font-size: 1.15rem; line-height: 1.75; word-spacing: 0.1em; letter-spacing: 0; font-style: italic; text-align: justify; }
+.quoteblock blockquote { margin: 0; padding: 0; border: 0; }
+.quoteblock blockquote:before { content: "\201c"; float: left; font-size: 2.75em; font-weight: bold; line-height: 0.6em; margin-left: -0.6em; color: #002c5e; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); }
+.quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; }
+.quoteblock .attribution { margin-top: 0.5em; margin-right: 0.5ex; text-align: right; }
+.quoteblock .quoteblock { margin-left: 0; margin-right: 0; padding: 0.5em 0; border-left: 3px solid #666666; }
+.quoteblock .quoteblock blockquote { padding: 0 0 0 0.75em; }
+.quoteblock .quoteblock blockquote:before { display: none; }
+
+.verseblock { margin: 0 1em 1.25em 1em; }
+.verseblock pre { font-family: "Open Sans", "DejaVu Sans", sans; font-size: 1.15rem; color: #999999; font-weight: 300; text-rendering: optimizeLegibility; }
+.verseblock pre strong { font-weight: 400; }
+.verseblock .attribution { margin-top: 1.25rem; margin-left: 0.5ex; }
+
+.quoteblock .attribution, .verseblock .attribution { font-size: 0.8125em; line-height: 1.45; font-style: italic; }
+.quoteblock .attribution br, .verseblock .attribution br { display: none; }
+.quoteblock .attribution cite, .verseblock .attribution cite { display: block; letter-spacing: -0.025em; color: #666666; }
+
+.quoteblock.abstract { margin: 0 0 1.25em 0; display: block; }
+.quoteblock.abstract blockquote, .quoteblock.abstract blockquote p { text-align: left; word-spacing: 0; }
+.quoteblock.abstract blockquote:before, .quoteblock.abstract blockquote p:first-of-type:before { display: none; }
+
+table.tableblock { max-width: 100%; border-collapse: separate; }
+table.tableblock td > .paragraph:last-child p > p:last-child, table.tableblock th > p:last-child, table.tableblock td > p:last-child { margin-bottom: 0; }
+
+table.tableblock, th.tableblock, td.tableblock { border: 0 solid rgba(145, 135, 84, 0.15); }
+
+table.grid-all th.tableblock, table.grid-all td.tableblock { border-width: 0 1px 1px 0; }
+
+table.grid-all tfoot > tr > th.tableblock, table.grid-all tfoot > tr > td.tableblock { border-width: 1px 1px 0 0; }
+
+table.grid-cols th.tableblock, table.grid-cols td.tableblock { border-width: 0 1px 0 0; }
+
+table.grid-all * > tr > .tableblock:last-child, table.grid-cols * > tr > .tableblock:last-child { border-right-width: 0; }
+
+table.grid-rows th.tableblock, table.grid-rows td.tableblock { border-width: 0 0 1px 0; }
+
+table.grid-all tbody > tr:last-child > th.tableblock, table.grid-all tbody > tr:last-child > td.tableblock, table.grid-all thead:last-child > tr > th.tableblock, table.grid-rows tbody > tr:last-child > th.tableblock, table.grid-rows tbody > tr:last-child > td.tableblock, table.grid-rows thead:last-child > tr > th.tableblock { border-bottom-width: 0; }
+
+table.grid-rows tfoot > tr > th.tableblock, table.grid-rows tfoot > tr > td.tableblock { border-width: 1px 0 0 0; }
+
+table.frame-all { border-width: 1px; }
+
+table.frame-sides { border-width: 0 1px; }
+
+table.frame-topbot { border-width: 1px 0; }
+
+th.halign-left, td.halign-left { text-align: left; }
+
+th.halign-right, td.halign-right { text-align: right; }
+
+th.halign-center, td.halign-center { text-align: center; }
+
+th.valign-top, td.valign-top { vertical-align: top; }
+
+th.valign-bottom, td.valign-bottom { vertical-align: bottom; }
+
+th.valign-middle, td.valign-middle { vertical-align: middle; }
+
+table thead th, table tfoot th { font-weight: bold; }
+
+tbody tr th { display: table-cell; line-height: 1.5; background: rgba(119, 84, 22, 0.1); }
+
+tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p { color: rgba(0, 0, 0, 0.8); font-weight: bold; }
+
+p.tableblock > code:only-child { background: none; padding: 0; }
+
+p.tableblock { font-size: 1em; }
+
+td > div.verse { white-space: pre; }
+
+ol { margin-left: 1.75em; }
+
+ul li ol { margin-left: 1.5em; }
+
+dl dd { margin-left: 1.125em; }
+
+dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; }
+
+ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.625em; }
+
+ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; }
+
+ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; }
+
+ul.checklist li > p:first-child > .fa-square-o:first-child, ul.checklist li > p:first-child > .fa-check-square-o:first-child { width: 1em; font-size: 0.85em; }
+
+ul.checklist li > p:first-child > input[type="checkbox"]:first-child { width: 1em; position: relative; top: 1px; }
+
+ul.inline { margin: 0 auto 0.625em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; }
+ul.inline > li { list-style: none; float: left; margin-left: 1.375em; display: block; }
+ul.inline > li > * { display: block; }
+
+.unstyled dl dt { font-weight: normal; font-style: normal; }
+
+ol.arabic { list-style-type: decimal; }
+
+ol.decimal { list-style-type: decimal-leading-zero; }
+
+ol.loweralpha { list-style-type: lower-alpha; }
+
+ol.upperalpha { list-style-type: upper-alpha; }
+
+ol.lowerroman { list-style-type: lower-roman; }
+
+ol.upperroman { list-style-type: upper-roman; }
+
+ol.lowergreek { list-style-type: lower-greek; }
+
+.hdlist > table, .colist > table { border: 0; background: none; }
+.hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; }
+
+td.hdlist1, td.hdlist2 { vertical-align: top; padding: 0 0.625em; }
+
+td.hdlist1 { font-weight: bold; padding-bottom: 1.25em; }
+
+.literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; }
+
+.colist > table tr > td:first-of-type { padding: 0 0.75em; line-height: 1; }
+.colist > table tr > td:first-of-type img { max-width: initial; }
+.colist > table tr > td:last-of-type { padding: 0.25em 0; }
+
+.thumb, .th { line-height: 0; display: inline-block; border: solid 4px white; -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; }
+
+.imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; }
+.imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; }
+.imageblock > .title { margin-bottom: 0; }
+.imageblock.thumb, .imageblock.th { border-width: 6px; }
+.imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; }
+
+.image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; }
+.image.left { margin-right: 0.625em; }
+.image.right { margin-left: 0.625em; }
+
+a.image { text-decoration: none; display: inline-block; }
+a.image object { pointer-events: none; }
+
+sup.footnote, sup.footnoteref { font-size: 0.875em; position: static; vertical-align: super; }
+sup.footnote a, sup.footnoteref a { text-decoration: none; }
+sup.footnote a:active, sup.footnoteref a:active { text-decoration: underline; }
+
+#footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; }
+#footnotes hr { width: 20%; min-width: 6.25em; margin: -0.25em 0 0.75em 0; border-width: 1px 0 0 0; }
+#footnotes .footnote { padding: 0 0.375em 0 0.225em; line-height: 1.3334; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.05em; margin-bottom: 0.2em; }
+#footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; }
+#footnotes .footnote:last-of-type { margin-bottom: 0; }
+#content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; }
+
+.gist .file-data > table { border: 0; background: #fff; width: 100%; margin-bottom: 0; }
+.gist .file-data > table td.line-data { width: 99%; }
+
+div.unbreakable { page-break-inside: avoid; }
+
+.big { font-size: larger; }
+
+.small { font-size: smaller; }
+
+.underline { text-decoration: underline; }
+
+.overline { text-decoration: overline; }
+
+.line-through { text-decoration: line-through; }
+
+.aqua { color: #00bfbf; }
+
+.aqua-background { background-color: #00fafa; }
+
+.black { color: black; }
+
+.black-background { background-color: black; }
+
+.blue { color: #0000bf; }
+
+.blue-background { background-color: #0000fa; }
+
+.fuchsia { color: #bf00bf; }
+
+.fuchsia-background { background-color: #fa00fa; }
+
+.gray { color: #606060; }
+
+.gray-background { background-color: #7d7d7d; }
+
+.green { color: #006000; }
+
+.green-background { background-color: #007d00; }
+
+.lime { color: #00bf00; }
+
+.lime-background { background-color: #00fa00; }
+
+.maroon { color: #600000; }
+
+.maroon-background { background-color: #7d0000; }
+
+.navy { color: #000060; }
+
+.navy-background { background-color: #00007d; }
+
+.olive { color: #606000; }
+
+.olive-background { background-color: #7d7d00; }
+
+.purple { color: #600060; }
+
+.purple-background { background-color: #7d007d; }
+
+.red { color: #bf0000; }
+
+.red-background { background-color: #fa0000; }
+
+.silver { color: #909090; }
+
+.silver-background { background-color: #bcbcbc; }
+
+.teal { color: #006060; }
+
+.teal-background { background-color: #007d7d; }
+
+.white { color: #bfbfbf; }
+
+.white-background { background-color: #fafafa; }
+
+.yellow { color: #bfbf00; }
+
+.yellow-background { background-color: #fafa00; }
+
+span.icon > .fa { cursor: default; }
+
+.admonitionblock td.icon [class^="fa icon-"] { font-size: 2.5em; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; }
+.admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #004060; }
+.admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; }
+.admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; }
+.admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; }
+.admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; }
+
+.conum[data-value] { display: inline-block; color: #fff !important; background-color: rgba(0, 0, 0, 0.8); -webkit-border-radius: 100px; border-radius: 100px; text-align: center; font-size: 0.75em; width: 1.67em; height: 1.67em; line-height: 1.67em; font-family: "Open Sans", "DejaVu Sans", sans-serif; font-style: normal; font-weight: bold; }
+.conum[data-value] * { color: #fff !important; }
+.conum[data-value] + b { display: none; }
+.conum[data-value]:after { content: attr(data-value); }
+pre .conum[data-value] { position: relative; top: -0.125em; }
+
+b.conum * { color: inherit !important; }
+
+.conum:not([data-value]):empty { display: none; }
+
+#toc.toc2 ul ul { list-style-type: circle; padding-left: 2em; }
+
+.sect1 { padding-bottom: 0 !important; margin-bottom: 2.5em; }
+
+#header h1 { font-weight: bold; position: relative; left: -0.0625em; }
+
+#content h2, #content h3, #content #toctitle, #content .sidebarblock > .content > .title, #content h4, #content h5, #content #toctitle { position: relative; left: -0.0625em; }
+#content h2 { font-weight: bold; }
+
+.paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { color: black; }
+
+pre.pygments.highlight { background-color: rgba(16, 195, 196, 0.05); }
+
+.pygments .tok-err { border: none !important; color: #800000 !important; }
+
+.pygments .tok-c { color: #999 !important; }
diff --git a/doc/index.adoc b/doc/index.adoc
new file mode 100644
index 0000000..7cd54b9
--- /dev/null
+++ b/doc/index.adoc
@@ -0,0 +1,1065 @@
+
+= JCommander
+:author: Cédric Beust
+:email: cedric@beust.com
+:toc: left
+:source-highlighter: prettify
+:sectnums:
+
+++++
+<div style="float:right">
+<div style="display:inline-block">
+ <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
+ <input type="hidden" name="cmd" value="_donations">
+ <input type="hidden" name="business" value="cedric@beust.com">
+ <input type="hidden" name="lc" value="US">
+ <input type="hidden" name="item_name" value="Cedric Beust">
+ <input type="hidden" name="no_note" value="0">
+ <input type="hidden" name="currency_code" value="USD">
+ <input type="hidden" name="bn" value="PP-DonationsBF:btn_donate_LG.gif:NonHostedGuest">
+ <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
+ <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1" hidden="" style="display: none !important;">
+ </form>
+</div>
+</div>
+++++
+
+
+__"Because life is too short to parse command line parameters"__
+
+== Overview
+
+JCommander is a very small Java framework that makes it trivial to parse command line parameters.
+You annotate fields with descriptions of your options:
+
+[source,java]
+----
+import com.beust.jcommander.Parameter;
+
+public class Args {
+ @Parameter
+ private List<String> parameters = new ArrayList<>();
+
+ @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity")
+ private Integer verbose = 1;
+
+ @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+ private String groups;
+
+ @Parameter(names = "-debug", description = "Debug mode")
+ private boolean debug = false;
+}
+----
+
+and then you simply ask JCommander to parse:
+
+[source,java]
+----
+Args args = new Args();
+String[] argv = { "-log", "2", "-groups", "unit" };
+JCommander.newBuilder()
+ .addObject(args)
+ .build()
+ .parse(argv);
+
+Assert.assertEquals(jct.verbose.intValue(), 2);
+----
+
+Here is another example:
+
+[source,java]
+----
+class Main {
+ @Parameter(names={"--length", "-l"})
+ int length;
+ @Parameter(names={"--pattern", "-p"})
+ int pattern;
+
+ public static void main(String ... argv) {
+ Main main = new Main();
+ JCommander.newBuilder()
+ .addObject(main)
+ .build()
+ .parse(argv);
+ main.run();
+ }
+
+ public void run() {
+ System.out.printf("%d %d", length, pattern);
+ }
+}
+----
+
+[source,bash]
+----
+$ java Main -l 512 --pattern 2
+512 2
+----
+
+
+== Types of options
+
+The fields representing your parameters can be of any type. Basic types (`Integer`, `Boolean`, etc...) are supported by default and you can write type converters to support any other type (`File`, etc...).
+
+=== Boolean
+
+When a Parameter annotation is found on a field of type `boolean` or `Boolean`, JCommander interprets it as an option with an arity of 0:
+
+[source,java]
+----
+@Parameter(names = "-debug", description = "Debug mode")
+private boolean debug = false;
+----
+
+Such a parameter does not require any additional parameter on the command line and if it's detected during parsing, the corresponding field will be set to true. If you want to define a boolean parameter that's true by default, you can declare it as having an arity of 1. Users will then have to specify the value they want explicitly:
+
+[source,java]
+----
+@Parameter(names = "-debug", description = "Debug mode", arity = 1)
+private boolean debug = true;
+----
+
+Invoke with either of:
+
+[source,bash]
+----
+program -debug true
+program -debug false
+----
+
+When a Parameter annotation is found on a field of type `String`, `Integer`, `int`, `Long` or `long`, JCommander will parse the following parameter and it will attempt to cast it to the right type:
+
+[source,java]
+----
+@Parameter(names = "-log", description = "Level of verbosity")
+private Integer verbose = 1;
+----
+
+[source,bash]
+----
+java Main -log 3
+----
+
+will cause the field verbose to receive the value 3. However:
+
+[source,bash]
+----
+$ java Main -log test
+----
+
+will cause an exception to be thrown.
+
+=== Lists
+
+When a Parameter annotation is found on a field of type `List`, JCommander will interpret it as an option that can occur multiple times:
+
+[source,java]
+----
+@Parameter(names = "-host", description = "The host")
+private List<String> hosts = new ArrayList<>();
+----
+
+will allow you to parse the following command line:
+
+[source,bash]
+----
+$ java Main -host host1 -verbose -host host2
+----
+
+When JCommander is done parsing the line above, the field hosts will contain the strings `"host1"` and `"host2"`.
+
+=== Password
+
+If one of your parameters is a password or some other value that you do not wish to appear in your history or in clear, you can declare it of type password and JCommander will then ask you to enter it in the console:
+
+[source,java]
+----
+public class ArgsPassword {
+ @Parameter(names = "-password", description = "Connection password", password = true)
+ private String password;
+}
+----
+
+When you run your program, you will get the following prompt:
+
+[source,bash]
+----
+Value for -password (Connection password):
+----
+
+You will need to type the value at this point before JCommander resumes.
+
+=== Echo Input
+
+In Java 6, by default, you will not be able to see what you type for passwords entered at the prompt (Java 5 and lower will always show the password). However, you can override this by setting echoInput to `true` (default is `false` and this setting only has an effect when password is `true`):
+
+[source,java]
+----
+public class ArgsPassword {
+ @Parameter(names = "-password", description = "Connection password", password = true, echoInput = true)
+ private String password;
+}
+----
+
+== Custom types (converters and splitters)
+
+To bind parameters to custom types or change the way how JCommander splits parameters (default is to split via comma) JCommander provides two
+interfaces `IStringConverter` and `IParameterSplitter`.
+
+[[single-value]]
+=== Custom types - Single value
+
+Use either the `converter=` attribute of the `@Parameter` or implement `IStringConverterFactory`.
+
+==== By annotation
+
+By default, JCommander parses the command line into basic types only (strings, booleans, integers and longs). Very often, your application actually needs more complex types (such as files, host names, lists, etc.). To achieve this, you can write a type converter by implementing the following interface:
+
+[source,java]
+----
+public interface IStringConverter<T> {
+ T convert(String value);
+}
+----
+
+For example, here is a converter that turns a string into a File:
+
+[source,java]
+----
+public class FileConverter implements IStringConverter<File> {
+ @Override
+ public File convert(String value) {
+ return new File(value);
+ }
+}
+----
+
+Then, all you need to do is declare your field with the correct type and specify the converter as an attribute:
+
+[source,java]
+----
+@Parameter(names = "-file", converter = FileConverter.class)
+File file;
+----
+
+JCommander ships with a few common converters (for more info please see the implementations of `IStringConverter`).
+
+===== Note
+
+If a converter is used for a `List` field:
+
+[source,java]
+----
+@Parameter(names = "-files", converter = FileConverter.class)
+List<File> files;
+----
+
+And the application is called as follows:
+
+[source,bash]
+----
+$ java App -files file1,file2,file3
+----
+
+JCommander will split the string `file1,file2,file3` into `file1`, `file2`, `file3` and feed it one by one to the converter.
+
+For an alternative solution to parse a list of values, see <<list-value>>.
+
+==== By factory
+
+If the custom types you use appear multiple times in your application, having to specify the converter in each annotation can become tedious. To address this, you can use an `IStringConverterFactory`:
+
+[source,java]
+----
+public interface IStringConverterFactory {
+ <T> Class<? extends IStringConverter<T>> getConverter(Class<T> forType);
+}
+----
+
+For example, suppose you need to parse a string representing a host and a port:
+
+[source,bash]
+----
+$ java App -target example.com:8080
+----
+
+You define the holder class :
+
+[source,java]
+----
+public class HostPort {
+ public HostPort(String host, String port) {
+ this.host = host;
+ this.port = port;
+ }
+
+ final String host;
+ final Integer port;
+}
+----
+
+and the string converter to create instances of this class:
+
+[source,java]
+----
+class HostPortConverter implements IStringConverter<HostPort> {
+ @Override
+ public HostPort convert(String value) {
+ String[] s = value.split(":");
+ return new HostPort(s[0], Integer.parseInt(s[1]));
+ }
+}
+----
+
+The factory is straightforward:
+
+[source,java]
+----
+public class Factory implements IStringConverterFactory {
+ public Class<? extends IStringConverter<?>> getConverter(Class forType) {
+ if (forType.equals(HostPort.class)) return HostPortConverter.class;
+ else return null;
+ }
+----
+
+You can now use the type `HostPort` as a parameter without any converterClass attribute:
+
+[source,java]
+----
+public class ArgsConverterFactory {
+ @Parameter(names = "-hostport")
+ private HostPort hostPort;
+}
+----
+
+All you need to do is add the factory to your JCommander object:
+
+[source,java]
+----
+ArgsConverterFactory a = new ArgsConverterFactory();
+JCommander jc = JCommander.newBuilder()
+ .addObject(a)
+ .addConverterFactory(new Factory())
+ .build()
+ .parse("-hostport", "example.com:8080");
+
+Assert.assertEquals(a.hostPort.host, "example.com");
+Assert.assertEquals(a.hostPort.port.intValue(), 8080);
+----
+
+Another advantage of using string converter factories is that your factories can come from a dependency injection framework.
+
+[[list-value]]
+=== Custom types - List value
+
+Use the `listConverter=` attribute of the `@Parameter` annotation and assign a custom `IStringConverter` implementation to convert a `String` into a `List` of values.
+
+==== By annotation
+
+If your application needs a list of complex types, write a list type converter by implementing the same interface as before:
+
+[source,java]
+----
+public interface IStringConverter<T> {
+ T convert(String value);
+}
+----
+where `T` is a `List`.
+
+
+For example, here is a list converter that turns a string into a `List<File>`:
+
+[source,java]
+----
+public class FileListConverter implements IStringConverter<List<File>> {
+ @Override
+ public List<File> convert(String files) {
+ String [] paths = files.split(",");
+ List<File> fileList = new ArrayList<>();
+ for(String path : paths){
+ fileList.add(new File(path));
+ }
+ return fileList;
+ }
+}
+----
+
+Then, all you need to do is declare your field with the correct type and specify the list converter as an attribute:
+
+[source,java]
+----
+@Parameter(names = "-files", listConverter = FileListConverter.class)
+List<File> file;
+----
+
+Now if you call for application as in the following example:
+
+[source,bash]
+----
+$ java App -files file1,file2,file3
+----
+
+The parameter `file1,file2,file3` is given to the `listConverter` and will the properly processed.
+
+JCommander ships with a default converter for `String` values.
+
+
+=== Splitting
+
+Use the `splitter=` attribute of the `@Parameter` annotation and assign a custom `IParameterSplitter` implementation to handle how parameters are split in sub-parts.
+
+==== By annotation
+
+By default, JCommander tries to split parameters for `List` field types on commas.
+
+To split parameters on other characters, you can write a custom splitter by implementing the following interface:
+
+[source,java]
+----
+public interface IParameterSplitter {
+ List<String> split(String value);
+}
+----
+
+For example, here is a splitter that splits a string on semicolon:
+
+[source,java]
+----
+public static class SemiColonSplitter implements IParameterSplitter {
+ public List<String> split(String value) {
+ return Arrays.asList(value.split(";"));
+ }
+}
+----
+
+Then, all you need to do is declare your field with the correct type and specify the splitter as an attribute:
+
+[source,java]
+----
+@Parameter(names = "-files", converter = FileConverter.class, splitter = SemiColonSplitter.class)
+List<File> files;
+----
+
+JCommander will split the string `file1;file2;file3` into `file1`, `file2`, `file3` and feed it one by one to the converter.
+
+
+== Parameter validation
+
+Parameter validation can be performed in two different ways: at the individual parameter level or globally.
+
+=== Individual parameter validation
+
+You can ask JCommander to perform early validation on your parameters by providing a class that implements the following interface:
+
+[source,java]
+----
+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;
+}
+----
+
+Here is an example implementation that will make sure that the parameter is a positive integer:
+
+[source,java]
+----
+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 +")");
+ }
+ }
+}
+----
+
+Specify the name of a class implementing this interface in the `validateWith` attribute of your `@Parameter` annotations:
+
+[source,java]
+----
+@Parameter(names = "-age", validateWith = PositiveInteger.class)
+private Integer age;
+----
+
+Attempting to pass a negative integer to this option will cause a ParameterException to be thrown.
+
+Multiple validators may be specified:
+
+[source,java]
+----
+@Parameter(names = "-count", validateWith = { PositiveInteger.class, CustomOddNumberValidator.class })
+private Integer value;
+----
+
+
+=== Global parameter validation
+
+After parsing your parameters with JCommander, you might want to perform additional validation across these parameters, such as making sure that two mutually exclusive parameters are not both specified. Because of all the potential combinations involved in such validation, JCommander does not provide any annotation-based solution to perform this validation because such an approach would necessarily be very limited by the very nature of Java annotations. Instead, you should simply perform this validation in Java on all the arguments that JCommander just parsed.
+
+
+== Main parameter
+
+So far, all the `@Parameter` annotations we have seen had defined an attribute called `names`. You can define one (and at most one) parameter without any such attribute. This parameter needs to be a `List<String>` and it will contain all the parameters that are not options:
+
+[source,java]
+----
+@Parameter(description = "Files")
+private List<String> files = new ArrayList<>();
+
+@Parameter(names = "-debug", description = "Debugging level")
+private Integer debug = 1;
+----
+
+will allow you to parse:
+
+[source,bash]
+----
+$ java Main -debug file1 file2
+----
+
+and the field files will receive the strings `"file1"` and `"file2"`.
+
+== Private parameters
+
+Parameters can be private:
+
+[source,java]
+----
+public class ArgsPrivate {
+ @Parameter(names = "-verbose")
+ private Integer verbose = 1;
+
+ public Integer getVerbose() {
+ return verbose;
+ }
+}
+ArgsPrivate args = new ArgsPrivate();
+JCommander.newBuilder()
+ .addObject(args)
+ .build()
+ .parse("-verbose", "3");
+Assert.assertEquals(args.getVerbose().intValue(), 3);
+----
+
+== Parameter separators
+
+By default, parameters are separated by spaces, but you can change this setting to allow different separators:
+
+[source,bash]
+----
+$ java Main -log:3
+----
+
+or
+
+[source,bash]
+----
+$ java Main -level=42
+----
+
+You define the separator with the @Parameters annotation:
+
+[source,java]
+----
+@Parameters(separators = "=")
+public class SeparatorEqual {
+ @Parameter(names = "-level")
+ private Integer level = 2;
+}
+----
+
+== Multiple descriptions
+
+You can spread the description of your parameters on more than one class. For example, you can define the following two classes:
+
+[source,java]
+----
+public class ArgsMaster {
+ @Parameter(names = "-master")
+ private String master;
+}
+
+public class ArgsSlave {
+ @Parameter(names = "-slave")
+ private String slave;
+}
+----
+
+and pass these two objects to JCommander:
+
+[source,java]
+----
+ArgsMaster m = new ArgsMaster();
+ArgsSlave s = new ArgsSlave();
+String[] argv = { "-master", "master", "-slave", "slave" };
+JCommander.newBuilder()
+ .addObject(new Object[] { m , s })
+ .build()
+ .parse(argv);
+
+Assert.assertEquals(m.master, "master");
+Assert.assertEquals(s.slave, "slave");
+----
+
+== @ syntax
+
+JCommander supports the @ syntax, which allows you to put all your options into a file and pass this file as parameter:
+
+[[app-listing]]
+[source,bash]
+./tmp/parameters
+----
+-verbose
+file1
+file2
+file3
+----
+
+[source,bash]
+----
+$ java Main @/tmp/parameters
+----
+
+== Arities (multiple values for parameters)
+
+=== Fixed arities
+
+If some of your parameters require more than one value, such as the following example where two values are expected after -pairs:
+
+[source,bash]
+----
+$ java Main -pairs slave master foo.xml
+----
+
+then you need to define your parameter with the arity attribute and make that parameter a `List<String>`:
+
+[source,java]
+----
+@Parameter(names = "-pairs", arity = 2, description = "Pairs")
+private List<String> pairs;
+----
+
+You don't need to specify an arity for parameters of type `boolean` or `Boolean` (which have a default arity of 0) and of types `String`, `Integer`, `int`, `Long` and `long` (which have a default arity of 1).
+
+Also, note that only `List<String>` is allowed for parameters that define an arity. You will have to convert these values yourself if the parameters you need are of type `Integer` or other (this limitation is due to Java's erasure).
+
+=== Variable arities
+
+You can specify that a parameter can receive an indefinite number of parameters, up to the next option. For example:
+
+[source,bash]
+----
+program -foo a1 a2 a3 -bar
+program -foo a1 -bar
+----
+
+Such a parameter can be parsed in two different ways.
+
+==== With a list
+
+If the number of following parameters is unknown, your parameter must be of type `List<String>` and you
+need to set the boolean `variableArity` to `true`:
+
+[source,java]
+----
+@Parameter(names = "-foo", variableArity = true)
+public List<String> foo = new ArrayList<>();
+----
+
+==== With a class
+
+Alternatively, you can define a class in which the following parameters will be stored, based on their order
+of appearance:
+
+[source,java]
+----
+static class MvParameters {
+ @SubParameter(order = 0)
+ String from;
+
+ @SubParameter(order = 1)
+ String to;
+}
+
+@Test
+public void arity() {
+ class Parameters {
+ @Parameter(names = {"--mv"}, arity = 2)
+ private MvParameters mvParameters;
+ }
+
+ Parameters args = new Parameters();
+ JCommander.newBuilder()
+ .addObject(args)
+ .args(new String[]{"--mv", "from", "to"})
+ .build();
+
+ Assert.assertNotNull(args.mvParameters);
+ Assert.assertEquals(args.mvParameters.from, "from");
+ Assert.assertEquals(args.mvParameters.to, "to");
+}
+----
+
+== Multiple option names
+
+You can specify more than one option name:
+[source,java]
+----
+@Parameter(names = { "-d", "--outputDirectory" }, description = "Directory")
+private String outputDirectory;
+----
+
+will allow both following syntaxes:
+
+[source,bash]
+----
+$ java Main -d /tmp
+$ java Main --outputDirectory /tmp
+----
+
+== Other option configurations
+
+You can configure how options are looked up in a few different ways:
+
+- `JCommander#setCaseSensitiveOptions(boolean)`: specify whether options are case sensitive. If you call this method with `false`, then `"-param"` and `"-PARAM"` are considered equal.
+- `JCommander#setAllowAbbreviatedOptions(boolean)`: specify whether users can pass abbreviated options. If you call this method with `true` then users can pass `"-par"` to specify an option called `-param`. JCommander will throw a `ParameterException` if the abbreviated name is ambiguous.
+
+== Required and optional parameters
+
+If some of your parameters are mandatory, you can use the `required` attribute (which default to `false`):
+
+[source,java]
+----
+@Parameter(names = "-host", required = true)
+private String host;
+----
+
+If this parameter is not specified, JCommander will throw an exception telling you which options are missing.
+
+== Default values
+
+The most common way to specify a default value for your parameters is to initialize the field at declaration time:
+
+[source,java]
+----
+private Integer logLevel = 3;
+----
+
+For more complicated cases, you might want to be able to reuse identical default values across several main classes or be able to specify these default values in a centralized location such as a `.properties` or an XML file. In this case, you can use an `IDefaultProvider`:
+
+[source,java]
+----
+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);
+}
+----
+
+By passing an implementation of this interface to your JCommander object, you can now control which default value will be used for your options. Note that the value returned by this method will then be passed to a string converter, if any is applicable, thereby allowing you to specify default values for any types you need.
+
+For example, here is a default provider that will assign a default value of 42 for all your parameters except `"-debug"`:
+
+[source,java]
+----
+private static final IDefaultProvider DEFAULT_PROVIDER = new IDefaultProvider() {
+ @Override
+ public String getDefaultValueFor(String optionName) {
+ return "-debug".equals(optionName) ? "false" : "42";
+ }
+};
+
+// ...
+
+JCommander jc = JCommander.newBuilder()
+ .addObject(new Args())
+ .defaultProvider(DEFAULT_PROVIDER)
+ .build()
+
+----
+
+
+== Help parameter
+
+If one of your parameters is used to display some help or usage, you need use the help attribute:
+
+[source,java]
+----
+@Parameter(names = "--help", help = true)
+private boolean help;
+----
+
+If you omit this boolean, JCommander will instead issue an error message when it tries to validate your command and it finds that you didn't specify some of the required parameters.
+
+== More complex syntaxes (commands)
+
+Complex tools such as `git` or `svn` understand a whole set of commands, each of which with their own specific syntax:
+
+[source,bash]
+----
+$ git commit --amend -m "Bug fix"
+----
+
+Words such as `"commit"` above are called "commands" in JCommander, and you can specify them by creating one arg object per command:
+
+[source,java]
+----
+@Parameters(separators = "=", commandDescription = "Record changes to the repository")
+private class CommandCommit {
+
+ @Parameter(description = "The list of files to commit")
+ private List<String> files;
+
+ @Parameter(names = "--amend", description = "Amend")
+ private Boolean amend = false;
+
+ @Parameter(names = "--author")
+ private String author;
+}
+
+@Parameters(commandDescription = "Add file contents to the index")
+public class CommandAdd {
+
+ @Parameter(description = "File patterns to add to the index")
+ private List<String> patterns;
+
+ @Parameter(names = "-i")
+ private Boolean interactive = false;
+}
+----
+
+Then you register these commands with your JCommander object. After the parsing phase, you call `getParsedCommand()` on your JCommander object, and based on the command that is returned, you know which arg object to inspect (you can still use a main arg object if you want to support options before the first command appears on the command line):
+
+[source,java]
+----
+CommandMain cm = new CommandMain();
+CommandAdd add = new CommandAdd();
+CommandCommit commit = new CommandCommit();
+JCommander jc = JCommander.newBuilder()
+ .addObject(cm)
+ .addCommand("add", add);
+ .addCommand("commit", commit);
+ .build();
+
+jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java");
+
+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"));
+----
+
+== Exception
+
+Whenever JCommander detects an error, it will throw a `ParameterException`. Note that this is a Runtime Exception, since your application is probably not initialized correctly at this point. Also, `ParameterException` contains the
+`JCommander` instance and you can also invoke `usage()` on it if you need to display some help.
+
+== Usage
+
+You can invoke `usage()` on the JCommander instance that you used to parse your command line in order to generate a summary of all the options that your program understands:
+
+[source,bash]
+----
+Usage: <main class> [options]
+ Options:
+ -debug Debug mode (default: false)
+ -groups Comma-separated list of group names to be run
+ * -log, -verbose Level of verbosity (default: 1)
+ -long A long number (default: 0)
+----
+
+You can customize the name of your program by calling `setProgramName()` on your JCommander object. Options preceded by an asterisk are required.
+
+You can also specify the order in which each option should be displayed when calling `usage()` by setting the `order` attribute of the `@Parameter` annotation:
+
+[source,java]
+----
+class Parameters {
+ @Parameter(names = "--importantOption", order = 0)
+ private boolean a;
+
+ @Parameter(names = "--lessImportantOption", order = 3)
+ private boolean b;
+----
+
+== Hiding parameters
+
+If you don't want certain parameters to appear in the usage, you can mark them as "hidden":
+
+[source,java]
+----
+@Parameter(names = "-debug", description = "Debug mode", hidden = true)
+private boolean debug = false;
+----
+
+
+== Internationalization
+
+You can internationalize the descriptions of your parameters. First you use the `@Parameters` annotation at the top of your class to define the name of your message bundle, and then you use the `descriptionKey` attribute instead of description on all the `@Parameters` that require translations. This `descriptionKey` is the key to the string into your message bundle:
+
+[source,java]
+----
+@Parameters(resourceBundle = "MessageBundle")
+private class ArgsI18N2 {
+ @Parameter(names = "-host", description = "Host", descriptionKey = "host")
+ String hostName;
+}
+----
+
+Your bundle needs to define this key:
+
+[source,bash]
+----
+host: Hôte
+----
+
+JCommander will then use the default locale to resolve your descriptions.
+
+== Parameter delegates
+
+If you are writing many different tools in the same project, you will probably find that most of these tools can share configurations. While you can use inheritance with your objects to avoid repeating this code, the restriction to single inheritance of implementation might limit your flexibility. To address this problem, JCommander supports parameter delegates.
+
+When JCommander encounters an object annotated with `@ParameterDelegate` in one of your objects, it acts as if this object had been added as a description object itself:
+
+[source,java]
+----
+class Delegate {
+ @Parameter(names = "-port")
+ private int port;
+}
+
+class MainParams {
+ @Parameter(names = "-v")
+ private boolean verbose;
+
+ @ParametersDelegate
+ private Delegate delegate = new Delegate();
+}
+----
+
+The example above specifies a delegate parameter Delegate which is then referenced in MainParams. You only need to add a `MainParams` object to your
+JCommander configuration in order to use the delegate:
+
+[source,java]
+----
+MainParams p = new MainParams();
+JCommander.newBuilder().addObject(p).build()
+ .parse("-v", "-port", "1234");
+Assert.assertTrue(p.isVerbose);
+Assert.assertEquals(p.delegate.port, 1234);
+----
+
+== Dynamic parameters
+
+JCommander allows you to specify parameters that are not known at compile time, such as "-Da=b -Dc=d". Such parameters are specified with the `@DynamicParameter` annotation and must be of type `Map<String, String>`. Dynamic parameters are allowed to appear multiple times on the command line:
+
+[source,java]
+----
+@DynamicParameter(names = "-D", description = "Dynamic parameters go here")
+private Map<String, String> params = new HashMap<>();
+----
+
+You can specify a different assignment string than = by using the attribute assignment.
+
+== JCommander in other languages
+
+=== Kotlin
+
+[source,kotlin]
+----
+class Args {
+ @Parameter
+ var targets: List<String> = arrayListOf()
+
+ @Parameter(names = arrayOf("-bf", "--buildFile"), description = "The build file")
+ var buildFile: String? = null
+
+ @Parameter(names = arrayOf("--checkVersions"),
+ description = "Check if there are any newer versions of the dependencies")
+ var checkVersions = false
+}
+----
+
+=== Groovy
+
+Courtesy of Paul King:
+
+[source,groovy]
+----
+import com.beust.jcommander.*
+
+class Args {
+ @Parameter(names = ["-f", "--file"], description = "File to load. Can be specified multiple times.")
+ List<String> file
+}
+
+new Args().with {
+ JCommander.newBuilder().addObject(it).build().parse(argv)
+ file.each { println "file: ${new File(it).name}" }
+}
+----
+
+== More examples
+
+Here are the description files for a few projects that use JCommander:
+
+- https://github.com/cbeust/testng/blob/master/src/main/java/org/testng/CommandLineArgs.java[TestNG]
+- https://github.com/cbeust/kobalt/blob/master/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/Args.kt[Kobalt]
+
+== Mailing list
+
+Join the http://groups.google.com/group/jcommander[JCommander Google group] if you are interested in discussions about JCommander.
+
+== Javadocs
+
+The Javadocs for JCommander can be found http://jcommander.org/apidocs/[here].
+
+== License
+
+JCommander is released under the https://github.com/cbeust/jcommander/blob/master/license.txt[Apache 2.0 license].
+
+== Download
+
+You can download JCommander from the following locations:
+
+- http://github.com/cbeust/jcommander[Source on github]
+- Kobalt
+
+[source,groovy]
+----
+compile("com.beust:jcommander:1.71")
+----
+
+- Gradle
+
+[source,groovy]
+----
+compile "com.beust:jcommander:1.71"
+----
+
+- Maven:
+
+[source,xml]
+----
+<dependency>
+ <groupId>com.beust</groupId>
+ <artifactId>jcommander</artifactId>
+ <version>1.71</version>
+</dependency>
+----
+
+
+
diff --git a/doc/index.html b/doc/index.html
index da0368b..8975316 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -1,871 +1,2084 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html> <head>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<meta name="generator" content="Asciidoctor 1.5.5">
+<meta name="author" content="Cédric Beust">
<title>JCommander</title>
+<style>
+@import url(https://fonts.googleapis.com/css?family=Varela+Round|Open+Sans:400italic,600italic,400,600|Ubuntu+Mono:400);
+/*! normalize.css v2.1.2 | MIT License | git.io/normalize */
+/* ========================================================================== HTML5 display definitions ========================================================================== */
+/** Correct `block` display not defined in IE 8/9. */
+article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; }
-<!--
-<link rel="stylesheet" href="testng.css" type="text/css" />
--->
-
- <link type="text/css" rel="stylesheet" href="http://beust.com/beust.css" />
- <link type="text/css" rel="stylesheet" href="http://jcommander.org/jcommander.css" />
- <script type="text/javascript" src="http://beust.com/prettify.js"></script>
-
- <script type="text/javascript" src="http://beust.com/scripts/shCore.js"></script>
- <script type="text/javascript" src="http://beust.com/scripts/shBrushJava.js"></script>
- <script type="text/javascript" src="http://beust.com/scripts/shBrushXml.js"></script>
- <script type="text/javascript" src="http://beust.com/scripts/shBrushBash.js"></script>
- <script type="text/javascript" src="http://beust.com/scripts/shBrushPlain.js"></script>
- <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shCore.css"/>
- <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shThemeCedric.css"/>
- <script type="text/javascript" src="http://beust.com/toc.js"></script>
- <script type="text/javascript">
- SyntaxHighlighter.config.clipboardSwf = 'scripts/clipboard.swf';
- SyntaxHighlighter.defaults['gutter'] = false;
- SyntaxHighlighter.all();
- </script>
-</head>
+/** Correct `inline-block` display not defined in IE 8/9. */
+audio, canvas, video { display: inline-block; }
-<table width="100%">
- <tr>
- <td align="center">
-<h1>JCommander</h1>
-<h2>Because life is too short to parse command line parameters</h2>
-<h3>
- <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
- <input type="hidden" name="cmd" value="_donations">
- <input type="hidden" name="business" value="cedric@beust.com">
- <input type="hidden" name="lc" value="US">
- <input type="hidden" name="item_name" value="Cedric Beust">
- <input type="hidden" name="no_note" value="0">
- <input type="hidden" name="currency_code" value="USD">
- <input type="hidden" name="bn" value="PP-DonationsBF:btn_donate_LG.gif:NonHostedGuest">
- <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
- <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
- </form>
-</h3>
- </td>
- </tr>
- <tr>
- <td align="right">
- Created: July 13th, 2010
- </td>
- </tr>
- <tr>
- <td align="right">
- Last updated: January 14th, 2015
- </td>
- </tr>
- <tr><td align="right"><a href="mailto:cedric@beust.com">C&eacute;dric Beust</a></td></tr>
-</table>
+/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */
+audio:not([controls]) { display: none; height: 0; }
-<h2>Table of contents</h2>
-<div id="table-of-contents">
-</div>
+/** Address `[hidden]` styling not present in IE 8/9. Hide the `template` element in IE, Safari, and Firefox < 22. */
+[hidden], template { display: none; }
+script { display: none !important; }
+/* ========================================================================== Base ========================================================================== */
+/** 1. Set default font family to sans-serif. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */
+html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ }
-<h2><a class="section" name="Overview">Overview</a></h2>
+/** Remove default margin. */
+body { margin: 0; }
-JCommander is a very small Java framework that makes it trivial to parse command line parameters.
-<p>
-You annotate fields with descriptions of your options:
+/* ========================================================================== Links ========================================================================== */
+/** Remove the gray background color from active links in IE 10. */
+a { background: transparent; }
-<pre class="brush: java">
-import com.beust.jcommander.Parameter;
+/** Address `outline` inconsistency between Chrome and other browsers. */
+a:focus { outline: thin dotted; }
-public class JCommanderExample {
- @Parameter
- private List&lt;String&gt; parameters = new ArrayList&lt;String&gt;();
+/** Improve readability when focused and also mouse hovered in all browsers. */
+a:active, a:hover { outline: 0; }
- @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity")
- private Integer verbose = 1;
+/* ========================================================================== Typography ========================================================================== */
+/** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */
+h1 { font-size: 2em; margin: 0.67em 0; }
- @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
- private String groups;
+/** Address styling not present in IE 8/9, Safari 5, and Chrome. */
+abbr[title] { border-bottom: 1px dotted; }
- @Parameter(names = "-debug", description = "Debug mode")
- private boolean debug = false;
-}
-</pre>
+/** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */
+b, strong { font-weight: bold; }
-and then you simply ask JCommander to parse:
+/** Address styling not present in Safari 5 and Chrome. */
+dfn { font-style: italic; }
-<pre class="brush: java">
-JCommanderExample jct = new JCommanderExample();
-String[] argv = { "-log", "2", "-groups", "unit" };
-new JCommander(jct, argv);
+/** Address differences between Firefox and other browsers. */
+hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; }
-Assert.assertEquals(jct.verbose.intValue(), 2);
-</pre>
+/** Address styling not present in IE 8/9. */
+mark { background: #ff0; color: #000; }
-<h2><a class="section" name="Types_of_options">Types of options</a></h2>
+/** Correct font family set oddly in Safari 5 and Chrome. */
+code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; }
-The fields representing your parameters can be of any type. Basic types (<tt>Integer</tt>, <tt>Boolean</tt/>., etc...) are supported by default and you can write type converters to support any other type (<tt>File</tt>, etc...).
+/** Improve readability of pre-formatted text in all browsers. */
+pre { white-space: pre-wrap; }
-<h4>Boolean</h4>
+/** Set consistent quote types. */
+q { quotes: "\201C" "\201D" "\2018" "\2019"; }
-When a <tt>Parameter</tt> annotation is found on a field of type <tt>boolean</tt> or <tt>Boolean</tt>, JCommander interprets it as an option with an <em>arity</em> of 0:
+/** Address inconsistent and variable font size in all browsers. */
+small { font-size: 80%; }
-<pre class="brush: java">
-@Parameter(names = "-debug", description = "Debug mode")
-private boolean debug = false;
-</pre>
+/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */
+sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
-Such a parameter does not require any additional parameter on the command line and if it's detected during parsing, the corresponding field will be set to <tt>true</tt>.
+sup { top: -0.5em; }
-<p>
+sub { bottom: -0.25em; }
- If you want to define a boolean parameter that's <tt>true</tt> by default, you can declare it as having an arity of 1. Users will then have to specify the value they want explicitly:
+/* ========================================================================== Embedded content ========================================================================== */
+/** Remove border when inside `a` element in IE 8/9. */
+img { border: 0; }
- <pre class="brush: java">
- @Parameter(names = "-debug", description = "Debug mode", arity = 1)
- private boolean debug = true;
- </pre>
+/** Correct overflow displayed oddly in IE 9. */
+svg:not(:root) { overflow: hidden; }
- Invoke with either of:
+/* ========================================================================== Figures ========================================================================== */
+/** Address margin not present in IE 8/9 and Safari 5. */
+figure { margin: 0; }
- <pre class="brush: plain">
- program -debug true
- program -debug false
- </pre>
+/* ========================================================================== Forms ========================================================================== */
+/** Define consistent border, margin, and padding. */
+fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
-<h4>String, Integer, Long</h4>
+/** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */
+legend { border: 0; /* 1 */ padding: 0; /* 2 */ }
-When a <tt>Parameter</tt> annotation is found on a field of type <tt>String</tt>, <tt>Integer</tt>, <tt>int</tt>, <tt>Long</tt> or <tt>long</tt>, JCommander will parse the following parameter and it will attempt to cast it to the right type:
+/** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */
+button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ }
-<pre class="brush: java">
-@Parameter(names = "-log", description = "Level of verbosity")
-private Integer verbose = 1;
-</pre>
+/** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */
+button, input { line-height: normal; }
-<pre class="brush: plain">
-java Main -log 3
-</pre>
+/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */
+button, select { text-transform: none; }
-will cause the field <tt>verbose</tt> to receive the value 3, however:
+/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */
+button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ }
-<pre class="brush: plain">
-java Main -log test
-</pre>
+/** Re-set default cursor for disabled elements. */
+button[disabled], html input[disabled] { cursor: default; }
-will cause an exception to be thrown.
+/** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */
+input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ }
-<h4>Lists</h4>
+/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */
+input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; }
-When a <tt>Parameter</tt> annotation is found on a field of type <tt>List</tt>, JCommander will interpret it as an option that can occur multiple times:
+/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */
+input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
-<pre class="brush: java">
-@Parameter(names = "-host", description = "The host")
-private List&lt;String&gt; hosts = new ArrayList&lt;String&gt;();
-</pre>
+/** Remove inner padding and border in Firefox 4+. */
+button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
-will allow you to parse the following command line:
+/** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */
+textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ }
-<pre class="brush: plain">
-java Main -host host1 -verbose -host host2
-</pre>
+/* ========================================================================== Tables ========================================================================== */
+/** Remove most spacing between table cells. */
+table { border-collapse: collapse; border-spacing: 0; }
-When JCommander is done parsing the line above, the field <tt>hosts</tt> will contain the strings "host1" and "host2".
+meta.foundation-mq-small { font-family: "only screen and (min-width: 768px)"; width: 768px; }
-<h4>Password</h4>
+meta.foundation-mq-medium { font-family: "only screen and (min-width:1280px)"; width: 1280px; }
-If one of your parameters is a password or some other value that you do not wish to appear in your history or in clear, you can declare it of type <tt>password</tt> and JCommander will then ask you to enter it in the console:
+meta.foundation-mq-large { font-family: "only screen and (min-width:1440px)"; width: 1440px; }
-<pre class="brush: java">
-public class ArgsPassword {
- @Parameter(names = "-password", description = "Connection password", password = true)
- private String password;
-}
-</pre>
+*, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
-When you run your program, you will get the following prompt:
+html, body { font-size: 100%; }
-<pre class="brush: plain">
-Value for -password (Connection password):
-</pre>
+body { background: #fefdfd; color: rgba(0, 0, 0, 0.8); padding: 0; margin: 0; font-family: "Open Sans", sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; }
-You will need to type the value at this point before JCommander resumes.
+a:hover { cursor: pointer; }
-<h4>Echo Input</h4>
+img, object, embed { max-width: 100%; height: auto; }
-In Java 6, by default, you will not be able to see what you type for passwords entered at the prompt (Java 5 and lower will always show the password). However, you can override this by setting <tt>echoInput</tt> to "true" (default is "false" and this setting only has an effect when <tt>password</tt> is "true"):
-<pre class="brush: java">
-public class ArgsPassword {
- @Parameter(names = "-password", description = "Connection password", password = true, echoInput = true)
- private String password;
-}
-</pre>
+object, embed { height: 100%; }
-<h2><a class="section" name="Custom_types">Custom types</a></h2>
+img { -ms-interpolation-mode: bicubic; }
-<h3>By annotation</h3>
+#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; }
-By default, JCommander parses the command line into basic types only (strings, booleans, integers and longs). Very often, your application actually needs more complex types, such as files, host names, lists, etc... To achieve this, you can write a type converter by implementing the following interface:
+.left { float: left !important; }
-<pre class="brush: java">
-public interface IStringConverter&lt;T&gt; {
- T convert(String value);
-}
-</pre>
+.right { float: right !important; }
-For example, here is a converter that turns a string into a <tt>File</tt>:
+.text-left { text-align: left !important; }
-<pre class="brush: java">
-public class FileConverter implements IStringConverter&lt;File&gt; {
- @Override
- public File convert(String value) {
- return new File(value);
- }
-}
-</pre>
+.text-right { text-align: right !important; }
-Then, all you need to do is declare your field with the correct type and specify the converter as an attribute:
+.text-center { text-align: center !important; }
-<pre class="brush: java">
-@Parameter(names = "-file", converter = FileConverter.class)
-File file;
-</pre>
+.text-justify { text-align: justify !important; }
-JCommander ships with a few common converters (e.g. one that turns a comma separated list into a <tt>List&lt;String&gt;)</tt>.
+.hide { display: none; }
-<h3>By factory</h3>
+.antialiased { -webkit-font-smoothing: antialiased; }
-If the custom types you use appear multiple times in your application, having to specify the converter in each annotation can become tedious. To address this, you can use an <tt>IStringConverterFactory</tt>:
+img { display: inline-block; vertical-align: middle; }
-<pre class="brush: java">
-public interface IStringConverterFactory {
- &lt;T&gt; Class&lt;? extends IStringConverter&lt;T&gt;&gt; getConverter(Class&lt;T&gt; forType);
-}
-</pre>
+textarea { height: auto; min-height: 50px; }
-For example, suppose you need to parse a string representing a host and a port:
+select { width: 100%; }
-<pre class="brush: plain">
-java App -target example.com:8080
-</pre>
+object, svg { display: inline-block; vertical-align: middle; }
-You define the holder class :
+.center { margin-left: auto; margin-right: auto; }
-<pre class="brush: java">
-public class HostPort {
- private String host;
- private Integer port;
-}
-</pre>
+.spread { width: 100%; }
-and the string converter to create instances of this class:
+p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; }
-<pre class="brush: java">
-class HostPortConverter implements IStringConverter&lt;HostPort&gt; {
- @Override
- public HostPort convert(String value) {
- HostPort result = new HostPort();
- String[] s = value.split(":");
- result.host = s[0];
- result.port = Integer.parseInt(s[1]);
+.subheader, .admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { line-height: 1.25; color: #002c5e; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; }
- return result;
- }
-}
-</pre>
+/* Typography resets */
+div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; }
-The factory is straightforward:
+/* Default Link Styles */
+a { color: #005580; text-decoration: underline; line-height: inherit; }
+a:hover, a:focus { color: #078d71; }
+a img { border: none; }
-<pre class="brush: java">
-public class Factory implements IStringConverterFactory {
- public Class&lt;? extends IStringConverter&lt;?&gt;&gt; getConverter(Class forType) {
- if (forType.equals(HostPort.class)) return HostPortConverter.class;
- else return null;
- }
-</pre>
+/* Default paragraph styles */
+p { font-family: inherit; font-weight: normal; font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; text-rendering: optimizeLegibility; }
+p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; }
-You can now use the type <tt>HostPort</tt> as a parameter without any <tt>converterClass</tt> attribute:
+/* Default header styles */
+h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { font-family: "Varela Round", sans-serif; font-weight: 400; font-style: normal; color: #00326b; text-rendering: optimizeLegibility; margin-top: 0.8em; margin-bottom: 0.5em; line-height: 1.0625em; }
+h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { font-size: 60%; color: #057aff; line-height: 0; }
-<pre class="brush: java">
-public class ArgsConverterFactory {
- @Parameter(names = "-hostport")
- private HostPort hostPort;
-}
-</pre>
+h1 { font-size: 2.125em; }
+h2 { font-size: 1.6875em; }
-All you need to do is add the factory to your JCommander object:
+h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.375em; }
-<pre class="brush: java">
- ArgsConverterFactory a = new ArgsConverterFactory();
- JCommander jc = new JCommander(a);
- jc.addConverterFactory(new Factory());
- jc.parse("-hostport", "example.com:8080");
+h4 { font-size: 1.125em; }
- Assert.assertEquals(a.hostPort.host, "example.com");
- Assert.assertEquals(a.hostPort.port.intValue(), 8080);
-</pre>
+h5 { font-size: 1.125em; }
+h6 { font-size: 1em; }
-Another advantage of using string converter factories is that your factories can come from a dependency injection framework.
+hr { border: solid rgba(145, 135, 84, 0.15); border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; }
-<h2><a class="section" name="Parameter_validation">Parameter validation</a></h2>
+/* Helpful Typography Defaults */
+em, i { font-style: italic; line-height: inherit; }
-You can ask JCommander to perform early validation on your parameters by providing a class that implements the following interface:
+strong, b { font-weight: bold; line-height: inherit; }
-<pre class="brush:java">
-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;
-}
+small { font-size: 60%; line-height: inherit; }
-</pre>
+code { font-family: "Ubuntu Mono", "Inconsolata", monospace; font-weight: 400; color: #331d00; }
-Here is an example implementation that will make sure that the parameter is a positive integer:
+/* Lists */
+ul, ol, dl { font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; list-style-position: outside; font-family: inherit; }
-<pre class="brush:java">
-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 +")");
- }
- }
-}
-</pre>
+ul, ol { margin-left: 1.5em; }
+ul.no-bullet, ol.no-bullet { margin-left: 1.5em; }
-Specify the name of a class implementing this interface in the <tt>validateWith</tt> attribute of your <tt>@Parameter</tt> annotations:
+/* Unordered Lists */
+ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ }
+ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; }
+ul.square { list-style-type: square; }
+ul.circle { list-style-type: circle; }
+ul.disc { list-style-type: disc; }
+ul.no-bullet { list-style: none; }
-<pre class="brush:java">
-@Parameter(names = "-age", validateWith = PositiveInteger.class)
-private Integer age;
-</pre>
+/* Ordered Lists */
+ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; }
-Attempting to pass a negative integer to this option will cause a <tt>ParameterException</tt> to be thrown.
+/* Definition Lists */
+dl dt { margin-bottom: 0.3125em; font-weight: bold; }
+dl dd { margin-bottom: 1.25em; }
+/* Abbreviations */
+abbr, acronym { text-transform: uppercase; font-size: 90%; color: rgba(0, 0, 0, 0.8); border-bottom: 1px dotted #dddddd; cursor: help; }
-<h2><a class="section" name="Main_parameter">Main parameter</a></h2>
-So far, all the <tt>@Parameter</tt> annotations we have seen had defined an attribute called <tt>names</tt>. You can define one (and at most one) parameter without any such attribute. This parameter needs to be a <tt>List&lt;String&gt;</tt> and it will contain all the parameters that are not options:
+abbr { text-transform: none; }
-<pre class="brush: java">
-@Parameter(description = "Files")
-private List&lt;String&gt; files = new ArrayList&lt;String&gt;();
+/* Blockquotes */
+blockquote { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; }
+blockquote cite { display: block; font-size: 0.8125em; color: #666666; }
+blockquote cite:before { content: "\2014 \0020"; }
+blockquote cite a, blockquote cite a:visited { color: #666666; }
-@Parameter(names = "-debug", description = "Debugging level")
-private Integer debug = 1;
-</pre>
+blockquote, blockquote p { line-height: 1.5; color: #999999; }
-will allow you to parse:
+/* Microformats */
+.vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; }
+.vcard li { margin: 0; display: block; }
+.vcard .fn { font-weight: bold; font-size: 0.9375em; }
-<pre class="brush: plain">
-java Main -debug file1 file2
-</pre>
+.vevent .summary { font-weight: bold; }
+.vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; }
-and the field <tt>files</tt> will receive the strings "file1" and "file2".
+@media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.25; }
+ h1 { font-size: 2.75em; }
+ h2 { font-size: 2.3125em; }
+ h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; }
+ h4 { font-size: 1.4375em; } }
+/* Tables */
+table { background: white; margin-bottom: 1.25em; border: solid 1px rgba(145, 135, 84, 0.15); }
+table thead, table tfoot { background: rgba(119, 84, 22, 0.1); font-weight: bold; }
+table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 0.5em 0.625em 0.625em; font-size: inherit; color: rgba(0, 0, 0, 0.8); text-align: left; }
+table tr th, table tr td { padding: 0.5625em 0.625em; font-size: inherit; color: rgba(0, 0, 0, 0.8); }
+table tr.even, table tr.alt, table tr:nth-of-type(even) { background: rgba(119, 84, 22, 0.025); }
+table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.5; }
-<h2><a class="section" name="Private_parameters">Private parameters</a></h2>
+body { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; tab-size: 4; }
-Parameters can be private:
+h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.25; }
-<pre class="brush: java">
-public class ArgsPrivate {
- @Parameter(names = "-verbose")
- private Integer verbose = 1;
+.clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; }
+.clearfix:after, .float-group:after { clear: both; }
- public Integer getVerbose() {
- return verbose;
- }
-}
-</pre>
+*:not(pre) > code { font-size: 1.0625em; font-style: normal !important; letter-spacing: 0; padding: 0; line-height: 1.25; word-wrap: break-word; }
+*:not(pre) > code.nobreak { word-wrap: normal; }
+*:not(pre) > code.nowrap { white-space: nowrap; }
-<pre class="brush: java">
-ArgsPrivate args = new ArgsPrivate();
-new JCommander(args, "-verbose", "3");
-Assert.assertEquals(args.getVerbose().intValue(), 3);
-</pre>
+pre, pre > code { line-height: 1.4; color: inherit; font-family: "Liberation Mono", "Consolas", monospace; font-weight: normal; }
-<h2><a class="section" name="Separators">Parameter separators</a></h2>
+em em { font-style: normal; }
-By default, parameters are separated by spaces, but you can change this setting to allow different separators:
+strong strong { font-weight: normal; }
-<pre class="brush: plain">
-java Main -log:3
-</pre>
+.keyseq { color: rgba(51, 51, 51, 0.8); }
-or
+kbd { font-family: "Ubuntu Mono", "Inconsolata", monospace; display: inline-block; color: rgba(0, 0, 0, 0.8); font-size: 0.65em; line-height: 1.45; background-color: #f7f7f7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; margin: 0 0.15em; padding: 0.2em 0.5em; vertical-align: middle; position: relative; top: -0.1em; white-space: nowrap; }
-<pre class="brush: plain">
-java Main -level=42
-</pre>
+.keyseq kbd:first-child { margin-left: 0; }
-You define the separator with the <tt>@Parameters</tt> annotation:
+.keyseq kbd:last-child { margin-right: 0; }
-<pre class="brush: java">
-@Parameters(separators = "=")
-public class SeparatorEqual {
- @Parameter(names = "-level")
- private Integer level = 2;
-}
-</pre>
+.menuseq, .menu { color: rgba(0, 0, 0, 0.8); }
+b.button:before, b.button:after { position: relative; top: -1px; font-weight: normal; }
+b.button:before { content: "["; padding: 0 3px 0 2px; }
+b.button:after { content: "]"; padding: 0 2px 0 3px; }
+p a > code:hover { color: #1a0f00; }
-<h2><a class="section" name="Multiple_descriptions">Multiple descriptions</a></h2>
+#header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; }
+#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; }
+#header:after, #content:after, #footnotes:after, #footer:after { clear: both; }
-You can spread the description of your parameters on more than one
-class. For example, you can define the following two classes:
+#content { margin-top: 1.25em; }
-<p>
+#content:before { content: none; }
-<h3 class="sourcetitle">ArgsMaster.java</h3>
-<pre class="brush: java">
-public class ArgsMaster {
- @Parameter(names = "-master")
- private String master;
-}
-</pre>
+#header > h1:first-child { color: #703f1c; margin-top: 2.25rem; margin-bottom: 0; }
+#header > h1:first-child + #toc { margin-top: 8px; border-top: 1px solid rgba(145, 135, 84, 0.15); }
+#header > h1:only-child, body.toc2 #header > h1:nth-last-child(2) { border-bottom: 1px solid rgba(145, 135, 84, 0.15); padding-bottom: 8px; }
+#header .details { border-bottom: 1px solid rgba(145, 135, 84, 0.15); line-height: 1.45; padding-top: 0.25em; padding-bottom: 0.25em; padding-left: 0.25em; color: #666666; display: -ms-flexbox; display: -webkit-flex; display: flex; -ms-flex-flow: row wrap; -webkit-flex-flow: row wrap; flex-flow: row wrap; }
+#header .details span:first-child { margin-left: -0.125em; }
+#header .details span.email a { color: #999999; }
+#header .details br { display: none; }
+#header .details br + span:before { content: "\00a0\2013\00a0"; }
+#header .details br + span.author:before { content: "\00a0\22c5\00a0"; color: #999999; }
+#header .details br + span#revremark:before { content: "\00a0|\00a0"; }
+#header #revnumber { text-transform: capitalize; }
+#header #revnumber:after { content: "\00a0"; }
-<h3 class="sourcetitle">ArgsSlave.java</h3>
-<pre class="brush: java">
-public class ArgsSlave {
- @Parameter(names = "-slave")
- private String slave;
-}
-</pre>
+#content > h1:first-child:not([class]) { color: #703f1c; border-bottom: 1px solid rgba(145, 135, 84, 0.15); padding-bottom: 8px; margin-top: 0; padding-top: 1rem; margin-bottom: 1.25rem; }
-and pass these two objects to JCommander:
+#toc { border-bottom: 0px solid #dddddd; padding-bottom: 0.5em; }
+#toc > ul { margin-left: 0.125em; }
+#toc ul.sectlevel0 > li > a { font-style: italic; }
+#toc ul.sectlevel0 ul.sectlevel1 { margin: 0.5em 0; }
+#toc ul { font-family: "Varela Round", sans-serif; list-style-type: none; }
+#toc li { line-height: 1.3334; margin-top: 0.3334em; }
+#toc a { text-decoration: none; }
+#toc a:active { text-decoration: underline; }
-<pre class="brush: java">
-ArgsMaster m = new ArgsMaster();
-ArgsSlave s = new ArgsSlave();
-String[] argv = { "-master", "master", "-slave", "slave" };
-new JCommander(new Object[] { m , s }, argv);
+#toctitle { color: #002c5e; font-size: 1.2em; }
-Assert.assertEquals(m.master, "master");
-Assert.assertEquals(s.slave, "slave");
-</pre>
+@media only screen and (min-width: 768px) { #toctitle { font-size: 1.375em; }
+ body.toc2 { padding-left: 15em; padding-right: 0; }
+ #toc.toc2 { margin-top: 0 !important; background-color: #f2f2f4; position: fixed; width: 15em; left: 0; top: 0; border-right: 1px solid #dddddd; border-top-width: 0 !important; border-bottom-width: 0 !important; z-index: 1000; padding: 1.25em 1em; height: 100%; overflow: auto; }
+ #toc.toc2 #toctitle { margin-top: 0; margin-bottom: 0.8rem; font-size: 1.2em; }
+ #toc.toc2 > ul { font-size: 0.9em; margin-bottom: 0; }
+ #toc.toc2 ul ul { margin-left: 0; padding-left: 1em; }
+ #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; }
+ body.toc2.toc-right { padding-left: 0; padding-right: 15em; }
+ body.toc2.toc-right #toc.toc2 { border-right-width: 0; border-left: 1px solid #dddddd; left: auto; right: 0; } }
+@media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; padding-right: 0; }
+ #toc.toc2 { width: 20em; }
+ #toc.toc2 #toctitle { font-size: 1.375em; }
+ #toc.toc2 > ul { font-size: 0.95em; }
+ #toc.toc2 ul ul { padding-left: 1.25em; }
+ body.toc2.toc-right { padding-left: 0; padding-right: 20em; } }
+#content #toc { border-style: solid; border-width: 1px; border-color: #d6d6dd; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f4; -webkit-border-radius: 6px; border-radius: 6px; }
+#content #toc > :first-child { margin-top: 0; }
+#content #toc > :last-child { margin-bottom: 0; }
+#footer { max-width: 100%; background-color: #0b445a; padding: 1.25em; }
-<h2><a class="section" name="Syntax">@ syntax</a></h2>
+#footer-text { color: #fefdfd; line-height: 1.35; }
-JCommander supports the @ syntax, which allows you to put all your options into a file and pass this file as parameter:
+.sect1 { padding-bottom: 0.625em; }
-<p>
+@media only screen and (min-width: 768px) { .sect1 { padding-bottom: 1.25em; } }
+.sect1 + .sect1 { border-top: 0px solid #dddddd; }
-<div class="sourcetitle">/tmp/parameters</div>
-<pre class="brush: plain">
--verbose
-file1
-file2
-file3
-</pre>
-<pre class="brush: plain">
-java Main @/tmp/parameters
-</pre>
+#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; z-index: 1001; width: 1.5ex; margin-left: -1.5ex; display: block; text-decoration: none !important; visibility: hidden; text-align: center; font-weight: normal; }
+#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: "\00A7"; font-size: 0.85em; display: block; padding-top: 0.1em; }
+#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; }
+#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #00326b; text-decoration: none; }
+#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #002652; }
-<h2><a class="section" name="Arities">Arities (multiple values for parameters)</a></h2>
+.audioblock, .imageblock, .literalblock, .listingblock, .stemblock, .videoblock { margin-bottom: 1.25em; }
-<h3><a class="section" name="fixed-arities" indent="..">Fixed arities</a></h3>
+.admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-rendering: optimizeLegibility; text-align: left; }
-If some of your parameters require more than one value, such as the
-following example where two values are expected after <tt>-pairs</tt>:
+table.tableblock > caption.title { white-space: nowrap; overflow: visible; max-width: 0; }
-<pre class="brush: plain">
-java Main -pairs slave master foo.xml
-</pre>
+.paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { color: #703f1c; }
-then you need to define your parameter with the <tt>arity</tt>
-attribute and make that parameter a <tt>List&lt;String&gt;</tt>:
+table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { font-size: inherit; }
-<pre class="brush: java">
-@Parameter(names = "-pairs", arity = 2, description = "Pairs")
-private List&lt;String&gt; pairs;
-</pre>
+.admonitionblock > table { border-collapse: separate; border: 0; background: none; width: 100%; }
+.admonitionblock > table td.icon { text-align: center; width: 80px; }
+.admonitionblock > table td.icon img { max-width: initial; }
+.admonitionblock > table td.icon .title { font-weight: bold; font-family: "Varela Round", sans-serif; text-transform: uppercase; }
+.admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px solid rgba(145, 135, 84, 0.15); color: #666666; }
+.admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; }
-You don't need to specify an arity for parameters of type
-<tt>boolean</tt> or <tt>Boolean</tt> (which have a default arity of 0)
-and of types <tt>String</tt>, <tt>Integer</tt>, <tt>int</tt>,
-<tt>Long</tt> and <tt>long</tt> (which have a default arity of 1).
+.exampleblock > .content { border-style: solid; border-width: 1px; border-color: #eddbdb; margin-bottom: 1.25em; padding: 1.25em; background: #fefdfd; -webkit-border-radius: 6px; border-radius: 6px; }
+.exampleblock > .content > :first-child { margin-top: 0; }
+.exampleblock > .content > :last-child { margin-bottom: 0; }
-<p>
-Also, note that only <tt>List&lt;String&gt;</tt> is allowed for
-parameters that define an arity. You will have to convert these values
-yourself if the parameters you need are of type <tt>Integer</tt> or
-other (this limitation is due to Java's erasure).
+.sidebarblock { border-style: solid; border-width: 1px; border-color: #d6d6dd; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f4; -webkit-border-radius: 6px; border-radius: 6px; }
+.sidebarblock > :first-child { margin-top: 0; }
+.sidebarblock > :last-child { margin-bottom: 0; }
+.sidebarblock > .content > .title { color: #002c5e; margin-top: 0; }
-<h3><a class="section" name="variable-arities" indent="..">Variable arities</a></h3>
+.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; }
-You can specify that a parameter can receive an indefinite number of parameters, up to the next option. For example:
+.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint { background: rgba(16, 195, 196, 0.05); }
+.sidebarblock .literalblock pre, .sidebarblock .listingblock pre:not(.highlight), .sidebarblock .listingblock pre[class="highlight"], .sidebarblock .listingblock pre[class^="highlight "], .sidebarblock .listingblock pre.CodeRay, .sidebarblock .listingblock pre.prettyprint { background: #f2f1f1; }
-<pre class="brush: java">
-program -foo a1 a2 a3 -bar
-program -foo a1 -bar
-</pre>
+.literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { border: 1px solid rgba(16, 195, 196, 0.125); -webkit-border-radius: 6px; border-radius: 6px; word-wrap: break-word; padding: 1em; font-size: 0.8125em; }
+.literalblock pre.nowrap, .literalblock pre[class].nowrap, .listingblock pre.nowrap, .listingblock pre[class].nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; }
+@media only screen and (min-width: 768px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.90625em; } }
+@media only screen and (min-width: 1280px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 1em; } }
-Such a parameter must be of type <tt>List&lt;String&gt;</tt> and have the boolean <tt>variableArity</tt> set to <tt>true</tt>
+.literalblock.output pre { color: rgba(16, 195, 196, 0.05); background-color: inherit; }
-<pre class="brush: java">
-@Parameter(names = "-foo", variableArity = true)
-public List&lt;String&gt; foo = new ArrayList&lt;String&gt;();
-</pre>
+.listingblock pre.highlightjs { padding: 0; }
+.listingblock pre.highlightjs > code { padding: 1em; -webkit-border-radius: 6px; border-radius: 6px; }
-<h2><a class="section" name="Multiple_option_names">Multiple option names</a></h2>
+.listingblock > .content { position: relative; }
-You can specify more than one option name:
+.listingblock code[data-lang]:before { display: none; content: attr(data-lang); position: absolute; font-size: 0.75em; top: 0.425rem; right: 0.5rem; line-height: 1; text-transform: uppercase; color: #999; }
-<pre class="brush: java">
+.listingblock:hover code[data-lang]:before { display: block; }
- @Parameter(names = { "-d", "--outputDirectory" }, description = "Directory")
- private String outputDirectory;
+.listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; }
-</pre>
+.listingblock.terminal pre .command:not([data-prompt]):before { content: "$"; }
-will allow both following syntaxes:
+table.pyhltable { border-collapse: separate; border: 0; margin-bottom: 0; background: none; }
-<pre class="brush: plain">
-java Main -d /tmp
-java Main --outputDirectory /tmp
-</pre>
+table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; line-height: 1.4; }
-<h2><a class="section" name="Other option configurations">Other option configurations</a></h2>
+table.pyhltable td.code { padding-left: .75em; padding-right: 0; }
-You can configure how options are looked up in a few different ways:
+pre.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid rgba(145, 135, 84, 0.15); }
-<ul>
- <li><tt>JCommander#setCaseSensitiveOptions(boolean)</tt>: specify whether options are case sensitive. If you call this method with <tt>false</tt>, then <tt>"-param"</tt> and
- <tt>"-PARAM"</tt> are considered equal.
- </li>
- <li><tt>JCommander#setAllowAbbreviatedOptions(boolean)</tt>: specify whether users can
- pass abbreviated options. If you call this method with <tt>true</tt> then users
- can pass <tt>"-par"</tt> to specify an option called <tt>-param</tt>. JCommander will
- throw a <tt>ParameterException</tt> if the abbreviated name is ambiguous.
- </li>
-</ul>
+pre.pygments .lineno { display: inline-block; margin-right: .25em; }
-<h2><a class="section" name="Required_and_optional">Required and optional parameters</a></h2>
+table.pyhltable .linenodiv { background: none !important; padding-right: 0 !important; }
-If some of your parameters are mandatory, you can use the
-<tt>required</tt> attribute (which default to <tt>false</tt>):
+.quoteblock { margin: 0 1em 1.25em 1.5em; display: table; }
+.quoteblock > .title { margin-left: -1.5em; margin-bottom: 0.75em; }
+.quoteblock blockquote, .quoteblock blockquote p { color: #999999; font-size: 1.15rem; line-height: 1.75; word-spacing: 0.1em; letter-spacing: 0; font-style: italic; text-align: justify; }
+.quoteblock blockquote { margin: 0; padding: 0; border: 0; }
+.quoteblock blockquote:before { content: "\201c"; float: left; font-size: 2.75em; font-weight: bold; line-height: 0.6em; margin-left: -0.6em; color: #002c5e; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); }
+.quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; }
+.quoteblock .attribution { margin-top: 0.5em; margin-right: 0.5ex; text-align: right; }
+.quoteblock .quoteblock { margin-left: 0; margin-right: 0; padding: 0.5em 0; border-left: 3px solid #666666; }
+.quoteblock .quoteblock blockquote { padding: 0 0 0 0.75em; }
+.quoteblock .quoteblock blockquote:before { display: none; }
-<pre class="brush: java">
+.verseblock { margin: 0 1em 1.25em 1em; }
+.verseblock pre { font-family: "Open Sans", "DejaVu Sans", sans; font-size: 1.15rem; color: #999999; font-weight: 300; text-rendering: optimizeLegibility; }
+.verseblock pre strong { font-weight: 400; }
+.verseblock .attribution { margin-top: 1.25rem; margin-left: 0.5ex; }
- @Parameter(names = "-host", required = true)
- private String host;
+.quoteblock .attribution, .verseblock .attribution { font-size: 0.8125em; line-height: 1.45; font-style: italic; }
+.quoteblock .attribution br, .verseblock .attribution br { display: none; }
+.quoteblock .attribution cite, .verseblock .attribution cite { display: block; letter-spacing: -0.025em; color: #666666; }
-</pre>
+.quoteblock.abstract { margin: 0 0 1.25em 0; display: block; }
+.quoteblock.abstract blockquote, .quoteblock.abstract blockquote p { text-align: left; word-spacing: 0; }
+.quoteblock.abstract blockquote:before, .quoteblock.abstract blockquote p:first-of-type:before { display: none; }
-If this parameter is not specified, JCommander will throw an exception
-telling you which options are missing.
+table.tableblock { max-width: 100%; border-collapse: separate; }
+table.tableblock td > .paragraph:last-child p > p:last-child, table.tableblock th > p:last-child, table.tableblock td > p:last-child { margin-bottom: 0; }
-<h2><a class="section" name="Default_values">Default values</a></h2>
+table.tableblock, th.tableblock, td.tableblock { border: 0 solid rgba(145, 135, 84, 0.15); }
-The most common way to specify a default value for your parameters is to initialize the field at declaration time:
+table.grid-all th.tableblock, table.grid-all td.tableblock { border-width: 0 1px 1px 0; }
-<pre class="brush: java">
-private Integer logLevel = 3;
-</pre>
+table.grid-all tfoot > tr > th.tableblock, table.grid-all tfoot > tr > td.tableblock { border-width: 1px 1px 0 0; }
-For more complicated cases, you might want to be able to reuse identical default values across several main classes or be able to specify these default values in a centralized location such as a .properties or an XML fie. In this case, you can use an <tt>IDefaultProvider</tt>
+table.grid-cols th.tableblock, table.grid-cols td.tableblock { border-width: 0 1px 0 0; }
-<pre class="brush: java">
-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);
-}
-</pre>
+table.grid-all * > tr > .tableblock:last-child, table.grid-cols * > tr > .tableblock:last-child { border-right-width: 0; }
-By passing an implementation of this interface to your <tt>JCommander</tt> object, you can now control which default value will be used for your options. Note that the value returned by this method will then be passed to a string converter, if any is applicable, thereby allowing you to specify default values for any types you need.
+table.grid-rows th.tableblock, table.grid-rows td.tableblock { border-width: 0 0 1px 0; }
-<p>
+table.grid-all tbody > tr:last-child > th.tableblock, table.grid-all tbody > tr:last-child > td.tableblock, table.grid-all thead:last-child > tr > th.tableblock, table.grid-rows tbody > tr:last-child > th.tableblock, table.grid-rows tbody > tr:last-child > td.tableblock, table.grid-rows thead:last-child > tr > th.tableblock { border-bottom-width: 0; }
-For example, here is a default provider that will assign a default value of 42 for all your parameters except "-debug":
+table.grid-rows tfoot > tr > th.tableblock, table.grid-rows tfoot > tr > td.tableblock { border-width: 1px 0 0 0; }
-<pre class="brush: java">
-private static final IDefaultProvider DEFAULT_PROVIDER = new IDefaultProvider() {
- @Override
- public String getDefaultValueFor(String optionName) {
- return "-debug".equals(optionName) ? "false" : "42";
- }
-};
+table.frame-all { border-width: 1px; }
-// ...
+table.frame-sides { border-width: 0 1px; }
-JCommander jc = new JCommander(new Args());
-jc.setDefaultProvider(DEFAULT_PROVIDER);
-</pre>
+table.frame-topbot { border-width: 1px 0; }
-<h2><a class="section" name="Help_parameter">Help parameter</a></h2>
+th.halign-left, td.halign-left { text-align: left; }
-If one of your parameters is used to display some help or usage, you need use the <tt>help</tt> attribute:
+th.halign-right, td.halign-right { text-align: right; }
-<pre class="brush: java">
- @Parameter(names = "--help", help = true)
- private boolean help;
-</pre>
+th.halign-center, td.halign-center { text-align: center; }
-If you omit this boolean, JCommander will instead issue an error message when it tries to validate your command and it finds that you didn't specify some of the required parameters.
+th.valign-top, td.valign-top { vertical-align: top; }
-<h2><a class="section" name="Complex">More complex syntaxes (commands)</a></h2>
+th.valign-bottom, td.valign-bottom { vertical-align: bottom; }
-Complex tools such as <tt>git</tt> or <tt>svn</tt> understand a whole set of commands, each of which with their own specific syntax:
+th.valign-middle, td.valign-middle { vertical-align: middle; }
-<pre class="brush: plain">
- git commit --amend -m "Bug fix"
-</pre>
+table thead th, table tfoot th { font-weight: bold; }
-Words such as "commit" above are called "commands" in JCommander, and you can specify them by creating one arg object per command:
+tbody tr th { display: table-cell; line-height: 1.5; background: rgba(119, 84, 22, 0.1); }
-<pre class="brush: java">
-@Parameters(separators = "=", commandDescription = "Record changes to the repository")
-private class CommandCommit {
+tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p { color: rgba(0, 0, 0, 0.8); font-weight: bold; }
- @Parameter(description = "The list of files to commit")
- private List&lt;String&gt; files;
+p.tableblock > code:only-child { background: none; padding: 0; }
- @Parameter(names = "--amend", description = "Amend")
- private Boolean amend = false;
+p.tableblock { font-size: 1em; }
- @Parameter(names = "--author")
- private String author;
-}
-</pre>
+td > div.verse { white-space: pre; }
-<pre class="brush: java">
-@Parameters(commandDescription = "Add file contents to the index")
-public class CommandAdd {
+ol { margin-left: 1.75em; }
- @Parameter(description = "File patterns to add to the index")
- private List&lt;String&gt; patterns;
+ul li ol { margin-left: 1.5em; }
- @Parameter(names = "-i")
- private Boolean interactive = false;
-}
-</pre>
+dl dd { margin-left: 1.125em; }
-Then you register these commands with your JCommander object. After the parsing phase, you call <tt>getParsedCommand()</tt> on your JCommander object, and based on the command that is returned, you know which arg object to inspect (you can still use a main arg object if you want to support options before the first command appears on the command line):
+dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; }
-<pre class="brush: java">
-CommandMain cm = new CommandMain();
-JCommander jc = new JCommander(cm);
+ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.625em; }
-CommandAdd add = new CommandAdd();
-jc.addCommand("add", add);
-CommandCommit commit = new CommandCommit();
-jc.addCommand("commit", commit);
+ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; }
-jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java");
+ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; }
-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"));
-</pre>
+ul.checklist li > p:first-child > .fa-square-o:first-child, ul.checklist li > p:first-child > .fa-check-square-o:first-child { width: 1em; font-size: 0.85em; }
-<h2><a class="section" name="Exceptions">Exception</a></h2>
+ul.checklist li > p:first-child > input[type="checkbox"]:first-child { width: 1em; position: relative; top: 1px; }
-Whenever JCommander detects an error, it will throw a
-<tt>ParameterException</tt>. Note that this is a Runtime Exception,
-since your application is probably not initialized correctly at this
-point.
+ul.inline { margin: 0 auto 0.625em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; }
+ul.inline > li { list-style: none; float: left; margin-left: 1.375em; display: block; }
+ul.inline > li > * { display: block; }
+.unstyled dl dt { font-weight: normal; font-style: normal; }
-<h2><a class="section" name="Usage">Usage</a></h2>
+ol.arabic { list-style-type: decimal; }
-You can invoke <tt>usage()</tt> on the <tt>JCommander</tt> instance that you used to parse your command line in order to generate a summary of all the options that your program understands:
+ol.decimal { list-style-type: decimal-leading-zero; }
-<pre class="brush: plain">
-Usage: &lt;main class&gt; [options]
- Options:
- -debug Debug mode (default: false)
- -groups Comma-separated list of group names to be run
- * -log, -verbose Level of verbosity (default: 1)
- -long A long number (default: 0)
-</pre>
+ol.loweralpha { list-style-type: lower-alpha; }
-You can customize the name of your program by calling <tt>setProgramName()</tt> on your <tt>JCommander</tt> object.
+ol.upperalpha { list-style-type: upper-alpha; }
-Options preceded by an asterisk are required.
+ol.lowerroman { list-style-type: lower-roman; }
-<h2><a class="section" name="Hiding">Hiding parameters</a></h2>
+ol.upperroman { list-style-type: upper-roman; }
-If you don't want certain parameters to appear in the usage, you can mark them as "hidden":
+ol.lowergreek { list-style-type: lower-greek; }
-<pre class="brush: java">
-@Parameter(names = "-debug", description = "Debug mode", hidden = true)
-private boolean debug = false;
-</pre>
+.hdlist > table, .colist > table { border: 0; background: none; }
+.hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; }
-<h2><a class="section" name="Internationalization">Internationalization</a></h2>
+td.hdlist1, td.hdlist2 { vertical-align: top; padding: 0 0.625em; }
-You can internationalize the descriptions of your parameters.
+td.hdlist1 { font-weight: bold; padding-bottom: 1.25em; }
-<p>
+.literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; }
-First you use the <tt>@Parameters</tt> annotation at the top of your class to define the name of your message bundle, and then you use the <tt>descriptionKey</tt> attribute instead of <tt>description</tt> on all the <tt>@Parameters</tt> that require translations. This <tt>descriptionKey</tt> is the key to the string into your message bundle:
+.colist > table tr > td:first-of-type { padding: 0 0.75em; line-height: 1; }
+.colist > table tr > td:first-of-type img { max-width: initial; }
+.colist > table tr > td:last-of-type { padding: 0.25em 0; }
-<h3 class="sourcetitle">I18N.java</h3>
-<pre class="brush:java">
-@Parameters(resourceBundle = "MessageBundle")
-private class ArgsI18N2 {
- @Parameter(names = "-host", description = "Host", descriptionKey = "host")
- String hostName;
-}
-</pre>
+.thumb, .th { line-height: 0; display: inline-block; border: solid 4px white; -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; }
-Your bundle needs to define this key:
+.imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; }
+.imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; }
+.imageblock > .title { margin-bottom: 0; }
+.imageblock.thumb, .imageblock.th { border-width: 6px; }
+.imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; }
-<br>
+.image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; }
+.image.left { margin-right: 0.625em; }
+.image.right { margin-left: 0.625em; }
-<h3 class="sourcetitle">MessageBundle_fr_FR.properties</h3>
-<pre class="brush: plain">
-host: H&ocirc;te
-</pre>
+a.image { text-decoration: none; display: inline-block; }
+a.image object { pointer-events: none; }
-JCommander will then use the default locale to resolve your descriptions.
+sup.footnote, sup.footnoteref { font-size: 0.875em; position: static; vertical-align: super; }
+sup.footnote a, sup.footnoteref a { text-decoration: none; }
+sup.footnote a:active, sup.footnoteref a:active { text-decoration: underline; }
-<h2><a class="section" name="Parameter_delegates">Parameter delegates</a></h2>
+#footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; }
+#footnotes hr { width: 20%; min-width: 6.25em; margin: -0.25em 0 0.75em 0; border-width: 1px 0 0 0; }
+#footnotes .footnote { padding: 0 0.375em 0 0.225em; line-height: 1.3334; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.05em; margin-bottom: 0.2em; }
+#footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; }
+#footnotes .footnote:last-of-type { margin-bottom: 0; }
+#content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; }
-If you are writing many different tools in the same project, you will probably find that most of these tools can share configurations. While you can use inheritance with your objects to avoid repeating this code, the restriction to single inheritance of implementation might limit your flexibility. To address this problem, JCommander supports parameter delegates.
+.gist .file-data > table { border: 0; background: #fff; width: 100%; margin-bottom: 0; }
+.gist .file-data > table td.line-data { width: 99%; }
-<p>
+div.unbreakable { page-break-inside: avoid; }
-When JCommander encounters an object annotated with <tt>@ParameterDelegate</tt> in one of your objects, it acts as if this object had been added as a description object itself:
+.big { font-size: larger; }
-<pre class="brush: java">
-class Delegate {
- @Parameter(names = "-port")
- private int port;
-}
+.small { font-size: smaller; }
-class MainParams {
- @Parameter(names = "-v")
- private boolean verbose;
+.underline { text-decoration: underline; }
- @ParametersDelegate
- private Delegate delegate = new Delegate();
-}
-</pre>
+.overline { text-decoration: overline; }
-The example above specifies a delegate parameter <tt>Delegate</tt> which is then referenced in <tt>MainParams</tt>. You only need to add a <tt>MainParams</tt> object to your JCommander configuration in order to use the delegate:
+.line-through { text-decoration: line-through; }
-<pre class="brush: java">
-MainParams p = new MainParams();
-new JCommander(p).parse("-v", "-port", "1234");
-Assert.assertTrue(p.isVerbose);
-Assert.assertEquals(p.delegate.port, 1234);
-</pre>
+.aqua { color: #00bfbf; }
+
+.aqua-background { background-color: #00fafa; }
+
+.black { color: black; }
+
+.black-background { background-color: black; }
+
+.blue { color: #0000bf; }
+
+.blue-background { background-color: #0000fa; }
+
+.fuchsia { color: #bf00bf; }
+
+.fuchsia-background { background-color: #fa00fa; }
+
+.gray { color: #606060; }
+
+.gray-background { background-color: #7d7d7d; }
+
+.green { color: #006000; }
+
+.green-background { background-color: #007d00; }
+
+.lime { color: #00bf00; }
+
+.lime-background { background-color: #00fa00; }
+
+.maroon { color: #600000; }
+
+.maroon-background { background-color: #7d0000; }
+
+.navy { color: #000060; }
+
+.navy-background { background-color: #00007d; }
+
+.olive { color: #606000; }
+
+.olive-background { background-color: #7d7d00; }
+
+.purple { color: #600060; }
+
+.purple-background { background-color: #7d007d; }
+
+.red { color: #bf0000; }
+
+.red-background { background-color: #fa0000; }
+
+.silver { color: #909090; }
+
+.silver-background { background-color: #bcbcbc; }
+
+.teal { color: #006060; }
+
+.teal-background { background-color: #007d7d; }
+
+.white { color: #bfbfbf; }
+
+.white-background { background-color: #fafafa; }
+
+.yellow { color: #bfbf00; }
+
+.yellow-background { background-color: #fafa00; }
+
+span.icon > .fa { cursor: default; }
-<h2><a class="section" name="DynamicParameters">Dynamic parameters</a></h2>
+.admonitionblock td.icon [class^="fa icon-"] { font-size: 2.5em; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; }
+.admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #004060; }
+.admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; }
+.admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; }
+.admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; }
+.admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; }
-JCommander allows you to specify parameters that are not known at compile time, such as <tt>"-Da=b -Dc=d"</tt>. Such parameters are specified with the <tt><a href="apidocs/com/beust/jcommander/DynamicParameter.html">@DynamicParameter</a></tt> annotation and must be of type <tt>Map&lt;String, String&gt;</tt>. Dynamic parameters are allowed to appear multiple times on the command line:
+.conum[data-value] { display: inline-block; color: #fff !important; background-color: rgba(0, 0, 0, 0.8); -webkit-border-radius: 100px; border-radius: 100px; text-align: center; font-size: 0.75em; width: 1.67em; height: 1.67em; line-height: 1.67em; font-family: "Open Sans", "DejaVu Sans", sans-serif; font-style: normal; font-weight: bold; }
+.conum[data-value] * { color: #fff !important; }
+.conum[data-value] + b { display: none; }
+.conum[data-value]:after { content: attr(data-value); }
+pre .conum[data-value] { position: relative; top: -0.125em; }
-<pre class="brush: java">
-@DynamicParameter(names = "-D", description = "Dynamic parameters go here")
-private Map&lt;String, String&gt; params = new HashMap&lt;String, String&gt;();
-</pre>
+b.conum * { color: inherit !important; }
-You can specify a different assignment string than <tt>=</tt> by using the attribute <tt>assignment</tt>.
+.conum:not([data-value]):empty { display: none; }
-<h2><a class="section" name="Scala">JCommander in Scala</a></h2>
+#toc.toc2 ul ul { list-style-type: circle; padding-left: 2em; }
-Here is a quick example of how to use JCommander in Scala (courtesy of Patrick Linskey):
+.sect1 { padding-bottom: 0 !important; margin-bottom: 2.5em; }
-<pre class="brush: java">
-import java.io.File
-import com.beust.jcommander.{JCommander, Parameter}
-import collection.JavaConversions._
+#header h1 { font-weight: bold; position: relative; left: -0.0625em; }
-object Main {
- object Args {
- // Declared as var because JCommander assigns a new collection declared
- // as java.util.List because that's what JCommander will replace it with.
- // It'd be nice if JCommander would just use the provided List so this
- // could be a val and a Scala LinkedList.
- @Parameter(
- names = Array("-f", "--file"),
- description = "File to load. Can be specified multiple times.")
- var file: java.util.List[String] = null
+#content h2, #content h3, #content #toctitle, #content .sidebarblock > .content > .title, #content h4, #content h5, #content #toctitle { position: relative; left: -0.0625em; }
+#content h2 { font-weight: bold; }
+
+.paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { color: black; }
+
+pre.pygments.highlight { background-color: rgba(16, 195, 196, 0.05); }
+
+.pygments .tok-err { border: none !important; color: #800000 !important; }
+
+.pygments .tok-c { color: #999 !important; }
+
+</style>
+</head>
+<body class="article toc2 toc-left">
+<div id="header">
+<h1>JCommander</h1>
+<div class="details">
+<span id="author" class="author">Cédric Beust</span><br>
+<span id="email" class="email"><a href="mailto:cedric@beust.com">cedric@beust.com</a></span><br>
+</div>
+<div id="toc" class="toc2">
+<div id="toctitle">Table of Contents</div>
+<ul class="sectlevel1">
+<li><a href="#_overview">1. Overview</a></li>
+<li><a href="#_types_of_options">2. Types of options</a>
+<ul class="sectlevel2">
+<li><a href="#_boolean">2.1. Boolean</a></li>
+<li><a href="#_lists">2.2. Lists</a></li>
+<li><a href="#_password">2.3. Password</a></li>
+<li><a href="#_echo_input">2.4. Echo Input</a></li>
+</ul>
+</li>
+<li><a href="#_custom_types_converters_and_splitters">3. Custom types (converters and splitters)</a>
+<ul class="sectlevel2">
+<li><a href="#single-value">3.1. Custom types - Single value</a></li>
+<li><a href="#list-value">3.2. Custom types - List value</a></li>
+<li><a href="#_splitting">3.3. Splitting</a></li>
+</ul>
+</li>
+<li><a href="#_parameter_validation">4. Parameter validation</a>
+<ul class="sectlevel2">
+<li><a href="#_individual_parameter_validation">4.1. Individual parameter validation</a></li>
+<li><a href="#_global_parameter_validation">4.2. Global parameter validation</a></li>
+</ul>
+</li>
+<li><a href="#_main_parameter">5. Main parameter</a></li>
+<li><a href="#_private_parameters">6. Private parameters</a></li>
+<li><a href="#_parameter_separators">7. Parameter separators</a></li>
+<li><a href="#_multiple_descriptions">8. Multiple descriptions</a></li>
+<li><a href="#__syntax">9. @ syntax</a></li>
+<li><a href="#_arities_multiple_values_for_parameters">10. Arities (multiple values for parameters)</a>
+<ul class="sectlevel2">
+<li><a href="#_fixed_arities">10.1. Fixed arities</a></li>
+<li><a href="#_variable_arities">10.2. Variable arities</a></li>
+</ul>
+</li>
+<li><a href="#_multiple_option_names">11. Multiple option names</a></li>
+<li><a href="#_other_option_configurations">12. Other option configurations</a></li>
+<li><a href="#_required_and_optional_parameters">13. Required and optional parameters</a></li>
+<li><a href="#_default_values">14. Default values</a></li>
+<li><a href="#_help_parameter">15. Help parameter</a></li>
+<li><a href="#_more_complex_syntaxes_commands">16. More complex syntaxes (commands)</a></li>
+<li><a href="#_exception">17. Exception</a></li>
+<li><a href="#_usage">18. Usage</a></li>
+<li><a href="#_hiding_parameters">19. Hiding parameters</a></li>
+<li><a href="#_internationalization">20. Internationalization</a></li>
+<li><a href="#_parameter_delegates">21. Parameter delegates</a></li>
+<li><a href="#_dynamic_parameters">22. Dynamic parameters</a></li>
+<li><a href="#_jcommander_in_other_languages">23. JCommander in other languages</a>
+<ul class="sectlevel2">
+<li><a href="#_kotlin">23.1. Kotlin</a></li>
+<li><a href="#_groovy">23.2. Groovy</a></li>
+</ul>
+</li>
+<li><a href="#_more_examples">24. More examples</a></li>
+<li><a href="#_mailing_list">25. Mailing list</a></li>
+<li><a href="#_javadocs">26. Javadocs</a></li>
+<li><a href="#_license">27. License</a></li>
+<li><a href="#_download">28. Download</a></li>
+</ul>
+</div>
+</div>
+<div id="content">
+<div id="preamble">
+<div class="sectionbody">
+<div style="float:right">
+<div style="display:inline-block">
+ <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
+ <input type="hidden" name="cmd" value="_donations">
+ <input type="hidden" name="business" value="cedric@beust.com">
+ <input type="hidden" name="lc" value="US">
+ <input type="hidden" name="item_name" value="Cedric Beust">
+ <input type="hidden" name="no_note" value="0">
+ <input type="hidden" name="currency_code" value="USD">
+ <input type="hidden" name="bn" value="PP-DonationsBF:btn_donate_LG.gif:NonHostedGuest">
+ <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
+ <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1" hidden="" style="display: none !important;">
+ </form>
+</div>
+</div>
+<div class="paragraph">
+<p><em>"Because life is too short to parse command line parameters"</em></p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_overview">1. Overview</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>JCommander is a very small Java framework that makes it trivial to parse command line parameters.
+You annotate fields with descriptions of your options:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">import com.beust.jcommander.Parameter;
+
+public class Args {
+ @Parameter
+ private List&lt;String&gt; parameters = new ArrayList&lt;&gt;();
+
+ @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity")
+ private Integer verbose = 1;
+
+ @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+ private String groups;
+
+ @Parameter(names = "-debug", description = "Debug mode")
+ private boolean debug = false;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>and then you simply ask JCommander to parse:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">Args args = new Args();
+String[] argv = { "-log", "2", "-groups", "unit" };
+JCommander.newBuilder()
+ .addObject(args)
+ .build()
+ .parse(argv);
+
+Assert.assertEquals(jct.verbose.intValue(), 2);</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Here is another example:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">class Main {
+ @Parameter(names={"--length", "-l"})
+ int length;
+ @Parameter(names={"--pattern", "-p"})
+ int pattern;
+
+ public static void main(String ... argv) {
+ Main main = new Main();
+ JCommander.newBuilder()
+ .addObject(main)
+ .build()
+ .parse(argv);
+ main.run();
+ }
+
+ public void run() {
+ System.out.printf("%d %d", length, pattern);
+ }
+}</code></pre>
+</div>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java Main -l 512 --pattern 2
+512 2</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_types_of_options">2. Types of options</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>The fields representing your parameters can be of any type. Basic types (<code>Integer</code>, <code>Boolean</code>, etc&#8230;&#8203;) are supported by default and you can write type converters to support any other type (<code>File</code>, etc&#8230;&#8203;).</p>
+</div>
+<div class="sect2">
+<h3 id="_boolean">2.1. Boolean</h3>
+<div class="paragraph">
+<p>When a Parameter annotation is found on a field of type <code>boolean</code> or <code>Boolean</code>, JCommander interprets it as an option with an arity of 0:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-debug", description = "Debug mode")
+private boolean debug = false;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Such a parameter does not require any additional parameter on the command line and if it&#8217;s detected during parsing, the corresponding field will be set to true. If you want to define a boolean parameter that&#8217;s true by default, you can declare it as having an arity of 1. Users will then have to specify the value they want explicitly:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-debug", description = "Debug mode", arity = 1)
+private boolean debug = true;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Invoke with either of:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">program -debug true
+program -debug false</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>When a Parameter annotation is found on a field of type <code>String</code>, <code>Integer</code>, <code>int</code>, <code>Long</code> or <code>long</code>, JCommander will parse the following parameter and it will attempt to cast it to the right type:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-log", description = "Level of verbosity")
+private Integer verbose = 1;</code></pre>
+</div>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">java Main -log 3</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>will cause the field verbose to receive the value 3. However:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java Main -log test</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>will cause an exception to be thrown.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_lists">2.2. Lists</h3>
+<div class="paragraph">
+<p>When a Parameter annotation is found on a field of type <code>List</code>, JCommander will interpret it as an option that can occur multiple times:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-host", description = "The host")
+private List&lt;String&gt; hosts = new ArrayList&lt;&gt;();</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>will allow you to parse the following command line:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java Main -host host1 -verbose -host host2</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>When JCommander is done parsing the line above, the field hosts will contain the strings <code>"host1"</code> and <code>"host2"</code>.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_password">2.3. Password</h3>
+<div class="paragraph">
+<p>If one of your parameters is a password or some other value that you do not wish to appear in your history or in clear, you can declare it of type password and JCommander will then ask you to enter it in the console:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class ArgsPassword {
+ @Parameter(names = "-password", description = "Connection password", password = true)
+ private String password;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>When you run your program, you will get the following prompt:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">Value for -password (Connection password):</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You will need to type the value at this point before JCommander resumes.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_echo_input">2.4. Echo Input</h3>
+<div class="paragraph">
+<p>In Java 6, by default, you will not be able to see what you type for passwords entered at the prompt (Java 5 and lower will always show the password). However, you can override this by setting echoInput to <code>true</code> (default is <code>false</code> and this setting only has an effect when password is <code>true</code>):</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class ArgsPassword {
+ @Parameter(names = "-password", description = "Connection password", password = true, echoInput = true)
+ private String password;
+}</code></pre>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_custom_types_converters_and_splitters">3. Custom types (converters and splitters)</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>To bind parameters to custom types or change the way how JCommander splits parameters (default is to split via comma) JCommander provides two
+interfaces <code>IStringConverter</code> and <code>IParameterSplitter</code>.</p>
+</div>
+<div class="sect2">
+<h3 id="single-value">3.1. Custom types - Single value</h3>
+<div class="paragraph">
+<p>Use either the <code>converter=</code> attribute of the <code>@Parameter</code> or implement <code>IStringConverterFactory</code>.</p>
+</div>
+<div class="sect3">
+<h4 id="_by_annotation">3.1.1. By annotation</h4>
+<div class="paragraph">
+<p>By default, JCommander parses the command line into basic types only (strings, booleans, integers and longs). Very often, your application actually needs more complex types (such as files, host names, lists, etc.). To achieve this, you can write a type converter by implementing the following interface:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public interface IStringConverter&lt;T&gt; {
+ T convert(String value);
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>For example, here is a converter that turns a string into a File:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class FileConverter implements IStringConverter&lt;File&gt; {
+ @Override
+ public File convert(String value) {
+ return new File(value);
+ }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Then, all you need to do is declare your field with the correct type and specify the converter as an attribute:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-file", converter = FileConverter.class)
+File file;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>JCommander ships with a few common converters (for more info please see the implementations of <code>IStringConverter</code>).</p>
+</div>
+<div class="sect4">
+<h5 id="_note">Note</h5>
+<div class="paragraph">
+<p>If a converter is used for a <code>List</code> field:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-files", converter = FileConverter.class)
+List&lt;File&gt; files;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>And the application is called as follows:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java App -files file1,file2,file3</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>JCommander will split the string <code>file1,file2,file3</code> into <code>file1</code>, <code>file2</code>, <code>file3</code> and feed it one by one to the converter.</p>
+</div>
+<div class="paragraph">
+<p>For an alternative solution to parse a list of values, see <a href="#list-value">Custom types - List value</a>.</p>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_by_factory">3.1.2. By factory</h4>
+<div class="paragraph">
+<p>If the custom types you use appear multiple times in your application, having to specify the converter in each annotation can become tedious. To address this, you can use an <code>IStringConverterFactory</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public interface IStringConverterFactory {
+ &lt;T&gt; Class&lt;? extends IStringConverter&lt;T&gt;&gt; getConverter(Class&lt;T&gt; forType);
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>For example, suppose you need to parse a string representing a host and a port:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java App -target example.com:8080</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You define the holder class :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class HostPort {
+ public HostPort(String host, String port) {
+ this.host = host;
+ this.port = port;
}
- def main(args: Array[String]): Unit = {
- new JCommander(Args, args.toArray: _*)
- for (filename <- Args.file) {
- val f = new File(filename)
- printf("file: %s\n", f.getName)
+ final String host;
+ final Integer port;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>and the string converter to create instances of this class:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">class HostPortConverter implements IStringConverter&lt;HostPort&gt; {
+ @Override
+ public HostPort convert(String value) {
+ String[] s = value.split(":");
+ return new HostPort(s[0], Integer.parseInt(s[1]));
+ }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The factory is straightforward:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class Factory implements IStringConverterFactory {
+ public Class&lt;? extends IStringConverter&lt;?&gt;&gt; getConverter(Class forType) {
+ if (forType.equals(HostPort.class)) return HostPortConverter.class;
+ else return null;
+ }</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can now use the type <code>HostPort</code> as a parameter without any converterClass attribute:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class ArgsConverterFactory {
+ @Parameter(names = "-hostport")
+ private HostPort hostPort;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>All you need to do is add the factory to your JCommander object:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">ArgsConverterFactory a = new ArgsConverterFactory();
+JCommander jc = JCommander.newBuilder()
+ .addObject(a)
+ .addConverterFactory(new Factory())
+ .build()
+ .parse("-hostport", "example.com:8080");
+
+Assert.assertEquals(a.hostPort.host, "example.com");
+Assert.assertEquals(a.hostPort.port.intValue(), 8080);</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Another advantage of using string converter factories is that your factories can come from a dependency injection framework.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="list-value">3.2. Custom types - List value</h3>
+<div class="paragraph">
+<p>Use the <code>listConverter=</code> attribute of the <code>@Parameter</code> annotation and assign a custom <code>IStringConverter</code> implementation to convert a <code>String</code> into a <code>List</code> of values.</p>
+</div>
+<div class="sect3">
+<h4 id="_by_annotation_2">3.2.1. By annotation</h4>
+<div class="paragraph">
+<p>If your application needs a list of complex types, write a list type converter by implementing the same interface as before:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public interface IStringConverter&lt;T&gt; {
+ T convert(String value);
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>where <code>T</code> is a <code>List</code>.</p>
+</div>
+<div class="paragraph">
+<p>For example, here is a list converter that turns a string into a <code>List&lt;File&gt;</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class FileListConverter implements IStringConverter&lt;List&lt;File&gt;&gt; {
+ @Override
+ public List&lt;File&gt; convert(String files) {
+ String [] paths = files.split(",");
+ List&lt;File&gt; fileList = new ArrayList&lt;&gt;();
+ for(String path : paths){
+ fileList.add(new File(path));
}
+ return fileList;
+ }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Then, all you need to do is declare your field with the correct type and specify the list converter as an attribute:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-files", listConverter = FileListConverter.class)
+List&lt;File&gt; file;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Now if you call for application as in the following example:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java App -files file1,file2,file3</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The parameter <code>file1,file2,file3</code> is given to the <code>listConverter</code> and will the properly processed.</p>
+</div>
+<div class="paragraph">
+<p>JCommander ships with a default converter for <code>String</code> values.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_splitting">3.3. Splitting</h3>
+<div class="paragraph">
+<p>Use the <code>splitter=</code> attribute of the <code>@Parameter</code> annotation and assign a custom <code>IParameterSplitter</code> implementation to handle how parameters are split in sub-parts.</p>
+</div>
+<div class="sect3">
+<h4 id="_by_annotation_3">3.3.1. By annotation</h4>
+<div class="paragraph">
+<p>By default, JCommander tries to split parameters for <code>List</code> field types on commas.</p>
+</div>
+<div class="paragraph">
+<p>To split parameters on other characters, you can write a custom splitter by implementing the following interface:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public interface IParameterSplitter {
+ List&lt;String&gt; split(String value);
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>For example, here is a splitter that splits a string on semicolon:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public static class SemiColonSplitter implements IParameterSplitter {
+ public List&lt;String&gt; split(String value) {
+ return Arrays.asList(value.split(";"));
+ }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Then, all you need to do is declare your field with the correct type and specify the splitter as an attribute:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-files", converter = FileConverter.class, splitter = SemiColonSplitter.class)
+List&lt;File&gt; files;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>JCommander will split the string <code>file1;file2;file3</code> into <code>file1</code>, <code>file2</code>, <code>file3</code> and feed it one by one to the converter.</p>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_parameter_validation">4. Parameter validation</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Parameter validation can be performed in two different ways: at the individual parameter level or globally.</p>
+</div>
+<div class="sect2">
+<h3 id="_individual_parameter_validation">4.1. Individual parameter validation</h3>
+<div class="paragraph">
+<p>You can ask JCommander to perform early validation on your parameters by providing a class that implements the following interface:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">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;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Here is an example implementation that will make sure that the parameter is a positive integer:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class PositiveInteger implements IParameterValidator {
+ public void validate(String name, String value)
+ throws ParameterException {
+ int n = Integer.parseInt(value);
+ if (n &lt; 0) {
+ throw new ParameterException("Parameter " + name + " should be positive (found " + value +")");
+ }
+ }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Specify the name of a class implementing this interface in the <code>validateWith</code> attribute of your <code>@Parameter</code> annotations:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-age", validateWith = PositiveInteger.class)
+private Integer age;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Attempting to pass a negative integer to this option will cause a ParameterException to be thrown.</p>
+</div>
+<div class="paragraph">
+<p>Multiple validators may be specified:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-count", validateWith = { PositiveInteger.class, CustomOddNumberValidator.class })
+private Integer value;</code></pre>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_global_parameter_validation">4.2. Global parameter validation</h3>
+<div class="paragraph">
+<p>After parsing your parameters with JCommander, you might want to perform additional validation across these parameters, such as making sure that two mutually exclusive parameters are not both specified. Because of all the potential combinations involved in such validation, JCommander does not provide any annotation-based solution to perform this validation because such an approach would necessarily be very limited by the very nature of Java annotations. Instead, you should simply perform this validation in Java on all the arguments that JCommander just parsed.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_main_parameter">5. Main parameter</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>So far, all the <code>@Parameter</code> annotations we have seen had defined an attribute called <code>names</code>. You can define one (and at most one) parameter without any such attribute. This parameter needs to be a <code>List&lt;String&gt;</code> and it will contain all the parameters that are not options:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(description = "Files")
+private List&lt;String&gt; files = new ArrayList&lt;&gt;();
+
+@Parameter(names = "-debug", description = "Debugging level")
+private Integer debug = 1;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>will allow you to parse:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java Main -debug file1 file2</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>and the field files will receive the strings <code>"file1"</code> and <code>"file2"</code>.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_private_parameters">6. Private parameters</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Parameters can be private:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class ArgsPrivate {
+ @Parameter(names = "-verbose")
+ private Integer verbose = 1;
+
+ public Integer getVerbose() {
+ return verbose;
}
}
-</pre>
+ArgsPrivate args = new ArgsPrivate();
+JCommander.newBuilder()
+ .addObject(args)
+ .build()
+ .parse("-verbose", "3");
+Assert.assertEquals(args.getVerbose().intValue(), 3);</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_parameter_separators">7. Parameter separators</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>By default, parameters are separated by spaces, but you can change this setting to allow different separators:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java Main -log:3</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>or</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java Main -level=42</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You define the separator with the @Parameters annotation:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameters(separators = "=")
+public class SeparatorEqual {
+ @Parameter(names = "-level")
+ private Integer level = 2;
+}</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_multiple_descriptions">8. Multiple descriptions</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>You can spread the description of your parameters on more than one class. For example, you can define the following two classes:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">public class ArgsMaster {
+ @Parameter(names = "-master")
+ private String master;
+}
-<h2><a class="section" name="Groovy">JCommander in Groovy</a></h2>
+public class ArgsSlave {
+ @Parameter(names = "-slave")
+ private String slave;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>and pass these two objects to JCommander:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">ArgsMaster m = new ArgsMaster();
+ArgsSlave s = new ArgsSlave();
+String[] argv = { "-master", "master", "-slave", "slave" };
+JCommander.newBuilder()
+ .addObject(new Object[] { m , s })
+ .build()
+ .parse(argv);
-Here is a quick example of how to use JCommander in Groovy (courtesy of Paul King):
+Assert.assertEquals(m.master, "master");
+Assert.assertEquals(s.slave, "slave");</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="__syntax">9. @ syntax</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>JCommander supports the @ syntax, which allows you to put all your options into a file and pass this file as parameter:</p>
+</div>
+<div id="app-listing" class="listingblock">
+<div class="title">/tmp/parameters</div>
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">-verbose
+file1
+file2
+file3</code></pre>
+</div>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java Main @/tmp/parameters</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_arities_multiple_values_for_parameters">10. Arities (multiple values for parameters)</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_fixed_arities">10.1. Fixed arities</h3>
+<div class="paragraph">
+<p>If some of your parameters require more than one value, such as the following example where two values are expected after -pairs:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java Main -pairs slave master foo.xml</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>then you need to define your parameter with the arity attribute and make that parameter a <code>List&lt;String&gt;</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-pairs", arity = 2, description = "Pairs")
+private List&lt;String&gt; pairs;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You don&#8217;t need to specify an arity for parameters of type <code>boolean</code> or <code>Boolean</code> (which have a default arity of 0) and of types <code>String</code>, <code>Integer</code>, <code>int</code>, <code>Long</code> and <code>long</code> (which have a default arity of 1).</p>
+</div>
+<div class="paragraph">
+<p>Also, note that only <code>List&lt;String&gt;</code> is allowed for parameters that define an arity. You will have to convert these values yourself if the parameters you need are of type <code>Integer</code> or other (this limitation is due to Java&#8217;s erasure).</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_variable_arities">10.2. Variable arities</h3>
+<div class="paragraph">
+<p>You can specify that a parameter can receive an indefinite number of parameters, up to the next option. For example:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">program -foo a1 a2 a3 -bar
+program -foo a1 -bar</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Such a parameter can be parsed in two different ways.</p>
+</div>
+<div class="sect3">
+<h4 id="_with_a_list">10.2.1. With a list</h4>
+<div class="paragraph">
+<p>If the number of following parameters is unknown, your parameter must be of type <code>List&lt;String&gt;</code> and you
+need to set the boolean <code>variableArity</code> to <code>true</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-foo", variableArity = true)
+public List&lt;String&gt; foo = new ArrayList&lt;&gt;();</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_with_a_class">10.2.2. With a class</h4>
+<div class="paragraph">
+<p>Alternatively, you can define a class in which the following parameters will be stored, based on their order
+of appearance:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">static class MvParameters {
+ @SubParameter(order = 0)
+ String from;
+
+ @SubParameter(order = 1)
+ String to;
+}
+@Test
+public void arity() {
+ class Parameters {
+ @Parameter(names = {"--mv"}, arity = 2)
+ private MvParameters mvParameters;
+ }
-<pre class="brush: java">
-import com.beust.jcommander.*
+ Parameters args = new Parameters();
+ JCommander.newBuilder()
+ .addObject(args)
+ .args(new String[]{"--mv", "from", "to"})
+ .build();
-class Args {
- @Parameter(names = ["-f", "--file"], description = "File to load. Can be specified multiple times.")
- List&lt;String&gt; file
-}
+ Assert.assertNotNull(args.mvParameters);
+ Assert.assertEquals(args.mvParameters.from, "from");
+ Assert.assertEquals(args.mvParameters.to, "to");
+}</code></pre>
+</div>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_multiple_option_names">11. Multiple option names</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>You can specify more than one option name:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = { "-d", "--outputDirectory" }, description = "Directory")
+private String outputDirectory;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>will allow both following syntaxes:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ java Main -d /tmp
+$ java Main --outputDirectory /tmp</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_other_option_configurations">12. Other option configurations</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>You can configure how options are looked up in a few different ways:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><code>JCommander#setCaseSensitiveOptions(boolean)</code>: specify whether options are case sensitive. If you call this method with <code>false</code>, then <code>"-param"</code> and <code>"-PARAM"</code> are considered equal.</p>
+</li>
+<li>
+<p><code>JCommander#setAllowAbbreviatedOptions(boolean)</code>: specify whether users can pass abbreviated options. If you call this method with <code>true</code> then users can pass <code>"-par"</code> to specify an option called <code>-param</code>. JCommander will throw a <code>ParameterException</code> if the abbreviated name is ambiguous.</p>
+</li>
+</ul>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_required_and_optional_parameters">13. Required and optional parameters</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>If some of your parameters are mandatory, you can use the <code>required</code> attribute (which default to <code>false</code>):</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-host", required = true)
+private String host;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If this parameter is not specified, JCommander will throw an exception telling you which options are missing.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_default_values">14. Default values</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>The most common way to specify a default value for your parameters is to initialize the field at declaration time:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">private Integer logLevel = 3;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>For more complicated cases, you might want to be able to reuse identical default values across several main classes or be able to specify these default values in a centralized location such as a <code>.properties</code> or an XML file. In this case, you can use an <code>IDefaultProvider</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">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);
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>By passing an implementation of this interface to your JCommander object, you can now control which default value will be used for your options. Note that the value returned by this method will then be passed to a string converter, if any is applicable, thereby allowing you to specify default values for any types you need.</p>
+</div>
+<div class="paragraph">
+<p>For example, here is a default provider that will assign a default value of 42 for all your parameters except <code>"-debug"</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">private static final IDefaultProvider DEFAULT_PROVIDER = new IDefaultProvider() {
+ @Override
+ public String getDefaultValueFor(String optionName) {
+ return "-debug".equals(optionName) ? "false" : "42";
+ }
+};
-new Args().with {
- new JCommander(it, args)
- file.each { println "file: ${new File(it).name}" }
-}
-</pre>
+// ...
-<h2><a class="section" name="More_examples">More examples</a></h2>
+JCommander jc = JCommander.newBuilder()
+ .addObject(new Args())
+ .defaultProvider(DEFAULT_PROVIDER)
+ .build()</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_help_parameter">15. Help parameter</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>If one of your parameters is used to display some help or usage, you need use the help attribute:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "--help", help = true)
+private boolean help;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If you omit this boolean, JCommander will instead issue an error message when it tries to validate your command and it finds that you didn&#8217;t specify some of the required parameters.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_more_complex_syntaxes_commands">16. More complex syntaxes (commands)</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Complex tools such as <code>git</code> or <code>svn</code> understand a whole set of commands, each of which with their own specific syntax:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">$ git commit --amend -m "Bug fix"</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Words such as <code>"commit"</code> above are called "commands" in JCommander, and you can specify them by creating one arg object per command:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameters(separators = "=", commandDescription = "Record changes to the repository")
+private class CommandCommit {
-TestNG uses JCommander to parse its own command line, here is <a href="http://github.com/cbeust/testng/blob/master/src/main/java/org/testng/CommandLineArgs.java">its definition file</a>.
+ @Parameter(description = "The list of files to commit")
+ private List&lt;String&gt; files;
-<h2><a class="section" name="Mailing_list">Mailing list</a></h2>
+ @Parameter(names = "--amend", description = "Amend")
+ private Boolean amend = false;
-Join the <a href="http://groups.google.com/group/jcommander">JCommander Google group</a> if you are interested in discussions about JCommander.
+ @Parameter(names = "--author")
+ private String author;
+}
-<h2><a class="section" name="Javadocs">Javadocs</a></h2>
+@Parameters(commandDescription = "Add file contents to the index")
+public class CommandAdd {
-The Javadocs for JCommander can be found <a href="apidocs/">here</a>.
+ @Parameter(description = "File patterns to add to the index")
+ private List&lt;String&gt; patterns;
-<h2><a class="section" name="License">License</a></h2>
+ @Parameter(names = "-i")
+ private Boolean interactive = false;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Then you register these commands with your JCommander object. After the parsing phase, you call <code>getParsedCommand()</code> on your JCommander object, and based on the command that is returned, you know which arg object to inspect (you can still use a main arg object if you want to support options before the first command appears on the command line):</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">CommandMain cm = new CommandMain();
+CommandAdd add = new CommandAdd();
+CommandCommit commit = new CommandCommit();
+JCommander jc = JCommander.newBuilder()
+ .addObject(cm)
+ .addCommand("add", add);
+ .addCommand("commit", commit);
+ .build();
-JCommander is released under the <a
-href="https://github.com/cbeust/jcommander/blob/master/license.txt">Apache 2.0</a> license.
+jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java");
-<h2><a class="section" name="Download">Download</a></h2>
+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"));</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_exception">17. Exception</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Whenever JCommander detects an error, it will throw a <code>ParameterException</code>. Note that this is a Runtime Exception, since your application is probably not initialized correctly at this point. Also, <code>ParameterException</code> contains the
+<code>JCommander</code> instance and you can also invoke <code>usage()</code> on it if you need to display some help.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_usage">18. Usage</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>You can invoke <code>usage()</code> on the JCommander instance that you used to parse your command line in order to generate a summary of all the options that your program understands:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">Usage: &lt;main class&gt; [options]
+ Options:
+ -debug Debug mode (default: false)
+ -groups Comma-separated list of group names to be run
+ * -log, -verbose Level of verbosity (default: 1)
+ -long A long number (default: 0)</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can customize the name of your program by calling <code>setProgramName()</code> on your JCommander object. Options preceded by an asterisk are required.</p>
+</div>
+<div class="paragraph">
+<p>You can also specify the order in which each option should be displayed when calling <code>usage()</code> by setting the <code>order</code> attribute of the <code>@Parameter</code> annotation:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">class Parameters {
+ @Parameter(names = "--importantOption", order = 0)
+ private boolean a;
+
+ @Parameter(names = "--lessImportantOption", order = 3)
+ private boolean b;</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_hiding_parameters">19. Hiding parameters</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>If you don&#8217;t want certain parameters to appear in the usage, you can mark them as "hidden":</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameter(names = "-debug", description = "Debug mode", hidden = true)
+private boolean debug = false;</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_internationalization">20. Internationalization</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>You can internationalize the descriptions of your parameters. First you use the <code>@Parameters</code> annotation at the top of your class to define the name of your message bundle, and then you use the <code>descriptionKey</code> attribute instead of description on all the <code>@Parameters</code> that require translations. This <code>descriptionKey</code> is the key to the string into your message bundle:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@Parameters(resourceBundle = "MessageBundle")
+private class ArgsI18N2 {
+ @Parameter(names = "-host", description = "Host", descriptionKey = "host")
+ String hostName;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Your bundle needs to define this key:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-bash" data-lang="bash">host: Hôte</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>JCommander will then use the default locale to resolve your descriptions.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_parameter_delegates">21. Parameter delegates</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>If you are writing many different tools in the same project, you will probably find that most of these tools can share configurations. While you can use inheritance with your objects to avoid repeating this code, the restriction to single inheritance of implementation might limit your flexibility. To address this problem, JCommander supports parameter delegates.</p>
+</div>
+<div class="paragraph">
+<p>When JCommander encounters an object annotated with <code>@ParameterDelegate</code> in one of your objects, it acts as if this object had been added as a description object itself:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">class Delegate {
+ @Parameter(names = "-port")
+ private int port;
+}
-You can download JCommander from the following locations:
+class MainParams {
+ @Parameter(names = "-v")
+ private boolean verbose;
-<ul>
- <li><a href="http://github.com/cbeust/jcommander">Source on github</a></li>
- <li>If you are using Maven, add the following dependency to your <tt>pom.xml</tt>:
+ @ParametersDelegate
+ private Delegate delegate = new Delegate();
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The example above specifies a delegate parameter Delegate which is then referenced in MainParams. You only need to add a <code>MainParams</code> object to your
+JCommander configuration in order to use the delegate:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">MainParams p = new MainParams();
+JCommander.newBuilder().addObject(p).build()
+ .parse("-v", "-port", "1234");
+Assert.assertTrue(p.isVerbose);
+Assert.assertEquals(p.delegate.port, 1234);</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_dynamic_parameters">22. Dynamic parameters</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>JCommander allows you to specify parameters that are not known at compile time, such as "-Da=b -Dc=d". Such parameters are specified with the <code>@DynamicParameter</code> annotation and must be of type <code>Map&lt;String, String&gt;</code>. Dynamic parameters are allowed to appear multiple times on the command line:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-java" data-lang="java">@DynamicParameter(names = "-D", description = "Dynamic parameters go here")
+private Map&lt;String, String&gt; params = new HashMap&lt;&gt;();</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can specify a different assignment string than = by using the attribute assignment.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_jcommander_in_other_languages">23. JCommander in other languages</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_kotlin">23.1. Kotlin</h3>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-kotlin" data-lang="kotlin">class Args {
+ @Parameter
+ var targets: List&lt;String&gt; = arrayListOf()
+
+ @Parameter(names = arrayOf("-bf", "--buildFile"), description = "The build file")
+ var buildFile: String? = null
+
+ @Parameter(names = arrayOf("--checkVersions"),
+ description = "Check if there are any newer versions of the dependencies")
+ var checkVersions = false
+}</code></pre>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_groovy">23.2. Groovy</h3>
+<div class="paragraph">
+<p>Courtesy of Paul King:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import com.beust.jcommander.*
- <pre class="brush: xml">
+class Args {
+ @Parameter(names = ["-f", "--file"], description = "File to load. Can be specified multiple times.")
+ List&lt;String&gt; file
+}
-<dependency>
+new Args().with {
+ JCommander.newBuilder().addObject(it).build().parse(argv)
+ file.each { println "file: ${new File(it).name}" }
+}</code></pre>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_more_examples">24. More examples</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Here are the description files for a few projects that use JCommander:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><a href="https://github.com/cbeust/testng/blob/master/src/main/java/org/testng/CommandLineArgs.java">TestNG</a></p>
+</li>
+<li>
+<p><a href="https://github.com/cbeust/kobalt/blob/master/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/Args.kt">Kobalt</a></p>
+</li>
+</ul>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_mailing_list">25. Mailing list</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Join the <a href="http://groups.google.com/group/jcommander">JCommander Google group</a> if you are interested in discussions about JCommander.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_javadocs">26. Javadocs</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>The Javadocs for JCommander can be found <a href="http://jcommander.org/apidocs/">here</a>.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_license">27. License</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>JCommander is released under the <a href="https://github.com/cbeust/jcommander/blob/master/license.txt">Apache 2.0 license</a>.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_download">28. Download</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>You can download JCommander from the following locations:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><a href="http://github.com/cbeust/jcommander">Source on github</a></p>
+</li>
+<li>
+<p>Kobalt</p>
+</li>
+</ul>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">compile("com.beust:jcommander:1.71")</code></pre>
+</div>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>Gradle</p>
+</li>
+</ul>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">compile "com.beust:jcommander:1.71"</code></pre>
+</div>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>Maven:</p>
+</li>
+</ul>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code class="language-xml" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;com.beust&lt;/groupId&gt;
&lt;artifactId&gt;jcommander&lt;/artifactId&gt;
- &lt;version&gt;1.30&lt;/version&gt;
-</dependency>
- </pre>
-
-</ul>
-
+ &lt;version&gt;1.71&lt;/version&gt;
+&lt;/dependency&gt;</code></pre>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div id="footer">
+<div id="footer-text">
+Last updated 2017-05-02 10:07:41 PDT
+</div>
+</div>
+<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css">
+<script src="https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.js"></script>
+<script>prettyPrint()</script>
</body>
-
-<script type="text/javascript" src="http://beust.com/toc.js"></script>
-<script type="text/javascript"> generateToc(); </script>
-
-</html>
+</html> \ No newline at end of file
diff --git a/doc/old-index.html b/doc/old-index.html
new file mode 100644
index 0000000..4472088
--- /dev/null
+++ b/doc/old-index.html
@@ -0,0 +1,938 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html> <head>
+<title>JCommander</title>
+
+<!--
+<link rel="stylesheet" href="testng.css" type="text/css" />
+-->
+
+ <link type="text/css" rel="stylesheet" href="http://beust.com/beust.css" />
+ <link type="text/css" rel="stylesheet" href="http://jcommander.org/jcommander.css" />
+ <script type="text/javascript" src="http://beust.com/prettify.js"></script>
+
+ <script type="text/javascript" src="http://beust.com/scripts/shCore.js"></script>
+ <script type="text/javascript" src="http://beust.com/scripts/shBrushJava.js"></script>
+ <script type="text/javascript" src="http://beust.com/scripts/shBrushXml.js"></script>
+ <script type="text/javascript" src="http://beust.com/scripts/shBrushBash.js"></script>
+ <script type="text/javascript" src="http://beust.com/scripts/shBrushPlain.js"></script>
+ <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shCore.css"/>
+ <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shThemeCedric.css"/>
+ <script type="text/javascript" src="http://beust.com/toc.js"></script>
+ <script type="text/javascript">
+ SyntaxHighlighter.config.clipboardSwf = 'scripts/clipboard.swf';
+ SyntaxHighlighter.defaults['gutter'] = false;
+ SyntaxHighlighter.all();
+ </script>
+</head>
+
+<table width="100%">
+ <tr>
+ <td align="center">
+<h1>JCommander</h1>
+<h2>Because life is too short to parse command line parameters</h2>
+<h3>
+ <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
+ <input type="hidden" name="cmd" value="_donations">
+ <input type="hidden" name="business" value="cedric@beust.com">
+ <input type="hidden" name="lc" value="US">
+ <input type="hidden" name="item_name" value="Cedric Beust">
+ <input type="hidden" name="no_note" value="0">
+ <input type="hidden" name="currency_code" value="USD">
+ <input type="hidden" name="bn" value="PP-DonationsBF:btn_donate_LG.gif:NonHostedGuest">
+ <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
+ <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
+ </form>
+</h3>
+ </td>
+ </tr>
+ <tr>
+ <td align="right">
+ Created: July 13th, 2010
+ </td>
+ </tr>
+ <tr>
+ <td align="right">
+ Last updated: July 6th, 2015
+ </td>
+ </tr>
+ <tr><td align="right"><a href="mailto:cedric@beust.com">C&eacute;dric Beust</a></td></tr>
+</table>
+
+<h2>Table of contents</h2>
+<div id="table-of-contents">
+</div>
+
+
+
+<h2><a class="section" name="Overview">Overview</a></h2>
+
+JCommander is a very small Java framework that makes it trivial to parse command line parameters.
+<p>
+You annotate fields with descriptions of your options:
+
+<pre class="brush: java">
+import com.beust.jcommander.Parameter;
+
+public class JCommanderExample {
+ @Parameter
+ private List&lt;String&gt; parameters = new ArrayList&lt;&gt;();
+
+ @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity")
+ private Integer verbose = 1;
+
+ @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+ private String groups;
+
+ @Parameter(names = "-debug", description = "Debug mode")
+ private boolean debug = false;
+}
+</pre>
+
+and then you simply ask JCommander to parse:
+
+<pre class="brush: java">
+JCommanderExample jct = new JCommanderExample();
+String[] argv = { "-log", "2", "-groups", "unit" };
+new JCommander(jct, argv);
+
+Assert.assertEquals(jct.verbose.intValue(), 2);
+</pre>
+
+An example that mirrors more of what you might see in the "real world" might
+look like this:
+
+<pre class="brush: java">
+class Main {
+ @Parameter(names={"--length", "-l"})
+ int length;
+ @Parameter(names={"--pattern", "-p"})
+ int pattern;
+
+ public static void main(String ... args) {
+ Main main = new Main();
+ new JCommander(main, args);
+ main.run();
+ }
+
+ public void run() {
+ System.out.printf("%d %d", length, pattern);
+ }
+}
+</pre>
+
+If you were to run <code>java Main -l 512 --pattern 2</code>, this would
+output:
+
+<pre>512 2</pre>
+
+<h2><a class="section" name="Types_of_options">Types of options</a></h2>
+
+The fields representing your parameters can be of any type. Basic types (<tt>Integer</tt>, <tt>Boolean</tt/>., etc...) are supported by default and you can write type converters to support any other type (<tt>File</tt>, etc...).
+
+<h4>Boolean</h4>
+
+When a <tt>Parameter</tt> annotation is found on a field of type <tt>boolean</tt> or <tt>Boolean</tt>, JCommander interprets it as an option with an <em>arity</em> of 0:
+
+<pre class="brush: java">
+@Parameter(names = "-debug", description = "Debug mode")
+private boolean debug = false;
+</pre>
+
+Such a parameter does not require any additional parameter on the command line and if it's detected during parsing, the corresponding field will be set to <tt>true</tt>.
+
+<p>
+
+ If you want to define a boolean parameter that's <tt>true</tt> by default, you can declare it as having an arity of 1. Users will then have to specify the value they want explicitly:
+
+ <pre class="brush: java">
+ @Parameter(names = "-debug", description = "Debug mode", arity = 1)
+ private boolean debug = true;
+ </pre>
+
+ Invoke with either of:
+
+ <pre class="brush: plain">
+ program -debug true
+ program -debug false
+ </pre>
+
+<h4>String, Integer, Long</h4>
+
+When a <tt>Parameter</tt> annotation is found on a field of type <tt>String</tt>, <tt>Integer</tt>, <tt>int</tt>, <tt>Long</tt> or <tt>long</tt>, JCommander will parse the following parameter and it will attempt to cast it to the right type:
+
+<pre class="brush: java">
+@Parameter(names = "-log", description = "Level of verbosity")
+private Integer verbose = 1;
+</pre>
+
+<pre class="brush: plain">
+java Main -log 3
+</pre>
+
+will cause the field <tt>verbose</tt> to receive the value 3, however:
+
+<pre class="brush: plain">
+java Main -log test
+</pre>
+
+will cause an exception to be thrown.
+
+<h4>Lists</h4>
+
+When a <tt>Parameter</tt> annotation is found on a field of type <tt>List</tt>, JCommander will interpret it as an option that can occur multiple times:
+
+<pre class="brush: java">
+@Parameter(names = "-host", description = "The host")
+private List&lt;String&gt; hosts = new ArrayList&lt;&gt;();
+</pre>
+
+will allow you to parse the following command line:
+
+<pre class="brush: plain">
+java Main -host host1 -verbose -host host2
+</pre>
+
+When JCommander is done parsing the line above, the field <tt>hosts</tt> will contain the strings "host1" and "host2".
+
+<h4>Password</h4>
+
+If one of your parameters is a password or some other value that you do not wish to appear in your history or in clear, you can declare it of type <tt>password</tt> and JCommander will then ask you to enter it in the console:
+
+<pre class="brush: java">
+public class ArgsPassword {
+ @Parameter(names = "-password", description = "Connection password", password = true)
+ private String password;
+}
+</pre>
+
+When you run your program, you will get the following prompt:
+
+<pre class="brush: plain">
+Value for -password (Connection password):
+</pre>
+
+You will need to type the value at this point before JCommander resumes.
+
+<h4>Echo Input</h4>
+
+In Java 6, by default, you will not be able to see what you type for passwords entered at the prompt (Java 5 and lower will always show the password). However, you can override this by setting <tt>echoInput</tt> to "true" (default is "false" and this setting only has an effect when <tt>password</tt> is "true"):
+<pre class="brush: java">
+public class ArgsPassword {
+ @Parameter(names = "-password", description = "Connection password", password = true, echoInput = true)
+ private String password;
+}
+</pre>
+
+<h2><a class="section" name="Custom_types">Custom types</a></h2>
+
+<h3>By annotation</h3>
+
+By default, JCommander parses the command line into basic types only (strings, booleans, integers and longs). Very often, your application actually needs more complex types, such as files, host names, lists, etc... To achieve this, you can write a type converter by implementing the following interface:
+
+<pre class="brush: java">
+public interface IStringConverter&lt;T&gt; {
+ T convert(String value);
+}
+</pre>
+
+For example, here is a converter that turns a string into a <tt>File</tt>:
+
+<pre class="brush: java">
+public class FileConverter implements IStringConverter&lt;File&gt; {
+ @Override
+ public File convert(String value) {
+ return new File(value);
+ }
+}
+</pre>
+
+Then, all you need to do is declare your field with the correct type and specify the converter as an attribute:
+
+<pre class="brush: java">
+@Parameter(names = "-file", converter = FileConverter.class)
+File file;
+</pre>
+
+JCommander ships with a few common converters (e.g. one that turns a comma separated list into a <tt>List&lt;String&gt;)</tt>.
+
+<h3>By factory</h3>
+
+If the custom types you use appear multiple times in your application, having to specify the converter in each annotation can become tedious. To address this, you can use an <tt>IStringConverterFactory</tt>:
+
+<pre class="brush: java">
+public interface IStringConverterFactory {
+ &lt;T&gt; Class&lt;? extends IStringConverter&lt;T&gt;&gt; getConverter(Class&lt;T&gt; forType);
+}
+</pre>
+
+For example, suppose you need to parse a string representing a host and a port:
+
+<pre class="brush: plain">
+java App -target example.com:8080
+</pre>
+
+You define the holder class :
+
+<pre class="brush: java">
+public class HostPort {
+ private String host;
+ private Integer port;
+}
+</pre>
+
+and the string converter to create instances of this class:
+
+<pre class="brush: java">
+class HostPortConverter implements IStringConverter&lt;HostPort&gt; {
+ @Override
+ 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;
+ }
+}
+</pre>
+
+The factory is straightforward:
+
+<pre class="brush: java">
+public class Factory implements IStringConverterFactory {
+ public Class&lt;? extends IStringConverter&lt;?&gt;&gt; getConverter(Class forType) {
+ if (forType.equals(HostPort.class)) return HostPortConverter.class;
+ else return null;
+ }
+</pre>
+
+You can now use the type <tt>HostPort</tt> as a parameter without any <tt>converterClass</tt> attribute:
+
+<pre class="brush: java">
+public class ArgsConverterFactory {
+ @Parameter(names = "-hostport")
+ private HostPort hostPort;
+}
+</pre>
+
+
+All you need to do is add the factory to your JCommander object:
+
+<pre class="brush: java">
+ ArgsConverterFactory a = new ArgsConverterFactory();
+ JCommander jc = new JCommander(a);
+ jc.addConverterFactory(new Factory());
+ jc.parse("-hostport", "example.com:8080");
+
+ Assert.assertEquals(a.hostPort.host, "example.com");
+ Assert.assertEquals(a.hostPort.port.intValue(), 8080);
+</pre>
+
+
+Another advantage of using string converter factories is that your factories can come from a dependency injection framework.
+
+
+<h3>By instance factory</h3>
+
+Since version 1.57, instance factories are supported:
+
+<pre class="brush: java">
+public interface IStringConverterInstanceFactory {
+ IStringConverter&lt;?&gt; getConverterInstance(Parameter parameter, Class&lt;?&gt; forType);
+}
+</pre>
+
+This allows to return converters using
+<a href="https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html">anonymous classes</a>,
+<a href="https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html">Java 8 lambda expressions</a>,
+among others.
+
+<h2><a class="section" name="Parameter_validation">Parameter validation</a></h2>
+
+Parameter validation can be performed in two different ways: at the individual parameter level or globally.
+
+<h3><a class="section" indent=".." id="individual_validation">Individual parameter validation</a></h3>
+You can ask JCommander to perform early validation on your parameters by providing a class that implements the following interface:
+
+<pre class="brush:java">
+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;
+}
+
+</pre>
+
+Here is an example implementation that will make sure that the parameter is a positive integer:
+
+<pre class="brush:java">
+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 +")");
+ }
+ }
+}
+</pre>
+
+Specify the name of a class implementing this interface in the <tt>validateWith</tt> attribute of your <tt>@Parameter</tt> annotations:
+
+<pre class="brush:java">
+@Parameter(names = "-age", validateWith = PositiveInteger.class)
+private Integer age;
+</pre>
+
+Attempting to pass a negative integer to this option will cause a <tt>ParameterException</tt> to be thrown.
+
+
+<h3><a class="section" indent=".." id="global_validation">Global parameter validation</a></h3>
+
+After parsing your parameters with JCommander, you might want to perform additional validation
+across these parameters, such as making sure that two mutually exclusive parameters are not
+both specified. Because of all the potential combinations involved in such validation,
+JCommander does not provide any annotation-based solution to perform this validation because such
+an approach would necessarily be very limited by the very nature of Java annotations. Instead,
+you should simple perform this validation in Java on all the arguments that JCommander
+just parsed.
+
+
+<h2><a class="section" name="Main_parameter">Main parameter</a></h2>
+So far, all the <tt>@Parameter</tt> annotations we have seen had defined an attribute called <tt>names</tt>. You can define one (and at most one) parameter without any such attribute. This parameter needs to be a <tt>List&lt;String&gt;</tt> and it will contain all the parameters that are not options:
+
+<pre class="brush: java">
+@Parameter(description = "Files")
+private List&lt;String&gt; files = new ArrayList&lt;&gt;();
+
+@Parameter(names = "-debug", description = "Debugging level")
+private Integer debug = 1;
+</pre>
+
+will allow you to parse:
+
+<pre class="brush: plain">
+java Main -debug file1 file2
+</pre>
+
+and the field <tt>files</tt> will receive the strings "file1" and "file2".
+
+<h2><a class="section" name="Private_parameters">Private parameters</a></h2>
+
+Parameters can be private:
+
+<pre class="brush: java">
+public class ArgsPrivate {
+ @Parameter(names = "-verbose")
+ private Integer verbose = 1;
+
+ public Integer getVerbose() {
+ return verbose;
+ }
+}
+</pre>
+
+<pre class="brush: java">
+ArgsPrivate args = new ArgsPrivate();
+new JCommander(args, "-verbose", "3");
+Assert.assertEquals(args.getVerbose().intValue(), 3);
+</pre>
+
+<h2><a class="section" name="Separators">Parameter separators</a></h2>
+
+By default, parameters are separated by spaces, but you can change this setting to allow different separators:
+
+<pre class="brush: plain">
+java Main -log:3
+</pre>
+
+or
+
+<pre class="brush: plain">
+java Main -level=42
+</pre>
+
+You define the separator with the <tt>@Parameters</tt> annotation:
+
+<pre class="brush: java">
+@Parameters(separators = "=")
+public class SeparatorEqual {
+ @Parameter(names = "-level")
+ private Integer level = 2;
+}
+</pre>
+
+
+
+
+
+<h2><a class="section" name="Multiple_descriptions">Multiple descriptions</a></h2>
+
+You can spread the description of your parameters on more than one
+class. For example, you can define the following two classes:
+
+<p>
+
+<h3 class="sourcetitle">ArgsMaster.java</h3>
+<pre class="brush: java">
+public class ArgsMaster {
+ @Parameter(names = "-master")
+ private String master;
+}
+</pre>
+
+<h3 class="sourcetitle">ArgsSlave.java</h3>
+<pre class="brush: java">
+public class ArgsSlave {
+ @Parameter(names = "-slave")
+ private String slave;
+}
+</pre>
+
+and pass these two objects to JCommander:
+
+<pre class="brush: java">
+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");
+</pre>
+
+
+<h2><a class="section" name="Syntax">@ syntax</a></h2>
+
+JCommander supports the @ syntax, which allows you to put all your options into a file and pass this file as parameter:
+
+<p>
+
+<div class="sourcetitle">/tmp/parameters</div>
+<pre class="brush: plain">
+-verbose
+file1
+file2
+file3
+</pre>
+<pre class="brush: plain">
+java Main @/tmp/parameters
+</pre>
+
+<p>The file is read using the default charset unless <code>JCommander#setAtFileCharset</code> had been called.</p>
+
+<p>Ths feature can be disabled by calling <code>JCommander#setExpandAtSign</code>.</p>
+
+<h2><a class="section" name="Arities">Arities (multiple values for parameters)</a></h2>
+
+<h3><a class="section" name="fixed-arities" indent="..">Fixed arities</a></h3>
+
+If some of your parameters require more than one value, such as the
+following example where two values are expected after <tt>-pairs</tt>:
+
+<pre class="brush: plain">
+java Main -pairs slave master foo.xml
+</pre>
+
+then you need to define your parameter with the <tt>arity</tt>
+attribute and make that parameter a <tt>List&lt;String&gt;</tt>:
+
+<pre class="brush: java">
+@Parameter(names = "-pairs", arity = 2, description = "Pairs")
+private List&lt;String&gt; pairs;
+</pre>
+
+You don't need to specify an arity for parameters of type
+<tt>boolean</tt> or <tt>Boolean</tt> (which have a default arity of 0)
+and of types <tt>String</tt>, <tt>Integer</tt>, <tt>int</tt>,
+<tt>Long</tt> and <tt>long</tt> (which have a default arity of 1).
+
+<p>
+Also, note that only <tt>List&lt;String&gt;</tt> is allowed for
+parameters that define an arity. You will have to convert these values
+yourself if the parameters you need are of type <tt>Integer</tt> or
+other (this limitation is due to Java's erasure).
+
+<h3><a class="section" name="variable-arities" indent="..">Variable arities</a></h3>
+
+You can specify that a parameter can receive an indefinite number of parameters, up to the next option. For example:
+
+<pre class="brush: java">
+program -foo a1 a2 a3 -bar
+program -foo a1 -bar
+</pre>
+
+Such a parameter must be of type <tt>List&lt;String&gt;</tt> and have the boolean <tt>variableArity</tt> set to <tt>true</tt>
+
+<pre class="brush: java">
+@Parameter(names = "-foo", variableArity = true)
+public List&lt;String&gt; foo = new ArrayList&lt;&gt;();
+</pre>
+
+<h2><a class="section" name="Multiple_option_names">Multiple option names</a></h2>
+
+You can specify more than one option name:
+
+<pre class="brush: java">
+
+ @Parameter(names = { "-d", "--outputDirectory" }, description = "Directory")
+ private String outputDirectory;
+
+</pre>
+
+will allow both following syntaxes:
+
+<pre class="brush: plain">
+java Main -d /tmp
+java Main --outputDirectory /tmp
+</pre>
+
+<h2><a class="section" name="Other option configurations">Other option configurations</a></h2>
+
+You can configure how options are looked up in a few different ways:
+
+<ul>
+ <li><tt>JCommander#setCaseSensitiveOptions(boolean)</tt>: specify whether options are case sensitive. If you call this method with <tt>false</tt>, then <tt>"-param"</tt> and
+ <tt>"-PARAM"</tt> are considered equal.
+ </li>
+ <li><tt>JCommander#setAllowAbbreviatedOptions(boolean)</tt>: specify whether users can
+ pass abbreviated options. If you call this method with <tt>true</tt> then users
+ can pass <tt>"-par"</tt> to specify an option called <tt>-param</tt>. JCommander will
+ throw a <tt>ParameterException</tt> if the abbreviated name is ambiguous.
+ </li>
+</ul>
+
+<h2><a class="section" name="Required_and_optional">Required and optional parameters</a></h2>
+
+If some of your parameters are mandatory, you can use the
+<tt>required</tt> attribute (which default to <tt>false</tt>):
+
+<pre class="brush: java">
+
+ @Parameter(names = "-host", required = true)
+ private String host;
+
+</pre>
+
+If this parameter is not specified, JCommander will throw an exception
+telling you which options are missing.
+
+<h2><a class="section" name="Default_values">Default values</a></h2>
+
+The most common way to specify a default value for your parameters is to initialize the field at declaration time:
+
+<pre class="brush: java">
+private Integer logLevel = 3;
+</pre>
+
+For more complicated cases, you might want to be able to reuse identical default values across several main classes or be able to specify these default values in a centralized location such as a .properties or an XML fie. In this case, you can use an <tt>IDefaultProvider</tt>
+
+<pre class="brush: java">
+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);
+}
+</pre>
+
+By passing an implementation of this interface to your <tt>JCommander</tt> object, you can now control which default value will be used for your options. Note that the value returned by this method will then be passed to a string converter, if any is applicable, thereby allowing you to specify default values for any types you need.
+
+<p>
+
+For example, here is a default provider that will assign a default value of 42 for all your parameters except "-debug":
+
+<pre class="brush: java">
+private static final IDefaultProvider DEFAULT_PROVIDER = new IDefaultProvider() {
+ @Override
+ public String getDefaultValueFor(String optionName) {
+ return "-debug".equals(optionName) ? "false" : "42";
+ }
+};
+
+// ...
+
+JCommander jc = new JCommander(new Args());
+jc.setDefaultProvider(DEFAULT_PROVIDER);
+</pre>
+
+<h2><a class="section" name="Help_parameter">Help parameter</a></h2>
+
+If one of your parameters is used to display some help or usage, you need use the <tt>help</tt> attribute:
+
+<pre class="brush: java">
+ @Parameter(names = "--help", help = true)
+ private boolean help;
+</pre>
+
+If you omit this boolean, JCommander will instead issue an error message when it tries to validate your command and it finds that you didn't specify some of the required parameters.
+
+<h2><a class="section" name="Complex">More complex syntaxes (commands)</a></h2>
+
+Complex tools such as <tt>git</tt> or <tt>svn</tt> understand a whole set of commands, each of which with their own specific syntax:
+
+<pre class="brush: plain">
+ git commit --amend -m "Bug fix"
+</pre>
+
+Words such as "commit" above are called "commands" in JCommander, and you can specify them by creating one arg object per command:
+
+<pre class="brush: java">
+@Parameters(separators = "=", commandDescription = "Record changes to the repository")
+private class CommandCommit {
+
+ @Parameter(description = "The list of files to commit")
+ private List&lt;String&gt; files;
+
+ @Parameter(names = "--amend", description = "Amend")
+ private Boolean amend = false;
+
+ @Parameter(names = "--author")
+ private String author;
+}
+</pre>
+
+<pre class="brush: java">
+@Parameters(commandDescription = "Add file contents to the index")
+public class CommandAdd {
+
+ @Parameter(description = "File patterns to add to the index")
+ private List&lt;String&gt; patterns;
+
+ @Parameter(names = "-i")
+ private Boolean interactive = false;
+}
+</pre>
+
+Then you register these commands with your JCommander object. After the parsing phase, you call <tt>getParsedCommand()</tt> on your JCommander object, and based on the command that is returned, you know which arg object to inspect (you can still use a main arg object if you want to support options before the first command appears on the command line):
+
+<pre class="brush: java">
+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");
+
+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"));
+</pre>
+
+<h2><a class="section" name="Exceptions">Exception</a></h2>
+
+Whenever JCommander detects an error, it will throw a
+<tt>ParameterException</tt>. Note that this is a Runtime Exception,
+since your application is probably not initialized correctly at this
+point.
+
+
+<h2><a class="section" name="Usage">Usage</a></h2>
+
+You can invoke <tt>usage()</tt> on the <tt>JCommander</tt> instance that you used to parse your command line in order to generate a summary of all the options that your program understands:
+
+<pre class="brush: plain">
+Usage: &lt;main class&gt; [options]
+ Options:
+ -debug Debug mode (default: false)
+ -groups Comma-separated list of group names to be run
+ * -log, -verbose Level of verbosity (default: 1)
+ -long A long number (default: 0)
+</pre>
+
+You can customize the name of your program by calling <tt>setProgramName()</tt> on your <tt>JCommander</tt> object.
+
+Options preceded by an asterisk are required.
+
+<h2><a class="section" name="Hiding">Hiding parameters</a></h2>
+
+If you don't want certain parameters to appear in the usage, you can mark them as "hidden":
+
+<pre class="brush: java">
+@Parameter(names = "-debug", description = "Debug mode", hidden = true)
+private boolean debug = false;
+</pre>
+
+<h2><a class="section" name="Internationalization">Internationalization</a></h2>
+
+You can internationalize the descriptions of your parameters.
+
+<p>
+
+First you use the <tt>@Parameters</tt> annotation at the top of your class to define the name of your message bundle, and then you use the <tt>descriptionKey</tt> attribute instead of <tt>description</tt> on all the <tt>@Parameters</tt> that require translations. This <tt>descriptionKey</tt> is the key to the string into your message bundle:
+
+<h3 class="sourcetitle">I18N.java</h3>
+<pre class="brush:java">
+@Parameters(resourceBundle = "MessageBundle")
+private class ArgsI18N2 {
+ @Parameter(names = "-host", description = "Host", descriptionKey = "host")
+ String hostName;
+}
+</pre>
+
+Your bundle needs to define this key:
+
+<br>
+
+<h3 class="sourcetitle">MessageBundle_fr_FR.properties</h3>
+<pre class="brush: plain">
+host: H&ocirc;te
+</pre>
+
+JCommander will then use the default locale to resolve your descriptions.
+
+<h2><a class="section" name="Parameter_delegates">Parameter delegates</a></h2>
+
+If you are writing many different tools in the same project, you will probably find that most of these tools can share configurations. While you can use inheritance with your objects to avoid repeating this code, the restriction to single inheritance of implementation might limit your flexibility. To address this problem, JCommander supports parameter delegates.
+
+<p>
+
+When JCommander encounters an object annotated with <tt>@ParameterDelegate</tt> in one of your objects, it acts as if this object had been added as a description object itself:
+
+<pre class="brush: java">
+class Delegate {
+ @Parameter(names = "-port")
+ private int port;
+}
+
+class MainParams {
+ @Parameter(names = "-v")
+ private boolean verbose;
+
+ @ParametersDelegate
+ private Delegate delegate = new Delegate();
+}
+</pre>
+
+The example above specifies a delegate parameter <tt>Delegate</tt> which is then referenced in <tt>MainParams</tt>. You only need to add a <tt>MainParams</tt> object to your JCommander configuration in order to use the delegate:
+
+<pre class="brush: java">
+MainParams p = new MainParams();
+new JCommander(p).parse("-v", "-port", "1234");
+Assert.assertTrue(p.isVerbose);
+Assert.assertEquals(p.delegate.port, 1234);
+</pre>
+
+<h2><a class="section" name="DynamicParameters">Dynamic parameters</a></h2>
+
+JCommander allows you to specify parameters that are not known at compile time, such as <tt>"-Da=b -Dc=d"</tt>. Such parameters are specified with the <tt><a href="apidocs/com/beust/jcommander/DynamicParameter.html">@DynamicParameter</a></tt> annotation and must be of type <tt>Map&lt;String, String&gt;</tt>. Dynamic parameters are allowed to appear multiple times on the command line:
+
+<pre class="brush: java">
+@DynamicParameter(names = "-D", description = "Dynamic parameters go here")
+private Map&lt;String, String&gt; params = new HashMap&lt;&gt;();
+</pre>
+
+You can specify a different assignment string than <tt>=</tt> by using the attribute <tt>assignment</tt>.
+
+<h2><a class="section" name="Scala">JCommander in Scala</a></h2>
+
+Here is a quick example of how to use JCommander in Scala (courtesy of Patrick Linskey):
+
+<pre class="brush: java">
+import java.io.File
+import com.beust.jcommander.{JCommander, Parameter}
+import collection.JavaConversions._
+
+object Main {
+ object Args {
+ // Declared as var because JCommander assigns a new collection declared
+ // as java.util.List because that's what JCommander will replace it with.
+ // It'd be nice if JCommander would just use the provided List so this
+ // could be a val and a Scala LinkedList.
+ @Parameter(
+ names = Array("-f", "--file"),
+ description = "File to load. Can be specified multiple times.")
+ var file: java.util.List[String] = null
+ }
+
+ def main(args: Array[String]): Unit = {
+ new JCommander(Args, args.toArray: _*)
+ for (filename <- Args.file) {
+ val f = new File(filename)
+ printf("file: %s\n", f.getName)
+ }
+ }
+}
+</pre>
+
+<h2><a class="section" name="Groovy">JCommander in Groovy</a></h2>
+
+Here is a quick example of how to use JCommander in Groovy (courtesy of Paul King):
+
+
+<pre class="brush: java">
+import com.beust.jcommander.*
+
+class Args {
+ @Parameter(names = ["-f", "--file"], description = "File to load. Can be specified multiple times.")
+ List&lt;String&gt; file
+}
+
+new Args().with {
+ new JCommander(it, args)
+ file.each { println "file: ${new File(it).name}" }
+}
+</pre>
+
+<h2><a class="section" name="More_examples">More examples</a></h2>
+
+TestNG uses JCommander to parse its own command line, here is <a href="http://github.com/cbeust/testng/blob/master/src/main/java/org/testng/CommandLineArgs.java">its definition file</a>.
+
+<h2><a class="section" name="Mailing_list">Mailing list</a></h2>
+
+Join the <a href="http://groups.google.com/group/jcommander">JCommander Google group</a> if you are interested in discussions about JCommander.
+
+<h2><a class="section" name="Javadocs">Javadocs</a></h2>
+
+The Javadocs for JCommander can be found <a href="apidocs/">here</a>.
+
+<h2><a class="section" name="License">License</a></h2>
+
+JCommander is released under the <a
+href="https://github.com/cbeust/jcommander/blob/master/license.txt">Apache 2.0</a> license.
+
+<h2><a class="section" name="Download">Download</a></h2>
+
+You can download JCommander from the following locations:
+
+<ul>
+ <li><a href="http://github.com/cbeust/jcommander">Source on github</a></li>
+ <li>Gradle
+
+ <pre class="brush: plain">
+ compile "com.beust:jcommander:1.48"
+ </pre>
+
+ <li>Maven:
+
+ <pre class="brush: xml">
+
+<dependency>
+ &lt;groupId&gt;com.beust&lt;/groupId&gt;
+ &lt;artifactId&gt;jcommander&lt;/artifactId&gt;
+ &lt;version&gt;1.48&lt;/version&gt;
+</dependency>
+ </pre>
+
+</ul>
+
+</body>
+
+<script type="text/javascript" src="http://beust.com/toc.js"></script>
+<script type="text/javascript"> generateToc(); </script>
+
+</html>
diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle
new file mode 100644
index 0000000..e65a133
--- /dev/null
+++ b/gradle/publishing.gradle
@@ -0,0 +1,63 @@
+import java.text.SimpleDateFormat
+
+Date buildTimeAndDate = new Date()
+ext {
+ buildTime = new SimpleDateFormat('yyyy-MM-dd').format(buildTimeAndDate)
+ buildDate = new SimpleDateFormat('HH:mm:ss.SSSZ').format(buildTimeAndDate)
+ cedricVersion = '1.51'
+}
+
+apply plugin: 'maven-publish'
+apply plugin: 'com.jfrog.bintray'
+
+jar {
+ manifest {
+ attributes(
+ 'Built-By': System.properties['user.name'],
+ 'Created-By': System.properties['java.version'] + " (" + System.properties['java.vendor'] + " " + System.getProperty("java.vm.version") + ")",
+ 'Build-Date': project.buildTime,
+ 'Build-Time': project.buildDate,
+ 'Specification-Title': project.name,
+ 'Specification-Version': project.version,
+ )
+ }
+}
+
+publishing {
+ publications {
+ mavenCustom(MavenPublication) {
+ from components.java
+ artifact sourceJar
+
+ groupId 'com.beust'
+ artifactId 'jcommander'
+ version project.version
+
+// pom.withXml {
+// asNode().children().last() + pomConfig
+// }
+ }
+ }
+}
+
+task install(dependsOn: publishToMavenLocal)
+
+Properties properties = new Properties()
+properties.load(project.rootProject.file('local.properties').newDataInputStream())
+
+bintray {
+ user = properties.getProperty("bintray.user")
+ key = properties.getProperty("bintray.apikey")
+ publications = ['mavenCustom']
+ pkg {
+ repo = 'maven'
+ name = 'jcommander'
+ desc = 'Command line parsing for Java'
+ licenses = ['Apache-2.0']
+ labels = ['java']
+
+ version {
+ name = project.version //Bintray logical version name
+ }
+ }
+}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..3c7abdf
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..eb24d33
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Apr 30 06:34:58 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-2.3-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt
new file mode 100644
index 0000000..ae8156e
--- /dev/null
+++ b/kobalt/src/Build.kt
@@ -0,0 +1,59 @@
+
+import com.beust.kobalt.plugin.java.javaCompiler
+import com.beust.kobalt.plugin.osgi.*
+import com.beust.kobalt.plugin.packaging.assemble
+import com.beust.kobalt.plugin.publish.bintray
+import com.beust.kobalt.project
+import org.apache.maven.model.Developer
+import org.apache.maven.model.License
+import org.apache.maven.model.Model
+import org.apache.maven.model.Scm
+
+val jcommander = project {
+ name = "jcommander"
+ group = "com.beust"
+ artifactId = name
+ version = "1.71"
+ description = "A Java library to parse command line options"
+
+ dependenciesTest {
+ compile("org.testng:testng:6.10")
+ exclude("com.beust:jcommander:1.48")
+ }
+
+ assemble {
+ mavenJars {
+ }
+ }
+
+ bintray {
+ publish = true
+ sign = true
+ }
+
+ javaCompiler {
+ args("-target", "1.7", "-source", "1.7")
+ }
+
+ osgi {}
+
+ pom = Model().apply {
+ name = project.name
+ description = "Command line parsing"
+ url = "http://jcommander.org"
+ licenses = listOf(License().apply {
+ name = "Apache 2.0"
+ url = "http://www.apache.org/licenses/LICENSE-2.0"
+ })
+ scm = Scm().apply {
+ url = "http://github.com/cbeust/jcommander"
+ connection = "https://github.com/cbeust/jcommander.git"
+ developerConnection = "git@github.com:cbeust/jcommander.git"
+ }
+ developers = listOf(Developer().apply {
+ name = "Cedric Beust"
+ email = "cedric@beust.com"
+ })
+ }
+
+}
diff --git a/kobalt/wrapper/kobalt-wrapper.jar b/kobalt/wrapper/kobalt-wrapper.jar
new file mode 100644
index 0000000..9a65ba4
--- /dev/null
+++ b/kobalt/wrapper/kobalt-wrapper.jar
Binary files differ
diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties
new file mode 100644
index 0000000..518ed5f
--- /dev/null
+++ b/kobalt/wrapper/kobalt-wrapper.properties
@@ -0,0 +1 @@
+kobalt.version=1.0.78 \ No newline at end of file
diff --git a/kobaltw b/kobaltw
new file mode 100755
index 0000000..c5186d5
--- /dev/null
+++ b/kobaltw
@@ -0,0 +1,2 @@
+#!/usr/bin/env sh
+java -jar "`dirname "$0"`/kobalt/wrapper/kobalt-wrapper.jar" $*
diff --git a/lib/kotlin-reflect.jar b/lib/kotlin-reflect.jar
new file mode 100644
index 0000000..22ae976
--- /dev/null
+++ b/lib/kotlin-reflect.jar
Binary files differ
diff --git a/lib/kotlin-runtime-sources.jar b/lib/kotlin-runtime-sources.jar
new file mode 100644
index 0000000..debcf93
--- /dev/null
+++ b/lib/kotlin-runtime-sources.jar
Binary files differ
diff --git a/lib/kotlin-runtime.jar b/lib/kotlin-runtime.jar
new file mode 100644
index 0000000..0165c8a
--- /dev/null
+++ b/lib/kotlin-runtime.jar
Binary files differ
diff --git a/misc.xml b/misc.xml
new file mode 100644
index 0000000..e46d847
--- /dev/null
+++ b/misc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
+</project \ No newline at end of file
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 1cedb33..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,283 +0,0 @@
-<!--
-
- 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.
-
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.beust</groupId>
- <artifactId>jcommander</artifactId>
- <packaging>jar</packaging>
- <name>JCommander</name>
- <version>1.48</version>
- <description>A Java framework to parse command line options with annotations.</description>
- <url>http://beust.com/jcommander</url>
- <licenses>
- <license>
- <name>The Apache Software License, Version 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
- <scm>
- <connection>scm:git:git@github.com:cbeust/jcommander.git</connection>
- <developerConnection>scm:git:git@github.com:cbeust/jcommander.git</developerConnection>
- <url>git@github.com:cbeust/jcommander.git</url>
- </scm>
-
- <distributionManagement>
- <repository>
- <id>sonatype-nexus-staging</id>
- <name>Nexus Staging Repository</name>
- <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
- </repository>
- </distributionManagement>
-
- <developers>
- <developer>
- <name>Cedric Beust</name>
- </developer>
- </developers>
-
- <parent>
- <groupId>org.sonatype.oss</groupId>
- <artifactId>oss-parent</artifactId>
- <version>3</version>
- </parent>
-
- <build>
- <plugins>
-
- <!-- Bundle sources -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-source-plugin</artifactId>
- <version>2.1.1</version>
- <executions>
- <execution>
- <id>attach-sources</id>
- <goals>
- <goal>jar</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- <!-- Compilation -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.3.1</version>
- <configuration>
- <source>1.5</source>
- <target>1.5</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
-
- <!-- Resource handling -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-resources-plugin</artifactId>
- <version>2.4.1</version>
- <configuration>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
-
- <!-- OSGi manifest creation -->
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <version>2.1.0</version>
- <executions>
- <execution>
- <id>bundle-manifest</id>
- <phase>process-classes</phase>
- <goals>
- <goal>manifest</goal>
- </goals>
- <configuration>
- <instructions>
- <_versionpolicy>$(@)</_versionpolicy>
- </instructions>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- <!-- Add OSGi manifest in JAR -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <version>2.3.1</version>
- <configuration>
- <archive>
- <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
- </archive>
- </configuration>
- </plugin>
-
- <!-- Tests -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <version>2.10</version>
- <configuration>
- <skipTests>true</skipTests>
- </configuration>
- <dependencies>
- <dependency>
- <groupId>com.beust</groupId>
- <artifactId>jcommander</artifactId>
- <version>1.30</version>
-<!--
- <version>${project.version}</version>
--->
- </dependency>
- </dependencies>
- </plugin>
-
- <!-- Generating Javadoc -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <version>2.7</version>
- <configuration>
- <excludePackageNames>*.internal</excludePackageNames>
- </configuration>
- </plugin>
- </plugins>
- <pluginManagement>
- <plugins>
- <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
- <plugin>
- <groupId>org.eclipse.m2e</groupId>
- <artifactId>lifecycle-mapping</artifactId>
- <version>1.0.0</version>
- <configuration>
- <lifecycleMappingMetadata>
- <pluginExecutions>
- <pluginExecution>
- <pluginExecutionFilter>
- <groupId>org.apache.felix</groupId>
- <artifactId>
- maven-bundle-plugin
- </artifactId>
- <versionRange>
- [2.1.0,)
- </versionRange>
- <goals>
- <goal>manifest</goal>
- </goals>
- </pluginExecutionFilter>
- <action>
- <ignore />
- </action>
- </pluginExecution>
- </pluginExecutions>
- </lifecycleMappingMetadata>
- </configuration>
- </plugin>
- </plugins>
- </pluginManagement>
- </build>
-
- <dependencies>
- <dependency>
- <groupId>org.testng</groupId>
- <artifactId>testng</artifactId>
- <version>6.1.1</version>
- <type>jar</type>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <artifactId>jcommander</artifactId>
- <groupId>com.beust</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- </dependencies>
-
- <profiles>
-
- <!--
- Do a license check by running : mvn -P license license:check
- UPdate the license check by running : mvn -P license license:format
- -->
- <profile>
- <id>license</id>
- <build>
- <plugins>
- <plugin>
- <groupId>com.mycila.maven-license-plugin</groupId>
- <artifactId>maven-license-plugin</artifactId>
- <version>1.7.0</version>
- <configuration>
- <quiet>false</quiet>
- <header>src/main/license/license-header.txt</header>
- <includes>
- <include>src/**</include>
- <include>pom.xml</include>
- </includes>
- <excludes>
- <exclude>**/.git/**</exclude>
- <!-- ignore files produced during a build -->
- <exclude>**/target/**</exclude>
- </excludes>
- <useDefaultExcludes>false</useDefaultExcludes>
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>check</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
- <!-- Signing with gpg -->
- <!--
- Sign the artifacts by calling
- mvn -P sign [..]
- -->
- <profile>
- <id>sign</id>
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-gpg-plugin</artifactId>
- <version>1.4</version>
- <executions>
- <execution>
- <id>sign-artifacts</id>
- <phase>verify</phase>
- <goals>
- <goal>sign</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
-
-</project>
diff --git a/src/main/java/com/beust/jcommander/DynamicParameter.java b/src/main/java/com/beust/jcommander/DynamicParameter.java
index 2159c1f..bdb5010 100644
--- a/src/main/java/com/beust/jcommander/DynamicParameter.java
+++ b/src/main/java/com/beust/jcommander/DynamicParameter.java
@@ -37,14 +37,14 @@ public @interface DynamicParameter {
boolean hidden() default false;
/**
- * The validation class to use.
+ * The validation classes to use.
*/
- Class<? extends IParameterValidator> validateWith() default NoValidator.class;
+ Class<? extends IParameterValidator>[] validateWith() default NoValidator.class;
/**
* The character(s) used to assign the values.
*/
String assignment() default "=";
- Class<? extends IValueValidator> validateValueWith() default NoValueValidator.class;
+ Class<? extends IValueValidator>[] validateValueWith() default NoValueValidator.class;
}
diff --git a/src/main/java/com/beust/jcommander/IStringConverterFactory.java b/src/main/java/com/beust/jcommander/IStringConverterFactory.java
index 0e53ca0..3e26020 100644
--- a/src/main/java/com/beust/jcommander/IStringConverterFactory.java
+++ b/src/main/java/com/beust/jcommander/IStringConverterFactory.java
@@ -24,6 +24,7 @@ package com.beust.jcommander;
* your argument classes.
*
* @author cbeust
+ * @see IStringConverterInstanceFactory
*/
public interface IStringConverterFactory {
<T> Class<? extends IStringConverter<T>> getConverter(Class<T> forType);
diff --git a/src/main/java/com/beust/jcommander/IStringConverterInstanceFactory.java b/src/main/java/com/beust/jcommander/IStringConverterInstanceFactory.java
new file mode 100644
index 0000000..1a87b5b
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/IStringConverterInstanceFactory.java
@@ -0,0 +1,20 @@
+package com.beust.jcommander;
+
+/**
+ * A factory to create {@link IStringConverter} instances.
+ *
+ * This interface lets you specify your converters in one place instead of having them repeated all over your argument classes.
+ *
+ * @author simon04
+ * @see IStringConverterFactory
+ */
+public interface IStringConverterInstanceFactory {
+ /**
+ * Obtain a converter instance for parsing {@code parameter} as type {@code forType}
+ * @param parameter the parameter to parse
+ * @param forType the type class
+ * @param optionName the name of the option used on the command line
+ * @return a converter instance
+ */
+ IStringConverter<?> getConverterInstance(Parameter parameter, Class<?> forType, String optionName);
+}
diff --git a/src/main/java/com/beust/jcommander/JCommander.java b/src/main/java/com/beust/jcommander/JCommander.java
index 2e049a1..59073c6 100644
--- a/src/main/java/com/beust/jcommander/JCommander.java
+++ b/src/main/java/com/beust/jcommander/JCommander.java
@@ -7,7 +7,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -18,37 +18,19 @@
package com.beust.jcommander;
+import com.beust.jcommander.FuzzyMap.IKey;
+import com.beust.jcommander.converters.*;
+import com.beust.jcommander.internal.*;
+
import java.io.BufferedReader;
-import java.io.FileReader;
import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
+import java.lang.reflect.*;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
import java.util.ResourceBundle;
-
-import com.beust.jcommander.FuzzyMap.IKey;
-import com.beust.jcommander.converters.IParameterSplitter;
-import com.beust.jcommander.converters.NoConverter;
-import com.beust.jcommander.converters.StringConverter;
-import com.beust.jcommander.internal.Console;
-import com.beust.jcommander.internal.DefaultConsole;
-import com.beust.jcommander.internal.DefaultConverterFactory;
-import com.beust.jcommander.internal.JDK6Console;
-import com.beust.jcommander.internal.Lists;
-import com.beust.jcommander.internal.Maps;
-import com.beust.jcommander.internal.Nullable;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* The main class for JCommander. It's responsible for parsing the object that contains
@@ -63,1537 +45,1647 @@ import com.beust.jcommander.internal.Nullable;
* @author Cedric Beust <cedric@beust.com>
*/
public class JCommander {
- public static final String DEBUG_PROPERTY = "jcommander.debug";
-
- /**
- * A map to look up parameter description per option name.
- */
- private Map<IKey, ParameterDescription> m_descriptions;
-
- /**
- * The objects that contain fields annotated with @Parameter.
- */
- private List<Object> m_objects = Lists.newArrayList();
-
- private boolean m_firstTimeMainParameter = true;
-
- /**
- * This field/method will contain whatever command line parameter is not an option.
- * It is expected to be a List<String>.
- */
- private Parameterized m_mainParameter = null;
-
- /**
- * The object on which we found the main parameter field.
- */
- private Object m_mainParameterObject;
-
- /**
- * The annotation found on the main parameter field.
- */
- private Parameter m_mainParameterAnnotation;
-
- private ParameterDescription m_mainParameterDescription;
-
- /**
- * A set of all the parameterizeds that are required. During the reflection phase,
- * this field receives all the fields that are annotated with required=true
- * and during the parsing phase, all the fields that are assigned a value
- * are removed from it. At the end of the parsing phase, if it's not empty,
- * then some required fields did not receive a value and an exception is
- * thrown.
- */
- private Map<Parameterized, ParameterDescription> m_requiredFields = Maps.newHashMap();
-
- /**
- * A map of all the parameterized fields/methods.
- */
- private Map<Parameterized, ParameterDescription> m_fields = Maps.newHashMap();
-
- private ResourceBundle m_bundle;
-
- /**
- * A default provider returns default values for the parameters.
- */
- private IDefaultProvider m_defaultProvider;
-
- /**
- * List of commands and their instance.
- */
- private Map<ProgramName, JCommander> m_commands = Maps.newLinkedHashMap();
-
- /**
- * Alias database for reverse lookup
- */
- private Map<IKey, ProgramName> aliasMap = Maps.newLinkedHashMap();
-
- /**
- * The name of the command after the parsing has run.
- */
- private String m_parsedCommand;
-
- /**
- * The name of command or alias as it was passed to the
- * command line
- */
- private String m_parsedAlias;
-
- private ProgramName m_programName;
-
- private Comparator<? super ParameterDescription> m_parameterDescriptionComparator
- = new Comparator<ParameterDescription>() {
- @Override
- public int compare(ParameterDescription p0, ParameterDescription p1) {
- return p0.getLongestName().compareTo(p1.getLongestName());
- }
- };
-
- private int m_columnSize = 79;
-
- private boolean m_helpWasSpecified;
-
- private List<String> m_unknownArgs = Lists.newArrayList();
- private boolean m_acceptUnknownOptions = false;
- private boolean m_allowParameterOverwriting = false;
-
- private static Console m_console;
-
- /**
- * The factories used to look up string converters.
- */
- private static LinkedList<IStringConverterFactory> CONVERTER_FACTORIES = Lists.newLinkedList();
-
- static {
- CONVERTER_FACTORIES.addFirst(new DefaultConverterFactory());
- };
-
- /**
- * Creates a new un-configured JCommander object.
- */
- public JCommander() {
- }
-
- /**
- * @param object The arg object expected to contain {@link Parameter} annotations.
- */
- public JCommander(Object object) {
- addObject(object);
- createDescriptions();
- }
-
- /**
- * @param object The arg object expected to contain {@link Parameter} annotations.
- * @param bundle The bundle to use for the descriptions. Can be null.
- */
- public JCommander(Object object, @Nullable ResourceBundle bundle) {
- addObject(object);
- setDescriptionsBundle(bundle);
- }
-
- /**
- * @param object The arg object expected to contain {@link Parameter} annotations.
- * @param bundle The bundle to use for the descriptions. Can be null.
- * @param args The arguments to parse (optional).
- */
- public JCommander(Object object, ResourceBundle bundle, String... args) {
- addObject(object);
- setDescriptionsBundle(bundle);
- parse(args);
- }
-
- /**
- * @param object The arg object expected to contain {@link Parameter} annotations.
- * @param args The arguments to parse (optional).
- */
- public JCommander(Object object, String... args) {
- addObject(object);
- parse(args);
- }
-
- public static Console getConsole() {
- if (m_console == null) {
- try {
- Method consoleMethod = System.class.getDeclaredMethod("console", new Class<?>[0]);
- Object console = consoleMethod.invoke(null, new Object[0]);
- m_console = new JDK6Console(console);
- } catch (Throwable t) {
- m_console = new DefaultConsole();
- }
- }
- return m_console;
- }
-
- /**
- * Adds the provided arg object to the set of objects that this commander
- * will parse arguments into.
- *
- * @param object The arg object expected to contain {@link Parameter}
- * annotations. If <code>object</code> is an array or is {@link Iterable},
- * the child objects will be added instead.
- */
- // declared final since this is invoked from constructors
- public final void addObject(Object object) {
- if (object instanceof Iterable) {
- // Iterable
- for (Object o : (Iterable<?>) object) {
- m_objects.add(o);
- }
- } else if (object.getClass().isArray()) {
- // Array
- for (Object o : (Object[]) object) {
- m_objects.add(o);
- }
- } else {
- // Single object
- m_objects.add(object);
- }
- }
-
- /**
- * Sets the {@link ResourceBundle} to use for looking up descriptions.
- * Set this to <code>null</code> to use description text directly.
- */
- // declared final since this is invoked from constructors
- public final void setDescriptionsBundle(ResourceBundle bundle) {
- m_bundle = bundle;
- }
-
- /**
- * Parse and validate the command line parameters.
- */
- public void parse(String... args) {
- parse(true /* validate */, args);
- }
-
- /**
- * Parse the command line parameters without validating them.
- */
- public void parseWithoutValidation(String... args) {
- parse(false /* no validation */, args);
- }
-
- private void parse(boolean validate, String... args) {
- StringBuilder sb = new StringBuilder("Parsing \"");
- sb.append(join(args).append("\"\n with:").append(join(m_objects.toArray())));
- p(sb.toString());
-
- if (m_descriptions == null) createDescriptions();
- initializeDefaultValues();
- parseValues(expandArgs(args), validate);
- if (validate) validateOptions();
- }
-
- private StringBuilder join(Object[] args) {
- StringBuilder result = new StringBuilder();
- for (int i = 0; i < args.length; i++) {
- if (i > 0) result.append(" ");
- result.append(args[i]);
- }
- return result;
- }
-
- private void initializeDefaultValues() {
- if (m_defaultProvider != null) {
- for (ParameterDescription pd : m_descriptions.values()) {
- initializeDefaultValue(pd);
- }
-
- for (Map.Entry<ProgramName, JCommander> entry : m_commands.entrySet()) {
- entry.getValue().initializeDefaultValues();
- }
- }
- }
-
- /**
- * Make sure that all the required parameters have received a value.
- */
- private void validateOptions() {
- // No validation if we found a help parameter
- if (m_helpWasSpecified) {
- return;
- }
-
- if (! m_requiredFields.isEmpty()) {
- StringBuilder missingFields = new StringBuilder();
- for (ParameterDescription pd : m_requiredFields.values()) {
- missingFields.append(pd.getNames()).append(" ");
- }
- throw new ParameterException("The following "
- + pluralize(m_requiredFields.size(), "option is required: ", "options are required: ")
- + missingFields);
- }
-
- if (m_mainParameterDescription != null) {
- if (m_mainParameterDescription.getParameter().required() &&
- !m_mainParameterDescription.isAssigned()) {
- throw new ParameterException("Main parameters are required (\""
- + m_mainParameterDescription.getDescription() + "\")");
- }
- }
- }
-
- private static String pluralize(int quantity, String singular, String plural) {
- return quantity == 1 ? singular : plural;
- }
-
- /**
- * Expand the command line parameters to take @ parameters into account.
- * When @ is encountered, the content of the file that follows is inserted
- * in the command line.
- *
- * @param originalArgv the original command line parameters
- * @return the new and enriched command line parameters
- */
- private String[] expandArgs(String[] originalArgv) {
- List<String> vResult1 = Lists.newArrayList();
-
- //
- // Expand @
- //
- for (String arg : originalArgv) {
-
- if (arg.startsWith("@")) {
- String fileName = arg.substring(1);
- vResult1.addAll(readFile(fileName));
- }
- else {
- List<String> expanded = expandDynamicArg(arg);
- vResult1.addAll(expanded);
- }
- }
-
- // Expand separators
- //
- List<String> vResult2 = Lists.newArrayList();
- for (int i = 0; i < vResult1.size(); i++) {
- String arg = vResult1.get(i);
- String[] v1 = vResult1.toArray(new String[0]);
- if (isOption(v1, arg)) {
- String sep = getSeparatorFor(v1, arg);
- if (! " ".equals(sep)) {
- String[] sp = arg.split("[" + sep + "]", 2);
- for (String ssp : sp) {
- vResult2.add(ssp);
- }
+ public static final String DEBUG_PROPERTY = "jcommander.debug";
+
+ /**
+ * A map to look up parameter description per option name.
+ */
+ private Map<IKey, ParameterDescription> descriptions;
+
+ /**
+ * The objects that contain fields annotated with @Parameter.
+ */
+ private List<Object> objects = Lists.newArrayList();
+
+ private boolean firstTimeMainParameter = true;
+
+ /**
+ * This field/method will contain whatever command line parameter is not an option.
+ * It is expected to be a List<String>.
+ */
+ private Parameterized mainParameter = null;
+
+ /**
+ * The object on which we found the main parameter field.
+ */
+ private Object mainParameterObject;
+
+ /**
+ * The annotation found on the main parameter field.
+ */
+ private Parameter mainParameterAnnotation;
+
+ private ParameterDescription mainParameterDescription;
+
+ /**
+ * A set of all the parameterizeds that are required. During the reflection phase,
+ * this field receives all the fields that are annotated with required=true
+ * and during the parsing phase, all the fields that are assigned a value
+ * are removed from it. At the end of the parsing phase, if it's not empty,
+ * then some required fields did not receive a value and an exception is
+ * thrown.
+ */
+ private Map<Parameterized, ParameterDescription> requiredFields = Maps.newHashMap();
+
+ /**
+ * A map of all the parameterized fields/methods.
+ */
+ private Map<Parameterized, ParameterDescription> fields = Maps.newHashMap();
+
+ /**
+ * List of commands and their instance.
+ */
+ private Map<ProgramName, JCommander> commands = Maps.newLinkedHashMap();
+
+ /**
+ * Alias database for reverse lookup
+ */
+ private Map<IKey, ProgramName> aliasMap = Maps.newLinkedHashMap();
+
+ /**
+ * The name of the command after the parsing has run.
+ */
+ private String parsedCommand;
+
+ /**
+ * The name of command or alias as it was passed to the
+ * command line
+ */
+ private String parsedAlias;
+
+ private ProgramName programName;
+
+ private boolean helpWasSpecified;
+
+ private List<String> unknownArgs = Lists.newArrayList();
+
+ private static Console console;
+
+ private final Options options;
+
+ /**
+ * Options shared with sub commands
+ */
+ private static class Options {
+
+ private ResourceBundle bundle;
+
+ /**
+ * A default provider returns default values for the parameters.
+ */
+ private IDefaultProvider defaultProvider;
+
+ private Comparator<? super ParameterDescription> parameterDescriptionComparator
+ = new Comparator<ParameterDescription>() {
+ @Override
+ public int compare(ParameterDescription p0, ParameterDescription p1) {
+ Parameter a0 = p0.getParameterAnnotation();
+ Parameter a1 = p1.getParameterAnnotation();
+ if (a0 != null && a0.order() != -1 && a1 != null && a1.order() != -1) {
+ return Integer.compare(a0.order(), a1.order());
+ } else if (a0 != null && a0.order() != -1) {
+ return -1;
+ } else if (a1 != null && a1.order() != -1) {
+ return 1;
+ } else {
+ return p0.getLongestName().compareTo(p1.getLongestName());
+ }
+ }
+ };
+ private int columnSize = 79;
+ private boolean acceptUnknownOptions = false;
+ private boolean allowParameterOverwriting = false;
+ private boolean expandAtSign = true;
+ private int verbose = 0;
+ private boolean caseSensitiveOptions = true;
+ private boolean allowAbbreviatedOptions = false;
+ /**
+ * The factories used to look up string converters.
+ */
+ private final List<IStringConverterInstanceFactory> converterInstanceFactories = new CopyOnWriteArrayList<>();
+ private Charset atFileCharset = Charset.defaultCharset();
+ }
+
+ private JCommander(Options options) {
+ if (options == null) {
+ throw new NullPointerException("options");
+ }
+ this.options = options;
+ addConverterFactory(new DefaultConverterFactory());
+ }
+
+ /**
+ * Creates a new un-configured JCommander object.
+ */
+ public JCommander() {
+ this(new Options());
+ }
+
+ /**
+ * @param object The arg object expected to contain {@link Parameter} annotations.
+ */
+ public JCommander(Object object) {
+ this(object, (ResourceBundle) null);
+ }
+
+ /**
+ * @param object The arg object expected to contain {@link Parameter} annotations.
+ * @param bundle The bundle to use for the descriptions. Can be null.
+ */
+ public JCommander(Object object, @Nullable ResourceBundle bundle) {
+ this(object, bundle, (String[]) null);
+ }
+
+ /**
+ * @param object The arg object expected to contain {@link Parameter} annotations.
+ * @param bundle The bundle to use for the descriptions. Can be null.
+ * @param args The arguments to parse (optional).
+ */
+ public JCommander(Object object, @Nullable ResourceBundle bundle, String... args) {
+ this();
+ addObject(object);
+ if (bundle != null) {
+ setDescriptionsBundle(bundle);
+ }
+ createDescriptions();
+ if (args != null) {
+ parse(args);
+ }
+ }
+
+ /**
+ * @param object The arg object expected to contain {@link Parameter} annotations.
+ * @param args The arguments to parse (optional).
+ *
+ * @deprecated Construct a JCommander instance first and then call parse() on it.
+ */
+ @Deprecated()
+ public JCommander(Object object, String... args) {
+ this(object);
+ parse(args);
+ }
+
+ /**
+ * Disables expanding {@code @file}.
+ *
+ * JCommander supports the {@code @file} syntax, which allows you to put all your options
+ * into a file and pass this file as parameter @param expandAtSign whether to expand {@code @file}.
+ */
+ public void setExpandAtSign(boolean expandAtSign) {
+ options.expandAtSign = expandAtSign;
+ }
+
+ public static Console getConsole() {
+ if (console == null) {
+ try {
+ Method consoleMethod = System.class.getDeclaredMethod("console");
+ Object console = consoleMethod.invoke(null);
+ JCommander.console = new JDK6Console(console);
+ } catch (Throwable t) {
+ console = new DefaultConsole();
+ }
+ }
+ return console;
+ }
+
+ /**
+ * Adds the provided arg object to the set of objects that this commander
+ * will parse arguments into.
+ *
+ * @param object The arg object expected to contain {@link Parameter}
+ * annotations. If <code>object</code> is an array or is {@link Iterable},
+ * the child objects will be added instead.
+ */
+ // declared final since this is invoked from constructors
+ public final void addObject(Object object) {
+ if (object instanceof Iterable) {
+ // Iterable
+ for (Object o : (Iterable<?>) object) {
+ objects.add(o);
+ }
+ } else if (object.getClass().isArray()) {
+ // Array
+ for (Object o : (Object[]) object) {
+ objects.add(o);
+ }
} else {
- vResult2.add(arg);
+ // Single object
+ objects.add(object);
}
- } else {
- vResult2.add(arg);
- }
}
- return vResult2.toArray(new String[vResult2.size()]);
- }
+ /**
+ * Sets the {@link ResourceBundle} to use for looking up descriptions.
+ * Set this to <code>null</code> to use description text directly.
+ */
+ // declared final since this is invoked from constructors
+ public final void setDescriptionsBundle(ResourceBundle bundle) {
+ options.bundle = bundle;
+ }
- private List<String> expandDynamicArg(String arg) {
- for (ParameterDescription pd : m_descriptions.values()) {
- if (pd.isDynamicParameter()) {
- for (String name : pd.getParameter().names()) {
- if (arg.startsWith(name) && !arg.equals(name)) {
- return Arrays.asList(name, arg.substring(name.length()));
- }
+ /**
+ * Parse and validate the command line parameters.
+ */
+ public void parse(String... args) {
+ try {
+ parse(true /* validate */, args);
+ } catch(ParameterException ex) {
+ ex.setJCommander(this);
+ throw ex;
}
- }
}
- return Arrays.asList(arg);
- }
+ /**
+ * Parse the command line parameters without validating them.
+ */
+ public void parseWithoutValidation(String... args) {
+ parse(false /* no validation */, args);
+ }
- private boolean isOption(String[] args, String arg) {
- String prefixes = getOptionPrefixes(args, arg);
- return arg.length() > 0 && prefixes.indexOf(arg.charAt(0)) >= 0;
- }
+ private void parse(boolean validate, String... args) {
+ StringBuilder sb = new StringBuilder("Parsing \"");
+ sb.append(join(args).append("\"\n with:").append(join(objects.toArray())));
+ p(sb.toString());
- private ParameterDescription getPrefixDescriptionFor(String arg) {
- for (Map.Entry<IKey, ParameterDescription> es : m_descriptions.entrySet()) {
- if (arg.startsWith(es.getKey().getName())) return es.getValue();
+ if (descriptions == null) createDescriptions();
+ initializeDefaultValues();
+ parseValues(expandArgs(args), validate);
+ if (validate) validateOptions();
}
- return null;
- }
+ private StringBuilder join(Object[] args) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < args.length; i++) {
+ if (i > 0) result.append(" ");
+ result.append(args[i]);
+ }
+ return result;
+ }
- /**
- * If arg is an option, we can look it up directly, but if it's a value,
- * we need to find the description for the option that precedes it.
- */
- private ParameterDescription getDescriptionFor(String[] args, String arg) {
- ParameterDescription result = getPrefixDescriptionFor(arg);
- if (result != null) return result;
+ private void initializeDefaultValues() {
+ if (options.defaultProvider != null) {
+ for (ParameterDescription pd : descriptions.values()) {
+ initializeDefaultValue(pd);
+ }
- for (String a : args) {
- ParameterDescription pd = getPrefixDescriptionFor(arg);
- if (pd != null) result = pd;
- if (a.equals(arg)) return result;
+ for (Map.Entry<ProgramName, JCommander> entry : commands.entrySet()) {
+ entry.getValue().initializeDefaultValues();
+ }
+ }
}
- throw new ParameterException("Unknown parameter: " + arg);
- }
+ /**
+ * Make sure that all the required parameters have received a value.
+ */
+ private void validateOptions() {
+ // No validation if we found a help parameter
+ if (helpWasSpecified) {
+ return;
+ }
- private String getSeparatorFor(String[] args, String arg) {
- ParameterDescription pd = getDescriptionFor(args, arg);
+ if (!requiredFields.isEmpty()) {
+ List<String> missingFields = new ArrayList<>();
+ for (ParameterDescription pd : requiredFields.values()) {
+ missingFields.add("[" + String.join(" | ", pd.getParameter().names()) + "]");
+ }
+ String message = String.join(", ", missingFields);
+ throw new ParameterException("The following "
+ + pluralize(requiredFields.size(), "option is required: ", "options are required: ")
+ + message);
+ }
+
+ if (mainParameterDescription != null) {
+ if (mainParameterDescription.getParameter().required() &&
+ !mainParameterDescription.isAssigned()) {
+ throw new ParameterException("Main parameters are required (\""
+ + mainParameterDescription.getDescription() + "\")");
+ }
+ }
+ }
- // Could be null if only main parameters were passed
- if (pd != null) {
- Parameters p = pd.getObject().getClass().getAnnotation(Parameters.class);
- if (p != null) return p.separators();
+ private static String pluralize(int quantity, String singular, String plural) {
+ return quantity == 1 ? singular : plural;
}
- return " ";
- }
+ /**
+ * Expand the command line parameters to take @ parameters into account.
+ * When @ is encountered, the content of the file that follows is inserted
+ * in the command line.
+ *
+ * @param originalArgv the original command line parameters
+ * @return the new and enriched command line parameters
+ */
+ private String[] expandArgs(String[] originalArgv) {
+ List<String> vResult1 = Lists.newArrayList();
- private String getOptionPrefixes(String[] args, String arg) {
- ParameterDescription pd = getDescriptionFor(args, arg);
+ //
+ // Expand @
+ //
+ for (String arg : originalArgv) {
- // Could be null if only main parameters were passed
- if (pd != null) {
- Parameters p = pd.getObject().getClass()
- .getAnnotation(Parameters.class);
- if (p != null) return p.optionPrefixes();
+ if (arg.startsWith("@") && options.expandAtSign) {
+ String fileName = arg.substring(1);
+ vResult1.addAll(readFile(fileName));
+ } else {
+ List<String> expanded = expandDynamicArg(arg);
+ vResult1.addAll(expanded);
+ }
+ }
+
+ // Expand separators
+ //
+ List<String> vResult2 = Lists.newArrayList();
+ for (String arg : vResult1) {
+ if (isOption(arg)) {
+ String sep = getSeparatorFor(arg);
+ if (!" ".equals(sep)) {
+ String[] sp = arg.split("[" + sep + "]", 2);
+ for (String ssp : sp) {
+ vResult2.add(ssp);
+ }
+ } else {
+ vResult2.add(arg);
+ }
+ } else {
+ vResult2.add(arg);
+ }
+ }
+
+ return vResult2.toArray(new String[vResult2.size()]);
}
- String result = Parameters.DEFAULT_OPTION_PREFIXES;
- // See if any of the objects contains a @Parameters(optionPrefixes)
- StringBuilder sb = new StringBuilder();
- for (Object o : m_objects) {
- Parameters p = o.getClass().getAnnotation(Parameters.class);
- if (p != null && !Parameters.DEFAULT_OPTION_PREFIXES.equals(p.optionPrefixes())) {
- sb.append(p.optionPrefixes());
- }
+ private List<String> expandDynamicArg(String arg) {
+ for (ParameterDescription pd : descriptions.values()) {
+ if (pd.isDynamicParameter()) {
+ for (String name : pd.getParameter().names()) {
+ if (arg.startsWith(name) && !arg.equals(name)) {
+ return Arrays.asList(name, arg.substring(name.length()));
+ }
+ }
+ }
+ }
+
+ return Arrays.asList(arg);
}
- if (! Strings.isStringEmpty(sb.toString())) {
- result = sb.toString();
+ private boolean matchArg(String arg, IKey key) {
+ String kn = options.caseSensitiveOptions
+ ? key.getName()
+ : key.getName().toLowerCase();
+ if (options.allowAbbreviatedOptions) {
+ if (kn.startsWith(arg)) return true;
+ } else {
+ ParameterDescription pd = descriptions.get(key);
+ if (pd != null) {
+ // It's an option. If the option has a separator (e.g. -author==foo) then
+ // we only do a beginsWith match
+ String separator = getSeparatorFor(arg);
+ if (! " ".equals(separator)) {
+ if (arg.startsWith(kn)) return true;
+ } else {
+ if (kn.equals(arg)) return true;
+ }
+ } else {
+ // It's a command do a strict equality check
+ if (kn.equals(arg)) return true;
+ }
+ }
+ return false;
}
- return result;
- }
+ private boolean isOption(String passedArg) {
+ if (options.acceptUnknownOptions) return true;
- /**
- * Reads the file specified by filename and returns the file content as a string.
- * End of lines are replaced by a space.
- *
- * @param fileName the command line filename
- * @return the file content as a string.
- */
- private static List<String> readFile(String fileName) {
- List<String> result = Lists.newArrayList();
+ String arg = options.caseSensitiveOptions ? passedArg : passedArg.toLowerCase();
- try {
- BufferedReader bufRead = new BufferedReader(new FileReader(fileName));
+ for (IKey key : descriptions.keySet()) {
+ if (matchArg(arg, key)) return true;
+ }
+ for (IKey key : commands.keySet()) {
+ if (matchArg(arg, key)) return true;
+ }
- String line;
+ return false;
+ }
- // Read through file one line at time. Print line # and line
- while ((line = bufRead.readLine()) != null) {
- // Allow empty lines and # comments in these at files
- if (line.length() > 0 && ! line.trim().startsWith("#")) {
- result.add(line);
+ private ParameterDescription getPrefixDescriptionFor(String arg) {
+ for (Map.Entry<IKey, ParameterDescription> es : descriptions.entrySet()) {
+ if (arg.startsWith(es.getKey().getName())) return es.getValue();
}
- }
- bufRead.close();
+ return null;
}
- catch (IOException e) {
- throw new ParameterException("Could not read file " + fileName + ": " + e);
+
+ /**
+ * If arg is an option, we can look it up directly, but if it's a value,
+ * we need to find the description for the option that precedes it.
+ */
+ private ParameterDescription getDescriptionFor(String arg) {
+ return getPrefixDescriptionFor(arg);
}
- return result;
- }
+ private String getSeparatorFor(String arg) {
+ ParameterDescription pd = getDescriptionFor(arg);
- /**
- * Remove spaces at both ends and handle double quotes.
- */
- private static String trim(String string) {
- String result = string.trim();
- if (result.startsWith("\"") && result.endsWith("\"") && result.length() > 1) {
- result = result.substring(1, result.length() - 1);
+ // Could be null if only main parameters were passed
+ if (pd != null) {
+ Parameters p = pd.getObject().getClass().getAnnotation(Parameters.class);
+ if (p != null) return p.separators();
+ }
+
+ return " ";
}
- return result;
- }
- /**
- * Create the ParameterDescriptions for all the \@Parameter found.
- */
- private void createDescriptions() {
- m_descriptions = Maps.newHashMap();
+ /**
+ * Reads the file specified by filename and returns the file content as a string.
+ * End of lines are replaced by a space.
+ *
+ * @param fileName the command line filename
+ * @return the file content as a string.
+ */
+ private List<String> readFile(String fileName) {
+ List<String> result = Lists.newArrayList();
+
+ try (BufferedReader bufRead = Files.newBufferedReader(Paths.get(fileName), options.atFileCharset)) {
+ String line;
+ // Read through file one line at time. Print line # and line
+ while ((line = bufRead.readLine()) != null) {
+ // Allow empty lines and # comments in these at files
+ if (line.length() > 0 && !line.trim().startsWith("#")) {
+ result.add(line);
+ }
+ }
+ } catch (IOException e) {
+ throw new ParameterException("Could not read file " + fileName + ": " + e);
+ }
- for (Object object : m_objects) {
- addDescription(object);
+ return result;
}
- }
- private void addDescription(Object object) {
- Class<?> cls = object.getClass();
+ /**
+ * Remove spaces at both ends and handle double quotes.
+ */
+ private static String trim(String string) {
+ String result = string.trim();
+ if (result.startsWith("\"") && result.endsWith("\"") && result.length() > 1) {
+ result = result.substring(1, result.length() - 1);
+ }
+ return result;
+ }
- List<Parameterized> parameterizeds = Parameterized.parseArg(object);
- for (Parameterized parameterized : parameterizeds) {
- WrappedParameter wp = parameterized.getWrappedParameter();
- if (wp != null && wp.getParameter() != null) {
- Parameter annotation = wp.getParameter();
- //
- // @Parameter
- //
- Parameter p = annotation;
- if (p.names().length == 0) {
- p("Found main parameter:" + parameterized);
- if (m_mainParameter != null) {
- throw new ParameterException("Only one @Parameter with no names attribute is"
- + " allowed, found:" + m_mainParameter + " and " + parameterized);
- }
- m_mainParameter = parameterized;
- m_mainParameterObject = object;
- m_mainParameterAnnotation = p;
- m_mainParameterDescription =
- new ParameterDescription(object, p, parameterized, m_bundle, this);
+ /**
+ * Create the ParameterDescriptions for all the \@Parameter found.
+ */
+ private void createDescriptions() {
+ descriptions = Maps.newHashMap();
+
+ for (Object object : objects) {
+ addDescription(object);
+ }
+ }
+
+ private void addDescription(Object object) {
+ Class<?> cls = object.getClass();
+
+ List<Parameterized> parameterizeds = Parameterized.parseArg(object);
+ for (Parameterized parameterized : parameterizeds) {
+ WrappedParameter wp = parameterized.getWrappedParameter();
+ if (wp != null && wp.getParameter() != null) {
+ Parameter annotation = wp.getParameter();
+ //
+ // @Parameter
+ //
+ Parameter p = annotation;
+ if (p.names().length == 0) {
+ p("Found main parameter:" + parameterized);
+ if (mainParameter != null) {
+ throw new ParameterException("Only one @Parameter with no names attribute is"
+ + " allowed, found:" + mainParameter + " and " + parameterized);
+ }
+ mainParameter = parameterized;
+ mainParameterObject = object;
+ mainParameterAnnotation = p;
+ mainParameterDescription =
+ new ParameterDescription(object, p, parameterized, options.bundle, this);
+ } else {
+ ParameterDescription pd =
+ new ParameterDescription(object, p, parameterized, options.bundle, this);
+ for (String name : p.names()) {
+ if (descriptions.containsKey(new StringKey(name))) {
+ throw new ParameterException("Found the option " + name + " multiple times");
+ }
+ p("Adding description for " + name);
+ fields.put(parameterized, pd);
+ descriptions.put(new StringKey(name), pd);
+
+ if (p.required()) requiredFields.put(parameterized, pd);
+ }
+ }
+ } else if (parameterized.getDelegateAnnotation() != null) {
+ //
+ // @ParametersDelegate
+ //
+ Object delegateObject = parameterized.get(object);
+ if (delegateObject == null) {
+ throw new ParameterException("Delegate field '" + parameterized.getName()
+ + "' cannot be null.");
+ }
+ addDescription(delegateObject);
+ } else if (wp != null && wp.getDynamicParameter() != null) {
+ //
+ // @DynamicParameter
+ //
+ DynamicParameter dp = wp.getDynamicParameter();
+ for (String name : dp.names()) {
+ if (descriptions.containsKey(name)) {
+ throw new ParameterException("Found the option " + name + " multiple times");
+ }
+ p("Adding description for " + name);
+ ParameterDescription pd =
+ new ParameterDescription(object, dp, parameterized, options.bundle, this);
+ fields.put(parameterized, pd);
+ descriptions.put(new StringKey(name), pd);
+
+ if (dp.required()) requiredFields.put(parameterized, pd);
+ }
+ }
+ }
+ }
+
+ private void initializeDefaultValue(ParameterDescription pd) {
+ for (String optionName : pd.getParameter().names()) {
+ String def = options.defaultProvider.getDefaultValueFor(optionName);
+ if (def != null) {
+ p("Initializing " + optionName + " with default value:" + def);
+ pd.addValue(def, true /* default */);
+ // remove the parameter from the list of fields to be required
+ requiredFields.remove(pd.getParameterized());
+ return;
+ }
+ }
+ }
+
+ /**
+ * Main method that parses the values and initializes the fields accordingly.
+ */
+ private void parseValues(String[] args, boolean validate) {
+ // This boolean becomes true if we encounter a command, which indicates we need
+ // to stop parsing (the parsing of the command will be done in a sub JCommander
+ // object)
+ boolean commandParsed = false;
+ int i = 0;
+ boolean isDashDash = false; // once we encounter --, everything goes into the main parameter
+ while (i < args.length && !commandParsed) {
+ String arg = args[i];
+ String a = trim(arg);
+ args[i] = a;
+ p("Parsing arg: " + a);
+
+ JCommander jc = findCommandByAlias(arg);
+ int increment = 1;
+ if (!isDashDash && !"--".equals(a) && isOption(a) && jc == null) {
+ //
+ // Option
+ //
+ ParameterDescription pd = findParameterDescription(a);
+
+ if (pd != null) {
+ if (pd.getParameter().password()) {
+ increment = processPassword(args, i, pd, validate);
+ } else {
+ if (pd.getParameter().variableArity()) {
+ //
+ // Variable arity?
+ //
+ increment = processVariableArity(args, i, pd, validate);
+ } else {
+ //
+ // Regular option
+ //
+ Class<?> fieldType = pd.getParameterized().getType();
+
+ // Boolean, set to true as soon as we see it, unless it specified
+ // an arity of 1, in which case we need to read the next value
+ if ((fieldType == boolean.class || fieldType == Boolean.class)
+ && pd.getParameter().arity() == -1) {
+ // Flip the value this boolean was initialized with
+ Boolean value = (Boolean) pd.getParameterized().get(pd.getObject());
+ pd.addValue(value ? "false" : "true");
+ requiredFields.remove(pd.getParameterized());
+ } else {
+ increment = processFixedArity(args, i, pd, validate, fieldType);
+ }
+ // If it's a help option, remember for later
+ if (pd.isHelp()) {
+ helpWasSpecified = true;
+ }
+ }
+ }
+ } else {
+ if (options.acceptUnknownOptions) {
+ unknownArgs.add(arg);
+ i++;
+ while (i < args.length && !isOption(args[i])) {
+ unknownArgs.add(args[i++]);
+ }
+ increment = 0;
+ } else {
+ throw new ParameterException("Unknown option: " + arg);
+ }
+ }
+ } else {
+ //
+ // Main parameter
+ //
+ if ("--".equals(arg) && !isDashDash) {
+ isDashDash = true;
+ }
+ else if (commands.isEmpty()) {
+ //
+ // Regular (non-command) parsing
+ //
+ List mp = getMainParameter(arg);
+ String value = a; // If there's a non-quoted version, prefer that one
+ Object convertedValue = value;
+
+ if (mainParameter.getGenericType() instanceof ParameterizedType) {
+ ParameterizedType p = (ParameterizedType) mainParameter.getGenericType();
+ Type cls = p.getActualTypeArguments()[0];
+ if (cls instanceof Class) {
+ convertedValue = convertValue(mainParameter, (Class) cls, null, value);
+ }
+ }
+
+ for(final Class<? extends IParameterValidator> validator : mainParameterAnnotation.validateWith() ) {
+ ParameterDescription.validateParameter(mainParameterDescription,
+ validator,
+ "Default", value);
+ }
+
+ mainParameterDescription.setAssigned(true);
+ mp.add(convertedValue);
+ } else {
+ //
+ // Command parsing
+ //
+ if (jc == null && validate) {
+ throw new MissingCommandException("Expected a command, got " + arg, arg);
+ } else if (jc != null) {
+ parsedCommand = jc.programName.name;
+ parsedAlias = arg; //preserve the original form
+
+ // Found a valid command, ask it to parse the remainder of the arguments.
+ // Setting the boolean commandParsed to true will force the current
+ // loop to end.
+ jc.parse(validate, subArray(args, i + 1));
+ commandParsed = true;
+ }
+ }
+ }
+ i += increment;
+ }
+
+ // Mark the parameter descriptions held in fields as assigned
+ for (ParameterDescription parameterDescription : descriptions.values()) {
+ if (parameterDescription.isAssigned()) {
+ fields.get(parameterDescription.getParameterized()).setAssigned(true);
+ }
+ }
+
+ }
+
+ private class DefaultVariableArity implements IVariableArity {
+
+ @Override
+ public int processVariableArity(String optionName, String[] options) {
+ int i = 0;
+ while (i < options.length && !isOption(options[i])) {
+ i++;
+ }
+ return i;
+ }
+ }
+
+ private final IVariableArity DEFAULT_VARIABLE_ARITY = new DefaultVariableArity();
+
+ private final int determineArity(String[] args, int index, ParameterDescription pd, IVariableArity va) {
+ List<String> currentArgs = Lists.newArrayList();
+ for (int j = index + 1; j < args.length; j++) {
+ currentArgs.add(args[j]);
+ }
+ return va.processVariableArity(pd.getParameter().names()[0],
+ currentArgs.toArray(new String[0]));
+ }
+
+ /**
+ * @return the number of options that were processed.
+ */
+ private int processPassword(String[] args, int index, ParameterDescription pd, boolean validate) {
+ final int passwordArity = determineArity(args, index, pd, DEFAULT_VARIABLE_ARITY);
+ if (passwordArity == 0) {
+ // password option with password not specified, use the Console to retrieve the password
+ char[] password = readPassword(pd.getDescription(), pd.getParameter().echoInput());
+ pd.addValue(new String(password));
+ requiredFields.remove(pd.getParameterized());
+ return 1;
+ } else if (passwordArity == 1) {
+ // password option with password specified
+ return processFixedArity(args, index, pd, validate, List.class, 1);
} else {
- ParameterDescription pd =
- new ParameterDescription(object, p, parameterized, m_bundle, this);
- for (String name : p.names()) {
- if (m_descriptions.containsKey(new StringKey(name))) {
- throw new ParameterException("Found the option " + name + " multiple times");
+ throw new ParameterException("Password parameter must have at most 1 argument.");
+ }
+ }
+
+ /**
+ * @return the number of options that were processed.
+ */
+ private int processVariableArity(String[] args, int index, ParameterDescription pd, boolean validate) {
+ Object arg = pd.getObject();
+ IVariableArity va;
+ if (!(arg instanceof IVariableArity)) {
+ va = DEFAULT_VARIABLE_ARITY;
+ } else {
+ va = (IVariableArity) arg;
+ }
+
+ int arity = determineArity(args, index, pd, va);
+ int result = processFixedArity(args, index, pd, validate, List.class, arity);
+ return result;
+ }
+
+ private int processFixedArity(String[] args, int index, ParameterDescription pd, boolean validate,
+ Class<?> fieldType) {
+ // Regular parameter, use the arity to tell use how many values
+ // we need to consume
+ int arity = pd.getParameter().arity();
+ int n = (arity != -1 ? arity : 1);
+
+ return processFixedArity(args, index, pd, validate, fieldType, n);
+ }
+
+ private int processFixedArity(String[] args, int originalIndex, ParameterDescription pd, boolean validate,
+ Class<?> fieldType, int arity) {
+ int index = originalIndex;
+ String arg = args[index];
+ // Special case for boolean parameters of arity 0
+ if (arity == 0 &&
+ (Boolean.class.isAssignableFrom(fieldType)
+ || boolean.class.isAssignableFrom(fieldType))) {
+ // Flip the value this boolean was initialized with
+ Boolean value = (Boolean) pd.getParameterized().get(pd.getObject());
+ pd.addValue(value ? "false" : "true");
+ requiredFields.remove(pd.getParameterized());
+ } else if (arity == 0) {
+ throw new ParameterException("Expected a value after parameter " + arg);
+
+ } else if (index < args.length - 1) {
+ int offset = "--".equals(args[index + 1]) ? 1 : 0;
+
+ Object finalValue = null;
+ if (index + arity < args.length) {
+ for (int j = 1; j <= arity; j++) {
+ String value = trim(args[index + j + offset]);
+ finalValue = pd.addValue(arg, value, false, validate, j - 1);
+ requiredFields.remove(pd.getParameterized());
+ }
+
+ if (finalValue != null && validate) {
+ pd.validateValueParameter(arg, finalValue);
+ }
+ index += arity + offset;
+ } else {
+ throw new ParameterException("Expected " + arity + " values after " + arg);
}
- p("Adding description for " + name);
- m_fields.put(parameterized, pd);
- m_descriptions.put(new StringKey(name), pd);
+ } else {
+ throw new ParameterException("Expected a value after parameter " + arg);
+ }
+
+ return arity + 1;
+ }
+
+ /**
+ * Invoke Console.readPassword through reflection to avoid depending
+ * on Java 6.
+ */
+ private char[] readPassword(String description, boolean echoInput) {
+ getConsole().print(description + ": ");
+ return getConsole().readPassword(echoInput);
+ }
- if (p.required()) m_requiredFields.put(parameterized, pd);
- }
+ private String[] subArray(String[] args, int index) {
+ int l = args.length - index;
+ String[] result = new String[l];
+ System.arraycopy(args, index, result, 0, l);
+
+ return result;
+ }
+
+ /**
+ * @return the field that's meant to receive all the parameters that are not options.
+ *
+ * @param arg the arg that we're about to add (only passed here to output a meaningful
+ * error message).
+ */
+ private List<?> getMainParameter(String arg) {
+ if (mainParameter == null) {
+ throw new ParameterException(
+ "Was passed main parameter '" + arg + "' but no main parameter was defined in your arg class");
}
- } else if (parameterized.getDelegateAnnotation() != null) {
+
+ List<?> result = (List<?>) mainParameter.get(mainParameterObject);
+ if (result == null) {
+ result = Lists.newArrayList();
+ if (!List.class.isAssignableFrom(mainParameter.getType())) {
+ throw new ParameterException("Main parameter field " + mainParameter
+ + " needs to be of type List, not " + mainParameter.getType());
+ }
+ mainParameter.set(mainParameterObject, result);
+ }
+ if (firstTimeMainParameter) {
+ result.clear();
+ firstTimeMainParameter = false;
+ }
+ return result;
+ }
+
+ public String getMainParameterDescription() {
+ if (descriptions == null) createDescriptions();
+ return mainParameterAnnotation != null ? mainParameterAnnotation.description()
+ : null;
+ }
+
+ /**
+ * Set the program name (used only in the usage).
+ */
+ public void setProgramName(String name) {
+ setProgramName(name, new String[0]);
+ }
+
+ /**
+ * Get the program name (used only in the usage).
+ */
+ public String getProgramName(){
+ return programName == null ? null : programName.getName();
+ }
+
+ /**
+ * Set the program name
+ *
+ * @param name program name
+ * @param aliases aliases to the program name
+ */
+ public void setProgramName(String name, String... aliases) {
+ programName = new ProgramName(name, Arrays.asList(aliases));
+ }
+
+ /**
+ * Display the usage for this command.
+ */
+ public void usage(String commandName) {
+ StringBuilder sb = new StringBuilder();
+ usage(commandName, sb);
+ getConsole().println(sb.toString());
+ }
+
+ /**
+ * Store the help for the command in the passed string builder.
+ */
+ public void usage(String commandName, StringBuilder out) {
+ usage(commandName, out, "");
+ }
+
+ /**
+ * Store the help for the command in the passed string builder, indenting
+ * every line with "indent".
+ */
+ public void usage(String commandName, StringBuilder out, String indent) {
+ String description = getCommandDescription(commandName);
+ JCommander jc = findCommandByAlias(commandName);
+ if (description != null) {
+ out.append(indent).append(description);
+ out.append("\n");
+ }
+ jc.usage(out, indent);
+ }
+
+ /**
+ * @return the description of the command.
+ */
+ public String getCommandDescription(String commandName) {
+ JCommander jc = findCommandByAlias(commandName);
+ if (jc == null) {
+ throw new ParameterException("Asking description for unknown command: " + commandName);
+ }
+
+ Object arg = jc.getObjects().get(0);
+ Parameters p = arg.getClass().getAnnotation(Parameters.class);
+ ResourceBundle bundle = null;
+ String result = null;
+ if (p != null) {
+ result = p.commandDescription();
+ String bundleName = p.resourceBundle();
+ if (!"".equals(bundleName)) {
+ bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
+ } else {
+ bundle = options.bundle;
+ }
+
+ if (bundle != null) {
+ String descriptionKey = p.commandDescriptionKey();
+ if (!"".equals(descriptionKey)) {
+ result = getI18nString(bundle, descriptionKey, p.commandDescription());
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @return The internationalized version of the string if available, otherwise
+ * return def.
+ */
+ private String getI18nString(ResourceBundle bundle, String key, String def) {
+ String s = bundle != null ? bundle.getString(key) : null;
+ return s != null ? s : def;
+ }
+
+ /**
+ * Display the help on System.out.
+ */
+ public void usage() {
+ StringBuilder sb = new StringBuilder();
+ usage(sb);
+ getConsole().println(sb.toString());
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private JCommander jCommander = new JCommander();
+ private String[] args = null;
+
+ public Builder() {
+ }
+
+ /**
+ * Adds the provided arg object to the set of objects that this commander
+ * will parse arguments into.
+ *
+ * @param o The arg object expected to contain {@link Parameter}
+ * annotations. If <code>object</code> is an array or is {@link Iterable},
+ * the child objects will be added instead.
+ */
+ public Builder addObject(Object o) {
+ jCommander.addObject(o);
+ return this;
+ }
+
+ /**
+ * Sets the {@link ResourceBundle} to use for looking up descriptions.
+ * Set this to <code>null</code> to use description text directly.
+ */
+ public Builder resourceBundle(ResourceBundle bundle) {
+ jCommander.setDescriptionsBundle(bundle);
+ return this;
+ }
+
+ public Builder args(String[] args) {
+ this.args = args;
+ return this;
+ }
+
+ /**
+ * Disables expanding {@code @file}.
+ *
+ * JCommander supports the {@code @file} syntax, which allows you to put all your options
+ * into a file and pass this file as parameter @param expandAtSign whether to expand {@code @file}.
+ */
+ public Builder expandAtSign(Boolean expand) {
+ jCommander.setExpandAtSign(expand);
+ return this;
+ }
+
+ /**
+ * Set the program name (used only in the usage).
+ */
+ public Builder programName(String name) {
+ jCommander.setProgramName(name);
+ return this;
+ }
+
+ public Builder columnSize(int columnSize) {
+ jCommander.setColumnSize(columnSize);
+ return this;
+ }
+
+ /**
+ * Define the default provider for this instance.
+ */
+ public Builder defaultProvider(IDefaultProvider provider) {
+ jCommander.setDefaultProvider(provider);
+ return this;
+ }
+
+ /**
+ * Adds a factory to lookup string converters. The added factory is used prior to previously added factories.
+ * @param factory the factory determining string converters
+ */
+ public Builder addConverterFactory(IStringConverterFactory factory) {
+ jCommander.addConverterFactory(factory);
+ return this;
+ }
+
+ public Builder verbose(int verbose) {
+ jCommander.setVerbose(verbose);
+ return this;
+ }
+
+ public Builder allowAbbreviatedOptions(boolean b) {
+ jCommander.setAllowAbbreviatedOptions(b);
+ return this;
+ }
+
+ public Builder acceptUnknownOptions(boolean b) {
+ jCommander.setAcceptUnknownOptions(b);
+ return this;
+ }
+
+ public Builder allowParameterOverwriting(boolean b) {
+ jCommander.setAllowParameterOverwriting(b);
+ return this;
+ }
+
+ public Builder atFileCharset(Charset charset) {
+ jCommander.setAtFileCharset(charset);
+ return this;
+ }
+
+ public Builder addConverterInstanceFactory(IStringConverterInstanceFactory factory) {
+ jCommander.addConverterInstanceFactory(factory);
+ return this;
+ }
+
+ public Builder addCommand(Object command) {
+ jCommander.addCommand(command);
+ return this;
+ }
+
+ public Builder addCommand(String name, Object command, String... aliases) {
+ jCommander.addCommand(name, command, aliases);
+ return this;
+ }
+
+ public JCommander build() {
+ if (args != null) {
+ jCommander.parse(args);
+ }
+ return jCommander;
+ }
+ }
+
+
+ /**
+ * Store the help in the passed string builder.
+ */
+ public void usage(StringBuilder out) {
+ usage(out, "");
+ }
+
+ public void usage(StringBuilder out, String indent) {
+ if (descriptions == null) createDescriptions();
+ boolean hasCommands = !commands.isEmpty();
+ boolean hasOptions = !descriptions.isEmpty();
+
+ //indenting
+ int descriptionIndent = 6;
+ int indentCount = indent.length() + descriptionIndent;
+
//
- // @ParametersDelegate
+ // First line of the usage
//
- Object delegateObject = parameterized.get(object);
- if (delegateObject == null){
- throw new ParameterException("Delegate field '" + parameterized.getName()
- + "' cannot be null.");
+ String programName = this.programName != null ? this.programName.getDisplayName() : "<main class>";
+ StringBuilder mainLine = new StringBuilder();
+ mainLine.append(indent).append("Usage: ").append(programName);
+ if (hasOptions) mainLine.append(" [options]");
+ if (hasCommands) mainLine.append(indent).append(" [command] [command options]");
+ if (mainParameterDescription != null) {
+ mainLine.append(" ").append(mainParameterDescription.getDescription());
}
- addDescription(delegateObject);
- } else if (wp != null && wp.getDynamicParameter() != null) {
+ wrapDescription(out, indentCount, mainLine.toString());
+ out.append("\n");
+
//
- // @DynamicParameter
+ // Align the descriptions at the "longestName" column
//
- DynamicParameter dp = wp.getDynamicParameter();
- for (String name : dp.names()) {
- if (m_descriptions.containsKey(name)) {
- throw new ParameterException("Found the option " + name + " multiple times");
- }
- p("Adding description for " + name);
- ParameterDescription pd =
- new ParameterDescription(object, dp, parameterized, m_bundle, this);
- m_fields.put(parameterized, pd);
- m_descriptions.put(new StringKey(name), pd);
-
- if (dp.required()) m_requiredFields.put(parameterized, pd);
- }
- }
- }
-
-// while (!Object.class.equals(cls)) {
-// for (Field f : cls.getDeclaredFields()) {
-// p("Field:" + cls.getSimpleName() + "." + f.getName());
-// f.setAccessible(true);
-// Annotation annotation = f.getAnnotation(Parameter.class);
-// Annotation delegateAnnotation = f.getAnnotation(ParametersDelegate.class);
-// Annotation dynamicParameter = f.getAnnotation(DynamicParameter.class);
-// if (annotation != null) {
-// //
-// // @Parameter
-// //
-// Parameter p = (Parameter) annotation;
-// if (p.names().length == 0) {
-// p("Found main parameter:" + f);
-// if (m_mainParameterField != null) {
-// throw new ParameterException("Only one @Parameter with no names attribute is"
-// + " allowed, found:" + m_mainParameterField + " and " + f);
-// }
-// m_mainParameterField = parameterized;
-// m_mainParameterObject = object;
-// m_mainParameterAnnotation = p;
-// m_mainParameterDescription = new ParameterDescription(object, p, f, m_bundle, this);
-// } else {
-// for (String name : p.names()) {
-// if (m_descriptions.containsKey(name)) {
-// throw new ParameterException("Found the option " + name + " multiple times");
-// }
-// p("Adding description for " + name);
-// ParameterDescription pd = new ParameterDescription(object, p, f, m_bundle, this);
-// m_fields.put(f, pd);
-// m_descriptions.put(name, pd);
-//
-// if (p.required()) m_requiredFields.put(f, pd);
-// }
-// }
-// } else if (delegateAnnotation != null) {
-// //
-// // @ParametersDelegate
-// //
-// try {
-// Object delegateObject = f.get(object);
-// if (delegateObject == null){
-// throw new ParameterException("Delegate field '" + f.getName() + "' cannot be null.");
-// }
-// addDescription(delegateObject);
-// } catch (IllegalAccessException e) {
-// }
-// } else if (dynamicParameter != null) {
-// //
-// // @DynamicParameter
-// //
-// DynamicParameter dp = (DynamicParameter) dynamicParameter;
-// for (String name : dp.names()) {
-// if (m_descriptions.containsKey(name)) {
-// throw new ParameterException("Found the option " + name + " multiple times");
-// }
-// p("Adding description for " + name);
-// ParameterDescription pd = new ParameterDescription(object, dp, f, m_bundle, this);
-// m_fields.put(f, pd);
-// m_descriptions.put(name, pd);
-//
-// if (dp.required()) m_requiredFields.put(f, pd);
-// }
-// }
-// }
-// // Traverse the super class until we find Object.class
-// cls = cls.getSuperclass();
-// }
- }
-
- private void initializeDefaultValue(ParameterDescription pd) {
- for (String optionName : pd.getParameter().names()) {
- String def = m_defaultProvider.getDefaultValueFor(optionName);
- if (def != null) {
- p("Initializing " + optionName + " with default value:" + def);
- pd.addValue(def, true /* default */);
- return;
- }
- }
- }
-
- /**
- * Main method that parses the values and initializes the fields accordingly.
- */
- private void parseValues(String[] args, boolean validate) {
- // This boolean becomes true if we encounter a command, which indicates we need
- // to stop parsing (the parsing of the command will be done in a sub JCommander
- // object)
- boolean commandParsed = false;
- int i = 0;
- boolean isDashDash = false; // once we encounter --, everything goes into the main parameter
- while (i < args.length && ! commandParsed) {
- String arg = args[i];
- String a = trim(arg);
- args[i] = a;
- p("Parsing arg: " + a);
-
- JCommander jc = findCommandByAlias(arg);
- int increment = 1;
- if (! isDashDash && ! "--".equals(a) && isOption(args, a) && jc == null) {
+ int longestName = 0;
+ List<ParameterDescription> sorted = Lists.newArrayList();
+ for (ParameterDescription pd : fields.values()) {
+ if (!pd.getParameter().hidden()) {
+ sorted.add(pd);
+ // + to have an extra space between the name and the description
+ int length = pd.getNames().length() + 2;
+ if (length > longestName) {
+ longestName = length;
+ }
+ }
+ }
+
//
- // Option
+ // Sort the options
//
- ParameterDescription pd = findParameterDescription(a);
+ Collections.sort(sorted, getParameterDescriptionComparator());
- if (pd != null) {
- if (pd.getParameter().password()) {
- //
- // Password option, use the Console to retrieve the password
- //
- char[] password = readPassword(pd.getDescription(), pd.getParameter().echoInput());
- pd.addValue(new String(password));
- m_requiredFields.remove(pd.getParameterized());
- } else {
- if (pd.getParameter().variableArity()) {
- //
- // Variable arity?
- //
- increment = processVariableArity(args, i, pd);
- } else {
- //
- // Regular option
- //
- Class<?> fieldType = pd.getParameterized().getType();
-
- // Boolean, set to true as soon as we see it, unless it specified
- // an arity of 1, in which case we need to read the next value
- if ((fieldType == boolean.class || fieldType == Boolean.class)
- && pd.getParameter().arity() == -1) {
- pd.addValue("true");
- m_requiredFields.remove(pd.getParameterized());
- } else {
- increment = processFixedArity(args, i, pd, fieldType);
- }
- // If it's a help option, remember for later
- if (pd.isHelp()) {
- m_helpWasSpecified = true;
- }
+ //
+ // Display all the names and descriptions
+ //
+ if (sorted.size() > 0) out.append(indent).append(" Options:\n");
+ for (ParameterDescription pd : sorted) {
+ WrappedParameter parameter = pd.getParameter();
+ out.append(indent).append(" "
+ + (parameter.required() ? "* " : " ")
+ + pd.getNames()
+ + "\n");
+ wrapDescription(out, indentCount, s(indentCount) + pd.getDescription());
+ Object def = pd.getDefault();
+ if (pd.isDynamicParameter()) {
+ out.append("\n" + s(indentCount))
+ .append("Syntax: " + parameter.names()[0]
+ + "key" + parameter.getAssignment()
+ + "value");
}
- }
- } else {
- if (m_acceptUnknownOptions) {
- m_unknownArgs.add(arg);
- i++;
- while (i < args.length && ! isOption(args, args[i])) {
- m_unknownArgs.add(args[i++]);
+ if (def != null && !pd.isHelp()) {
+ String displayedDef = Strings.isStringEmpty(def.toString())
+ ? "<empty string>"
+ : def.toString();
+ out.append("\n" + s(indentCount))
+ .append("Default: " + (parameter.password() ? "********" : displayedDef));
+ }
+ Class<?> type = pd.getParameterized().getType();
+ if (type.isEnum()) {
+ out.append("\n" + s(indentCount))
+ .append("Possible Values: " + EnumSet.allOf((Class<? extends Enum>) type));
}
- increment = 0;
- } else {
- throw new ParameterException("Unknown option: " + arg);
- }
+ out.append("\n");
}
- }
- else {
+
//
- // Main parameter
+ // If commands were specified, show them as well
//
- if (! Strings.isStringEmpty(arg)) {
- if ("--".equals(arg)) {
- isDashDash = true;
- a = trim(args[++i]);
- }
- if (m_commands.isEmpty()) {
- //
- // Regular (non-command) parsing
- //
- List mp = getMainParameter(arg);
- String value = a; // If there's a non-quoted version, prefer that one
- Object convertedValue = value;
-
- if (m_mainParameter.getGenericType() instanceof ParameterizedType) {
- ParameterizedType p = (ParameterizedType) m_mainParameter.getGenericType();
- Type cls = p.getActualTypeArguments()[0];
- if (cls instanceof Class) {
- convertedValue = convertValue(m_mainParameter, (Class) cls, value);
- }
+ if (hasCommands) {
+ out.append(indent + " Commands:\n");
+ // The magic value 3 is the number of spaces between the name of the option
+ // and its description
+ for (Map.Entry<ProgramName, JCommander> commands : this.commands.entrySet()) {
+ Object arg = commands.getValue().getObjects().get(0);
+ Parameters p = arg.getClass().getAnnotation(Parameters.class);
+ if (p == null || !p.hidden()) {
+ ProgramName progName = commands.getKey();
+ String dispName = progName.getDisplayName();
+ String description = getCommandDescription(progName.getName());
+ wrapDescription(out, indentCount + descriptionIndent,
+ indent + " " + dispName + " " + description);
+ out.append("\n");
+
+ // Options for this command
+ JCommander jc = findCommandByAlias(progName.getName());
+ jc.usage(out, indent + " ");
+ out.append("\n");
+ }
}
+ }
+ }
- ParameterDescription.validateParameter(m_mainParameterDescription,
- m_mainParameterAnnotation.validateWith(),
- "Default", value);
-
- m_mainParameterDescription.setAssigned(true);
- mp.add(convertedValue);
- }
- else {
- //
- // Command parsing
- //
- if (jc == null && validate) {
- throw new MissingCommandException("Expected a command, got " + arg);
- } else if (jc != null){
- m_parsedCommand = jc.m_programName.m_name;
- m_parsedAlias = arg; //preserve the original form
-
- // Found a valid command, ask it to parse the remainder of the arguments.
- // Setting the boolean commandParsed to true will force the current
- // loop to end.
- jc.parse(subArray(args, i + 1));
- commandParsed = true;
+ private Comparator<? super ParameterDescription> getParameterDescriptionComparator() {
+ return options.parameterDescriptionComparator;
+ }
+
+ public void setParameterDescriptionComparator(Comparator<? super ParameterDescription> c) {
+ options.parameterDescriptionComparator = c;
+ }
+
+ public void setColumnSize(int columnSize) {
+ options.columnSize = columnSize;
+ }
+
+ public int getColumnSize() {
+ return options.columnSize;
+ }
+
+ /**
+ * Wrap a potentially long line to {@link #getColumnSize()}.
+ *
+ * @param out the output
+ * @param indent the indentation in spaces for lines after the first line.
+ * @param description the text to wrap. No extra spaces are inserted before {@code
+ * description}. If the first line needs to be indented prepend the
+ * correct number of spaces to {@code description}.
+ */
+ private void wrapDescription(StringBuilder out, int indent, String description) {
+ int max = getColumnSize();
+ String[] words = description.split(" ");
+ int current = 0;
+ int i = 0;
+ while (i < words.length) {
+ String word = words[i];
+ if (word.length() > max || current + 1 + word.length() <= max) {
+ out.append(word);
+ current += word.length();
+ if (i != words.length - 1) {
+ out.append(" ");
+ current++;
+ }
+ } else {
+ out.append("\n").append(s(indent)).append(word).append(" ");
+ current = indent + 1 + word.length();
}
- }
+ i++;
}
- }
- i += increment;
}
- // Mark the parameter descriptions held in m_fields as assigned
- for (ParameterDescription parameterDescription : m_descriptions.values()) {
- if (parameterDescription.isAssigned()) {
- m_fields.get(parameterDescription.getParameterized()).setAssigned(true);
- }
+ /**
+ * @return a Collection of all the \@Parameter annotations found on the
+ * target class. This can be used to display the usage() in a different
+ * format (e.g. HTML).
+ */
+ public List<ParameterDescription> getParameters() {
+ return new ArrayList<>(fields.values());
+ }
+
+ /**
+ * @return the main parameter description or null if none is defined.
+ */
+ public ParameterDescription getMainParameter() {
+ return mainParameterDescription;
}
- }
+ private void p(String string) {
+ if (options.verbose > 0 || System.getProperty(JCommander.DEBUG_PROPERTY) != null) {
+ getConsole().println("[JCommander] " + string);
+ }
+ }
- private class DefaultVariableArity implements IVariableArity {
+ /**
+ * Define the default provider for this instance.
+ */
+ public void setDefaultProvider(IDefaultProvider defaultProvider) {
+ options.defaultProvider = defaultProvider;
+ }
- @Override
- public int processVariableArity(String optionName, String[] options) {
- int i = 0;
- while (i < options.length && !isOption(options, options[i])) {
- i++;
- }
- return i;
- }
- }
- private final IVariableArity DEFAULT_VARIABLE_ARITY = new DefaultVariableArity();
-
- private int m_verbose = 0;
-
- private boolean m_caseSensitiveOptions = true;
- private boolean m_allowAbbreviatedOptions = false;
-
- /**
- * @return the number of options that were processed.
- */
- private int processVariableArity(String[] args, int index, ParameterDescription pd) {
- Object arg = pd.getObject();
- IVariableArity va;
- if (! (arg instanceof IVariableArity)) {
- va = DEFAULT_VARIABLE_ARITY;
- } else {
- va = (IVariableArity) arg;
- }
-
- List<String> currentArgs = Lists.newArrayList();
- for (int j = index + 1; j < args.length; j++) {
- currentArgs.add(args[j]);
- }
- int arity = va.processVariableArity(pd.getParameter().names()[0],
- currentArgs.toArray(new String[0]));
-
- int result = processFixedArity(args, index, pd, List.class, arity);
- return result;
- }
-
- private int processFixedArity(String[] args, int index, ParameterDescription pd,
- Class<?> fieldType) {
- // Regular parameter, use the arity to tell use how many values
- // we need to consume
- int arity = pd.getParameter().arity();
- int n = (arity != -1 ? arity : 1);
-
- return processFixedArity(args, index, pd, fieldType, n);
- }
-
- private int processFixedArity(String[] args, int originalIndex, ParameterDescription pd,
- Class<?> fieldType, int arity) {
- int index = originalIndex;
- String arg = args[index];
- // Special case for boolean parameters of arity 0
- if (arity == 0 &&
- (Boolean.class.isAssignableFrom(fieldType)
- || boolean.class.isAssignableFrom(fieldType))) {
- pd.addValue("true");
- m_requiredFields.remove(pd.getParameterized());
- } else if (index < args.length - 1) {
- int offset = "--".equals(args[index + 1]) ? 1 : 0;
-
- if (index + arity < args.length) {
- for (int j = 1; j <= arity; j++) {
- pd.addValue(trim(args[index + j + offset]));
- m_requiredFields.remove(pd.getParameterized());
- }
- index += arity + offset;
- } else {
- throw new ParameterException("Expected " + arity + " values after " + arg);
- }
- } else {
- throw new ParameterException("Expected a value after parameter " + arg);
- }
-
- return arity + 1;
- }
-
- /**
- * Invoke Console.readPassword through reflection to avoid depending
- * on Java 6.
- */
- private char[] readPassword(String description, boolean echoInput) {
- getConsole().print(description + ": ");
- return getConsole().readPassword(echoInput);
- }
-
- private String[] subArray(String[] args, int index) {
- int l = args.length - index;
- String[] result = new String[l];
- System.arraycopy(args, index, result, 0, l);
-
- return result;
- }
-
- /**
- * @return the field that's meant to receive all the parameters that are not options.
- *
- * @param arg the arg that we're about to add (only passed here to output a meaningful
- * error message).
- */
- private List<?> getMainParameter(String arg) {
- if (m_mainParameter == null) {
- throw new ParameterException(
- "Was passed main parameter '" + arg + "' but no main parameter was defined");
- }
-
- List<?> result = (List<?>) m_mainParameter.get(m_mainParameterObject);
- if (result == null) {
- result = Lists.newArrayList();
- if (! List.class.isAssignableFrom(m_mainParameter.getType())) {
- throw new ParameterException("Main parameter field " + m_mainParameter
- + " needs to be of type List, not " + m_mainParameter.getType());
- }
- m_mainParameter.set(m_mainParameterObject, result);
- }
- if (m_firstTimeMainParameter) {
- result.clear();
- m_firstTimeMainParameter = false;
- }
- return result;
- }
-
- public String getMainParameterDescription() {
- if (m_descriptions == null) createDescriptions();
- return m_mainParameterAnnotation != null ? m_mainParameterAnnotation.description()
- : null;
- }
-
-// private int longestName(Collection<?> objects) {
-// int result = 0;
-// for (Object o : objects) {
-// int l = o.toString().length();
-// if (l > result) result = l;
-// }
-//
-// return result;
-// }
-
- /**
- * Set the program name (used only in the usage).
- */
- public void setProgramName(String name) {
- setProgramName(name, new String[0]);
- }
-
- /**
- * Set the program name
- *
- * @param name program name
- * @param aliases aliases to the program name
- */
- public void setProgramName(String name, String... aliases) {
- m_programName = new ProgramName(name, Arrays.asList(aliases));
- }
-
- /**
- * Display the usage for this command.
- */
- public void usage(String commandName) {
- StringBuilder sb = new StringBuilder();
- usage(commandName, sb);
- getConsole().println(sb.toString());
- }
-
- /**
- * Store the help for the command in the passed string builder.
- */
- public void usage(String commandName, StringBuilder out) {
- usage(commandName, out, "");
- }
-
- /**
- * Store the help for the command in the passed string builder, indenting
- * every line with "indent".
- */
- public void usage(String commandName, StringBuilder out, String indent) {
- String description = getCommandDescription(commandName);
- JCommander jc = findCommandByAlias(commandName);
- if (description != null) {
- out.append(indent).append(description);
- out.append("\n");
- }
- jc.usage(out, indent);
- }
-
- /**
- * @return the description of the command.
- */
- public String getCommandDescription(String commandName) {
- JCommander jc = findCommandByAlias(commandName);
- if (jc == null) {
- throw new ParameterException("Asking description for unknown command: " + commandName);
- }
-
- Object arg = jc.getObjects().get(0);
- Parameters p = arg.getClass().getAnnotation(Parameters.class);
- ResourceBundle bundle = null;
- String result = null;
- if (p != null) {
- result = p.commandDescription();
- String bundleName = p.resourceBundle();
- if (!"".equals(bundleName)) {
- bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
- } else {
- bundle = m_bundle;
- }
-
- if (bundle != null) {
- result = getI18nString(bundle, p.commandDescriptionKey(), p.commandDescription());
- }
- }
-
- return result;
- }
-
- /**
- * @return The internationalized version of the string if available, otherwise
- * return def.
- */
- private String getI18nString(ResourceBundle bundle, String key, String def) {
- String s = bundle != null ? bundle.getString(key) : null;
- return s != null ? s : def;
- }
-
- /**
- * Display the help on System.out.
- */
- public void usage() {
- StringBuilder sb = new StringBuilder();
- usage(sb);
- getConsole().println(sb.toString());
- }
-
- /**
- * Store the help in the passed string builder.
- */
- public void usage(StringBuilder out) {
- usage(out, "");
- }
-
- public void usage(StringBuilder out, String indent) {
- if (m_descriptions == null) createDescriptions();
- boolean hasCommands = !m_commands.isEmpty();
-
- //
- // First line of the usage
- //
- String programName = m_programName != null ? m_programName.getDisplayName() : "<main class>";
- out.append(indent).append("Usage: " + programName + " [options]");
- if (hasCommands) out.append(indent).append(" [command] [command options]");
- if (m_mainParameterDescription != null) {
- out.append(" " + m_mainParameterDescription.getDescription());
- }
- out.append("\n");
-
- //
- // Align the descriptions at the "longestName" column
- //
- int longestName = 0;
- List<ParameterDescription> sorted = Lists.newArrayList();
- for (ParameterDescription pd : m_fields.values()) {
- if (! pd.getParameter().hidden()) {
- sorted.add(pd);
- // + to have an extra space between the name and the description
- int length = pd.getNames().length() + 2;
- if (length > longestName) {
- longestName = length;
- }
- }
- }
-
- //
- // Sort the options
- //
- Collections.sort(sorted, getParameterDescriptionComparator());
-
- //
- // Display all the names and descriptions
- //
- int descriptionIndent = 6;
- if (sorted.size() > 0) out.append(indent).append(" Options:\n");
- for (ParameterDescription pd : sorted) {
- WrappedParameter parameter = pd.getParameter();
- out.append(indent).append(" "
- + (parameter.required() ? "* " : " ")
- + pd.getNames()
- + "\n"
- + indent + s(descriptionIndent));
- int indentCount = indent.length() + descriptionIndent;
- wrapDescription(out, indentCount, pd.getDescription());
- Object def = pd.getDefault();
- if (pd.isDynamicParameter()) {
- out.append("\n" + s(indentCount + 1))
- .append("Syntax: " + parameter.names()[0]
- + "key" + parameter.getAssignment()
- + "value");
- }
- if (def != null) {
- String displayedDef = Strings.isStringEmpty(def.toString())
- ? "<empty string>"
- : def.toString();
- out.append("\n" + s(indentCount + 1))
- .append("Default: " + (parameter.password()?"********" : displayedDef));
- }
- Class<?> type = pd.getParameterized().getType();
- if(type.isEnum()){
- out.append("\n" + s(indentCount + 1))
- .append("Possible Values: " + EnumSet.allOf((Class<? extends Enum>) type));
- }
- out.append("\n");
- }
-
- //
- // If commands were specified, show them as well
- //
- if (hasCommands) {
- out.append(" Commands:\n");
- // The magic value 3 is the number of spaces between the name of the option
- // and its description
- for (Map.Entry<ProgramName, JCommander> commands : m_commands.entrySet()) {
- Object arg = commands.getValue().getObjects().get(0);
- Parameters p = arg.getClass().getAnnotation(Parameters.class);
- if (!p.hidden()) {
- ProgramName progName = commands.getKey();
- String dispName = progName.getDisplayName();
- out.append(indent).append(" " + dispName); // + s(spaceCount) + getCommandDescription(progName.name) + "\n");
-
- // Options for this command
- usage(progName.getName(), out, " ");
- out.append("\n");
- }
- }
- }
- }
-
- private Comparator<? super ParameterDescription> getParameterDescriptionComparator() {
- return m_parameterDescriptionComparator;
- }
-
- public void setParameterDescriptionComparator(Comparator<? super ParameterDescription> c) {
- m_parameterDescriptionComparator = c;
- }
-
- public void setColumnSize(int columnSize) {
- m_columnSize = columnSize;
- }
-
- public int getColumnSize() {
- return m_columnSize;
- }
-
- private void wrapDescription(StringBuilder out, int indent, String description) {
- int max = getColumnSize();
- String[] words = description.split(" ");
- int current = indent;
- int i = 0;
- while (i < words.length) {
- String word = words[i];
- if (word.length() > max || current + word.length() <= max) {
- out.append(" ").append(word);
- current += word.length() + 1;
- } else {
- out.append("\n").append(s(indent + 1)).append(word);
- current = indent;
- }
- i++;
- }
- }
-
- /**
- * @return a Collection of all the \@Parameter annotations found on the
- * target class. This can be used to display the usage() in a different
- * format (e.g. HTML).
- */
- public List<ParameterDescription> getParameters() {
- return new ArrayList<ParameterDescription>(m_fields.values());
- }
-
- /**
- * @return the main parameter description or null if none is defined.
- */
- public ParameterDescription getMainParameter() {
- return m_mainParameterDescription;
- }
-
- private void p(String string) {
- if (m_verbose > 0 || System.getProperty(JCommander.DEBUG_PROPERTY) != null) {
- getConsole().println("[JCommander] " + string);
- }
- }
-
- /**
- * Define the default provider for this instance.
- */
- public void setDefaultProvider(IDefaultProvider defaultProvider) {
- m_defaultProvider = defaultProvider;
-
- for (Map.Entry<ProgramName, JCommander> entry : m_commands.entrySet()) {
- entry.getValue().setDefaultProvider(defaultProvider);
- }
- }
-
- public void addConverterFactory(IStringConverterFactory converterFactory) {
- CONVERTER_FACTORIES.addFirst(converterFactory);
- }
-
- public <T> Class<? extends IStringConverter<T>> findConverter(Class<T> cls) {
- for (IStringConverterFactory f : CONVERTER_FACTORIES) {
- Class<? extends IStringConverter<T>> result = f.getConverter(cls);
- if (result != null) return result;
- }
-
- return null;
- }
-
- public Object convertValue(ParameterDescription pd, String value) {
- return convertValue(pd.getParameterized(), pd.getParameterized().getType(), value);
- }
-
- /**
- * @param type The type of the actual parameter
- * @param value The value to convert
- */
- public Object convertValue(Parameterized parameterized, Class type,
- String value) {
- Parameter annotation = parameterized.getParameter();
-
- // Do nothing if it's a @DynamicParameter
- if (annotation == null) return value;
-
- Class<? extends IStringConverter<?>> converterClass = annotation.converter();
- boolean listConverterWasSpecified = annotation.listConverter() != NoConverter.class;
-
- //
- // Try to find a converter on the annotation
- //
- if (converterClass == null || converterClass == NoConverter.class) {
- // If no converter specified and type is enum, used enum values to convert
- if (type.isEnum()){
- converterClass = type;
- } else {
- converterClass = findConverter(type);
- }
- }
-
- if (converterClass == null) {
- Type elementType = parameterized.findFieldGenericType();
- converterClass = elementType != null
- ? findConverter((Class<? extends IStringConverter<?>>) elementType)
- : StringConverter.class;
- // Check for enum type parameter
- if (converterClass == null && Enum.class.isAssignableFrom((Class) elementType)) {
- converterClass = (Class<? extends IStringConverter<?>>) elementType;
- }
- }
-
- IStringConverter<?> converter;
- Object result = null;
- try {
- String[] names = annotation.names();
- String optionName = names.length > 0 ? names[0] : "[Main class]";
- if (converterClass != null && converterClass.isEnum()) {
+ /**
+ * Adds a factory to lookup string converters. The added factory is used prior to previously added factories.
+ * @param converterFactory the factory determining string converters
+ */
+ public void addConverterFactory(final IStringConverterFactory converterFactory) {
+ addConverterInstanceFactory(new IStringConverterInstanceFactory() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public IStringConverter<?> getConverterInstance(Parameter parameter, Class<?> forType, String optionName) {
+ final Class<? extends IStringConverter<?>> converterClass = converterFactory.getConverter(forType);
+ try {
+ if(optionName == null) {
+ optionName = parameter.names().length > 0 ? parameter.names()[0] : "[Main class]";
+ }
+ return converterClass != null ? instantiateConverter(optionName, converterClass) : null;
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ throw new ParameterException(e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Adds a factory to lookup string converters. The added factory is used prior to previously added factories.
+ * @param converterInstanceFactory the factory generating string converter instances
+ */
+ public void addConverterInstanceFactory(IStringConverterInstanceFactory converterInstanceFactory) {
+ options.converterInstanceFactories.add(0, converterInstanceFactory);
+ }
+
+ private IStringConverter<?> findConverterInstance(Parameter parameter, Class<?> forType, String optionName) {
+ for (IStringConverterInstanceFactory f : options.converterInstanceFactories) {
+ IStringConverter<?> result = f.getConverterInstance(parameter, forType, optionName);
+ if (result != null) return result;
+ }
+
+ return null;
+ }
+
+ /**
+ * @param type The type of the actual parameter
+ * @param optionName
+ * @param value The value to convert
+ */
+ public Object convertValue(final Parameterized parameterized, Class type, String optionName, String value) {
+ final Parameter annotation = parameterized.getParameter();
+
+ // Do nothing if it's a @DynamicParameter
+ if (annotation == null) return value;
+
+ if(optionName == null) {
+ optionName = annotation.names().length > 0 ? annotation.names()[0] : "[Main class]";
+ }
+
+ IStringConverter<?> converter = null;
+ if (type.isAssignableFrom(List.class)) {
+ // If a list converter was specified, pass the value to it for direct conversion
+ converter = tryInstantiateConverter(optionName, annotation.listConverter());
+ }
+ if (type.isAssignableFrom(List.class) && converter == null) {
+ // No list converter: use the single value converter and pass each parsed value to it individually
+ final IParameterSplitter splitter = tryInstantiateConverter(null, annotation.splitter());
+ converter = new DefaultListConverter(splitter, new IStringConverter() {
+ @Override
+ public Object convert(String value) {
+ final Type genericType = parameterized.findFieldGenericType();
+ return convertValue(parameterized, genericType instanceof Class ? (Class) genericType : String.class, null, value);
+ }
+ });
+ }
+
+ if (converter == null) {
+ converter = tryInstantiateConverter(optionName, annotation.converter());
+ }
+ if (converter == null) {
+ converter = findConverterInstance(annotation, type, optionName);
+ }
+ if (converter == null && type.isEnum()) {
+ converter = new EnumConverter(optionName, type);
+ }
+ if (converter == null) {
+ converter = new StringConverter();
+ }
+ return converter.convert(value);
+ }
+
+ private static <T> T tryInstantiateConverter(String optionName, Class<T> converterClass) {
+ if (converterClass == NoConverter.class || converterClass == null) {
+ return null;
+ }
try {
- result = Enum.valueOf((Class<? extends Enum>) converterClass, value);
- } catch (IllegalArgumentException e) {
- try {
- result = Enum.valueOf((Class<? extends Enum>) converterClass, value.toUpperCase());
- } catch (IllegalArgumentException ex) {
- throw new ParameterException("Invalid value for " + optionName + " parameter. Allowed values:" +
- EnumSet.allOf((Class<? extends Enum>) converterClass));
+ return instantiateConverter(optionName, converterClass);
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException ignore) {
+ return null;
+ }
+ }
+
+ private static <T> T instantiateConverter(String optionName, Class<? extends T> converterClass)
+ throws InstantiationException, IllegalAccessException,
+ InvocationTargetException {
+ Constructor<T> ctor = null;
+ Constructor<T> stringCtor = null;
+ for (Constructor<T> c : (Constructor<T>[]) converterClass.getDeclaredConstructors()) {
+ c.setAccessible(true);
+ Class<?>[] types = c.getParameterTypes();
+ if (types.length == 1 && types[0].equals(String.class)) {
+ stringCtor = c;
+ } else if (types.length == 0) {
+ ctor = c;
+ }
+ }
+
+ return stringCtor != null
+ ? stringCtor.newInstance(optionName)
+ : ctor != null
+ ? ctor.newInstance()
+ : null;
+ }
+
+ /**
+ * Add a command object.
+ */
+ public void addCommand(String name, Object object) {
+ addCommand(name, object, new String[0]);
+ }
+
+ public void addCommand(Object object) {
+ Parameters p = object.getClass().getAnnotation(Parameters.class);
+ if (p != null && p.commandNames().length > 0) {
+ for (String commandName : p.commandNames()) {
+ addCommand(commandName, object);
}
- } catch (Exception e) {
- throw new ParameterException("Invalid value for " + optionName + " parameter. Allowed values:" +
- EnumSet.allOf((Class<? extends Enum>) converterClass));
- }
- } else {
- converter = instantiateConverter(optionName, converterClass);
- if (type.isAssignableFrom(List.class)
- && parameterized.getGenericType() instanceof ParameterizedType) {
-
- // The field is a List
- if (listConverterWasSpecified) {
- // If a list converter was specified, pass the value to it
- // for direct conversion
- IStringConverter<?> listConverter =
- instantiateConverter(optionName, annotation.listConverter());
- result = listConverter.convert(value);
- } else {
- // No list converter: use the single value converter and pass each
- // parsed value to it individually
- result = convertToList(value, converter, annotation.splitter());
- }
} else {
- result = converter.convert(value);
- }
- }
- } catch (InstantiationException e) {
- throw new ParameterException(e);
- } catch (IllegalAccessException e) {
- throw new ParameterException(e);
- } catch (InvocationTargetException e) {
- throw new ParameterException(e);
- }
-
- return result;
- }
-
- /**
- * Use the splitter to split the value into multiple values and then convert
- * each of them individually.
- */
- private Object convertToList(String value, IStringConverter<?> converter,
- Class<? extends IParameterSplitter> splitterClass)
- throws InstantiationException, IllegalAccessException {
- IParameterSplitter splitter = splitterClass.newInstance();
- List<Object> result = Lists.newArrayList();
- for (String param : splitter.split(value)) {
- result.add(converter.convert(param));
- }
- return result;
- }
-
- private IStringConverter<?> instantiateConverter(String optionName,
- Class<? extends IStringConverter<?>> converterClass)
- throws IllegalArgumentException, InstantiationException, IllegalAccessException,
- InvocationTargetException {
- Constructor<IStringConverter<?>> ctor = null;
- Constructor<IStringConverter<?>> stringCtor = null;
- Constructor<IStringConverter<?>>[] ctors
- = (Constructor<IStringConverter<?>>[]) converterClass.getDeclaredConstructors();
- for (Constructor<IStringConverter<?>> c : ctors) {
- Class<?>[] types = c.getParameterTypes();
- if (types.length == 1 && types[0].equals(String.class)) {
- stringCtor = c;
- } else if (types.length == 0) {
- ctor = c;
- }
- }
-
- IStringConverter<?> result = stringCtor != null
- ? stringCtor.newInstance(optionName)
- : (ctor != null
- ? ctor.newInstance()
- : null);
-
- return result;
- }
-
- /**
- * Add a command object.
- */
- public void addCommand(String name, Object object) {
- addCommand(name, object, new String[0]);
- }
-
- public void addCommand(Object object) {
- Parameters p = object.getClass().getAnnotation(Parameters.class);
- if (p != null && p.commandNames().length > 0) {
- for (String commandName : p.commandNames()) {
- addCommand(commandName, object);
- }
- } else {
- throw new ParameterException("Trying to add command " + object.getClass().getName()
- + " without specifying its names in @Parameters");
- }
- }
-
- /**
- * Add a command object and its aliases.
- */
- public void addCommand(String name, Object object, String... aliases) {
- JCommander jc = new JCommander(object);
- jc.setProgramName(name, aliases);
- jc.setDefaultProvider(m_defaultProvider);
- jc.setAcceptUnknownOptions(m_acceptUnknownOptions);
- ProgramName progName = jc.m_programName;
- m_commands.put(progName, jc);
+ throw new ParameterException("Trying to add command " + object.getClass().getName()
+ + " without specifying its names in @Parameters");
+ }
+ }
+
+ /**
+ * Add a command object and its aliases.
+ */
+ public void addCommand(String name, Object object, String... aliases) {
+ JCommander jc = new JCommander(options);
+ jc.addObject(object);
+ jc.createDescriptions();
+ jc.setProgramName(name, aliases);
+ ProgramName progName = jc.programName;
+ commands.put(progName, jc);
/*
* Register aliases
*/
- //register command name as an alias of itself for reverse lookup
- //Note: Name clash check is intentionally omitted to resemble the
- // original behaviour of clashing commands.
- // Aliases are, however, are strictly checked for name clashes.
- aliasMap.put(new StringKey(name), progName);
- for (String a : aliases) {
- IKey alias = new StringKey(a);
- //omit pointless aliases to avoid name clash exception
- if (!alias.equals(name)) {
- ProgramName mappedName = aliasMap.get(alias);
- if (mappedName != null && !mappedName.equals(progName)) {
- throw new ParameterException("Cannot set alias " + alias
- + " for " + name
- + " command because it has already been defined for "
- + mappedName.m_name + " command");
- }
- aliasMap.put(alias, progName);
- }
- }
- }
-
- public Map<String, JCommander> getCommands() {
- Map<String, JCommander> res = Maps.newLinkedHashMap();
- for (Map.Entry<ProgramName, JCommander> entry : m_commands.entrySet()) {
- res.put(entry.getKey().m_name, entry.getValue());
- }
- return res;
- }
-
- public String getParsedCommand() {
- return m_parsedCommand;
- }
-
- /**
- * The name of the command or the alias in the form it was
- * passed to the command line. <code>null</code> if no
- * command or alias was specified.
- *
- * @return Name of command or alias passed to command line. If none passed: <code>null</code>.
- */
- public String getParsedAlias() {
- return m_parsedAlias;
- }
-
- /**
- * @return n spaces
- */
- private String s(int count) {
- StringBuilder result = new StringBuilder();
- for (int i = 0; i < count; i++) {
- result.append(" ");
- }
-
- return result.toString();
- }
-
- /**
- * @return the objects that JCommander will fill with the result of
- * parsing the command line.
- */
- public List<Object> getObjects() {
- return m_objects;
- }
-
- private ParameterDescription findParameterDescription(String arg) {
- return FuzzyMap.findInMap(m_descriptions, new StringKey(arg), m_caseSensitiveOptions,
- m_allowAbbreviatedOptions);
- }
-
- private JCommander findCommand(ProgramName name) {
- return FuzzyMap.findInMap(m_commands, name,
- m_caseSensitiveOptions, m_allowAbbreviatedOptions);
-// if (! m_caseSensitiveOptions) {
-// return m_commands.get(name);
-// } else {
-// for (ProgramName c : m_commands.keySet()) {
-// if (c.getName().equalsIgnoreCase(name.getName())) {
-// return m_commands.get(c);
-// }
-// }
-// }
-// return null;
- }
-
- private ProgramName findProgramName(String name) {
- return FuzzyMap.findInMap(aliasMap, new StringKey(name),
- m_caseSensitiveOptions, m_allowAbbreviatedOptions);
- }
-
- /*
- * Reverse lookup JCommand object by command's name or its alias
- */
- private JCommander findCommandByAlias(String commandOrAlias) {
- ProgramName progName = findProgramName(commandOrAlias);
- if (progName == null) {
- return null;
- }
- JCommander jc = findCommand(progName);
- if (jc == null) {
- throw new IllegalStateException(
- "There appears to be inconsistency in the internal command database. " +
- " This is likely a bug. Please report.");
- }
- return jc;
- }
-
- /**
- * Encapsulation of either a main application or an individual command.
- */
- private static final class ProgramName implements IKey {
- private final String m_name;
- private final List<String> m_aliases;
-
- ProgramName(String name, List<String> aliases) {
- m_name = name;
- m_aliases = aliases;
- }
-
- @Override
- public String getName() {
- return m_name;
- }
-
- private String getDisplayName() {
- StringBuilder sb = new StringBuilder();
- sb.append(m_name);
- if (!m_aliases.isEmpty()) {
- sb.append("(");
- Iterator<String> aliasesIt = m_aliases.iterator();
- while (aliasesIt.hasNext()) {
- sb.append(aliasesIt.next());
- if (aliasesIt.hasNext()) {
- sb.append(",");
- }
- }
- sb.append(")");
- }
- return sb.toString();
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- ProgramName other = (ProgramName) obj;
- if (m_name == null) {
- if (other.m_name != null)
- return false;
- } else if (!m_name.equals(other.m_name))
- return false;
- return true;
+ //register command name as an alias of itself for reverse lookup
+ //Note: Name clash check is intentionally omitted to resemble the
+ // original behaviour of clashing commands.
+ // Aliases are, however, are strictly checked for name clashes.
+ aliasMap.put(new StringKey(name), progName);
+ for (String a : aliases) {
+ IKey alias = new StringKey(a);
+ //omit pointless aliases to avoid name clash exception
+ if (!alias.equals(name)) {
+ ProgramName mappedName = aliasMap.get(alias);
+ if (mappedName != null && !mappedName.equals(progName)) {
+ throw new ParameterException("Cannot set alias " + alias
+ + " for " + name
+ + " command because it has already been defined for "
+ + mappedName.name + " command");
+ }
+ aliasMap.put(alias, progName);
+ }
+ }
+ }
+
+ public Map<String, JCommander> getCommands() {
+ Map<String, JCommander> res = Maps.newLinkedHashMap();
+ for (Map.Entry<ProgramName, JCommander> entry : commands.entrySet()) {
+ res.put(entry.getKey().name, entry.getValue());
+ }
+ return res;
+ }
+
+ public String getParsedCommand() {
+ return parsedCommand;
+ }
+
+ /**
+ * The name of the command or the alias in the form it was
+ * passed to the command line. <code>null</code> if no
+ * command or alias was specified.
+ *
+ * @return Name of command or alias passed to command line. If none passed: <code>null</code>.
+ */
+ public String getParsedAlias() {
+ return parsedAlias;
+ }
+
+ /**
+ * @return n spaces
+ */
+ private String s(int count) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < count; i++) {
+ result.append(" ");
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * @return the objects that JCommander will fill with the result of
+ * parsing the command line.
+ */
+ public List<Object> getObjects() {
+ return objects;
+ }
+
+ private ParameterDescription findParameterDescription(String arg) {
+ return FuzzyMap.findInMap(descriptions, new StringKey(arg),
+ options.caseSensitiveOptions, options.allowAbbreviatedOptions);
+ }
+
+ private JCommander findCommand(ProgramName name) {
+ return FuzzyMap.findInMap(commands, name,
+ options.caseSensitiveOptions, options.allowAbbreviatedOptions);
+ }
+
+ private ProgramName findProgramName(String name) {
+ return FuzzyMap.findInMap(aliasMap, new StringKey(name),
+ options.caseSensitiveOptions, options.allowAbbreviatedOptions);
}
/*
- * Important: ProgramName#toString() is used by longestName(Collection) function
- * to format usage output.
+ * Reverse lookup JCommand object by command's name or its alias
+ */
+ private JCommander findCommandByAlias(String commandOrAlias) {
+ ProgramName progName = findProgramName(commandOrAlias);
+ if (progName == null) {
+ return null;
+ }
+ JCommander jc = findCommand(progName);
+ if (jc == null) {
+ throw new IllegalStateException(
+ "There appears to be inconsistency in the internal command database. " +
+ " This is likely a bug. Please report.");
+ }
+ return jc;
+ }
+
+ /**
+ * Encapsulation of either a main application or an individual command.
*/
- @Override
- public String toString() {
- return getDisplayName();
-
- }
- }
-
- public void setVerbose(int verbose) {
- m_verbose = verbose;
- }
-
- public void setCaseSensitiveOptions(boolean b) {
- m_caseSensitiveOptions = b;
- }
-
- public void setAllowAbbreviatedOptions(boolean b) {
- m_allowAbbreviatedOptions = b;
- }
-
- public void setAcceptUnknownOptions(boolean b) {
- m_acceptUnknownOptions = b;
- }
-
- public List<String> getUnknownOptions() {
- return m_unknownArgs;
- }
- public void setAllowParameterOverwriting(boolean b) {
- m_allowParameterOverwriting = b;
- }
-
- public boolean isParameterOverwritingAllowed() {
- return m_allowParameterOverwriting;
- }
-// public void setCaseSensitiveCommands(boolean b) {
-// m_caseSensitiveCommands = b;
-// }
-}
+ private static final class ProgramName implements IKey {
+ private final String name;
+ private final List<String> aliases;
+
+ ProgramName(String name, List<String> aliases) {
+ this.name = name;
+ this.aliases = aliases;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ private String getDisplayName() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(name);
+ if (!aliases.isEmpty()) {
+ sb.append("(");
+ Iterator<String> aliasesIt = aliases.iterator();
+ while (aliasesIt.hasNext()) {
+ sb.append(aliasesIt.next());
+ if (aliasesIt.hasNext()) {
+ sb.append(",");
+ }
+ }
+ sb.append(")");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ProgramName other = (ProgramName) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ /*
+ * Important: ProgramName#toString() is used by longestName(Collection) function
+ * to format usage output.
+ */
+ @Override
+ public String toString() {
+ return getDisplayName();
+
+ }
+ }
+
+ public void setVerbose(int verbose) {
+ options.verbose = verbose;
+ }
+
+ public void setCaseSensitiveOptions(boolean b) {
+ options.caseSensitiveOptions = b;
+ }
+
+ public void setAllowAbbreviatedOptions(boolean b) {
+ options.allowAbbreviatedOptions = b;
+ }
+
+ public void setAcceptUnknownOptions(boolean b) {
+ options.acceptUnknownOptions = b;
+ }
+
+ public List<String> getUnknownOptions() {
+ return unknownArgs;
+ }
+
+ public void setAllowParameterOverwriting(boolean b) {
+ options.allowParameterOverwriting = b;
+ }
+
+ public boolean isParameterOverwritingAllowed() {
+ return options.allowParameterOverwriting;
+ }
+
+ /**
+ * Sets the charset used to expand {@code @files}.
+ * @param charset the charset
+ */
+ public void setAtFileCharset(Charset charset) {
+ options.atFileCharset = charset;
+ }
+
+}
diff --git a/src/main/java/com/beust/jcommander/MissingCommandException.java b/src/main/java/com/beust/jcommander/MissingCommandException.java
index 1d572ab..7e8980b 100644
--- a/src/main/java/com/beust/jcommander/MissingCommandException.java
+++ b/src/main/java/com/beust/jcommander/MissingCommandException.java
@@ -26,11 +26,22 @@ package com.beust.jcommander;
@SuppressWarnings("serial")
public class MissingCommandException extends ParameterException {
- public MissingCommandException(String string) {
- super(string);
+ /**
+ * the command passed by the user.
+ */
+ private final String unknownCommand;
+
+ public MissingCommandException(String message) {
+ this(message, null);
+ }
+
+ public MissingCommandException(String message, String command) {
+ super(message);
+ this.unknownCommand = command;
}
- public MissingCommandException(Throwable t) {
- super(t);
+ public String getUnknownCommand() {
+ return unknownCommand;
}
+
}
diff --git a/src/main/java/com/beust/jcommander/Parameter.java b/src/main/java/com/beust/jcommander/Parameter.java
index d8cf87d..2fc00c5 100644
--- a/src/main/java/com/beust/jcommander/Parameter.java
+++ b/src/main/java/com/beust/jcommander/Parameter.java
@@ -90,12 +90,12 @@ public @interface Parameter {
/**
* Validate the parameter found on the command line.
*/
- Class<? extends IParameterValidator> validateWith() default NoValidator.class;
+ Class<? extends IParameterValidator>[] validateWith() default NoValidator.class;
/**
* Validate the value for this parameter.
*/
- Class<? extends IValueValidator> validateValueWith() default NoValueValidator.class;
+ Class<? extends IValueValidator>[] validateValueWith() default NoValueValidator.class;
/**
* @return true if this parameter has a variable arity. See @{IVariableArity}
@@ -122,9 +122,14 @@ public @interface Parameter {
/**
* If true, this parameter can be overwritten through a file or another appearance of the parameter
- * @return
+ * @return nc
*/
boolean forceNonOverwritable() default false;
+ /**
+ * If specified, this number will be used to order the description of this parameter when usage() is invoked.
+ * @return
+ */
+ int order() default -1;
}
diff --git a/src/main/java/com/beust/jcommander/ParameterDescription.java b/src/main/java/com/beust/jcommander/ParameterDescription.java
index 2ef2d5f..bed5ba1 100644
--- a/src/main/java/com/beust/jcommander/ParameterDescription.java
+++ b/src/main/java/com/beust/jcommander/ParameterDescription.java
@@ -21,36 +21,28 @@ package com.beust.jcommander;
import com.beust.jcommander.validators.NoValidator;
import com.beust.jcommander.validators.NoValueValidator;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.*;
import java.util.ResourceBundle;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
public class ParameterDescription {
- private Object m_object;
+ private Object object;
- private WrappedParameter m_wrappedParameter;
- private Parameter m_parameterAnnotation;
- private DynamicParameter m_dynamicParameterAnnotation;
+ private WrappedParameter wrappedParameter;
+ private Parameter parameterAnnotation;
+ private DynamicParameter dynamicParameterAnnotation;
/** The field/method */
- private Parameterized m_parameterized;
+ private Parameterized parameterized;
/** Keep track of whether a value was added to flag an error */
- private boolean m_assigned = false;
- private ResourceBundle m_bundle;
- private String m_description;
- private JCommander m_jCommander;
- private Object m_default;
+ private boolean assigned = false;
+ private ResourceBundle bundle;
+ private String description;
+ private JCommander jCommander;
+ private Object defaultObject;
/** Longest of the names(), used to present usage() alphabetically */
- private String m_longestName = "";
+ private String longestName = "";
public ParameterDescription(Object object, DynamicParameter annotation,
Parameterized parameterized,
@@ -61,15 +53,15 @@ public class ParameterDescription {
+ "Map but is " + parameterized.getType().getName());
}
- m_dynamicParameterAnnotation = annotation;
- m_wrappedParameter = new WrappedParameter(m_dynamicParameterAnnotation);
+ dynamicParameterAnnotation = annotation;
+ wrappedParameter = new WrappedParameter(dynamicParameterAnnotation);
init(object, parameterized, bundle, jc);
}
public ParameterDescription(Object object, Parameter annotation, Parameterized parameterized,
ResourceBundle bundle, JCommander jc) {
- m_parameterAnnotation = annotation;
- m_wrappedParameter = new WrappedParameter(m_parameterAnnotation);
+ parameterAnnotation = annotation;
+ wrappedParameter = new WrappedParameter(parameterAnnotation);
init(object, parameterized, bundle, jc);
}
@@ -100,90 +92,86 @@ public class ParameterDescription {
}
private void initDescription(String description, String descriptionKey, String[] names) {
- m_description = description;
+ this.description = description;
if (! "".equals(descriptionKey)) {
- if (m_bundle != null) {
- m_description = m_bundle.getString(descriptionKey);
- } else {
-// JCommander.getConsole().println("Warning: field " + object.getClass() + "." + field.getName()
-// + " has a descriptionKey but no bundle was defined with @ResourceBundle, using " +
-// "default description:'" + m_description + "'");
+ if (bundle != null) {
+ this.description = bundle.getString(descriptionKey);
}
}
for (String name : names) {
- if (name.length() > m_longestName.length()) m_longestName = name;
+ if (name.length() > longestName.length()) longestName = name;
}
}
@SuppressWarnings("unchecked")
private void init(Object object, Parameterized parameterized, ResourceBundle bundle,
JCommander jCommander) {
- m_object = object;
- m_parameterized = parameterized;
- m_bundle = bundle;
- if (m_bundle == null) {
- m_bundle = findResourceBundle(object);
+ this.object = object;
+ this.parameterized = parameterized;
+ this.bundle = bundle;
+ if (this.bundle == null) {
+ this.bundle = findResourceBundle(object);
}
- m_jCommander = jCommander;
+ this.jCommander = jCommander;
- if (m_parameterAnnotation != null) {
+ if (parameterAnnotation != null) {
String description;
if (Enum.class.isAssignableFrom(parameterized.getType())
- && m_parameterAnnotation.description().isEmpty()) {
+ && parameterAnnotation.description().isEmpty()) {
description = "Options: " + EnumSet.allOf((Class<? extends Enum>) parameterized.getType());
}else {
- description = m_parameterAnnotation.description();
+ description = parameterAnnotation.description();
}
- initDescription(description, m_parameterAnnotation.descriptionKey(),
- m_parameterAnnotation.names());
- } else if (m_dynamicParameterAnnotation != null) {
- initDescription(m_dynamicParameterAnnotation.description(),
- m_dynamicParameterAnnotation.descriptionKey(),
- m_dynamicParameterAnnotation.names());
+ initDescription(description, parameterAnnotation.descriptionKey(),
+ parameterAnnotation.names());
+ } else if (dynamicParameterAnnotation != null) {
+ initDescription(dynamicParameterAnnotation.description(),
+ dynamicParameterAnnotation.descriptionKey(),
+ dynamicParameterAnnotation.names());
} else {
throw new AssertionError("Shound never happen");
}
try {
- m_default = parameterized.get(object);
+ defaultObject = parameterized.get(object);
} catch (Exception e) {
}
//
// Validate default values, if any and if applicable
//
- if (m_default != null) {
- if (m_parameterAnnotation != null) {
- validateDefaultValues(m_parameterAnnotation.names());
+ if (defaultObject != null) {
+ if (parameterAnnotation != null) {
+ validateDefaultValues(parameterAnnotation.names());
}
}
}
private void validateDefaultValues(String[] names) {
String name = names.length > 0 ? names[0] : "";
- validateValueParameter(name, m_default);
+ validateValueParameter(name, defaultObject);
}
public String getLongestName() {
- return m_longestName;
+ return longestName;
}
public Object getDefault() {
- return m_default;
+ return defaultObject;
}
public String getDescription() {
- return m_description;
+ return description;
}
public Object getObject() {
- return m_object;
+ return object;
}
public String getNames() {
StringBuilder sb = new StringBuilder();
- String[] names = m_wrappedParameter.names();
+ String[] names = wrappedParameter.names();
for (int i = 0; i < names.length; i++) {
if (i > 0) sb.append(", ");
sb.append(names[i]);
@@ -192,17 +180,17 @@ public class ParameterDescription {
}
public WrappedParameter getParameter() {
- return m_wrappedParameter;
+ return wrappedParameter;
}
public Parameterized getParameterized() {
- return m_parameterized;
+ return parameterized;
}
private boolean isMultiOption() {
- Class<?> fieldType = m_parameterized.getType();
+ Class<?> fieldType = parameterized.getType();
return fieldType.equals(List.class) || fieldType.equals(Set.class)
- || m_parameterized.isDynamicParameter();
+ || parameterized.isDynamicParameter();
}
public void addValue(String value) {
@@ -213,12 +201,12 @@ public class ParameterDescription {
* @return true if this parameter received a value during the parsing phase.
*/
public boolean isAssigned() {
- return m_assigned;
+ return assigned;
}
public void setAssigned(boolean b) {
- m_assigned = b;
+ assigned = b;
}
/**
@@ -227,52 +215,132 @@ public class ParameterDescription {
* converter, and if we can't find any, throw an exception.
*/
public void addValue(String value, boolean isDefault) {
+ addValue(null, value, isDefault, true, -1);
+ }
+
+ Object addValue(String name, String value, boolean isDefault, boolean validate, int currentIndex) {
p("Adding " + (isDefault ? "default " : "") + "value:" + value
- + " to parameter:" + m_parameterized.getName());
- String name = m_wrappedParameter.names()[0];
- if (m_assigned && ! isMultiOption() && !m_jCommander.isParameterOverwritingAllowed() || isNonOverwritableForced()) {
+ + " to parameter:" + parameterized.getName());
+ if(name == null) {
+ name = wrappedParameter.names()[0];
+ }
+ if (currentIndex == 00 && assigned && ! isMultiOption() && !jCommander.isParameterOverwritingAllowed()
+ || isNonOverwritableForced()) {
throw new ParameterException("Can only specify option " + name + " once.");
}
- validateParameter(name, value);
+ if (validate) {
+ validateParameter(name, value);
+ }
- Class<?> type = m_parameterized.getType();
+ Class<?> type = parameterized.getType();
- Object convertedValue = m_jCommander.convertValue(this, value);
- validateValueParameter(name, convertedValue);
+ Object convertedValue = jCommander.convertValue(getParameterized(), getParameterized().getType(), name, value);
+ if (validate) {
+ validateValueParameter(name, convertedValue);
+ }
boolean isCollection = Collection.class.isAssignableFrom(type);
+ Object finalValue;
if (isCollection) {
@SuppressWarnings("unchecked")
- Collection<Object> l = (Collection<Object>) m_parameterized.get(m_object);
+ Collection<Object> l = (Collection<Object>) parameterized.get(object);
if (l == null || fieldIsSetForTheFirstTime(isDefault)) {
- l = newCollection(type);
- m_parameterized.set(m_object, l);
+ l = newCollection(type);
+ parameterized.set(object, l);
}
if (convertedValue instanceof Collection) {
- l.addAll((Collection) convertedValue);
- } else { // if (isMainParameter || m_parameterAnnotation.arity() > 1) {
- l.add(convertedValue);
-// } else {
-// l.
+ l.addAll((Collection) convertedValue);
+ } else {
+ l.add(convertedValue);
}
+ finalValue = l;
} else {
- m_wrappedParameter.addValue(m_parameterized, m_object, convertedValue);
+ // If the field type is not a collection, see if it's a type that contains @SubParameters annotations
+ List<SubParameterIndex> subParameters = findSubParameters(type);
+ if (! subParameters.isEmpty()) {
+ // @SubParameters found
+ finalValue = handleSubParameters(value, currentIndex, type, subParameters);
+ } else {
+ // No, regular parameter
+ wrappedParameter.addValue(parameterized, object, convertedValue);
+ finalValue = convertedValue;
+ }
}
- if (! isDefault) m_assigned = true;
+ if (! isDefault) assigned = true;
+
+ return finalValue;
+ }
+
+ private Object handleSubParameters(String value, int currentIndex, Class<?> type,
+ List<SubParameterIndex> subParameters) {
+ Object finalValue;// Yes, assign each following argument to the corresponding field of that object
+ SubParameterIndex sai = null;
+ for (SubParameterIndex si: subParameters) {
+ if (si.order == currentIndex) {
+ sai = si;
+ break;
+ }
+ }
+ if (sai != null) {
+ Object objectValue = parameterized.get(object);
+ try {
+ if (objectValue == null) {
+ objectValue = type.newInstance();
+ parameterized.set(object, objectValue);
+ }
+ wrappedParameter.addValue(parameterized, objectValue, value, sai.field);
+ finalValue = objectValue;
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new ParameterException("Couldn't instantiate " + type, e);
+ }
+ } else {
+ throw new ParameterException("Couldn't find where to assign parameter " + value + " in " + type);
+ }
+ return finalValue;
+ }
+
+ public Parameter getParameterAnnotation() {
+ return parameterAnnotation;
+ }
+
+ class SubParameterIndex {
+ int order = -1;
+ Field field;
+
+ public SubParameterIndex(int order, Field field) {
+ this.order = order;
+ this.field = field;
+ }
+ }
+
+ private List<SubParameterIndex> findSubParameters(Class<?> type) {
+ List<SubParameterIndex> result = new ArrayList<>();
+ for (Field field: type.getDeclaredFields()) {
+ Annotation subParameter = field.getAnnotation(SubParameter.class);
+ if (subParameter != null) {
+ SubParameter sa = (SubParameter) subParameter;
+ result.add(new SubParameterIndex(sa.order(), field));
+ }
+ }
+ return result;
}
private void validateParameter(String name, String value) {
- Class<? extends IParameterValidator> validator = m_wrappedParameter.validateWith();
- if (validator != null) {
- validateParameter(this, validator, name, value);
+ final Class<? extends IParameterValidator> validators[] = wrappedParameter.validateWith();
+ if (validators != null && validators.length > 0) {
+ for(final Class<? extends IParameterValidator> validator: validators) {
+ validateParameter(this, validator, name, value);
+ }
}
}
- private void validateValueParameter(String name, Object value) {
- Class<? extends IValueValidator> validator = m_wrappedParameter.validateValueWith();
- if (validator != null) {
- validateValueParameter(validator, name, value);
+ void validateValueParameter(String name, Object value) {
+ final Class<? extends IValueValidator> validators[] = wrappedParameter.validateValueWith();
+ if (validators != null && validators.length > 0) {
+ for(final Class<? extends IValueValidator> validator: validators) {
+ validateValueParameter(validator, name, value);
+ }
}
}
@@ -283,9 +351,7 @@ public class ParameterDescription {
p("Validating value parameter:" + name + " value:" + value + " validator:" + validator);
}
validator.newInstance().validate(name, value);
- } catch (InstantiationException e) {
- throw new ParameterException("Can't instantiate validator:" + e);
- } catch (IllegalAccessException e) {
+ } catch (InstantiationException | IllegalAccessException e) {
throw new ParameterException("Can't instantiate validator:" + e);
}
}
@@ -294,6 +360,7 @@ public class ParameterDescription {
Class<? extends IParameterValidator> validator,
String name, String value) {
try {
+
if (validator != NoValidator.class) {
p("Validating parameter:" + name + " value:" + value + " validator:" + validator);
}
@@ -302,9 +369,7 @@ public class ParameterDescription {
IParameterValidator2 instance = (IParameterValidator2) validator.newInstance();
instance.validate(name, value, pd);
}
- } catch (InstantiationException e) {
- throw new ParameterException("Can't instantiate validator:" + e);
- } catch (IllegalAccessException e) {
+ } catch (InstantiationException | IllegalAccessException e) {
throw new ParameterException("Can't instantiate validator:" + e);
} catch(ParameterException ex) {
throw ex;
@@ -336,7 +401,7 @@ public class ParameterDescription {
* being added to the field.
*/
private boolean fieldIsSetForTheFirstTime(boolean isDefault) {
- return (!isDefault && !m_assigned);
+ return (!isDefault && !assigned);
}
private static void p(String string) {
@@ -347,18 +412,18 @@ public class ParameterDescription {
@Override
public String toString() {
- return "[ParameterDescription " + m_parameterized.getName() + "]";
+ return "[ParameterDescription " + parameterized.getName() + "]";
}
public boolean isDynamicParameter() {
- return m_dynamicParameterAnnotation != null;
+ return dynamicParameterAnnotation != null;
}
public boolean isHelp() {
- return m_wrappedParameter.isHelp();
+ return wrappedParameter.isHelp();
}
public boolean isNonOverwritableForced() {
- return m_wrappedParameter.isNonOverwritableForced();
+ return wrappedParameter.isNonOverwritableForced();
}
}
diff --git a/src/main/java/com/beust/jcommander/ParameterException.java b/src/main/java/com/beust/jcommander/ParameterException.java
index 2bba7d1..41570ff 100644
--- a/src/main/java/com/beust/jcommander/ParameterException.java
+++ b/src/main/java/com/beust/jcommander/ParameterException.java
@@ -26,7 +26,6 @@ package com.beust.jcommander;
*/
@SuppressWarnings("serial")
public class ParameterException extends RuntimeException {
-
public ParameterException(Throwable t) {
super(t);
}
@@ -37,6 +36,21 @@ public class ParameterException extends RuntimeException {
public ParameterException(String string, Throwable t) {
super(string, t);
- }
+ }
+
+ private JCommander jc;
+
+ public void setJCommander(JCommander jc) {
+ this.jc = jc;
+ }
+
+ public JCommander getJCommander() {
+ return jc;
+ }
+ public void usage() {
+ if (jc != null) {
+ jc.usage();
+ }
+ }
}
diff --git a/src/main/java/com/beust/jcommander/Parameterized.java b/src/main/java/com/beust/jcommander/Parameterized.java
index ff8753b..3264008 100644
--- a/src/main/java/com/beust/jcommander/Parameterized.java
+++ b/src/main/java/com/beust/jcommander/Parameterized.java
@@ -1,14 +1,18 @@
package com.beust.jcommander;
import com.beust.jcommander.internal.Lists;
+import com.beust.jcommander.internal.Sets;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* Encapsulate a field or a method annotated with @Parameter or @DynamicParameter
@@ -16,129 +20,175 @@ import java.util.List;
public class Parameterized {
// Either a method or a field
- private Field m_field;
- private Method m_method;
- private Method m_getter;
+ private Field field;
+ private Method method;
+ private Method getter;
// Either of these two
- private WrappedParameter m_wrappedParameter;
- private ParametersDelegate m_parametersDelegate;
+ private WrappedParameter wrappedParameter;
+ private ParametersDelegate parametersDelegate;
public Parameterized(WrappedParameter wp, ParametersDelegate pd,
Field field, Method method) {
- m_wrappedParameter = wp;
- m_method = method;
- m_field = field;
- if (m_field != null) {
- m_field.setAccessible(true);
+ wrappedParameter = wp;
+ this.method = method;
+ this.field = field;
+ if (this.field != null) {
+ setFieldAccessible(this.field);
}
- m_parametersDelegate = pd;
+ parametersDelegate = pd;
+ }
+
+ /**
+ * Recursive handler for describing the set of classes while
+ * using the setOfClasses parameter as a collector
+ *
+ * @param inputClass the class to analyze
+ * @param setOfClasses the set collector to collect the results
+ */
+ private static void describeClassTree(Class<?> inputClass, Set<Class<?>> setOfClasses) {
+ // can't map null class
+ if(inputClass == null) {
+ return;
+ }
+
+ // don't further analyze a class that has been analyzed already
+ if(Object.class.equals(inputClass) || setOfClasses.contains(inputClass)) {
+ return;
+ }
+
+ // add to analysis set
+ setOfClasses.add(inputClass);
+
+ // perform super class analysis
+ describeClassTree(inputClass.getSuperclass(), setOfClasses);
+
+ // perform analysis on interfaces
+ for(Class<?> hasInterface : inputClass.getInterfaces()) {
+ describeClassTree(hasInterface, setOfClasses);
+ }
+ }
+
+ /**
+ * Given an object return the set of classes that it extends
+ * or implements.
+ *
+ * @param arg object to describe
+ * @return set of classes that are implemented or extended by that object
+ */
+ private static Set<Class<?>> describeClassTree(Class<?> inputClass) {
+ if(inputClass == null) {
+ return Collections.emptySet();
+ }
+
+ // create result collector
+ Set<Class<?>> classes = Sets.newLinkedHashSet();
+
+ // describe tree
+ describeClassTree(inputClass, classes);
+
+ return classes;
}
public static List<Parameterized> parseArg(Object arg) {
List<Parameterized> result = Lists.newArrayList();
- Class<? extends Object> cls = arg.getClass();
- while (!Object.class.equals(cls)) {
+ Class<?> rootClass = arg.getClass();
+
+ // get the list of types that are extended or implemented by the root class
+ // and all of its parent types
+ Set<Class<?>> types = describeClassTree(rootClass);
+
+ // analyze each type
+ for(Class<?> cls : types) {
+
+ // check fields
for (Field f : cls.getDeclaredFields()) {
Annotation annotation = f.getAnnotation(Parameter.class);
Annotation delegateAnnotation = f.getAnnotation(ParametersDelegate.class);
Annotation dynamicParameter = f.getAnnotation(DynamicParameter.class);
if (annotation != null) {
result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
- f, null));
+ f, null));
} else if (dynamicParameter != null) {
result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null,
- f, null));
+ f, null));
} else if (delegateAnnotation != null) {
result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
- f, null));
+ f, null));
}
}
- cls = cls.getSuperclass();
- }
- // Reassigning
- cls = arg.getClass();
- while (!Object.class.equals(cls)) {
+ // check methods
for (Method m : cls.getDeclaredMethods()) {
+ m.setAccessible(true);
Annotation annotation = m.getAnnotation(Parameter.class);
Annotation delegateAnnotation = m.getAnnotation(ParametersDelegate.class);
Annotation dynamicParameter = m.getAnnotation(DynamicParameter.class);
if (annotation != null) {
result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
- null, m));
+ null, m));
} else if (dynamicParameter != null) {
- result.add(new Parameterized(new WrappedParameter((DynamicParameter) annotation), null,
- null, m));
+ result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null,
+ null, m));
} else if (delegateAnnotation != null) {
result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
- null, m));
+ null, m));
}
}
- cls = cls.getSuperclass();
}
return result;
}
public WrappedParameter getWrappedParameter() {
- return m_wrappedParameter;
+ return wrappedParameter;
}
public Class<?> getType() {
- if (m_method != null) {
- return m_method.getParameterTypes()[0];
+ if (method != null) {
+ return method.getParameterTypes()[0];
} else {
- return m_field.getType();
+ return field.getType();
}
}
public String getName() {
- if (m_method != null) {
- return m_method.getName();
+ if (method != null) {
+ return method.getName();
} else {
- return m_field.getName();
+ return field.getName();
}
}
public Object get(Object object) {
try {
- if (m_method != null) {
- if (m_getter == null) {
- m_getter = m_method.getDeclaringClass()
- .getMethod("g" + m_method.getName().substring(1),
- new Class[0]);
+ if (method != null) {
+ if (getter == null) {
+ getter = method.getDeclaringClass()
+ .getMethod("g" + method.getName().substring(1));
}
- return m_getter.invoke(object);
+ return getter.invoke(object);
} else {
- return m_field.get(object);
+ return field.get(object);
}
- } catch (SecurityException e) {
+ } catch (SecurityException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
throw new ParameterException(e);
} catch (NoSuchMethodException e) {
// Try to find a field
- String name = m_method.getName();
+ String name = method.getName();
String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4);
Object result = null;
try {
- Field field = m_method.getDeclaringClass().getDeclaredField(fieldName);
+ Field field = method.getDeclaringClass().getDeclaredField(fieldName);
if (field != null) {
- field.setAccessible(true);
+ setFieldAccessible(field);
result = field.get(object);
}
- } catch(NoSuchFieldException ex) {
- // ignore
- } catch(IllegalAccessException ex) {
+ } catch(NoSuchFieldException | IllegalAccessException ex) {
// ignore
}
return result;
- } catch (IllegalArgumentException e) {
- throw new ParameterException(e);
- } catch (IllegalAccessException e) {
- throw new ParameterException(e);
- } catch (InvocationTargetException e) {
- throw new ParameterException(e);
}
}
@@ -146,8 +196,8 @@ public class Parameterized {
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((m_field == null) ? 0 : m_field.hashCode());
- result = prime * result + ((m_method == null) ? 0 : m_method.hashCode());
+ result = prime * result + ((field == null) ? 0 : field.hashCode());
+ result = prime * result + ((method == null) ? 0 : method.hashCode());
return result;
}
@@ -160,73 +210,84 @@ public class Parameterized {
if (getClass() != obj.getClass())
return false;
Parameterized other = (Parameterized) obj;
- if (m_field == null) {
- if (other.m_field != null)
+ if (field == null) {
+ if (other.field != null)
return false;
- } else if (!m_field.equals(other.m_field))
+ } else if (!field.equals(other.field))
return false;
- if (m_method == null) {
- if (other.m_method != null)
+ if (method == null) {
+ if (other.method != null)
return false;
- } else if (!m_method.equals(other.m_method))
+ } else if (!method.equals(other.method))
return false;
return true;
}
public boolean isDynamicParameter(Field field) {
- if (m_method != null) {
- return m_method.getAnnotation(DynamicParameter.class) != null;
+ if (method != null) {
+ return method.getAnnotation(DynamicParameter.class) != null;
} else {
- return m_field.getAnnotation(DynamicParameter.class) != null;
+ return this.field.getAnnotation(DynamicParameter.class) != null;
+ }
+ }
+
+ private static void setFieldAccessible(Field f) {
+ if (Modifier.isFinal(f.getModifiers())) {
+ throw new ParameterException(
+ "Cannot use final field " + f.getDeclaringClass().getName() + "#" + f.getName() + " as a parameter;"
+ + " compile-time constant inlining may hide new values written to it.");
}
+ f.setAccessible(true);
+ }
+
+ private static String errorMessage(Method m, Exception ex) {
+ return "Could not invoke " + m + "\n Reason: " + ex.getMessage();
}
public void set(Object object, Object value) {
try {
- if (m_method != null) {
- m_method.invoke(object, value);
+ if (method != null) {
+ method.invoke(object, value);
} else {
- m_field.set(object, value);
+ field.set(object, value);
}
- } catch (IllegalArgumentException ex) {
- throw new ParameterException(ex);
- } catch (IllegalAccessException ex) {
- throw new ParameterException(ex);
+ } catch (IllegalAccessException | IllegalArgumentException ex) {
+ throw new ParameterException(errorMessage(method, ex));
} catch (InvocationTargetException ex) {
// If a ParameterException was thrown, don't wrap it into another one
if (ex.getTargetException() instanceof ParameterException) {
throw (ParameterException) ex.getTargetException();
} else {
- throw new ParameterException(ex);
+ throw new ParameterException(errorMessage(method, ex));
}
}
}
public ParametersDelegate getDelegateAnnotation() {
- return m_parametersDelegate;
+ return parametersDelegate;
}
public Type getGenericType() {
- if (m_method != null) {
- return m_method.getGenericParameterTypes()[0];
+ if (method != null) {
+ return method.getGenericParameterTypes()[0];
} else {
- return m_field.getGenericType();
+ return field.getGenericType();
}
}
public Parameter getParameter() {
- return m_wrappedParameter.getParameter();
+ return wrappedParameter.getParameter();
}
/**
* @return the generic type of the collection for this field, or null if not applicable.
*/
public Type findFieldGenericType() {
- if (m_method != null) {
+ if (method != null) {
return null;
} else {
- if (m_field.getGenericType() instanceof ParameterizedType) {
- ParameterizedType p = (ParameterizedType) m_field.getGenericType();
+ if (field.getGenericType() instanceof ParameterizedType) {
+ ParameterizedType p = (ParameterizedType) field.getGenericType();
Type cls = p.getActualTypeArguments()[0];
if (cls instanceof Class) {
return cls;
@@ -238,7 +299,7 @@ public class Parameterized {
}
public boolean isDynamicParameter() {
- return m_wrappedParameter.getDynamicParameter() != null;
+ return wrappedParameter.getDynamicParameter() != null;
}
}
diff --git a/src/main/java/com/beust/jcommander/Parameters.java b/src/main/java/com/beust/jcommander/Parameters.java
index f2e8c76..edab036 100644
--- a/src/main/java/com/beust/jcommander/Parameters.java
+++ b/src/main/java/com/beust/jcommander/Parameters.java
@@ -34,8 +34,6 @@ import static java.lang.annotation.ElementType.TYPE;
@Inherited
public @interface Parameters {
- public static final String DEFAULT_OPTION_PREFIXES = "-";
-
/**
* The name of the resource bundle to use for this class.
*/
@@ -47,11 +45,6 @@ public @interface Parameters {
String separators() default " ";
/**
- * What characters an option starts with.
- */
- String optionPrefixes() default DEFAULT_OPTION_PREFIXES;
-
- /**
* If the annotated class was added to {@link JCommander} as a command with
* {@link JCommander#addCommand}, then this string will be displayed in the
* description when @{link JCommander#usage} is invoked.
diff --git a/src/main/java/com/beust/jcommander/StringKey.java b/src/main/java/com/beust/jcommander/StringKey.java
index 09d1149..11a7d18 100644
--- a/src/main/java/com/beust/jcommander/StringKey.java
+++ b/src/main/java/com/beust/jcommander/StringKey.java
@@ -4,27 +4,27 @@ import com.beust.jcommander.FuzzyMap.IKey;
public class StringKey implements IKey {
- private String m_name;
+ private String name;
public StringKey(String name) {
- m_name = name;
+ this.name = name;
}
@Override
public String getName() {
- return m_name;
+ return name;
}
@Override
public String toString() {
- return m_name;
+ return name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@@ -37,10 +37,10 @@ public class StringKey implements IKey {
if (getClass() != obj.getClass())
return false;
StringKey other = (StringKey) obj;
- if (m_name == null) {
- if (other.m_name != null)
+ if (name == null) {
+ if (other.name != null)
return false;
- } else if (!m_name.equals(other.m_name))
+ } else if (!name.equals(other.name))
return false;
return true;
}
diff --git a/src/main/java/com/beust/jcommander/SubParameter.java b/src/main/java/com/beust/jcommander/SubParameter.java
new file mode 100644
index 0000000..22adfc4
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/SubParameter.java
@@ -0,0 +1,17 @@
+package com.beust.jcommander;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+
+/**
+ * @author Cedric Beust <cedric@refresh.io>
+ * @since 02 12, 2017
+ */
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({ FIELD, METHOD })
+public @interface SubParameter {
+ int order() default -1;
+}
diff --git a/src/main/java/com/beust/jcommander/WrappedParameter.java b/src/main/java/com/beust/jcommander/WrappedParameter.java
index f4e7d56..e49245a 100644
--- a/src/main/java/com/beust/jcommander/WrappedParameter.java
+++ b/src/main/java/com/beust/jcommander/WrappedParameter.java
@@ -1,5 +1,6 @@
package com.beust.jcommander;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -7,68 +8,81 @@ import java.lang.reflect.Method;
* Encapsulates the operations common to @Parameter and @DynamicParameter
*/
public class WrappedParameter {
- private Parameter m_parameter;
- private DynamicParameter m_dynamicParameter;
+ private Parameter parameter;
+ private DynamicParameter dynamicParameter;
public WrappedParameter(Parameter p) {
- m_parameter = p;
+ parameter = p;
}
public WrappedParameter(DynamicParameter p) {
- m_dynamicParameter = p;
+ dynamicParameter = p;
}
public Parameter getParameter() {
- return m_parameter;
+ return parameter;
}
public DynamicParameter getDynamicParameter() {
- return m_dynamicParameter;
+ return dynamicParameter;
}
public int arity() {
- return m_parameter != null ? m_parameter.arity() : 1;
+ return parameter != null ? parameter.arity() : 1;
}
public boolean hidden() {
- return m_parameter != null ? m_parameter.hidden() : m_dynamicParameter.hidden();
+ return parameter != null ? parameter.hidden() : dynamicParameter.hidden();
}
public boolean required() {
- return m_parameter != null ? m_parameter.required() : m_dynamicParameter.required();
+ return parameter != null ? parameter.required() : dynamicParameter.required();
}
public boolean password() {
- return m_parameter != null ? m_parameter.password() : false;
+ return parameter != null ? parameter.password() : false;
}
public String[] names() {
- return m_parameter != null ? m_parameter.names() : m_dynamicParameter.names();
+ return parameter != null ? parameter.names() : dynamicParameter.names();
}
public boolean variableArity() {
- return m_parameter != null ? m_parameter.variableArity() : false;
+ return parameter != null ? parameter.variableArity() : false;
}
- public Class<? extends IParameterValidator> validateWith() {
- return m_parameter != null ? m_parameter.validateWith() : m_dynamicParameter.validateWith();
+ public Class<? extends IParameterValidator>[] validateWith() {
+ return parameter != null ? parameter.validateWith() : dynamicParameter.validateWith();
}
- public Class<? extends IValueValidator> validateValueWith() {
- return m_parameter != null
- ? m_parameter.validateValueWith()
- : m_dynamicParameter.validateValueWith();
+ public Class<? extends IValueValidator>[] validateValueWith() {
+ return parameter != null
+ ? parameter.validateValueWith()
+ : dynamicParameter.validateValueWith();
}
public boolean echoInput() {
- return m_parameter != null ? m_parameter.echoInput() : false;
+ return parameter != null ? parameter.echoInput() : false;
}
public void addValue(Parameterized parameterized, Object object, Object value) {
- if (m_parameter != null) {
- parameterized.set(object, value);
+ try {
+ addValue(parameterized, object, value, null);
+ } catch (IllegalAccessException e) {
+ throw new ParameterException("Couldn't set " + object + " to " + value, e);
+ }
+ }
+
+ public void addValue(Parameterized parameterized, Object object, Object value, Field field)
+ throws IllegalAccessException {
+ if (parameter != null) {
+ if (field != null) {
+ field.set(object, value);
+ } else {
+ parameterized.set(object, value);
+ }
} else {
- String a = m_dynamicParameter.assignment();
+ String a = dynamicParameter.assignment();
String sv = value.toString();
int aInd = sv.indexOf(a);
@@ -86,13 +100,7 @@ public class WrappedParameter {
Method m;
m = findPut(parameterized.getType());
m.invoke(parameterized.get(object), key, value);
- } catch (SecurityException e) {
- e.printStackTrace();
- } catch(IllegalAccessException e) {
- e.printStackTrace();
- } catch(InvocationTargetException e) {
- e.printStackTrace();
- } catch (NoSuchMethodException e) {
+ } catch (SecurityException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
@@ -102,14 +110,14 @@ public class WrappedParameter {
}
public String getAssignment() {
- return m_dynamicParameter != null ? m_dynamicParameter.assignment() : "";
+ return dynamicParameter != null ? dynamicParameter.assignment() : "";
}
public boolean isHelp() {
- return m_parameter != null && m_parameter.help();
+ return parameter != null && parameter.help();
}
public boolean isNonOverwritableForced() {
- return m_parameter != null && m_parameter.forceNonOverwritable();
+ return parameter != null && parameter.forceNonOverwritable();
}
}
diff --git a/src/main/java/com/beust/jcommander/converters/BaseConverter.java b/src/main/java/com/beust/jcommander/converters/BaseConverter.java
index 4287163..02e94b8 100644
--- a/src/main/java/com/beust/jcommander/converters/BaseConverter.java
+++ b/src/main/java/com/beust/jcommander/converters/BaseConverter.java
@@ -27,14 +27,14 @@ import com.beust.jcommander.IStringConverter;
*/
abstract public class BaseConverter<T> implements IStringConverter<T> {
- private String m_optionName;
+ private String optionName;
public BaseConverter(String optionName) {
- m_optionName = optionName;
+ this.optionName = optionName;
}
public String getOptionName() {
- return m_optionName;
+ return optionName;
}
protected String getErrorString(String value, String to) {
diff --git a/src/main/java/com/beust/jcommander/converters/CharArrayConverter.java b/src/main/java/com/beust/jcommander/converters/CharArrayConverter.java
new file mode 100644
index 0000000..5252f63
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/CharArrayConverter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+
+/**
+ * Converts a String to a char[].
+ *
+ * @author Gary Gregory
+ */
+public class CharArrayConverter implements IStringConverter<char[]> {
+
+ public char[] convert(final String value) {
+ return value.toCharArray();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/beust/jcommander/converters/DefaultListConverter.java b/src/main/java/com/beust/jcommander/converters/DefaultListConverter.java
new file mode 100644
index 0000000..00bf9ac
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/DefaultListConverter.java
@@ -0,0 +1,36 @@
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+import com.beust.jcommander.internal.Lists;
+
+import java.util.List;
+
+/**
+ * A converter to obtain a list of elements.
+ * @param <T> the element type
+ * @author simon04
+ */
+public class DefaultListConverter<T> implements IStringConverter<List<T>> {
+
+ private final IParameterSplitter splitter;
+ private final IStringConverter<T> converter;
+
+ /**
+ * Constructs a new converter.
+ * @param splitter to split value into list of arguments
+ * @param converter to convert list of arguments to target element type
+ */
+ public DefaultListConverter(IParameterSplitter splitter, IStringConverter<T> converter) {
+ this.splitter = splitter;
+ this.converter = converter;
+ }
+
+ @Override
+ public List<T> convert(String value) {
+ List<T> result = Lists.newArrayList();
+ for (String param : splitter.split(value)) {
+ result.add(converter.convert(param));
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/com/beust/jcommander/converters/EnumConverter.java b/src/main/java/com/beust/jcommander/converters/EnumConverter.java
new file mode 100644
index 0000000..3e850bb
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/EnumConverter.java
@@ -0,0 +1,42 @@
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+import com.beust.jcommander.ParameterException;
+
+import java.util.EnumSet;
+
+/**
+ * A converter to parse enums
+ * @param <T> the enum type
+ * @author simon04
+ */
+public class EnumConverter<T extends Enum<T>> implements IStringConverter<T> {
+
+ private final String optionName;
+ private final Class<T> clazz;
+
+ /**
+ * Constructs a new converter.
+ * @param optionName the option name for error reporting
+ * @param clazz the enum class
+ */
+ public EnumConverter(String optionName, Class<T> clazz) {
+ this.optionName = optionName;
+ this.clazz = clazz;
+ }
+
+ @Override
+ public T convert(String value) {
+ try {
+ try {
+ return Enum.valueOf(clazz, value);
+ } catch (IllegalArgumentException e) {
+ return Enum.valueOf(clazz, value.toUpperCase());
+ }
+ } catch (Exception e) {
+ throw new ParameterException("Invalid value for " + optionName + " parameter. Allowed values:" +
+ EnumSet.allOf(clazz));
+
+ }
+ }
+}
diff --git a/src/main/java/com/beust/jcommander/converters/InetAddressConverter.java b/src/main/java/com/beust/jcommander/converters/InetAddressConverter.java
new file mode 100644
index 0000000..b6f391a
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/InetAddressConverter.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import com.beust.jcommander.IStringConverter;
+
+/**
+ * Converts {@code String}s to {@code InetAddress}'.
+ */
+public class InetAddressConverter implements IStringConverter<InetAddress> {
+
+ @Override
+ public InetAddress convert(String host) {
+ try {
+ return InetAddress.getByName(host);
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException(host, e);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java b/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java
index d5401a1..ac66df6 100644
--- a/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java
+++ b/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java
@@ -32,7 +32,7 @@ import java.util.Properties;
*/
public class PropertyFileDefaultProvider implements IDefaultProvider {
public static final String DEFAULT_FILE_NAME = "jcommander.properties";
- private Properties m_properties;
+ private Properties properties;
public PropertyFileDefaultProvider() {
init(DEFAULT_FILE_NAME);
@@ -44,10 +44,10 @@ public class PropertyFileDefaultProvider implements IDefaultProvider {
private void init(String fileName) {
try {
- m_properties = new Properties();
+ properties = new Properties();
URL url = ClassLoader.getSystemResource(fileName);
if (url != null) {
- m_properties.load(url.openStream());
+ properties.load(url.openStream());
} else {
throw new ParameterException("Could not find property file: " + fileName
+ " on the class path");
@@ -64,7 +64,7 @@ public class PropertyFileDefaultProvider implements IDefaultProvider {
index++;
}
String key = optionName.substring(index);
- return m_properties.getProperty(key);
+ return properties.getProperty(key);
}
}
diff --git a/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java b/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java
index 7eb5ae5..51e5d67 100644
--- a/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java
+++ b/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java
@@ -43,37 +43,38 @@ public class DefaultConverterFactory implements IStringConverterFactory {
/**
* A map of converters per class.
*/
- private static Map<Class, Class<? extends IStringConverter<?>>> m_classConverters;
+ private static Map<Class, Class<? extends IStringConverter<?>>> classConverters;
static {
- m_classConverters = Maps.newHashMap();
- m_classConverters.put(String.class, StringConverter.class);
- m_classConverters.put(Integer.class, IntegerConverter.class);
- m_classConverters.put(int.class, IntegerConverter.class);
- m_classConverters.put(Long.class, LongConverter.class);
- m_classConverters.put(long.class, LongConverter.class);
- m_classConverters.put(Float.class, FloatConverter.class);
- m_classConverters.put(float.class, FloatConverter.class);
- m_classConverters.put(Double.class, DoubleConverter.class);
- m_classConverters.put(double.class, DoubleConverter.class);
- m_classConverters.put(Boolean.class, BooleanConverter.class);
- m_classConverters.put(boolean.class, BooleanConverter.class);
- m_classConverters.put(File.class, FileConverter.class);
- m_classConverters.put(BigDecimal.class, BigDecimalConverter.class);
- m_classConverters.put(Date.class, ISO8601DateConverter.class);
+ classConverters = Maps.newHashMap();
+ classConverters.put(String.class, StringConverter.class);
+ classConverters.put(Integer.class, IntegerConverter.class);
+ classConverters.put(int.class, IntegerConverter.class);
+ classConverters.put(Long.class, LongConverter.class);
+ classConverters.put(long.class, LongConverter.class);
+ classConverters.put(Float.class, FloatConverter.class);
+ classConverters.put(float.class, FloatConverter.class);
+ classConverters.put(Double.class, DoubleConverter.class);
+ classConverters.put(double.class, DoubleConverter.class);
+ classConverters.put(Boolean.class, BooleanConverter.class);
+ classConverters.put(boolean.class, BooleanConverter.class);
+ classConverters.put(File.class, FileConverter.class);
+ classConverters.put(BigDecimal.class, BigDecimalConverter.class);
+ classConverters.put(Date.class, ISO8601DateConverter.class);
+ classConverters.put(URI.class, URIConverter.class);
+ classConverters.put(URL.class, URLConverter.class);
+
try {
Class<?> pathClass = Class.forName("java.nio.file.Path");
Class<?> pathConverterClass = Class.forName("com.beust.jcommander.converters.PathConverter");
- m_classConverters.put(pathClass, (Class<? extends IStringConverter<?>>)pathConverterClass);
- } catch (ClassNotFoundException e) {
- // Do nothing: Android does not have java.nio.file.Path
+ classConverters.put(pathClass, (Class<? extends IStringConverter<?>>)pathConverterClass);
+ } catch (ClassNotFoundException ex) {
+ // skip if class is not present (e.g. on Android)
}
- m_classConverters.put(URI.class, URIConverter.class);
- m_classConverters.put(URL.class, URLConverter.class);
}
public Class<? extends IStringConverter<?>> getConverter(Class forType) {
- return m_classConverters.get(forType);
+ return classConverters.get(forType);
}
}
diff --git a/src/main/java/com/beust/jcommander/internal/JDK6Console.java b/src/main/java/com/beust/jcommander/internal/JDK6Console.java
index 70cb186..507a575 100644
--- a/src/main/java/com/beust/jcommander/internal/JDK6Console.java
+++ b/src/main/java/com/beust/jcommander/internal/JDK6Console.java
@@ -13,8 +13,8 @@ public class JDK6Console implements Console {
public JDK6Console(Object console) throws Exception {
this.console = console;
- Method writerMethod = console.getClass().getDeclaredMethod("writer", new Class<?>[0]);
- writer = (PrintWriter) writerMethod.invoke(console, new Object[0]);
+ Method writerMethod = console.getClass().getDeclaredMethod("writer");
+ writer = (PrintWriter) writerMethod.invoke(console);
}
public void print(String msg) {
@@ -30,11 +30,11 @@ public class JDK6Console implements Console {
writer.flush();
Method method;
if (echoInput) {
- method = console.getClass().getDeclaredMethod("readLine", new Class<?>[0]);
- return ((String) method.invoke(console, new Object[0])).toCharArray();
+ method = console.getClass().getDeclaredMethod("readLine");
+ return ((String) method.invoke(console)).toCharArray();
} else {
- method = console.getClass().getDeclaredMethod("readPassword", new Class<?>[0]);
- return (char[]) method.invoke(console, new Object[0]);
+ method = console.getClass().getDeclaredMethod("readPassword");
+ return (char[]) method.invoke(console);
}
}
catch (Exception e) {
diff --git a/src/main/java/com/beust/jcommander/internal/Lists.java b/src/main/java/com/beust/jcommander/internal/Lists.java
index fdbee55..f18e6cc 100644
--- a/src/main/java/com/beust/jcommander/internal/Lists.java
+++ b/src/main/java/com/beust/jcommander/internal/Lists.java
@@ -27,27 +27,27 @@ import java.util.List;
public class Lists {
public static <K> List<K> newArrayList() {
- return new ArrayList<K>();
+ return new ArrayList<>();
}
public static <K> List<K> newArrayList(Collection<K> c) {
- return new ArrayList<K>(c);
+ return new ArrayList<>(c);
}
public static <K> List<K> newArrayList(K... c) {
- return new ArrayList<K>(Arrays.asList(c));
+ return new ArrayList<>(Arrays.asList(c));
}
public static <K> List<K> newArrayList(int size) {
- return new ArrayList<K>(size);
+ return new ArrayList<>(size);
}
public static <K> LinkedList<K> newLinkedList() {
- return new LinkedList<K>();
+ return new LinkedList<>();
}
public static <K> LinkedList<K> newLinkedList(Collection<K> c) {
- return new LinkedList<K>(c);
+ return new LinkedList<>(c);
}
diff --git a/src/main/java/com/beust/jcommander/internal/Maps.java b/src/main/java/com/beust/jcommander/internal/Maps.java
index e272122..2f45010 100644
--- a/src/main/java/com/beust/jcommander/internal/Maps.java
+++ b/src/main/java/com/beust/jcommander/internal/Maps.java
@@ -25,11 +25,11 @@ import java.util.Map;
public class Maps {
public static <K, V> Map<K,V> newHashMap() {
- return new HashMap<K, V>();
+ return new HashMap<>();
}
public static <K, V> Map<K,V> newLinkedHashMap() {
- return new LinkedHashMap<K, V>();
+ return new LinkedHashMap<>();
}
public static <T> Map<T, T> newHashMap(T... parameters) {
diff --git a/src/main/java/com/beust/jcommander/internal/Sets.java b/src/main/java/com/beust/jcommander/internal/Sets.java
index 77949c3..168c55f 100644
--- a/src/main/java/com/beust/jcommander/internal/Sets.java
+++ b/src/main/java/com/beust/jcommander/internal/Sets.java
@@ -25,11 +25,11 @@ import java.util.Set;
public class Sets {
public static <K> Set<K> newHashSet() {
- return new HashSet<K>();
+ return new HashSet<>();
}
public static <K> Set<K> newLinkedHashSet() {
- return new LinkedHashSet<K>();
+ return new LinkedHashSet<>();
}
}
diff --git a/src/test/java/com/beust/jcommander/ArgMultiNameValidator.java b/src/test/java/com/beust/jcommander/ArgMultiNameValidator.java
new file mode 100644
index 0000000..92a79bc
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/ArgMultiNameValidator.java
@@ -0,0 +1,19 @@
+package com.beust.jcommander;
+
+/**
+ * Created by jeremysolarz on 12/15/16.
+ */
+public class ArgMultiNameValidator {
+
+ public static class MultiNameValidator implements IValueValidator<String> {
+
+ public static String parsedName;
+
+ public void validate(String name, String value) throws ParameterException {
+ parsedName = name;
+ }
+ }
+
+ @Parameter(names = { "-name1", "-name2" }, description = "Names of parameter", validateValueWith = MultiNameValidator.class, required = true)
+ private String parameter;
+}
diff --git a/src/test/java/com/beust/jcommander/CmdTest.java b/src/test/java/com/beust/jcommander/CmdTest.java
index 6601193..229517d 100644
--- a/src/test/java/com/beust/jcommander/CmdTest.java
+++ b/src/test/java/com/beust/jcommander/CmdTest.java
@@ -17,7 +17,7 @@ public class CmdTest {
@Parameters(commandNames = "--cmd-two")
class CmdTwo {
@Parameter
- List<String> params = new java.util.LinkedList<String>();
+ List<String> params = new java.util.LinkedList<>();
}
public String parseArgs(boolean withDefault, String[] args) {
@@ -35,7 +35,7 @@ public class CmdTest {
// is named "WithoutValidation".
jc.parseWithoutValidation(args);
if (jc.getParsedCommand() == null) {
- LinkedList<String> newArgs = new LinkedList<String>();
+ LinkedList<String> newArgs = new LinkedList<>();
newArgs.add("--cmd-two");
newArgs.addAll(Arrays.asList(args));
jc.parse(newArgs.toArray(new String[0]));
@@ -70,7 +70,12 @@ public class CmdTest {
public void testArgsWithoutDefaultCmdFail(String expected,
boolean requireDefault, String[] args) {
if (requireDefault) {
- parseArgs(false, args);
+ try {
+ parseArgs(false, args);
+ } catch (MissingCommandException e) {
+ Assert.assertEquals(e.getUnknownCommand(), args[0]);
+ throw e;
+ }
} else {
throw new MissingCommandException("irrelevant test case");
}
@@ -83,4 +88,20 @@ public class CmdTest {
Assert.assertEquals(parseArgs(true, args), expected);
}
-} \ No newline at end of file
+ @Test
+ public void testIssue244() throws Exception {
+ class P1 {}
+ class P2 {
+ @Parameter(names = "--hello")
+ private int test;
+ }
+ P1 p1 = new P1();
+ P2 p2 = new P2();
+ JCommander j = new JCommander(p1);
+ j.addCommand("wonderful", p2);
+ j.setAllowAbbreviatedOptions(true);
+ j.parse("wond", "--he", "47");
+ Assert.assertEquals("wonderful", j.getParsedCommand());
+ Assert.assertEquals(47, p2.test);
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/ConverterFactoryTest.java b/src/test/java/com/beust/jcommander/ConverterFactoryTest.java
index e02166e..4b8b923 100644
--- a/src/test/java/com/beust/jcommander/ConverterFactoryTest.java
+++ b/src/test/java/com/beust/jcommander/ConverterFactoryTest.java
@@ -62,9 +62,10 @@ public class ConverterFactoryTest {
* Test that main parameters can be used with string converters,
* either with a factory or from the annotation.
*/
- private void mainWithHostPortParameters(IStringConverterFactory f, IHostPorts a) {
+ private void mainWithHostPortParameters(IStringConverterFactory f, IStringConverterInstanceFactory f2, IHostPorts a) {
JCommander jc = new JCommander(a);
if (f != null) jc.addConverterFactory(f);
+ if (f2 != null) jc.addConverterInstanceFactory(f2);
jc.parse("a.com:10", "b.com:20");
Assert.assertEquals(a.getHostPorts().get(0).host, "a.com");
Assert.assertEquals(a.getHostPorts().get(0).port.intValue(), 10);
@@ -74,12 +75,27 @@ public class ConverterFactoryTest {
@Test
public void mainWithoutFactory() {
- mainWithHostPortParameters(null, new ArgsMainParameter1());
+ mainWithHostPortParameters(null, null, new ArgsMainParameter2());
+ }
+
+ @Test(expectedExceptions = RuntimeException.class)
+ public void mainWithoutConverterWithoutFactory() {
+ mainWithHostPortParameters(null, null, new ArgsMainParameter1());
}
@Test
public void mainWithFactory() {
- mainWithHostPortParameters(CONVERTER_FACTORY, new ArgsMainParameter2());
+ mainWithHostPortParameters(CONVERTER_FACTORY, null, new ArgsMainParameter1());
+ }
+
+ @Test
+ public void mainWithInstanceFactory() {
+ mainWithHostPortParameters(null, new IStringConverterInstanceFactory() {
+ @Override
+ public IStringConverter<?> getConverterInstance(Parameter parameter, Class<?> forType, String optionName) {
+ return HostPort.class.equals(forType) ? new HostPortConverter() : null;
+ }
+ }, new ArgsMainParameter1());
}
}
diff --git a/src/test/java/com/beust/jcommander/DefaultProviderTest.java b/src/test/java/com/beust/jcommander/DefaultProviderTest.java
index 45ab6b6..45fad38 100644
--- a/src/test/java/com/beust/jcommander/DefaultProviderTest.java
+++ b/src/test/java/com/beust/jcommander/DefaultProviderTest.java
@@ -117,4 +117,24 @@ public class DefaultProviderTest {
Assert.assertEquals(a.log.intValue(), 19);
}
+ @Test
+ public void missingRequiredParameterWithDefaultValueProviderShouldNotRaiseParameterException() {
+ class ArgsRequired {
+ @Parameter(names = "-log", description = "Level of verbosity", required = true)
+ public Integer log;
+ }
+
+ IDefaultProvider defaultProvider = new IDefaultProvider() {
+ public String getDefaultValueFor(String optionName) {
+ return "-log".equals(optionName) ? "1" : "";
+ }
+ };
+
+ ArgsRequired a = new ArgsRequired();
+ JCommander jc = new JCommander(a);
+ jc.setDefaultProvider(defaultProvider);
+ jc.parse();
+
+ Assert.assertEquals(a.log.intValue(), 1);
+ }
}
diff --git a/src/test/java/com/beust/jcommander/DefaultValueTest.java b/src/test/java/com/beust/jcommander/DefaultValueTest.java
index 3b1f29c..403ecfd 100644
--- a/src/test/java/com/beust/jcommander/DefaultValueTest.java
+++ b/src/test/java/com/beust/jcommander/DefaultValueTest.java
@@ -36,7 +36,7 @@ public class DefaultValueTest {
public void emptyDefaultValueForListParameterStaysEmptyIfNotAssignedOrIsSetOtherwise() {
MyOptsWithEmptyDefaults opts = new MyOptsWithEmptyDefaults();
JCommander cmd = new JCommander(opts);
- cmd.parse(new String[]{"-a", "anotherValue"});
+ cmd.parse("-a", "anotherValue");
Assert.assertEquals(opts.list.size(), 1);
Assert.assertEquals(opts.list.get(0), "anotherValue");
Assert.assertEquals(opts.set.size(), 0);
@@ -46,7 +46,7 @@ public class DefaultValueTest {
public void defaultValueForListParametersGetsOverwrittenWithSpecifiedValueOrStaysAsDefaultOtherwise() {
MyOptsWithDefaultValues opts = new MyOptsWithDefaultValues();
JCommander cmd = new JCommander(opts);
- cmd.parse(new String[]{"-a", "anotherValue"});
+ cmd.parse("-a", "anotherValue");
Assert.assertEquals(opts.list.size(), 1);
Assert.assertEquals(opts.list.get(0), "anotherValue");
Assert.assertEquals(opts.set.size(), 1);
@@ -67,8 +67,8 @@ public class DefaultValueTest {
private void testSettingMultipleValuesToListTypeParameters(MyOpts opts) {
JCommander cmd = new JCommander(opts);
- cmd.parse(new String[]{"-a", "anotherValue", "-a", "anotherValue2",
- "-b", "anotherValue3", "-b", "anotherValue4"});
+ cmd.parse("-a", "anotherValue", "-a", "anotherValue2",
+ "-b", "anotherValue3", "-b", "anotherValue4");
Assert.assertEquals(opts.list.size(), 2);
Assert.assertEquals(opts.list.get(0), "anotherValue");
Assert.assertEquals(opts.list.get(1), "anotherValue2");
diff --git a/src/test/java/com/beust/jcommander/FinderTest.java b/src/test/java/com/beust/jcommander/FinderTest.java
index 94bf812..d4adda9 100644
--- a/src/test/java/com/beust/jcommander/FinderTest.java
+++ b/src/test/java/com/beust/jcommander/FinderTest.java
@@ -17,7 +17,7 @@ public class FinderTest {
Arg a = new Arg();
JCommander jc = new JCommander(a);
jc.setCaseSensitiveOptions(false);
- jc.parse(new String[] { "--PARAM", "foo" });
+ jc.parse("--PARAM", "foo");
Assert.assertEquals(a.param, "foo");
}
@@ -27,7 +27,6 @@ public class FinderTest {
JCommander jc = new JCommander(a);
jc.addCommand(conf);
jc.setCaseSensitiveOptions(false);
-// jc.setCaseSensitiveCommands(false);
jc.parse("--CONFIGURE");
String command = jc.getParsedCommand();
Assert.assertEquals(command, "--configure");
@@ -41,7 +40,7 @@ public class FinderTest {
Arg a = new Arg();
JCommander jc = new JCommander(a);
jc.setAllowAbbreviatedOptions(true);
- jc.parse(new String[] { "--par", "foo" });
+ jc.parse("--par", "foo");
Assert.assertEquals(a.param, "foo");
}
@@ -54,7 +53,7 @@ public class FinderTest {
JCommander jc = new JCommander(a);
jc.setCaseSensitiveOptions(false);
jc.setAllowAbbreviatedOptions(true);
- jc.parse(new String[] { "--PAR", "foo" });
+ jc.parse("--PAR", "foo");
Assert.assertEquals(a.param, "foo");
}
@@ -69,7 +68,7 @@ public class FinderTest {
Arg a = new Arg();
JCommander jc = new JCommander(a);
jc.setAllowAbbreviatedOptions(true);
- jc.parse(new String[] { "--par", "foo" });
+ jc.parse("--par", "foo");
Assert.assertEquals(a.param, "foo");
}
@@ -85,7 +84,7 @@ public class FinderTest {
JCommander jc = new JCommander(a);
jc.setCaseSensitiveOptions(false);
jc.setAllowAbbreviatedOptions(true);
- jc.parse(new String[] { "--PAR", "foo" });
+ jc.parse("--PAR", "foo");
Assert.assertEquals(a.param, "foo");
}
diff --git a/src/test/java/com/beust/jcommander/HiddenConverter.java b/src/test/java/com/beust/jcommander/HiddenConverter.java
new file mode 100644
index 0000000..cd36b85
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/HiddenConverter.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+public class HiddenConverter implements IStringConverter<String> {
+ private HiddenConverter() {
+ }
+
+ @Override
+ public String convert(String value) {
+ return value;
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/HiddenParameterSplitter.java b/src/test/java/com/beust/jcommander/HiddenParameterSplitter.java
new file mode 100644
index 0000000..a40f36f
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/HiddenParameterSplitter.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.beust.jcommander.converters.IParameterSplitter;
+
+public class HiddenParameterSplitter implements IParameterSplitter {
+ @Override
+ public List<String> split(String value) {
+ return Arrays.asList(value.split(";"));
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/JCommanderTest.java b/src/test/java/com/beust/jcommander/JCommanderTest.java
index ad2c5e8..e967ef7 100644
--- a/src/test/java/com/beust/jcommander/JCommanderTest.java
+++ b/src/test/java/com/beust/jcommander/JCommanderTest.java
@@ -2,13 +2,13 @@
* Copyright (C) 2010 the original author or authors.
* See the notice.md file distributed with this work for additional
* information regarding copyright ownership.
- *
+ * <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,1064 +18,1516 @@
package com.beust.jcommander;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigDecimal;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.ResourceBundle;
-import java.util.TreeSet;
-
-import org.testng.Assert;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import com.beust.jcommander.args.AlternateNamesForListArgs;
-import com.beust.jcommander.args.Args1;
-import com.beust.jcommander.args.Args1Setter;
-import com.beust.jcommander.args.Args2;
-import com.beust.jcommander.args.ArgsArityString;
-import com.beust.jcommander.args.ArgsBooleanArity;
-import com.beust.jcommander.args.ArgsBooleanArity0;
-import com.beust.jcommander.args.ArgsConverter;
-import com.beust.jcommander.args.ArgsEnum;
+import com.beust.jcommander.args.*;
import com.beust.jcommander.args.ArgsEnum.ChoiceType;
-import com.beust.jcommander.args.ArgsEquals;
-import com.beust.jcommander.args.ArgsHelp;
-import com.beust.jcommander.args.ArgsI18N1;
-import com.beust.jcommander.args.ArgsI18N2;
-import com.beust.jcommander.args.ArgsI18N2New;
-import com.beust.jcommander.args.ArgsInherited;
-import com.beust.jcommander.args.ArgsList;
-import com.beust.jcommander.args.ArgsMainParameter1;
-import com.beust.jcommander.args.ArgsMaster;
-import com.beust.jcommander.args.ArgsMultipleUnparsed;
-import com.beust.jcommander.args.ArgsOutOfMemory;
-import com.beust.jcommander.args.ArgsPrivate;
-import com.beust.jcommander.args.ArgsRequired;
-import com.beust.jcommander.args.ArgsSlave;
-import com.beust.jcommander.args.ArgsSlaveBogus;
-import com.beust.jcommander.args.ArgsValidate1;
-import com.beust.jcommander.args.ArgsWithSet;
-import com.beust.jcommander.args.Arity1;
-import com.beust.jcommander.args.SeparatorColon;
-import com.beust.jcommander.args.SeparatorEqual;
-import com.beust.jcommander.args.SeparatorMixed;
-import com.beust.jcommander.args.SlashSeparator;
-import com.beust.jcommander.args.VariableArity;
import com.beust.jcommander.command.CommandAdd;
import com.beust.jcommander.command.CommandCommit;
import com.beust.jcommander.command.CommandMain;
+import com.beust.jcommander.converters.FileConverter;
import com.beust.jcommander.internal.Lists;
import com.beust.jcommander.internal.Maps;
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.ResourceBundle;
@Test
public class JCommanderTest {
- public void simpleArgs() throws ParseException {
- Args1 args = new Args1();
- String[] argv = { "-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
- "-date", "2011-10-26", "-groups", "unit", "a", "b", "c" };
- new JCommander(args, argv);
-
- Assert.assertTrue(args.debug);
- Assert.assertEquals(args.verbose.intValue(), 2);
- Assert.assertEquals(args.groups, "unit");
- Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
- Assert.assertEquals(args.floa, 1.2f, 0.1f);
- Assert.assertEquals(args.doub, 1.3f, 0.1f);
- Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
- Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
- }
-
- @DataProvider
- public Object[][] alternateNamesListArgs() {
- return new Object[][] {
- new String[][] {new String[] {"--servers", "1", "-s", "2", "--servers", "3"}},
- new String[][] {new String[] {"-s", "1", "-s", "2", "--servers", "3"}},
- new String[][] {new String[] {"--servers", "1", "--servers", "2", "-s", "3"}},
- new String[][] {new String[] {"-s", "1", "--servers", "2", "-s", "3"}},
- new String[][] {new String[] {"-s", "1", "-s", "2", "--servers", "3"}},
- };
- }
-
- /**
- * Confirm that List<?> parameters with alternate names return the correct
- * List regardless of how the arguments are specified
- */
-
- @Test(dataProvider = "alternateNamesListArgs")
- public void testAlternateNamesForListArguments(String[] argv) {
- AlternateNamesForListArgs args = new AlternateNamesForListArgs();
-
- new JCommander(args, argv);
-
- Assert.assertEquals(args.serverNames.size(), 3);
- Assert.assertEquals(args.serverNames.get(0), argv[1]);
- Assert.assertEquals(args.serverNames.get(1), argv[3]);
- Assert.assertEquals(args.serverNames.get(2), argv[5]);
- }
-
-
- /**
- * Make sure that if there are args with multiple names (e.g. "-log" and "-verbose"),
- * the usage will only display it once.
- */
- public void repeatedArgs() {
- Args1 args = new Args1();
- String[] argv = { "-log", "2" };
- JCommander jc = new JCommander(args, argv);
- Assert.assertEquals(jc.getParameters().size(), 8);
- }
-
- /**
- * Not specifying a required option should throw an exception.
- */
- @Test(expectedExceptions = ParameterException.class)
- public void requiredFields1Fail() {
- Args1 args = new Args1();
- String[] argv = { "-debug" };
- new JCommander(args, argv);
- }
-
- /**
- * Getting the description of a nonexistent command should throw an exception.
- */
- @Test(expectedExceptions = ParameterException.class)
- public void nonexistentCommandShouldThrow() {
- String[] argv = { };
- JCommander jc = new JCommander(new Object(), argv);
- jc.getCommandDescription("foo");
- }
-
- /**
- * Required options with multiple names should work with all names.
- */
- private void multipleNames(String option) {
- Args1 args = new Args1();
- String[] argv = { option, "2" };
- new JCommander(args, argv);
- Assert.assertEquals(args.verbose.intValue(), 2);
- }
-
- public void multipleNames1() {
- multipleNames("-log");
- }
-
- public void multipleNames2() {
- multipleNames("-verbose");
- }
-
- private void i18n1(String bundleName, Locale locale, String expectedString) {
- ResourceBundle bundle = locale != null ? ResourceBundle.getBundle(bundleName, locale)
- : null;
-
- ArgsI18N1 i18n = new ArgsI18N1();
- String[] argv = { "-host", "localhost" };
- JCommander jc = new JCommander(i18n, bundle, argv);
-// jc.usage();
-
- ParameterDescription pd = jc.getParameters().get(0);
- Assert.assertEquals(pd.getDescription(), expectedString);
- }
-
- public void i18nNoLocale() {
- i18n1("MessageBundle", null, "Host");
- }
-
- public void i18nUsLocale() {
- i18n1("MessageBundle", new Locale("en", "US"), "Host");
- }
-
- public void i18nFrLocale() {
- i18n1("MessageBundle", new Locale("fr", "FR"), "Hôte");
- }
-
- private void i18n2(Object i18n) {
- String[] argv = { "-host", "localhost" };
- Locale.setDefault(new Locale("fr", "FR"));
- JCommander jc = new JCommander(i18n, argv);
- ParameterDescription pd = jc.getParameters().get(0);
- Assert.assertEquals(pd.getDescription(), "Hôte");
- }
-
- public void i18nWithResourceAnnotation() {
- i18n2(new ArgsI18N2());
- }
-
- public void i18nWithResourceAnnotationNew() {
- i18n2(new ArgsI18N2New());
- }
-
- public void noParseConstructor() {
- JCommander jCommander = new JCommander(new ArgsMainParameter1());
- jCommander.usage(new StringBuilder());
- // Before fix, this parse would throw an exception, because it calls createDescription, which
- // was already called by usage(), and can only be called once.
- jCommander.parse();
- }
-
- /**
- * Test a use case where there are required parameters, but you still want
- * to interrogate the options which are specified.
- */
- public void usageWithRequiredArgsAndResourceBundle() {
- ArgsHelp argsHelp = new ArgsHelp();
- JCommander jc = new JCommander(new Object[]{argsHelp, new ArgsRequired()},
- java.util.ResourceBundle.getBundle("MessageBundle"));
- // Should be able to display usage without triggering validation
- jc.usage(new StringBuilder());
- try {
- jc.parse("-h");
- Assert.fail("Should have thrown a required parameter exception");
- } catch (ParameterException e) {
- Assert.assertTrue(e.getMessage().contains("are required"));
- }
- Assert.assertTrue(argsHelp.help);
- }
-
- public void multiObjects() {
- ArgsMaster m = new ArgsMaster();
- ArgsSlave s = new ArgsSlave();
- String[] argv = { "-master", "master", "-slave", "slave" };
- new JCommander(new Object[] { m , s }, argv);
-
- Assert.assertEquals(m.master, "master");
- Assert.assertEquals(s.slave, "slave");
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void multiObjectsWithDuplicatesFail() {
- ArgsMaster m = new ArgsMaster();
- ArgsSlave s = new ArgsSlaveBogus();
- String[] argv = { "-master", "master", "-slave", "slave" };
- new JCommander(new Object[] { m , s }, argv);
- }
-
- public void arityString() {
- ArgsArityString args = new ArgsArityString();
- String[] argv = { "-pairs", "pair0", "pair1", "rest" };
- new JCommander(args, argv);
-
- Assert.assertEquals(args.pairs.size(), 2);
- Assert.assertEquals(args.pairs.get(0), "pair0");
- Assert.assertEquals(args.pairs.get(1), "pair1");
- Assert.assertEquals(args.rest.size(), 1);
- Assert.assertEquals(args.rest.get(0), "rest");
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void arity2Fail() {
- ArgsArityString args = new ArgsArityString();
- String[] argv = { "-pairs", "pair0" };
- new JCommander(args, argv);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void multipleUnparsedFail() {
- ArgsMultipleUnparsed args = new ArgsMultipleUnparsed();
- String[] argv = { };
- new JCommander(args, argv);
- }
-
- public void privateArgs() {
- ArgsPrivate args = new ArgsPrivate();
- new JCommander(args, "-verbose", "3");
- Assert.assertEquals(args.getVerbose().intValue(), 3);
- }
-
- public void converterArgs() {
- ArgsConverter args = new ArgsConverter();
- String fileName = "a";
- new JCommander(args, "-file", "/tmp/" + fileName,
- "-listStrings", "Tuesday,Thursday",
- "-listInts", "-1,8",
- "-listBigDecimals", "-11.52,100.12");
- Assert.assertEquals(args.file.getName(), fileName);
- Assert.assertEquals(args.listStrings.size(), 2);
- Assert.assertEquals(args.listStrings.get(0), "Tuesday");
- Assert.assertEquals(args.listStrings.get(1), "Thursday");
- Assert.assertEquals(args.listInts.size(), 2);
- Assert.assertEquals(args.listInts.get(0).intValue(), -1);
- Assert.assertEquals(args.listInts.get(1).intValue(), 8);
- Assert.assertEquals(args.listBigDecimals.size(), 2);
- Assert.assertEquals(args.listBigDecimals.get(0), new BigDecimal("-11.52"));
- Assert.assertEquals(args.listBigDecimals.get(1), new BigDecimal("100.12"));
- }
-
- private void argsBoolean1(String[] params, Boolean expected) {
- ArgsBooleanArity args = new ArgsBooleanArity();
- new JCommander(args, params);
- Assert.assertEquals(args.debug, expected);
- }
-
- private void argsBoolean0(String[] params, Boolean expected) {
- ArgsBooleanArity0 args = new ArgsBooleanArity0();
- new JCommander(args, params);
- Assert.assertEquals(args.debug, expected);
- }
-
- public void booleanArity1() {
- argsBoolean1(new String[] {}, Boolean.FALSE);
- argsBoolean1(new String[] { "-debug", "true" }, Boolean.TRUE);
- }
-
- public void booleanArity0() {
- argsBoolean0(new String[] {}, Boolean.FALSE);
- argsBoolean0(new String[] { "-debug"}, Boolean.TRUE);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void badParameterShouldThrowParameter1Exception() {
- Args1 args = new Args1();
- String[] argv = { "-log", "foo" };
- new JCommander(args, argv);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void badParameterShouldThrowParameter2Exception() {
- Args1 args = new Args1();
- String[] argv = { "-long", "foo" };
- new JCommander(args, argv);
- }
-
- public void listParameters() {
- Args2 a = new Args2();
- String[] argv = {"-log", "2", "-groups", "unit", "a", "b", "c", "-host", "host2"};
- new JCommander(a, argv);
- Assert.assertEquals(a.verbose.intValue(), 2);
- Assert.assertEquals(a.groups, "unit");
- Assert.assertEquals(a.hosts, Arrays.asList("host2"));
- Assert.assertEquals(a.parameters, Arrays.asList("a", "b", "c"));
- }
-
- public void separatorEqual() {
- SeparatorEqual s = new SeparatorEqual();
- String[] argv = { "-log=3", "--longoption=10" };
- new JCommander(s, argv);
- Assert.assertEquals(s.log.intValue(), 3);
- Assert.assertEquals(s.longOption.intValue(), 10);
- }
-
- public void separatorColon() {
- SeparatorColon s = new SeparatorColon();
- String[] argv = { "-verbose:true" };
- new JCommander(s, argv);
- Assert.assertTrue(s.verbose);
- }
-
- public void separatorBoth() {
- SeparatorColon s = new SeparatorColon();
- SeparatorEqual s2 = new SeparatorEqual();
- String[] argv = { "-verbose:true", "-log=3" };
- new JCommander(new Object[] { s, s2 }, argv);
- Assert.assertTrue(s.verbose);
- Assert.assertEquals(s2.log.intValue(), 3);
- }
-
- public void separatorMixed1() {
- SeparatorMixed s = new SeparatorMixed();
- String[] argv = { "-long:1", "-level=42" };
- new JCommander(s, argv);
- Assert.assertEquals(s.l.longValue(), 1l);
- Assert.assertEquals(s.level.intValue(), 42);
- }
-
- public void slashParameters() {
- SlashSeparator a = new SlashSeparator();
- String[] argv = { "/verbose", "/file", "/tmp/a" };
- new JCommander(a, argv);
- Assert.assertTrue(a.verbose);
- Assert.assertEquals(a.file, "/tmp/a");
- }
-
- public void inheritance() {
- ArgsInherited args = new ArgsInherited();
- String[] argv = { "-log", "3", "-child", "2" };
- new JCommander(args, argv);
- Assert.assertEquals(args.child.intValue(), 2);
- Assert.assertEquals(args.log.intValue(), 3);
- }
-
- public void negativeNumber() {
- Args1 a = new Args1();
- String[] argv = { "-verbose", "-3" };
- new JCommander(a, argv);
- Assert.assertEquals(a.verbose.intValue(), -3);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void requiredMainParameters() {
- ArgsRequired a = new ArgsRequired();
- String[] argv = {};
- new JCommander(a, argv);
- }
-
- public void usageShouldNotChange() {
- JCommander jc = new JCommander(new Args1(), new String[]{"-log", "1"});
- StringBuilder sb = new StringBuilder();
- jc.usage(sb);
- String expected = sb.toString();
- jc = new JCommander(new Args1(), new String[]{"-debug", "-log", "2", "-long", "5"});
- sb = new StringBuilder();
- jc.usage(sb);
- String actual = sb.toString();
- Assert.assertEquals(actual, expected);
- }
-
- private void verifyCommandOrdering(String[] commandNames, Object[] commands) {
- CommandMain cm = new CommandMain();
- JCommander jc = new JCommander(cm);
-
- for (int i = 0; i < commands.length; i++) {
- jc.addCommand(commandNames[i], commands[i]);
- }
-
- Map<String, JCommander> c = jc.getCommands();
- Assert.assertEquals(c.size(), commands.length);
-
- Iterator<String> it = c.keySet().iterator();
- for (int i = 0; i < commands.length; i++) {
- Assert.assertEquals(it.next(), commandNames[i]);
- }
- }
-
- public void commandsShouldBeShownInOrderOfInsertion() {
- verifyCommandOrdering(new String[] { "add", "commit" },
- new Object[] { new CommandAdd(), new CommandCommit() });
- verifyCommandOrdering(new String[] { "commit", "add" },
- new Object[] { new CommandCommit(), new CommandAdd() });
- }
-
- @DataProvider
- public static Object[][] f() {
- return new Integer[][] {
- new Integer[] { 3, 5, 1 },
- new Integer[] { 3, 8, 1 },
- new Integer[] { 3, 12, 2 },
- new Integer[] { 8, 12, 2 },
- new Integer[] { 9, 10, 1 },
- };
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void arity1Fail() {
- final Arity1 arguments = new Arity1();
- final JCommander jCommander = new JCommander(arguments);
- final String[] commands = {
- "-inspect"
- };
- jCommander.parse(commands);
- }
-
- public void arity1Success1() {
- final Arity1 arguments = new Arity1();
- final JCommander jCommander = new JCommander(arguments);
- final String[] commands = {
- "-inspect", "true"
- };
- jCommander.parse(commands);
- Assert.assertTrue(arguments.inspect);
- }
-
- public void arity1Success2() {
- final Arity1 arguments = new Arity1();
- final JCommander jCommander = new JCommander(arguments);
- final String[] commands = {
- "-inspect", "false"
- };
- jCommander.parse(commands);
- Assert.assertFalse(arguments.inspect);
- }
-
- @Parameters(commandDescription = "Help for the given commands.")
- public static class Help {
- public static final String NAME = "help";
-
- @Parameter(description = "List of commands.")
- public List<String> commands=new ArrayList<String>();
- }
-
- @Test(expectedExceptions = ParameterException.class,
- description = "Verify that the main parameter's type is checked to be a List")
- public void wrongMainTypeShouldThrow() {
- JCommander jc = new JCommander(new ArgsRequiredWrongMain());
- jc.parse(new String[] { "f1", "f2" });
- }
-
- @Test(description = "This used to run out of memory")
- public void oom() {
- JCommander jc = new JCommander(new ArgsOutOfMemory());
- jc.usage(new StringBuilder());
- }
-
- @Test
- public void getParametersShouldNotNpe() {
- JCommander jc = new JCommander(new Args1());
- List<ParameterDescription> parameters = jc.getParameters();
- }
-
- public void validationShouldWork1() {
- ArgsValidate1 a = new ArgsValidate1();
- JCommander jc = new JCommander(a);
- jc.parse(new String[] { "-age", "2 "});
- Assert.assertEquals(a.age, new Integer(2));
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void validationShouldWorkWithDefaultValues() {
- ArgsValidate2 a = new ArgsValidate2();
- new JCommander(a);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void validationShouldWork2() {
- ArgsValidate1 a = new ArgsValidate1();
- JCommander jc = new JCommander(a);
- jc.parse(new String[] { "-age", "-2 "});
- }
-
- public void atFileCanContainEmptyLines() throws IOException {
- File f = File.createTempFile("JCommander", null);
- f.deleteOnExit();
- FileWriter fw = new FileWriter(f);
- fw.write("-log\n");
- fw.write("\n");
- fw.write("2\n");
- fw.close();
- new JCommander(new Args1(), "@" + f.getAbsolutePath());
- }
-
- public void handleEqualSigns() {
- ArgsEquals a = new ArgsEquals();
- JCommander jc = new JCommander(a);
- jc.parse(new String[] { "-args=a=b,b=c" });
- Assert.assertEquals(a.args, "a=b,b=c");
- }
-
- @SuppressWarnings("serial")
- public void handleSets() {
- ArgsWithSet a = new ArgsWithSet();
- new JCommander(a, new String[] { "-s", "3,1,2" });
- Assert.assertEquals(a.set, new TreeSet<Integer>() {{ add(1); add(2); add(3); }});
- }
-
- private static final List<String> V = Arrays.asList("a", "b", "c", "d");
-
- @DataProvider
- public Object[][] variable() {
- return new Object[][] {
- new Object[] { 0, V.subList(0, 0), V },
- new Object[] { 1, V.subList(0, 1), V.subList(1, 4) },
- new Object[] { 2, V.subList(0, 2), V.subList(2, 4) },
- new Object[] { 3, V.subList(0, 3), V.subList(3, 4) },
- new Object[] { 4, V.subList(0, 4), V.subList(4, 4) },
- };
- }
-
- @Test(dataProvider = "variable")
- public void variableArity(int count, List<String> var, List<String> main) {
- VariableArity va = new VariableArity(count);
- new JCommander(va).parse("-variable", "a", "b", "c", "d");
- Assert.assertEquals(var, va.var);
- Assert.assertEquals(main, va.main);
- }
-
- public void enumArgs() {
- ArgsEnum args = new ArgsEnum();
- String[] argv = { "-choice", "ONE", "-choices", "ONE", "Two" };
- JCommander jc = new JCommander(args, argv);
-
- Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
-
- List<ChoiceType> expected = Arrays.asList(ChoiceType.ONE, ChoiceType.Two);
- Assert.assertEquals(expected, args.choices);
- Assert.assertEquals(jc.getParameters().get(0).getDescription(),
- "Options: " + EnumSet.allOf((Class<? extends Enum>) ArgsEnum.ChoiceType.class));
-
- }
-
- public void enumArgsCaseInsensitive() {
- ArgsEnum args = new ArgsEnum();
- String[] argv = { "-choice", "one"};
- JCommander jc = new JCommander(args, argv);
-
- Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void enumArgsFail() {
- ArgsEnum args = new ArgsEnum();
- String[] argv = { "-choice", "A" };
- new JCommander(args, argv);
- }
-
- public void testListAndSplitters() {
- ArgsList al = new ArgsList();
- JCommander j = new JCommander(al);
- j.parse("-groups", "a,b", "-ints", "41,42", "-hp", "localhost:1000;example.com:1001",
- "-hp2", "localhost:1000,example.com:1001", "-uppercase", "ab,cd");
- Assert.assertEquals(al.groups.get(0), "a");
- Assert.assertEquals(al.groups.get(1), "b");
- Assert.assertEquals(al.ints.get(0).intValue(), 41);
- Assert.assertEquals(al.ints.get(1).intValue(), 42);
- Assert.assertEquals(al.hostPorts.get(0).host, "localhost");
- Assert.assertEquals(al.hostPorts.get(0).port.intValue(), 1000);
- Assert.assertEquals(al.hostPorts.get(1).host, "example.com");
- Assert.assertEquals(al.hostPorts.get(1).port.intValue(), 1001);
- Assert.assertEquals(al.hp2.get(1).host, "example.com");
- Assert.assertEquals(al.hp2.get(1).port.intValue(), 1001);
- Assert.assertEquals(al.uppercase.get(0), "AB");
- Assert.assertEquals(al.uppercase.get(1), "CD");
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void shouldThrowIfUnknownOption() {
- class A {
- @Parameter(names = "-long")
- public long l;
- }
- A a = new A();
- new JCommander(a).parse("-lon", "32");
- }
-
- @Test(expectedExceptions = ParameterException.class)
- public void mainParameterShouldBeValidate() {
- class V implements IParameterValidator {
-
- @Override
- public void validate(String name, String value) throws ParameterException {
- Assert.assertEquals("a", value);
- }
- }
-
- class A {
- @Parameter(validateWith = V.class)
- public List<String> m;
- }
-
- A a = new A();
- new JCommander(a).parse("b");
- }
-
- @Parameters(commandNames = { "--configure" })
- public static class ConfigureArgs {
- }
-
- public static class BaseArgs {
- @Parameter(names = { "-h", "--help" }, description = "Show this help screen")
- private boolean help = false;
-
- @Parameter(names = { "--version", "-version" }, description = "Show the program version")
- private boolean version;
- }
-
- public void commandsWithSamePrefixAsOptionsShouldWork() {
- BaseArgs a = new BaseArgs();
- ConfigureArgs conf = new ConfigureArgs();
- JCommander jc = new JCommander(a);
- jc.addCommand(conf);
- jc.parse("--configure");
- }
-
- // Tests:
- // required unparsed parameter
- @Test(enabled = false,
- description = "For some reason, this test still asks the password on stdin")
- public void askedRequiredPassword() {
- class A {
- @Parameter(names = { "--password", "-p" }, description = "Private key password",
- password = true, required = true)
- public String password;
-
- @Parameter(names = { "--port", "-o" }, description = "Port to bind server to",
- required = true)
- public int port;
- }
- A a = new A();
- InputStream stdin = System.in;
- try {
- System.setIn(new ByteArrayInputStream("password".getBytes()));
- new JCommander(a,new String[]{"--port", "7","--password"});
- Assert.assertEquals(a.port, 7);
- Assert.assertEquals(a.password, "password");
- } finally {
- System.setIn(stdin);
- }
- }
-
- public void dynamicParameters() {
- class Command {
- @DynamicParameter(names = {"-P"}, description = "Additional command parameters")
- private Map<String, String> params = Maps.newHashMap();
- }
- JCommander commander = new JCommander();
- Command c = new Command();
- commander.addCommand("command", c);
- commander.parse(new String[] { "command", "-Pparam='name=value'" });
- Assert.assertEquals(c.params.get("param"), "'name=value'");
- }
-
- public void exeParser() {
- class Params {
- @Parameter( names= "-i")
- private String inputFile;
- }
-
- String args[] = { "-i", "" };
- Params p = new Params();
- new JCommander(p, args);
- }
-
- public void multiVariableArityList() {
- class Params {
- @Parameter(names = "-paramA", description = "ParamA", variableArity = true)
- private List<String> paramA = Lists.newArrayList();
-
- @Parameter(names = "-paramB", description = "ParamB", variableArity = true)
- private List<String> paramB = Lists.newArrayList();
- }
-
- {
- String args[] = { "-paramA", "a1", "a2", "-paramB", "b1", "b2", "b3" };
- Params p = new Params();
- new JCommander(p, args).parse();
- Assert.assertEquals(p.paramA, Arrays.asList(new String[] { "a1", "a2" }));
- Assert.assertEquals(p.paramB, Arrays.asList(new String[] { "b1", "b2", "b3" }));
- }
-
- {
- String args[] = { "-paramA", "a1", "a2", "-paramB", "b1", "-paramA", "a3" };
- Params p = new Params();
- new JCommander(p, args).parse();
- Assert.assertEquals(p.paramA, Arrays.asList(new String[] { "a1", "a2", "a3" }));
- Assert.assertEquals(p.paramB, Arrays.asList(new String[] { "b1" }));
- }
- }
-
- @Test(enabled = false,
- description = "Need to double check that the command description is i18n'ed in the usage")
- public void commandKey() {
- @Parameters(resourceBundle = "MessageBundle", commandDescriptionKey = "command")
- class Args {
- @Parameter(names="-myoption", descriptionKey="myoption")
- private boolean option;
- }
- JCommander j = new JCommander();
- Args a = new Args();
- j.addCommand("comm", a);
- j.usage();
- }
-
- public void tmp() {
- class A {
- @Parameter(names = "-b")
- public String b;
- }
- new JCommander(new A()).parse("");
- }
-
- public void unknownOptionWithDifferentPrefix() {
- @Parameters(optionPrefixes = "/")
- class SlashSeparator {
-
- @Parameter(names = "/verbose")
- public boolean verbose = false;
-
- @Parameter(names = "/file")
- public String file;
- }
- SlashSeparator ss = new SlashSeparator();
- try {
- new JCommander(ss).parse("/notAParam");
- } catch (ParameterException ex) {
- boolean result = ex.getMessage().contains("Unknown option");
- Assert.assertTrue(result);
- }
- }
-
- public void equalSeparator() {
- @Parameters(separators = "=", commandDescription = "My command")
- class MyClass {
-
- @Parameter(names = { "-p", "--param" }, required = true, description = "param desc...")
- private String param;
- }
- MyClass c = new MyClass();
- String expected = "\"hello\"world";
- new JCommander(c).parse("--param=" + expected);
- Assert.assertEquals(expected, c.param);
- }
-
- public void simpleArgsSetter() throws ParseException {
- Args1Setter args = new Args1Setter();
- String[] argv = { "-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
- "-date", "2011-10-26", "-groups", "unit", "a", "b", "c" };
- new JCommander(args, argv);
-
- Assert.assertTrue(args.debug);
- Assert.assertEquals(args.verbose.intValue(), 2);
- Assert.assertEquals(args.groups, "unit");
- Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
- Assert.assertEquals(args.floa, 1.2f, 0.1f);
- Assert.assertEquals(args.doub, 1.3f, 0.1f);
- Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
- Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
- }
-
- public void verifyHelp() {
- class Arg {
- @Parameter(names = "--help", help = true)
- public boolean help = false;
-
- @Parameter(names = "file", required = true)
- public String file;
- }
- Arg arg = new Arg();
- String[] argv = { "--help" };
- new JCommander(arg, argv);
-
- Assert.assertTrue(arg.help);
- }
-
- public void helpTest() {
- class Arg {
- @Parameter(names = { "?", "-help", "--help" }, description = "Shows help", help = true)
- private boolean help = false;
- }
- Arg arg = new Arg();
- JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-help" });
-// System.out.println("helpTest:" + arg.help);
- }
-
- @Test(enabled = false, description = "Should only be enable once multiple parameters are allowed")
- public void duplicateParameterNames() {
- class ArgBase {
- @Parameter(names = { "-host" })
- protected String host;
- }
-
- class Arg1 extends ArgBase {}
- Arg1 arg1 = new Arg1();
-
- class Arg2 extends ArgBase {}
- Arg2 arg2 = new Arg2();
-
- JCommander jc = new JCommander(new Object[] { arg1, arg2});
- jc.parse(new String[] { "-host", "foo" });
- Assert.assertEquals(arg1.host, "foo");
- Assert.assertEquals(arg2.host, "foo");
- }
-
- public void parameterWithOneDoubleQuote() {
- @Parameters(separators = "=")
- class Arg {
- @Parameter(names = { "-p", "--param" })
- private String param;
- }
- JCommander jc = new JCommander(new MyClass());
- jc.parse("-p=\"");
- }
-
- public void emptyStringAsDefault() {
- class Arg {
- @Parameter(names = "-x")
- String s = "";
- }
- Arg a = new Arg();
- StringBuilder sb = new StringBuilder();
- new JCommander(a).usage(sb);
- Assert.assertTrue(sb.toString().contains("Default: <empty string>"));
- }
-
- public void spaces() {
- class Arg {
- @Parameter(names = "-rule", description = "rule")
- private List<String> rules = new ArrayList<String>();
- }
- Arg a = new Arg();
- new JCommander(a, "-rule", "some test");
- Assert.assertEquals(a.rules, Arrays.asList("some test"));
- }
-
- static class V2 implements IParameterValidator2 {
- final static List<String> names = Lists.newArrayList();
- static boolean validateCalled = false;
-
- @Override
- public void validate(String name, String value) throws ParameterException {
- validateCalled = true;
- }
-
- @Override
- public void validate(String name, String value, ParameterDescription pd)
- throws ParameterException {
- names.addAll(Arrays.asList(pd.getParameter().names()));
- }
- }
-
- public void validator2() {
- class Arg {
- @Parameter(names = { "-h", "--host" }, validateWith = V2.class)
- String host;
- }
- Arg a = new Arg();
- V2.names.clear();
- V2.validateCalled = false;
- JCommander jc = new JCommander(a, "--host", "h");
- jc.setAcceptUnknownOptions(true);
- Assert.assertEquals(V2.names, Arrays.asList(new String[] { "-h", "--host" }));
- Assert.assertTrue(V2.validateCalled);
- }
-
- public void usageCommandsUnderUsage() {
- class Arg {
- }
- @Parameters(commandDescription = "command a")
- class ArgCommandA {
- @Parameter(description = "command a parameters")
- List<String> parameters;
- }
- @Parameters(commandDescription = "command b")
- class ArgCommandB {
- @Parameter(description = "command b parameters")
- List<String> parameters;
- }
-
- Arg a = new Arg();
-
- JCommander c = new JCommander(a);
- c.addCommand("a", new ArgCommandA());
- c.addCommand("b", new ArgCommandB());
-
- StringBuilder sb = new StringBuilder();
- c.usage(sb);
- Assert.assertTrue(sb.toString().contains("[command options]\n Commands:"));
- }
-
- public void usageWithEmpytLine() {
- class Arg {
- }
- @Parameters(commandDescription = "command a")
- class ArgCommandA {
- @Parameter(description = "command a parameters")
- List<String> parameters;
- }
- @Parameters(commandDescription = "command b")
- class ArgCommandB {
- @Parameter(description = "command b parameters")
- List<String> parameters;
- }
-
- Arg a = new Arg();
-
- JCommander c = new JCommander(a);
- c.addCommand("a", new ArgCommandA());
- c.addCommand("b", new ArgCommandB());
-
- StringBuilder sb = new StringBuilder();
- c.usage(sb);
- Assert.assertTrue(sb.toString().contains("command a parameters\n\n b"));
- }
-
- public void partialValidation() {
- class Arg {
- @Parameter(names = { "-h", "--host" })
- String host;
- }
- Arg a = new Arg();
- JCommander jc = new JCommander();
- jc.setAcceptUnknownOptions(true);
- jc.addObject(a);
- jc.parse("-a", "foo", "-h", "host");
- Assert.assertEquals(a.host, "host");
- Assert.assertEquals(jc.getUnknownOptions(), Lists.newArrayList("-a", "foo"));
- }
-
- /**
- * GITHUB-137.
- */
- public void listArgShouldBeCleared() {
- class Args {
- @Parameter(description = "[endpoint]")
- public List<String> endpoint = Lists.newArrayList("prod");
- }
- Args a = new Args();
- new JCommander(a, new String[] { "dev" });
- Assert.assertEquals(a.endpoint, Lists.newArrayList("dev"));
- }
-
- public void dashDashParameter() {
- class Arguments {
- @Parameter(names = { "-name" })
- public String name;
- @Parameter
- public List<String> mainParameters;
- }
-
- Arguments a = new Arguments();
- new JCommander(a, new String[] {
- "-name", "theName", "--", "param1", "param2"}
- );
- Assert.assertEquals(a.name, "theName");
- Assert.assertEquals(a.mainParameters.size(), 2);
- Assert.assertEquals(a.mainParameters.get(0), "param1");
- Assert.assertEquals(a.mainParameters.get(1), "param2");
- }
-
- public void dashDashParameter2() {
- class Arguments {
- @Parameter(names = { "-name" })
- public String name;
+
+ @Test
+ public void testLongMainParameterDescription() {
+ //setup
+ JCommander jc = new JCommander(new ArgsLongMainParameterDescription());
+ StringBuilder sb = new StringBuilder();
+
+ //action
+ jc.usage(sb);
+
+ //verify
+ for (String line : sb.toString().split("\n")) {
+ Assert.assertTrue(line.length() <= jc.getColumnSize(), "line length < column size");
+ }
+ }
+
+ @Test
+ public void testLongCommandDescription() throws Exception {
+ //setup
+ JCommander jc = new JCommander();
+ jc.addCommand(new ArgsLongCommandDescription());
+ StringBuilder sb = new StringBuilder();
+
+ //action
+ jc.usage(sb);
+
+ //verify
+ for (String line : sb.toString().split("\n")) {
+ Assert.assertTrue(line.length() <= jc.getColumnSize(), "line length < column size");
+ }
+ }
+
+ @Test
+ public void testDescriptionWrappingLongWord() {
+ //setup
+ StringBuilder sb = new StringBuilder();
+ final JCommander jc = new JCommander(new ArgsLongDescription());
+
+ //action
+ jc.usage(sb);
+
+ //verify
+ for (String line : sb.toString().split("\n")) {
+ Assert.assertTrue(line.length() <= jc.getColumnSize(), "line length < column size");
+ }
+ }
+
+ public void simpleArgs() throws ParseException {
+ Args1 args = new Args1();
+ String[] argv = {"-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
+ "-date", "2011-10-26", "-groups", "unit", "a", "b", "c"};
+ new JCommander(args, argv);
+
+ Assert.assertTrue(args.debug);
+ Assert.assertEquals(args.verbose.intValue(), 2);
+ Assert.assertEquals(args.groups, "unit");
+ Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
+ Assert.assertEquals(args.floa, 1.2f, 0.1f);
+ Assert.assertEquals(args.doub, 1.3f, 0.1f);
+ Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
+ Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
+ }
+
+ @DataProvider
+ public Object[][] alternateNamesListArgs() {
+ return new Object[][]{
+ new String[][]{new String[]{"--servers", "1", "-s", "2", "--servers", "3"}},
+ new String[][]{new String[]{"-s", "1", "-s", "2", "--servers", "3"}},
+ new String[][]{new String[]{"--servers", "1", "--servers", "2", "-s", "3"}},
+ new String[][]{new String[]{"-s", "1", "--servers", "2", "-s", "3"}},
+ new String[][]{new String[]{"-s", "1", "-s", "2", "--servers", "3"}},
+ };
+ }
+
+ /**
+ * Confirm that List<?> parameters with alternate names return the correct
+ * List regardless of how the arguments are specified
+ */
+
+ @Test(dataProvider = "alternateNamesListArgs")
+ public void testAlternateNamesForListParameters(String[] argv) {
+ AlternateNamesForListArgs args = new AlternateNamesForListArgs();
+
+ new JCommander(args, argv);
+
+ Assert.assertEquals(args.serverNames.size(), 3);
+ Assert.assertEquals(args.serverNames.get(0), argv[1]);
+ Assert.assertEquals(args.serverNames.get(1), argv[3]);
+ Assert.assertEquals(args.serverNames.get(2), argv[5]);
+ }
+
+
+ /**
+ * Make sure that if there are args with multiple names (e.g. "-log" and "-verbose"),
+ * the usage will only display it once.
+ */
+ public void repeatedArgs() {
+ Args1 args = new Args1();
+ String[] argv = {"-log", "2"};
+ JCommander jc = new JCommander(args, argv);
+ Assert.assertEquals(jc.getParameters().size(), 8);
+ }
+
+ /**
+ * Not specifying a required option should throw an exception.
+ */
+ @Test(expectedExceptions = ParameterException.class)
+ public void requiredFields1Fail() {
+ Args1 args = new Args1();
+ String[] argv = {"-debug"};
+ new JCommander(args, argv);
+ }
+
+ /**
+ * Getting the description of a nonexistent command should throw an exception.
+ */
+ @Test(expectedExceptions = ParameterException.class)
+ public void nonexistentCommandShouldThrow() {
+ String[] argv = {};
+ JCommander jc = new JCommander(new Object(), argv);
+ jc.getCommandDescription("foo");
+ }
+
+ /**
+ * Required options with multiple names should work with all names.
+ */
+ private void multipleNames(String option) {
+ Args1 args = new Args1();
+ String[] argv = {option, "2"};
+ new JCommander(args, argv);
+ Assert.assertEquals(args.verbose.intValue(), 2);
+ }
+
+ public void multipleNames1() {
+ multipleNames("-log");
+ }
+
+ public void multipleNames2() {
+ multipleNames("-verbose");
+ }
+
+ private void i18n1(String bundleName, Locale locale, String expectedString) {
+ ResourceBundle bundle = locale != null ? ResourceBundle.getBundle(bundleName, locale)
+ : null;
+
+ ArgsI18N1 i18n = new ArgsI18N1();
+ String[] argv = {"-host", "localhost"};
+ JCommander jc = new JCommander(i18n, bundle, argv);
+
+ ParameterDescription pd = jc.getParameters().get(0);
+ Assert.assertEquals(pd.getDescription(), expectedString);
+ }
+
+ public void i18nNoLocale() {
+ i18n1("MessageBundle", null, "Host");
+ }
+
+ public void i18nUsLocale() {
+ i18n1("MessageBundle", new Locale("en", "US"), "Host");
+ }
+
+ public void i18nFrLocale() {
+ i18n1("MessageBundle", new Locale("fr", "FR"), "Hôte");
+ }
+
+ private void i18n2(Object i18n) {
+ String[] argv = {"-host", "localhost"};
+ Locale.setDefault(new Locale("fr", "FR"));
+ JCommander jc = new JCommander(i18n, argv);
+ ParameterDescription pd = jc.getParameters().get(0);
+ Assert.assertEquals(pd.getDescription(), "Hôte");
+ }
+
+ public void i18nWithResourceAnnotation() {
+ i18n2(new ArgsI18N2());
+ }
+
+ public void i18nWithResourceAnnotationNew() {
+ i18n2(new ArgsI18N2New());
+ }
+
+ public void i18MissingKeyForCommand() {
+ ResourceBundle bundle = ResourceBundle.getBundle("MessageBundle", new Locale("en", "US"));
+ JCommander jc = new JCommander(new ArgsHelp(), bundle);
+ jc.addCommand(new ArgsLongCommandDescription());
+ StringBuilder sb = new StringBuilder();
+ jc.usage(sb);
+ String usage = sb.toString();
+ Assert.assertTrue(usage.contains("text"));
+ }
+
+ public void noParseConstructor() {
+ JCommander jCommander = new JCommander(new ArgsMainParameter1());
+ jCommander.usage(new StringBuilder());
+ // Before fix, this parse would throw an exception, because it calls createDescription, which
+ // was already called by usage(), and can only be called once.
+ jCommander.parse();
+ }
+
+ /**
+ * Test a use case where there are required parameters, but you still want
+ * to interrogate the options which are specified.
+ */
+ public void usageWithRequiredArgsAndResourceBundle() {
+ ArgsHelp argsHelp = new ArgsHelp();
+ JCommander jc = new JCommander(new Object[]{argsHelp, new ArgsRequired()},
+ java.util.ResourceBundle.getBundle("MessageBundle"));
+ // Should be able to display usage without triggering validation
+ jc.usage(new StringBuilder());
+ try {
+ jc.parse("-h");
+ Assert.fail("Should have thrown a required parameter exception");
+ } catch (ParameterException e) {
+ Assert.assertTrue(e.getMessage().contains("are required"));
+ }
+ Assert.assertTrue(argsHelp.help);
+ }
+
+ public void multiObjects() {
+ ArgsMaster m = new ArgsMaster();
+ ArgsSlave s = new ArgsSlave();
+ String[] argv = {"-master", "master", "-slave", "slave"};
+ new JCommander(new Object[]{m, s}, argv);
+
+ Assert.assertEquals(m.master, "master");
+ Assert.assertEquals(s.slave, "slave");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void multiObjectsWithDuplicatesFail() {
+ ArgsMaster m = new ArgsMaster();
+ ArgsSlave s = new ArgsSlaveBogus();
+ String[] argv = {"-master", "master", "-slave", "slave"};
+ new JCommander(new Object[]{m, s}, argv);
+ }
+
+ public void arityString() {
+ ArgsArityString args = new ArgsArityString();
+ String[] argv = {"-pairs", "pair0", "pair1", "rest"};
+ new JCommander(args, argv);
+
+ Assert.assertEquals(args.pairs.size(), 2);
+ Assert.assertEquals(args.pairs.get(0), "pair0");
+ Assert.assertEquals(args.pairs.get(1), "pair1");
+ Assert.assertEquals(args.rest.size(), 1);
+ Assert.assertEquals(args.rest.get(0), "rest");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void arity2Fail() {
+ ArgsArityString args = new ArgsArityString();
+ String[] argv = {"-pairs", "pair0"};
+ new JCommander(args, argv);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void multipleUnparsedFail() {
+ ArgsMultipleUnparsed args = new ArgsMultipleUnparsed();
+ String[] argv = {};
+ new JCommander(args, argv);
+ }
+
+ public void privateArgs() {
+ ArgsPrivate args = new ArgsPrivate();
+ new JCommander(args, "-verbose", "3");
+ Assert.assertEquals(args.getVerbose().intValue(), 3);
+ }
+
+ @Test(
+ expectedExceptions = ParameterException.class,
+ expectedExceptionsMessageRegExp = "Cannot use final field .*#_foo as a parameter;"
+ + " compile-time constant inlining may hide new values written to it.")
+ public void finalArgs() {
+ Object args = new Object() {
+ @Parameter(names = "-foo")
+ final int _foo = 0;
+ };
+ new JCommander(args).usage();
+ }
+
+ public void converterArgs() {
+ ArgsConverter args = new ArgsConverter();
+ String fileName = "a";
+ new JCommander(args, "-file", "/tmp/" + fileName,
+ "-listStrings", "Tuesday,Thursday",
+ "-listInts", "-1,8",
+ "-listBigDecimals", "-11.52,100.12");
+ Assert.assertEquals(args.file.getName(), fileName);
+ Assert.assertEquals(args.listStrings.size(), 2);
+ Assert.assertEquals(args.listStrings.get(0), "Tuesday");
+ Assert.assertEquals(args.listStrings.get(1), "Thursday");
+ Assert.assertEquals(args.listInts.size(), 2);
+ Assert.assertEquals(args.listInts.get(0).intValue(), -1);
+ Assert.assertEquals(args.listInts.get(1).intValue(), 8);
+ Assert.assertEquals(args.listBigDecimals.size(), 2);
+ Assert.assertEquals(args.listBigDecimals.get(0), new BigDecimal("-11.52"));
+ Assert.assertEquals(args.listBigDecimals.get(1), new BigDecimal("100.12"));
+ }
+
+ public void hiddenConverter() {
+ class Args {
+ @Parameter(names = "--path", converter = HiddenConverter.class)
+ public String path;
+ }
+
+ new JCommander(new Args(), "--path", "/tmp/a");
+ }
+
+ public void hiddenArgs() {
+ new JCommander(new HiddenArgs(), "--input", "/tmp/a", "--output", "/tmp/b");
+ }
+
+ public void hiddenSplitter() {
+ class Args {
+ @Parameter(names = "--extensions", splitter = HiddenParameterSplitter.class)
+ public List<String> extensions;
+ }
+ if (HiddenParameterSplitter.class.getConstructors().length == 0) {
+ return; // Compiler has optimised away the private constructor
+ }
+
+ Args args = new Args();
+ new JCommander(args, "--extensions", ".txt;.md");
+ Assert.assertEquals(Arrays.asList(".txt", ".md"), args.extensions);
+ }
+
+ private void argsBoolean1(String[] params, Boolean expected) {
+ ArgsBooleanArity args = new ArgsBooleanArity();
+ new JCommander(args, params);
+ Assert.assertEquals(args.debug, expected);
+ }
+
+ private void argsBoolean0(String[] params, Boolean expected) {
+ ArgsBooleanArity0 args = new ArgsBooleanArity0();
+ new JCommander(args, params);
+ Assert.assertEquals(args.debug, expected);
+ }
+
+ public void booleanArity1() {
+ argsBoolean1(new String[]{}, Boolean.FALSE);
+ argsBoolean1(new String[]{"-debug", "true"}, Boolean.TRUE);
+ }
+
+ public void booleanArity0() {
+ argsBoolean0(new String[]{}, Boolean.FALSE);
+ argsBoolean0(new String[]{"-debug"}, Boolean.TRUE);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void badParameterShouldThrowParameter1Exception() {
+ Args1 args = new Args1();
+ String[] argv = {"-log", "foo"};
+ new JCommander(args, argv);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void badParameterShouldThrowParameter2Exception() {
+ Args1 args = new Args1();
+ String[] argv = {"-long", "foo"};
+ new JCommander(args, argv);
+ }
+
+ public void listParameters() {
+ Args2 a = new Args2();
+ String[] argv = {"-log", "2", "-groups", "unit", "a", "b", "c", "-host", "host2"};
+ new JCommander(a, argv);
+ Assert.assertEquals(a.verbose.intValue(), 2);
+ Assert.assertEquals(a.groups, "unit");
+ Assert.assertEquals(a.hosts, Arrays.asList("host2"));
+ Assert.assertEquals(a.parameters, Arrays.asList("a", "b", "c"));
+ }
+
+ public void separatorEqual() {
+ SeparatorEqual s = new SeparatorEqual();
+ String[] argv = {"-log=3", "--longoption=10"};
+ new JCommander(s, argv);
+ Assert.assertEquals(s.log.intValue(), 3);
+ Assert.assertEquals(s.longOption.intValue(), 10);
+ }
+
+ public void separatorColon() {
+ SeparatorColon s = new SeparatorColon();
+ String[] argv = {"-verbose:true"};
+ new JCommander(s, argv);
+ Assert.assertTrue(s.verbose);
+ }
+
+ public void separatorBoth() {
+ SeparatorColon s = new SeparatorColon();
+ SeparatorEqual s2 = new SeparatorEqual();
+ String[] argv = {"-verbose:true", "-log=3"};
+ new JCommander(new Object[]{s, s2}, argv);
+ Assert.assertTrue(s.verbose);
+ Assert.assertEquals(s2.log.intValue(), 3);
+ }
+
+ public void separatorMixed1() {
+ SeparatorMixed s = new SeparatorMixed();
+ String[] argv = {"-long:1", "-level=42"};
+ new JCommander(s, argv);
+ Assert.assertEquals(s.l.longValue(), 1l);
+ Assert.assertEquals(s.level.intValue(), 42);
+ }
+
+ public void slashParameters() {
+ SlashSeparator a = new SlashSeparator();
+ String[] argv = {"/verbose", "/file", "/tmp/a"};
+ new JCommander(a, argv);
+ Assert.assertTrue(a.verbose);
+ Assert.assertEquals(a.file, "/tmp/a");
+ }
+
+ public void inheritance() {
+ ArgsInherited args = new ArgsInherited();
+ String[] argv = {"-log", "3", "-child", "2"};
+ new JCommander(args, argv);
+ Assert.assertEquals(args.child.intValue(), 2);
+ Assert.assertEquals(args.log.intValue(), 3);
+ }
+
+ public void negativeNumber() {
+ Args1 a = new Args1();
+ String[] argv = {"-verbose", "-3"};
+ new JCommander(a, argv);
+ Assert.assertEquals(a.verbose.intValue(), -3);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void requiredMainParameters() {
+ ArgsRequired a = new ArgsRequired();
+ String[] argv = {};
+ new JCommander(a, argv);
+ }
+
+ public void usageShouldNotChange() {
+ JCommander jc = new JCommander(new Args1(), "-log", "1");
+ StringBuilder sb = new StringBuilder();
+ jc.usage(sb);
+ String expected = sb.toString();
+ jc = new JCommander(new Args1(), "-debug", "-log", "2", "-long", "5");
+ sb = new StringBuilder();
+ jc.usage(sb);
+ String actual = sb.toString();
+ Assert.assertEquals(actual, expected);
+ }
+
+ private void verifyCommandOrdering(String[] commandNames, Object[] commands) {
+ CommandMain cm = new CommandMain();
+ JCommander jc = new JCommander(cm);
+
+ for (int i = 0; i < commands.length; i++) {
+ jc.addCommand(commandNames[i], commands[i]);
+ }
+
+ Map<String, JCommander> c = jc.getCommands();
+ Assert.assertEquals(c.size(), commands.length);
+
+ Iterator<String> it = c.keySet().iterator();
+ for (int i = 0; i < commands.length; i++) {
+ Assert.assertEquals(it.next(), commandNames[i]);
+ }
+ }
+
+ public void commandsShouldBeShownInOrderOfInsertion() {
+ verifyCommandOrdering(new String[]{"add", "commit"},
+ new Object[]{new CommandAdd(), new CommandCommit()});
+ verifyCommandOrdering(new String[]{"commit", "add"},
+ new Object[]{new CommandCommit(), new CommandAdd()});
+ }
+
+ @DataProvider
+ public static Object[][] f() {
+ return new Integer[][]{
+ new Integer[]{3, 5, 1},
+ new Integer[]{3, 8, 1},
+ new Integer[]{3, 12, 2},
+ new Integer[]{8, 12, 2},
+ new Integer[]{9, 10, 1},
+ };
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void arity1Fail() {
+ final Arity1 arguments = new Arity1();
+ final JCommander jCommander = new JCommander(arguments);
+ final String[] commands = {
+ "-inspect"
+ };
+ jCommander.parse(commands);
+ }
+
+ public void arity1Success1() {
+ final Arity1 arguments = new Arity1();
+ final JCommander jCommander = new JCommander(arguments);
+ final String[] commands = {
+ "-inspect", "true"
+ };
+ jCommander.parse(commands);
+ Assert.assertTrue(arguments.inspect);
+ }
+
+ public void arity1Success2() {
+ final Arity1 arguments = new Arity1();
+ final JCommander jCommander = new JCommander(arguments);
+ final String[] commands = {
+ "-inspect", "false"
+ };
+ jCommander.parse(commands);
+ Assert.assertFalse(arguments.inspect);
+ }
+
+ @Parameters(commandDescription = "Help for the given commands.")
+ public static class Help {
+ public static final String NAME = "help";
+
+ @Parameter(description = "List of commands.")
+ public List<String> commands = new ArrayList<>();
+ }
+
+ @Test(expectedExceptions = ParameterException.class,
+ description = "Verify that the main parameter's type is checked to be a List")
+ public void wrongMainTypeShouldThrow() {
+ JCommander jc = new JCommander(new ArgsRequiredWrongMain());
+ jc.parse("f1", "f2");
+ }
+
+ @Test(description = "This used to run out of memory")
+ public void oom() {
+ JCommander jc = new JCommander(new ArgsOutOfMemory());
+ jc.usage(new StringBuilder());
+ }
+
+ @Test
+ public void getParametersShouldNotNpe() {
+ JCommander jc = new JCommander(new Args1());
+ List<ParameterDescription> parameters = jc.getParameters();
+ }
+
+ public void validationShouldWork1() {
+ ArgsValidate1 a = new ArgsValidate1();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", "2 ");
+ Assert.assertEquals(a.age, new Integer(2));
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void validationShouldWorkWithDefaultValues() {
+ ArgsValidate2 a = new ArgsValidate2();
+ new JCommander(a).usage();
+ }
+
+ @Test
+ public void multipleValidators() {
+ for (int i = 1; i < 100; i += 2) {
+ ArgsMultiValidate a = new ArgsMultiValidate();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", String.valueOf(i));
+ }
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void multipleValidatorsFails1() {
+ ArgsMultiValidate a = new ArgsMultiValidate();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", "131");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void multipleValidatorsFails2() {
+ ArgsMultiValidate a = new ArgsMultiValidate();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", "0");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void validationShouldWork2() {
+ ArgsValidate1 a = new ArgsValidate1();
+ JCommander jc = new JCommander(a);
+ jc.parse("-age", "-2 ");
+ }
+
+ @Test
+ public void validationShouldReceiveRightParameterName() {
+ ArgMultiNameValidator validator = new ArgMultiNameValidator();
+ JCommander jc = new JCommander(validator);
+ String paramName = "-name2";
+ jc.parse(paramName, "param1");
+ Assert.assertEquals(ArgMultiNameValidator.MultiNameValidator.parsedName, paramName);
+ }
+
+ public void atFileCanContainEmptyLines() throws IOException {
+ File f = File.createTempFile("JCommander", null);
+ f.deleteOnExit();
+ FileWriter fw = new FileWriter(f);
+ fw.write("-log\n");
+ fw.write("\n");
+ fw.write("2\n");
+ fw.close();
+ new JCommander(new Args1(), "@" + f.getAbsolutePath());
+ }
+
+ public void atFileWithInNonDefaultCharset() throws IOException {
+ final Charset utf32 = Charset.forName("UTF-32");
+ final File f = File.createTempFile("JCommander", null);
+ f.deleteOnExit();
+ try (OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(f), utf32)) {
+ fw.write("-log\n");
+ fw.write("2\n");
+ fw.write("-groups\n");
+ fw.write("\u9731\n");
+ }
+ final Args1 args1 = new Args1();
+ final JCommander jc = new JCommander(args1);
+ try {
+ jc.parse("@" + f.getAbsolutePath());
+ throw new IllegalStateException("Expected exception to be thrown");
+ } catch (ParameterException expected) {
+ Assert.assertTrue(expected.getMessage().startsWith("Could not read file"));
+ }
+ jc.setAtFileCharset(utf32);
+ jc.parse("@" + f.getAbsolutePath());
+ Assert.assertEquals("\u9731", args1.groups);
+ }
+
+ public void handleEqualSigns() {
+ ArgsEquals a = new ArgsEquals();
+ JCommander jc = new JCommander(a);
+ jc.parse("-args=a=b,b=c");
+ Assert.assertEquals(a.args, "a=b,b=c");
+ }
+
+ @SuppressWarnings("serial")
+ public void handleSets() {
+ ArgsWithSet a = new ArgsWithSet();
+ new JCommander(a, "-s", "3,1,2");
+ Assert.assertEquals(a.set, new TreeSet<Integer>() {{
+ add(1);
+ add(2);
+ add(3);
+ }});
+ }
+
+ private static final List<String> V = Arrays.asList("a", "b", "c", "d");
+
+ @DataProvider
+ public Object[][] variable() {
+ return new Object[][]{
+ //new Object[]{0, V.subList(0, 0), V},
+ new Object[]{1, V.subList(0, 1), V.subList(1, 4)},
+ new Object[]{2, V.subList(0, 2), V.subList(2, 4)},
+ new Object[]{3, V.subList(0, 3), V.subList(3, 4)},
+ new Object[]{4, V.subList(0, 4), V.subList(4, 4)},
+ };
+ }
+
+ @Test(dataProvider = "variable")
+ public void variableArity(int count, List<String> var, List<String> main) {
+ VariableArity va = new VariableArity(count);
+ new JCommander(va).parse("-variable", "a", "b", "c", "d");
+ Assert.assertEquals(var, va.var);
+ Assert.assertEquals(main, va.main);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void variableArityZeroNonBoolean() {
+ VariableArity va = new VariableArity(0);
+ new JCommander(va).parse("-variable", "a", "b", "c", "d");
+ }
+
+ public void enumArgs() {
+ ArgsEnum args = new ArgsEnum();
+ String[] argv = {"-choice", "ONE", "-choices", "ONE", "Two"};
+ JCommander jc = new JCommander(args, argv);
+
+ Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
+
+ List<ChoiceType> expected = Arrays.asList(ChoiceType.ONE, ChoiceType.Two);
+ Assert.assertEquals(expected, args.choices);
+
+ for (ParameterDescription param : jc.getParameters()) {
+ // order can vary depending on JDK version
+ if (param.getLongestName().equals("-choice")) {
+ Assert.assertEquals(param.getDescription(),
+ "Options: " + EnumSet.allOf((Class<? extends Enum>) ArgsEnum.ChoiceType.class));
+ return;
+ }
+ }
+ Assert.fail("Could not find -choice parameter.");
+ }
+
+ public void enumArgs2() {
+ // issue #266
+ ArgsEnum args = new ArgsEnum();
+ new JCommander(args, "-choices", "ONE,Two");
+ Assert.assertEquals(Arrays.asList(ChoiceType.ONE, ChoiceType.Two), args.choices);
+ }
+
+ public void enumArgsCaseInsensitive() {
+ ArgsEnum args = new ArgsEnum();
+ String[] argv = {"-choice", "one"};
+ JCommander jc = new JCommander(args, argv);
+
+ Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void enumArgsFail() {
+ ArgsEnum args = new ArgsEnum();
+ String[] argv = {"-choice", "A"};
+ new JCommander(args, argv);
+ }
+
+ @Test
+ public void testDefaultListConverterForString() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-groups", "a,b");
+ Assert.assertEquals(al.groups.get(0), "a");
+ Assert.assertEquals(al.groups.get(1), "b");
+ }
+
+ @Test
+ public void testDefaultListConverterForStandardType() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-ints", "41,42");
+ Assert.assertEquals(al.ints.get(0).intValue(), 41);
+ Assert.assertEquals(al.ints.get(1).intValue(), 42);
+ }
+
+ @Test
+ public void testDefaultListConverterWithCustomConverterAndSplitter() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-hp", "localhost:1000;example.com:1001");
+ Assert.assertEquals(al.hostPorts.get(0).host, "localhost");
+ Assert.assertEquals(al.hostPorts.get(0).port.intValue(), 1000);
+ Assert.assertEquals(al.hostPorts.get(1).host, "example.com");
+ Assert.assertEquals(al.hostPorts.get(1).port.intValue(), 1001);
+ }
+
+ @Test
+ public void testDefaultListConverterWithCustomConverterAndDefaultSplitter() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-hp2", "localhost:1000,example.com:1001");
+ Assert.assertEquals(al.hp2.get(1).host, "example.com");
+ Assert.assertEquals(al.hp2.get(1).port.intValue(), 1001);
+ }
+
+ @Test
+ public void testCustomListConverter() {
+ ArgsList al = new ArgsList();
+ JCommander j = new JCommander(al);
+ j.parse("-uppercase", "ab,cd");
+ Assert.assertEquals(al.uppercase.get(0), "AB");
+ Assert.assertEquals(al.uppercase.get(1), "CD");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void shouldThrowIfUnknownOption() {
+ class A {
+ @Parameter(names = "-long")
+ public long l;
+ }
+ A a = new A();
+ new JCommander(a).parse("-lon", "32");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void mainParameterShouldBeValidate() {
+ class V implements IParameterValidator {
+
+ @Override
+ public void validate(String name, String value) throws ParameterException {
+ Assert.assertEquals("a", value);
+ }
+ }
+
+ class A {
+ @Parameter(validateWith = V.class)
+ public List<String> m;
+ }
+
+ A a = new A();
+ new JCommander(a).parse("b");
+ }
+
+ @Parameters(commandNames = {"--configure"})
+ public static class ConfigureArgs {
+ }
+
+ public static class BaseArgs {
+ @Parameter(names = {"-h", "--help"}, description = "Show this help screen")
+ private boolean help = false;
+
+ @Parameter(names = {"--version", "-version"}, description = "Show the program version")
+ private boolean version;
+ }
+
+ public void commandsWithSamePrefixAsOptionsShouldWork() {
+ BaseArgs a = new BaseArgs();
+ ConfigureArgs conf = new ConfigureArgs();
+ JCommander jc = new JCommander(a);
+ jc.addCommand(conf);
+ jc.parse("--configure");
+ }
+
+ public void dynamicParameters() {
+ class Command {
+ @DynamicParameter(names = {"-P"}, description = "Additional command parameters")
+ private Map<String, String> params = Maps.newHashMap();
+ }
+ JCommander commander = new JCommander();
+ Command c = new Command();
+ commander.addCommand("command", c);
+ commander.parse("command", "-Pparam='name=value'");
+ Assert.assertEquals(c.params.get("param"), "'name=value'");
+ }
+
+ public void exeParser() {
+ class Params {
+ @Parameter(names = "-i")
+ private String inputFile;
+ }
+
+ String args[] = {"-i", ""};
+ Params p = new Params();
+ new JCommander(p, args);
+ }
+
+ public void multiVariableArityList() {
+ class Params {
+ @Parameter(names = "-paramA", description = "ParamA", variableArity = true)
+ private List<String> paramA = Lists.newArrayList();
+
+ @Parameter(names = "-paramB", description = "ParamB", variableArity = true)
+ private List<String> paramB = Lists.newArrayList();
+ }
+
+ {
+ String args[] = {"-paramA", "a1", "a2", "-paramB", "b1", "b2", "b3"};
+ Params p = new Params();
+ new JCommander(p, args).parse();
+ Assert.assertEquals(p.paramA, Arrays.asList("a1", "a2"));
+ Assert.assertEquals(p.paramB, Arrays.asList("b1", "b2", "b3"));
+ }
+
+ {
+ String args[] = {"-paramA", "a1", "a2", "-paramB", "b1", "-paramA", "a3"};
+ Params p = new Params();
+ new JCommander(p, args).parse();
+ Assert.assertEquals(p.paramA, Arrays.asList("a1", "a2", "a3"));
+ Assert.assertEquals(p.paramB, Arrays.asList("b1"));
+ }
+ }
+
+ @Test(enabled = false,
+ description = "Need to double check that the command description is i18n'ed in the usage")
+ public void commandKey() {
+ @Parameters(resourceBundle = "MessageBundle", commandDescriptionKey = "command")
+ class Args {
+ @Parameter(names = "-myoption", descriptionKey = "myoption")
+ private boolean option;
+ }
+ JCommander j = new JCommander();
+ Args a = new Args();
+ j.addCommand("comm", a);
+ j.usage();
+ }
+
+ @Test(expectedExceptions = ParameterException.class,
+ expectedExceptionsMessageRegExp = "Was passed main parameter '' but no main parameter was defined.*")
+ public void tmp() {
+ class A {
+ @Parameter(names = "-b")
+ public String b;
+ }
+ new JCommander(new A()).parse("");
+ }
+
+ @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = "\"--b\": couldn't convert \"ThisIsATest\" to an integer")
+ public void multipleParameterNames() {
+ class MultipleParameterNames {
+ @Parameter(names = {"-b", "--b"})
+ public Integer b;
+ }
+ new JCommander(new MultipleParameterNames()).parse("--b", "ThisIsATest");
+ }
+
+ public void unknownOptionWithDifferentPrefix() {
+ @Parameters
+ class SlashSeparator {
+
+ @Parameter(names = "/verbose")
+ public boolean verbose = false;
+
+ @Parameter(names = "/file")
+ public String file;
+ }
+ SlashSeparator ss = new SlashSeparator();
+ try {
+ new JCommander(ss).parse("/notAParam");
+ } catch (ParameterException ex) {
+ boolean result = ex.getMessage().contains("in your arg class");
+ Assert.assertTrue(result);
+ }
+ }
+
+ public void equalSeparator() {
+ @Parameters(separators = "=", commandDescription = "My command")
+ class MyClass {
+
+ @Parameter(names = {"-p", "--param"}, required = true, description = "param desc...")
+ private String param;
+ }
+ MyClass c = new MyClass();
+ String expected = "\"hello\"world";
+ new JCommander(c).parse("--param=" + expected);
+ Assert.assertEquals(expected, c.param);
+ }
+
+ public void simpleArgsSetter() throws ParseException {
+ Args1Setter args = new Args1Setter();
+ String[] argv = {"-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
+ "-date", "2011-10-26", "-groups", "unit", "a", "b", "c"};
+ new JCommander(args, argv);
+
+ Assert.assertTrue(args.debug);
+ Assert.assertEquals(args.verbose.intValue(), 2);
+ Assert.assertEquals(args.groups, "unit");
+ Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
+ Assert.assertEquals(args.floa, 1.2f, 0.1f);
+ Assert.assertEquals(args.doub, 1.3f, 0.1f);
+ Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
+ Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
+ }
+
+ public void verifyHelp() {
+ class Arg {
+ @Parameter(names = "--help", help = true)
+ public boolean help = false;
+
+ @Parameter(names = "file", required = true)
+ public String file;
+ }
+ Arg arg = new Arg();
+ String[] argv = {"--help"};
+ new JCommander(arg, argv);
+
+ Assert.assertTrue(arg.help);
+ }
+
+ public void helpTest() {
+ class Arg {
+ @Parameter(names = {"?", "-help", "--help"}, description = "Shows help", help = true)
+ private boolean help = false;
+ }
+ Arg arg = new Arg();
+ JCommander jc = new JCommander(arg);
+ jc.parse("-help");
+ }
+
+ @Test
+ public void doNotDisplayHelpDefaultValue() {
+ class Arg {
+ @Parameter(names = "--help", help = true)
+ public boolean help = false;
+ }
+ Arg arg = new Arg();
+ String[] argv = {"--help"};
+ JCommander jc = new JCommander(arg, argv);
+
+ StringBuilder sb = new StringBuilder();
+
+ jc.usage(sb);
+
+ Assert.assertFalse(sb.toString().contains("Default"));
+ }
+
+ @Test(enabled = false, description = "Should only be enable once multiple parameters are allowed")
+ public void duplicateParameterNames() {
+ class ArgBase {
+ @Parameter(names = {"-host"})
+ protected String host;
+ }
+
+ class Arg1 extends ArgBase {
+ }
+ Arg1 arg1 = new Arg1();
+
+ class Arg2 extends ArgBase {
+ }
+ Arg2 arg2 = new Arg2();
+
+ JCommander jc = new JCommander(new Object[]{arg1, arg2});
+ jc.parse("-host", "foo");
+ Assert.assertEquals(arg1.host, "foo");
+ Assert.assertEquals(arg2.host, "foo");
+ }
+
+ @Test(enabled = true, description = "Disable top-level @/ampersand file expansion")
+ public void disabledAtSignExpansionTest() {
+ class Params {
+ @Parameter(names = {"-username"})
+ protected String username;
+ }
+
+ Params params = new Params();
+
+ JCommander jc = new JCommander(params);
+ jc.setExpandAtSign(false);
+ jc.parse("-username", "@tzellman");
+ Assert.assertEquals(params.username, "@tzellman");
+ }
+
+ @Test(enabled = true, description = "Enable top-level @/ampersand file expansion, which should throw in this case",
+ expectedExceptions = ParameterException.class)
+ public void enabledAtSignExpansionTest() {
+ class Params {
+ @Parameter(names = {"-username"})
+ protected String username;
+ }
+
+ Params params = new Params();
+
+ JCommander jc = new JCommander(params);
+ jc.parse("-username", "@tzellman");
+ Assert.assertEquals(params.username, "@tzellman");
+ }
+
+ public void parameterWithOneDoubleQuote() {
+ @Parameters(separators = "=")
+ class Arg {
+ @Parameter(names = {"-p", "--param"})
+ private String param;
+ }
+ JCommander jc = new JCommander(new MyClass());
+ jc.parse("-p=\"");
+ }
+
+ public void emptyStringAsDefault() {
+ class Arg {
+ @Parameter(names = "-x")
+ String s = "";
+ }
+ Arg a = new Arg();
+ StringBuilder sb = new StringBuilder();
+ new JCommander(a).usage(sb);
+ Assert.assertTrue(sb.toString().contains("Default: <empty string>"));
+ }
+
+ @Test
+ public void emptyStringShouldBeConsideredAsParameter() {
+ class Arg {
+ @Parameter(description = "parameters")
+ List<String> params;
+ }
+
+ Arg a = new Arg();
+ String[] args = {""};
+
+ new JCommander(a).parse(args);
+ Assert.assertEquals(a.params.size(), 1);
+// Assert.assertEquals();
+ }
+
+ @Test
+ public void doubleQuotedStringShouldBeConsideredAsParameter() {
+ class Arg {
+ @Parameter(description = "parameters")
+ List<String> params;
+ }
+
+ Arg a = new Arg();
+ String[] args = {"\"\""};
+
+ new JCommander(a).parse(args);
+ Assert.assertEquals(a.params.size(), 1);
+// Assert.assertEquals();
+ }
+
+ public void spaces() {
+ class Arg {
+ @Parameter(names = "-rule", description = "rule")
+ private List<String> rules = new ArrayList<>();
+ }
+ Arg a = new Arg();
+ new JCommander(a, "-rule", "some test");
+ Assert.assertEquals(a.rules, Arrays.asList("some test"));
+ }
+
+ static class V2 implements IParameterValidator2 {
+ final static List<String> names = Lists.newArrayList();
+ static boolean validateCalled = false;
+
+ @Override
+ public void validate(String name, String value) throws ParameterException {
+ validateCalled = true;
+ }
+
+ @Override
+ public void validate(String name, String value, ParameterDescription pd)
+ throws ParameterException {
+ names.addAll(Arrays.asList(pd.getParameter().names()));
+ }
+ }
+
+ public void validator2() {
+ class Arg {
+ @Parameter(names = {"-h", "--host"}, validateWith = V2.class)
+ String host;
+ }
+ Arg a = new Arg();
+ V2.names.clear();
+ V2.validateCalled = false;
+ JCommander jc = new JCommander(a, "--host", "h");
+ jc.setAcceptUnknownOptions(true);
+ Assert.assertEquals(V2.names, Arrays.asList("-h", "--host"));
+ Assert.assertTrue(V2.validateCalled);
+ }
+
+ public void usageCommandsUnderUsage() {
+ class Arg {
+ }
+ @Parameters(commandDescription = "command a")
+ class ArgCommandA {
+ @Parameter(description = "command a parameters")
+ List<String> parameters;
+ }
+ @Parameters(commandDescription = "command b")
+ class ArgCommandB {
+ @Parameter(description = "command b parameters")
+ List<String> parameters;
+ }
+
+ Arg a = new Arg();
+
+ JCommander c = new JCommander(a);
+ c.addCommand("a", new ArgCommandA());
+ c.addCommand("b", new ArgCommandB());
+
+ StringBuilder sb = new StringBuilder();
+ c.usage(sb);
+ Assert.assertTrue(sb.toString().contains("[command options]\n Commands:"));
+ }
+
+ public void usageWithEmpytLine() {
+ class Arg {
+ }
+ @Parameters(commandDescription = "command a")
+ class ArgCommandA {
+ @Parameter(description = "command a parameters")
+ List<String> parameters;
+ }
+ @Parameters(commandDescription = "command b")
+ class ArgCommandB {
+ @Parameter(description = "command b parameters")
+ List<String> parameters;
+ }
+
+ Arg a = new Arg();
+
+ JCommander c = new JCommander(a);
+ c.addCommand("a", new ArgCommandA());
+ c.addCommand("b", new ArgCommandB());
+
+ StringBuilder sb = new StringBuilder();
+ c.usage(sb);
+ Assert.assertTrue(sb.toString().contains("command a parameters\n\n b"));
+ }
+
+ public void usageWithSubCommands() {
+ class Arg {
+ }
+ @Parameters(commandDescription = "command a")
+ class ArgCommandA {
+ @Parameter(description = "command a parameters")
+ List<String> parameters;
+ }
+ @Parameters(commandDescription = "command b")
+ class ArgCommandB {
+ @Parameter(description = "command b parameters")
+ List<String> parameters;
+ }
+
+ Arg a = new Arg();
+
+ JCommander c = new JCommander(a);
+ c.setColumnSize(100);
+ c.addCommand("a", new ArgCommandA());
+
+ // b is a sub-command of a
+ JCommander aCommand = c.getCommands().get("a");
+ aCommand.addCommand("b", new ArgCommandB());
+
+ StringBuilder sb = new StringBuilder();
+ c.usage(sb);
+ Assert.assertTrue(sb.toString().contains("command a parameters\n Commands:"));
+ Assert.assertTrue(sb.toString().contains("command b\n Usage:"));
+ }
+
+ public void partialValidation() {
+ class Arg {
+ @Parameter(names = {"-h", "--host"})
+ String host;
+ }
+ Arg a = new Arg();
+ JCommander jc = new JCommander();
+ jc.setAcceptUnknownOptions(true);
+ jc.addObject(a);
+ jc.parse("-a", "foo", "-h", "host");
+ Assert.assertEquals(a.host, "host");
+ Assert.assertEquals(jc.getUnknownOptions(), Lists.newArrayList("-a", "foo"));
+ }
+
+ /**
+ * GITHUB-137.
+ */
+ public void listArgShouldBeCleared() {
+ class Args {
+ @Parameter(description = "[endpoint]")
+ public List<String> endpoint = Lists.newArrayList("prod");
+ }
+ Args a = new Args();
+ new JCommander(a, "dev");
+ Assert.assertEquals(a.endpoint, Lists.newArrayList("dev"));
+ }
+
+ @Test
+ public void dashDashEmpty() {
+ class Parameters {
+ @Parameter
+ public List<String> mainParameters = new ArrayList<>();
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "--");
+ Assert.assertTrue(a.mainParameters.isEmpty());
+ }
+
+ @Test
+ public void dashDashDashDash() {
+ class Parameters {
+ @Parameter
+ public List<String> mainParameters = new ArrayList<>();
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "--", "--");
+ Assert.assertEquals(a.mainParameters.size(), 1);
+ Assert.assertEquals(a.mainParameters.get(0), "--");
+ }
+
+ public void dashDashParameter() {
+ class Parameters {
+ @Parameter(names = {"-name"})
+ public String name;
+ @Parameter
+ public List<String> mainParameters;
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "-name", "theName", "--", "param1", "param2");
+ Assert.assertEquals(a.name, "theName");
+ Assert.assertEquals(a.mainParameters.size(), 2);
+ Assert.assertEquals(a.mainParameters.get(0), "param1");
+ Assert.assertEquals(a.mainParameters.get(1), "param2");
+ }
+
+ public void dashDashParameter2() {
+ class Parameters {
+ @Parameter(names = {"-name"})
+ public String name;
+ @Parameter
+ public List<String> mainParameters;
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "param1", "param2", "--", "param3", "-name", "theName");
+ Assert.assertNull(a.name);
+ Assert.assertEquals(a.mainParameters.size(), 5);
+ Assert.assertEquals(a.mainParameters.get(0), "param1");
+ Assert.assertEquals(a.mainParameters.get(1), "param2");
+ Assert.assertEquals(a.mainParameters.get(2), "param3");
+ Assert.assertEquals(a.mainParameters.get(3), "-name");
+ Assert.assertEquals(a.mainParameters.get(4), "theName");
+ }
+
+ public void access() {
+ class Parameters {
+ private int bar;
+
+ @Parameter(names = {"-bar", "-foo"})
+ private void setBar(int value) {
+ bar = value;
+ }
+
+ @Parameter(names = "-otherName")
+ private String otherName;
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "-bar", "1");
+ Assert.assertEquals(a.bar, 1);
+ }
+
+ public void noDash() {
+ class Parameters {
+ private int bar;
+
+ @Parameter(names = {"bar", "foo"})
+ private void setBar(int value) {
+ bar = value;
+ }
+
+ @Parameter(names = "otherName")
+ private String otherName;
+ }
+
+ Parameters a = new Parameters();
+ new JCommander(a, "bar", "1");
+ Assert.assertEquals(a.bar, 1);
+ }
+
+ public void commitTest() {
+ CommandCommit cc = new CommandCommit();
+ new JCommander(cc, "--author=cedric");
+ Assert.assertEquals(cc.author, "cedric");
+ }
+
+ static class CommandTemplate {
@Parameter
- public List<String> mainParameters;
- }
-
- Arguments a = new Arguments();
- new JCommander(a, new String[] {
- "param1", "param2", "--", "param3", "-name", "theName"}
- );
- Assert.assertNull(a.name);
- Assert.assertEquals(a.mainParameters.size(), 5);
- Assert.assertEquals(a.mainParameters.get(0), "param1");
- Assert.assertEquals(a.mainParameters.get(1), "param2");
- Assert.assertEquals(a.mainParameters.get(2), "param3");
- Assert.assertEquals(a.mainParameters.get(3), "-name");
- Assert.assertEquals(a.mainParameters.get(4), "theName");
- }
-
- @Test(enabled = false)
- public static void main(String[] args) throws Exception {
- new JCommanderTest().enumArgsFail();
-// class A {
-// @Parameter(names = "-short", required = true)
-// List<String> parameters;
-//
-// @Parameter(names = "-long", required = true)
-// public long l;
-// }
-// A a = new A();
-// new JCommander(a).parse();
-// System.out.println(a.l);
-// System.out.println(a.parameters);
-// ArgsList al = new ArgsList();
-// JCommander j = new JCommander(al);
-// j.setColumnSize(40);
-// j.usage();
-// new JCommanderTest().testListAndSplitters();
-// new JCommanderTest().converterArgs();
- }
-
- // Tests:
- // required unparsed parameter
+ private List<String> parameters = new ArrayList<>();
+
+ @Parameter(names = "help", help = true)
+ private boolean help;
+ }
+
+ public void noDashCommand() {
+ class P1 {
+ @Parameter(names = "hello")
+ private int test;
+ }
+ P1 p1 = new P1();
+ JCommander j = new JCommander();
+ j.addCommand("p1", p1);
+ j.parse("p1", "hello", "47");
+ Assert.assertEquals(p1.test, 47);
+ }
+
+ static class ValuesValidator implements IValueValidator<List<Integer>> {
+ @Override
+ public void validate(String name, List<Integer> values) throws ParameterException {
+ int previous = Integer.MIN_VALUE;
+ for (Integer i : values) {
+ if (i <= previous) {
+ throw new ParameterException("Invalid: values should be strictly increasing.");
+ }
+ previous = i;
+ }
+ }
+ }
+
+ @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = ".*strictly.*")
+ public void issue() {
+ class Parameters {
+ @Parameter(names = {"-v", "--values"},
+ required = true,
+ variableArity = true,
+ validateValueWith = ValuesValidator.class)
+ private List<Integer> values;
+ }
+
+ String[] commands = "-v 1 5 2".split("\\s+");
+ Parameters arg = new Parameters();
+ new JCommander(arg, commands);
+ }
+
+ static class MvParameters {
+ @SubParameter(order = 0)
+ String from;
+ @SubParameter(order = 1)
+ String to;
+ }
+
+ @Test
+ public void arity() {
+ class Parameters {
+ @Parameter(names = {"--mv"}, arity = 2)
+ private MvParameters mvParameters;
+ }
+
+ Parameters args = new Parameters();
+ JCommander.newBuilder()
+ .addObject(args)
+ .args(new String[]{"--mv", "from", "to"})
+ .build();
+
+ Assert.assertNotNull(args.mvParameters);
+ Assert.assertEquals(args.mvParameters.from, "from");
+ Assert.assertEquals(args.mvParameters.to, "to");
+ }
+
+ public void programName() {
+ JCommander jcommander = new JCommander();
+ String programName = "main";
+ jcommander.setProgramName(programName);
+ StringBuilder sb = new StringBuilder();
+ jcommander.usage(sb);
+
+ Assert.assertTrue(sb.toString().contains(programName));
+ Assert.assertEquals(jcommander.getProgramName(), programName);
+ }
+
+ public void dontShowOptionUsageIfThereAreNoOptions() {
+ class CommandTemplate {
+ @Parameter
+ List<String> parameters = new ArrayList<>();
+ }
+
+ CommandTemplate template = new CommandTemplate();
+ JCommander jcommander = new JCommander(template);
+ jcommander.setProgramName("main");
+ StringBuilder sb = new StringBuilder();
+ jcommander.usage(sb);
+ Assert.assertEquals(sb.toString().indexOf("options"), -1);
+ }
+
+ @Test
+ public void annotationsAndDynamicParameters() {
+ class DSimple {
+ @DynamicParameter(names = "-D", description = "Dynamic parameters go here")
+ public Map<String, String> params = Maps.newHashMap();
+
+ @DynamicParameter(names = "-A", assignment = "@")
+ public Map<String, String> params2 = Maps.newHashMap();
+ }
+
+ new JCommander(new DSimple()).usage(new StringBuilder());
+ }
+
+ @Test
+ public void twoCommandsSameOption() {
+ class GenerateOption {
+ @Parameter(names = {"--config"}, required = true, converter = FileConverter.class)
+ public File configFile;
+ }
+
+ class RegenerateOption {
+ @Parameter(names = {"--config"}, required = true, converter = FileConverter.class)
+ public File configFile;
+ }
+
+ GenerateOption generateOption = new GenerateOption();
+ RegenerateOption regenerateOption = new RegenerateOption();
+ JCommander.newBuilder()
+ .addCommand("--generate", generateOption)
+ .addCommand("--regenerate", regenerateOption)
+ .args(new String[]{"--generate", "--config", "foo.txt"})
+ .build();
+ Assert.assertEquals(generateOption.configFile.getName(), "foo.txt");
+ }
+
+ @Test
+ public void invertedBoolean() {
+ class Args {
+ @Parameter(names = {"--f"})
+ private boolean f = true;
+ }
+ Args args = new Args();
+ JCommander.newBuilder()
+ .addObject(args)
+ .args(new String[]{"--f"})
+ .build();
+ Assert.assertEquals(args.f, false);
+ }
+
+ @Test(enabled = false)
+ public static void main(String[] args) {
+
+ CommandTemplate template = new CommandTemplate();
+ JCommander jcommander = new JCommander(template);
+ jcommander.setProgramName("prog");
+ jcommander.parse("help");
+
+ if (template.help) {
+ jcommander.usage();
+ }
+ }
}
diff --git a/src/test/java/com/beust/jcommander/MethodSetterTest.java b/src/test/java/com/beust/jcommander/MethodSetterTest.java
index f995ad6..49bb42c 100644
--- a/src/test/java/com/beust/jcommander/MethodSetterTest.java
+++ b/src/test/java/com/beust/jcommander/MethodSetterTest.java
@@ -26,9 +26,6 @@ public class MethodSetterTest {
public void setRest(List<String> rest) {
this.rest = rest;
}
-// public List<String> getRest() {
-// return this.rest;
-// }
public List<String> rest;
}
ArgsArityStringSetter args = new ArgsArityStringSetter();
@@ -51,7 +48,7 @@ public class MethodSetterTest {
}
boolean passed = false;
try {
- new JCommander(new Arg(), new String[] { "--host", "host" });
+ new JCommander(new Arg(), "--host", "host");
} catch(ParameterException ex) {
Assert.assertEquals(ex.getCause(), null);
passed = true;
@@ -73,7 +70,7 @@ public class MethodSetterTest {
}
}
Arg arg = new Arg();
- new JCommander(arg, new String[] { "--port", "42" });
+ new JCommander(arg, "--port", "42");
Assert.assertEquals(arg.port, new Integer(42));
}
@@ -88,7 +85,7 @@ public class MethodSetterTest {
}
}
Arg arg = new Arg();
- JCommander jc = new JCommander(arg, new String[] { "--port", "42" });
+ JCommander jc = new JCommander(arg, "--port", "42");
ParameterDescription pd = jc.getParameters().get(0);
Assert.assertEquals(pd.getDefault(), 43);
}
diff --git a/src/test/java/com/beust/jcommander/ParametersDelegateTest.java b/src/test/java/com/beust/jcommander/ParametersDelegateTest.java
index 46c7c6a..1f9f9e0 100644
--- a/src/test/java/com/beust/jcommander/ParametersDelegateTest.java
+++ b/src/test/java/com/beust/jcommander/ParametersDelegateTest.java
@@ -143,7 +143,7 @@ public class ParametersDelegateTest {
public void mainParametersTest() {
class Delegate {
@Parameter
- public List<String> mainParams = new ArrayList<String>();
+ public List<String> mainParams = new ArrayList<>();
}
class Command {
@ParametersDelegate
@@ -200,11 +200,11 @@ public class ParametersDelegateTest {
public void duplicateMainParametersAreNotAllowed() {
class Delegate1 {
@Parameter
- public List<String> mainParams1 = new ArrayList<String>();
+ public List<String> mainParams1 = new ArrayList<>();
}
class Delegate2 {
@Parameter
- public List<String> mainParams2 = new ArrayList<String>();
+ public List<String> mainParams2 = new ArrayList<>();
}
class Command {
@ParametersDelegate
diff --git a/src/test/java/com/beust/jcommander/ParametersNotEmptyTest.java b/src/test/java/com/beust/jcommander/ParametersNotEmptyTest.java
new file mode 100644
index 0000000..7ebfc24
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/ParametersNotEmptyTest.java
@@ -0,0 +1,37 @@
+package com.beust.jcommander;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+@Test
+public class ParametersNotEmptyTest {
+
+ public class Args1 {
+ @Parameter(names = "-debug", description = "Debug mode")
+ public boolean debug = false;
+
+ @Parameter(names = "-date", description = "An ISO 8601 formatted date.")
+ public Date date;
+ }
+
+ @Test
+ public void testParameters() throws Exception {
+ JCommander jc = new JCommander(new Args1());
+ List<String> parameters = new ArrayList<>();
+ for (ParameterDescription pd : jc.getParameters()) {
+ parameters.add(pd.getNames());
+ }
+ Collections.sort(parameters);
+
+ Assert.assertEquals(parameters, new ArrayList<String>() {{
+ add("-date");
+ add("-debug");
+ }}
+ );
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/PasswordTest.java b/src/test/java/com/beust/jcommander/PasswordTest.java
new file mode 100644
index 0000000..ff1792e
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/PasswordTest.java
@@ -0,0 +1,111 @@
+package com.beust.jcommander;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class PasswordTest {
+
+ @DataProvider(name = "args")
+ public Object[][] createArgs() {
+ return new Object[][] {
+ { new PasswordTestingArgs() },
+ { new OptionalPasswordTestingArgs() },
+ };
+ }
+
+ public interface Args {
+
+ String getPassword();
+
+ int getPort();
+
+ }
+
+ public static class PasswordTestingArgs implements PasswordTest.Args {
+ @Parameter(names = {"--password", "-p"}, description = "Private key password",
+ password = true, required = true)
+ public String password;
+
+ @Parameter(names = {"--port", "-o"}, description = "Port to bind server to",
+ required = true)
+ public int port;
+
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+ }
+
+ @Test(dataProvider = "args")
+ public void passwordNotAsked(Args a) {
+ String expectedPassword = "somepassword";
+ int expectedPort = 7;
+ new JCommander(a, "--password", expectedPassword, "--port", String.valueOf(7));
+ Assert.assertEquals(a.getPort(), expectedPort);
+ Assert.assertEquals(a.getPassword(), expectedPassword);
+ }
+
+ @Test(dataProvider = "args", expectedExceptions = ParameterException.class)
+ public void passwordWithExcessiveArity(Args a) {
+ new JCommander(a, "--password", "somepassword", "someotherarg", "--port", String.valueOf(7));
+ }
+
+ @Test(dataProvider = "args")
+ public void passwordAsked(Args a) {
+ InputStream stdin = System.in;
+ String password = "password";
+ int port = 7;
+ try {
+ System.setIn(new ByteArrayInputStream(password.getBytes()));
+ new JCommander(a, "--port", String.valueOf(port), "--password");
+ Assert.assertEquals(a.getPort(), port);
+ Assert.assertEquals(a.getPassword(), password);
+ } finally {
+ System.setIn(stdin);
+ }
+ }
+
+ public static class OptionalPasswordTestingArgs implements PasswordTest.Args {
+ @Parameter(names = {"--password", "-p"}, description = "Private key password",
+ password = true)
+ public String password;
+
+ @Parameter(names = {"--port", "-o"}, description = "Port to bind server to",
+ required = true)
+ public int port;
+
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+ }
+
+ @Test
+ public void passwordOptionalNotProvided() {
+ Args a = new OptionalPasswordTestingArgs();
+ new JCommander(a, "--port", "7");
+ Assert.assertEquals(a.getPort(), 7);
+ Assert.assertEquals(a.getPassword(), null);
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void passwordRequredNotProvided() {
+ Args a = new PasswordTestingArgs();
+ new JCommander(a, "--port", "7");
+ }
+
+}
diff --git a/src/test/java/com/beust/jcommander/PositiveIntegerTest.java b/src/test/java/com/beust/jcommander/PositiveIntegerTest.java
index ec7d273..81d9b37 100644
--- a/src/test/java/com/beust/jcommander/PositiveIntegerTest.java
+++ b/src/test/java/com/beust/jcommander/PositiveIntegerTest.java
@@ -14,7 +14,7 @@ public class PositiveIntegerTest {
}
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-p", "8080" });
+ jc.parse("-p", "8080");
}
@@ -26,7 +26,7 @@ public class PositiveIntegerTest {
}
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-p", "" });
+ jc.parse("-p", "");
}
@Test(expectedExceptions = ParameterException.class)
@@ -37,7 +37,7 @@ public class PositiveIntegerTest {
}
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-p", "-1" });
+ jc.parse("-p", "-1");
}
@Test(expectedExceptions = ParameterException.class)
@@ -48,7 +48,7 @@ public class PositiveIntegerTest {
}
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "-p", "abc" });
+ jc.parse("-p", "abc");
}
@Test(expectedExceptions = ParameterException.class)
@@ -60,6 +60,6 @@ public class PositiveIntegerTest {
Arg arg = new Arg();
JCommander jc = new JCommander(arg);
- jc.parse(new String[] { "--port", " " });
+ jc.parse("--port", " ");
}
} \ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/SetConverter.java b/src/test/java/com/beust/jcommander/SetConverter.java
index c19df11..580fd98 100644
--- a/src/test/java/com/beust/jcommander/SetConverter.java
+++ b/src/test/java/com/beust/jcommander/SetConverter.java
@@ -6,7 +6,7 @@ import java.util.TreeSet;
public class SetConverter implements IStringConverter<SortedSet<Integer>> {
public SortedSet<Integer> convert(String value) {
- SortedSet<Integer> set = new TreeSet<Integer>();
+ SortedSet<Integer> set = new TreeSet<>();
String[] values = value.split(",");
for (String num : values) {
set.add(Integer.parseInt(num));
diff --git a/src/test/java/com/beust/jcommander/SimpleExample.java b/src/test/java/com/beust/jcommander/SimpleExample.java
new file mode 100644
index 0000000..b8000ca
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/SimpleExample.java
@@ -0,0 +1,50 @@
+package com.beust.jcommander;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.testng.Assert.assertEquals;
+
+class Main {
+ static PrintWriter out;
+ @Parameter(names={"--length", "-l"})
+ int length;
+ @Parameter(names={"--pattern", "-p"})
+ int pattern;
+
+ public static void main(String ... args) {
+ Main main = new Main();
+ new JCommander(main, args);
+ main.run();
+ }
+
+ public void run() {
+ out.printf("%d %d", length, pattern);
+ }
+}
+
+public class SimpleExample {
+ StringWriter out;
+
+ @BeforeMethod
+ public void setupMain(){
+ out=new StringWriter();
+ Main.out=new PrintWriter(out);
+ }
+
+ @Test
+ public void testLongArgs() {
+ Main.main("--length", "512", "--pattern", "2");
+ assertEquals("512 2", out.toString());
+ }
+
+ @Test
+ public void testShortArgs() {
+ Main.main("-l", "256", "-p", "171");
+ assertEquals("256 171", out.toString());
+ }
+
+}
diff --git a/src/test/java/com/beust/jcommander/TypeHierarchyTest.java b/src/test/java/com/beust/jcommander/TypeHierarchyTest.java
new file mode 100644
index 0000000..1ae3771
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/TypeHierarchyTest.java
@@ -0,0 +1,111 @@
+package com.beust.jcommander;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * <p>Test that parent classes and interfaces are used correctly</p>
+ */
+public class TypeHierarchyTest {
+
+ public interface Marker {
+ @Parameter(names = {"--available"})
+ void setAvailable(boolean available);
+ }
+
+ public class Base implements Marker {
+ boolean available = false;
+
+ public boolean isAvailable() {
+ return available;
+ }
+
+ @Override
+ public void setAvailable(boolean available) {
+ this.available = true;
+ }
+ }
+
+ public interface IMiddle {
+ @Parameter(names = {"--count"})
+ void setCount(int count);
+ }
+
+ public interface IMiddleMiddle extends IMiddle {
+ @Parameter(names = {"--again"})
+ void setCountAgain(int count);
+ }
+
+ public class Middle extends Base implements IMiddleMiddle {
+
+ private int count;
+ private int again;
+
+ public int getCount() {
+ return count;
+ }
+
+ @Override
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public int getCountAgain() {
+ return again;
+ }
+
+ @Override
+ public void setCountAgain(int again) {
+ this.again = again;
+ }
+ }
+
+ // trying to trip it up and get it to go from Child -> Composite -> Visitor
+ public interface Composite extends Visitor {
+ @Parameter(names = {"-n", "--name"})
+ void setName(String validate);
+ }
+
+ public interface Visitor {
+ @Parameter(names = {"--validate"})
+ void setValidate(boolean validate);
+ }
+
+ public class Child extends Middle implements Composite, Visitor{
+
+ private String name;
+ private boolean validate = false;
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isValidate() {
+ return validate;
+ }
+
+ @Override
+ public void setValidate(boolean validate) {
+ this.validate = validate;
+ }
+ }
+
+ @Test
+ public void testTypeHierarchy() {
+ final Child child = new Child();
+ JCommander commander = new JCommander(child);
+ commander.parse("--validate","-n","child-one","--count","14","--again","22","--available");
+
+ // test values through entire hierarchy
+ Assert.assertTrue(child.isValidate());
+ Assert.assertTrue(child.isAvailable());
+ Assert.assertEquals(14, child.getCount());
+ Assert.assertEquals(22, child.getCountAgain());
+ Assert.assertEquals("child-one", child.getName());
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java b/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java
index 6a3a98f..e237d73 100644
--- a/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java
+++ b/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java
@@ -10,9 +10,8 @@ public class ValidatePropertiesWhenParsingTest {
JCommander cmd = new JCommander();
cmd.addCommand("a", new A());
-// cmd.addCommand("b", new B());
- cmd.parse(new String[] { "a", "-path", "myPathToHappiness" });
+ cmd.parse("a", "-path", "myPathToHappiness");
}
public static class MyPathValidator implements IParameterValidator {
diff --git a/src/test/java/com/beust/jcommander/VariableArityTest.java b/src/test/java/com/beust/jcommander/VariableArityTest.java
index a90392f..c2d86b8 100644
--- a/src/test/java/com/beust/jcommander/VariableArityTest.java
+++ b/src/test/java/com/beust/jcommander/VariableArityTest.java
@@ -13,15 +13,15 @@ public class VariableArityTest {
@Parameter(names = { "-m", "--matrixData" }, variableArity = true,
description = "File containing a list of instances and their runtimes on various configurations", required = false)
- public List<String> modelMatrixFile = new LinkedList<String>();
+ public List<String> modelMatrixFile = new LinkedList<>();
@Parameter(names = { "-f", "--featureData" }, variableArity = true,
description = "File containing a list of instances and their corresponding features", required = true)
- public List<String> featureFile = new LinkedList<String>();
+ public List<String> featureFile = new LinkedList<>();
@Parameter(names = { "-c", "--configData" }, variableArity = true,
description = "File containing a list of configuration parameter values")
- public List<String> configFile = new LinkedList<String>();
+ public List<String> configFile = new LinkedList<>();
@Parameter(names = { "-o", "--outputFile" },
description = "File to output the resulting data to. Defaults to ./matrix-generation.zip", required = false)
@@ -52,10 +52,9 @@ public class VariableArityTest {
com.parse(split);
-// config.print();
Assert.assertNotEquals(config.seed, 0);
- Assert.assertEquals(config.modelMatrixFile, Arrays.asList(new String[] { "foo" }));
- Assert.assertEquals(config.featureFile, Arrays.asList(new String[] { "foo" }));
+ Assert.assertEquals(config.modelMatrixFile, Arrays.asList("foo"));
+ Assert.assertEquals(config.featureFile, Arrays.asList("foo"));
Assert.assertEquals(config.seed, 1024);
Assert.assertEquals(config.outputFile, "foo");
}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsEnum.java b/src/test/java/com/beust/jcommander/args/ArgsEnum.java
index bef663b..ba0628d 100644
--- a/src/test/java/com/beust/jcommander/args/ArgsEnum.java
+++ b/src/test/java/com/beust/jcommander/args/ArgsEnum.java
@@ -34,12 +34,12 @@ import com.beust.jcommander.Parameter;
*/
public class ArgsEnum {
- public enum ChoiceType { ONE, Two, THREE };
+ public enum ChoiceType { ONE, Two, THREE }
@Parameter(names = "-choice")
public ChoiceType choice = ChoiceType.ONE;
@Parameter(names = "-choices", variableArity = true)
- public List<ChoiceType> choices = new ArrayList<ChoiceType>();
+ public List<ChoiceType> choices = new ArrayList<>();
public static void main(String[] args1) {
ArgsEnum args = new ArgsEnum();
diff --git a/src/test/java/com/beust/jcommander/args/ArgsLongCommandDescription.java b/src/test/java/com/beust/jcommander/args/ArgsLongCommandDescription.java
new file mode 100644
index 0000000..398e514
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsLongCommandDescription.java
@@ -0,0 +1,12 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+@Parameters(commandNames = {"command"}, commandDescription = "text text text text text " +
+ "text text text text text text text text text text text text text text text " +
+ "really-really-really-long-word-or-url text text text text text text text.")
+public class ArgsLongCommandDescription {
+ @Parameter(names = {"-b"}, description = "boolean parameter")
+ public boolean var;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsLongMainParameterDescription.java b/src/test/java/com/beust/jcommander/args/ArgsLongMainParameterDescription.java
new file mode 100644
index 0000000..18bb06c
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsLongMainParameterDescription.java
@@ -0,0 +1,17 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ArgsLongMainParameterDescription {
+
+ @Parameter(description = "[text] [text] text text text text text text text text " +
+ "text text text text text text text text " +
+ "really-really-really-long-word-or-url text text text text text text text.")
+ public List<String> main = new ArrayList<>();
+
+ @Parameter(names = {"-b"}, description = "boolean parameter")
+ public boolean var;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsMultiValidate.java b/src/test/java/com/beust/jcommander/args/ArgsMultiValidate.java
new file mode 100644
index 0000000..1a913ff
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsMultiValidate.java
@@ -0,0 +1,36 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.IParameterValidator;
+import com.beust.jcommander.IValueValidator;
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.ParameterException;
+import com.beust.jcommander.validators.PositiveInteger;
+
+public class ArgsMultiValidate {
+
+ public static class OddIntegerParameterValidator implements IParameterValidator {
+ @Override
+ public void validate(String name, String value) throws ParameterException {
+ if(Integer.parseInt(value) %2 != 1) throw new ParameterException("param "+name+"="+value+" is not odd");
+ }
+ }
+
+ public static class LowerThan100ValueValidator implements IValueValidator<Integer> {
+ @Override
+ public void validate(String name, Integer value) throws ParameterException {
+ if(value >= 100) throw new ParameterException("param "+name+"="+value+" is greater than 100");
+ }
+ }
+
+ public static class GreaterTha0ValueValidator implements IValueValidator<Integer> {
+ @Override
+ public void validate(String name, Integer value) throws ParameterException {
+ if(value <= 0) throw new ParameterException("param "+name+"="+value+" is lower than 1");
+ }
+ }
+
+ @Parameter(names = "-age",
+ validateWith = {PositiveInteger.class,OddIntegerParameterValidator.class},
+ validateValueWith={GreaterTha0ValueValidator.class,LowerThan100ValueValidator.class})
+ public int age=29;
+}
diff --git a/src/test/java/com/beust/jcommander/args/HiddenArgs.java b/src/test/java/com/beust/jcommander/args/HiddenArgs.java
new file mode 100644
index 0000000..1b89334
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/HiddenArgs.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class HiddenArgs {
+ public HiddenArgs() {
+ }
+
+ @Parameter(names = "--input")
+ private String input;
+
+ private String output;
+
+ @Parameter(names = "--output")
+ private void setOutput(String output) {
+ this.output = output;
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/args/SlashSeparator.java b/src/test/java/com/beust/jcommander/args/SlashSeparator.java
index 64d3930..06b38e5 100644
--- a/src/test/java/com/beust/jcommander/args/SlashSeparator.java
+++ b/src/test/java/com/beust/jcommander/args/SlashSeparator.java
@@ -21,7 +21,7 @@ package com.beust.jcommander.args;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
-@Parameters(optionPrefixes = "/")
+@Parameters
public class SlashSeparator {
@Parameter(names = "/verbose")
diff --git a/src/test/java/com/beust/jcommander/args/VariableArity.java b/src/test/java/com/beust/jcommander/args/VariableArity.java
index 21a861d..ec4130e 100644
--- a/src/test/java/com/beust/jcommander/args/VariableArity.java
+++ b/src/test/java/com/beust/jcommander/args/VariableArity.java
@@ -8,19 +8,19 @@ import java.util.List;
public class VariableArity implements IVariableArity {
- private int m_count;
+ private int count;
public VariableArity(int count) {
- m_count = count;
+ this.count = count;
}
@Parameter
- public List<String> main = new ArrayList<String>();
+ public List<String> main = new ArrayList<>();
@Parameter(names = "-variable", variableArity = true)
- public List<String> var = new ArrayList<String>();
+ public List<String> var = new ArrayList<>();
public int processVariableArity(String optionName, String[] options) {
- return m_count;
+ return count;
}
}
diff --git a/src/test/java/com/beust/jcommander/command/CommandNoParametersAnnotation.java b/src/test/java/com/beust/jcommander/command/CommandNoParametersAnnotation.java
new file mode 100644
index 0000000..8b5c577
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/command/CommandNoParametersAnnotation.java
@@ -0,0 +1,9 @@
+package com.beust.jcommander.command;
+
+import com.beust.jcommander.Parameter;
+import java.util.List;
+
+public class CommandNoParametersAnnotation {
+ @Parameter(description = "Patterns of files to be added")
+ public List<String> patterns;
+}
diff --git a/src/test/java/com/beust/jcommander/command/CommandTest.java b/src/test/java/com/beust/jcommander/command/CommandTest.java
index cf921bd..5cb7c34 100644
--- a/src/test/java/com/beust/jcommander/command/CommandTest.java
+++ b/src/test/java/com/beust/jcommander/command/CommandTest.java
@@ -18,12 +18,13 @@
package com.beust.jcommander.command;
+import com.beust.jcommander.ArgsValidate2;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
-
import org.testng.Assert;
import org.testng.annotations.Test;
+import java.io.File;
import java.util.Arrays;
public class CommandTest {
@@ -75,11 +76,6 @@ public class CommandTest {
jc.addCommand("commit", commit);
jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java");
-// jc.setProgramName("TestCommander");
-// jc.usage();
-// jc.usage("add");
-// jc.usage("commit");
-
Assert.assertTrue(cm.verbose);
Assert.assertEquals(jc.getParsedCommand(), "commit");
Assert.assertTrue(commit.amend);
@@ -109,6 +105,55 @@ public class CommandTest {
Assert.assertFalse(out.toString().contains("hidden Hidden command to add file contents to the index"));
}
+ @Test
+ public void noParametersAnnotationOnCommandTest() {
+ CommandMain cm = new CommandMain();
+ JCommander jc = new JCommander(cm);
+ CommandNoParametersAnnotation noParametersAnnotation = new CommandNoParametersAnnotation();
+ jc.addCommand("no-annotation", noParametersAnnotation);
+
+ jc.setProgramName("TestCommander");
+ StringBuilder out = new StringBuilder();
+ jc.usage(out);
+
+ Assert.assertTrue(out.toString().contains("no-annotation"));
+ }
+
+ @Test
+ public void noTrailingSpaceInUsageTest() {
+ CommandMain cm = new CommandMain();
+ JCommander jc = new JCommander(cm);
+ CommandAdd add = new CommandAdd();
+ jc.addCommand("add", add);
+ CommandCommit commit = new CommandCommit();
+ jc.addCommand("commit", commit);
+ jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java");
+ StringBuilder out = new StringBuilder();
+ jc.usage(out);
+ String firstLine = out.toString().split("\n")[0];
+ Assert.assertFalse(firstLine.endsWith(" "), "Usage should not have trailing spaces");
+ }
+
+ @Test(expectedExceptions = ParameterException.class)
+ public void validateSubCommand() throws Exception {
+ JCommander jc = new JCommander(new CommandMain());
+ final ArgsValidate2 sub = new ArgsValidate2();
+ sub.template = null;
+ jc.addCommand("sub", sub);
+ jc.parse("sub", "-template", "foo");
+ }
+
+ @Test
+ public void doNotValidateSubCommand() throws Exception {
+ JCommander jc = new JCommander(new CommandMain());
+ final ArgsValidate2 sub = new ArgsValidate2();
+ sub.template = null;
+ jc.addCommand("sub", sub);
+ jc.parseWithoutValidation("sub", "-template", "foo");
+ Assert.assertEquals(sub.template, new File("foo"));
+
+ }
+
public static void main(String[] args) {
new CommandTest().shouldComplainIfNoAnnotations();
}
diff --git a/src/test/java/com/beust/jcommander/converters/CharArrayConverterTest.java b/src/test/java/com/beust/jcommander/converters/CharArrayConverterTest.java
new file mode 100644
index 0000000..596c806
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/converters/CharArrayConverterTest.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import java.net.UnknownHostException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@Test
+public class CharArrayConverterTest {
+
+ private static final String FIXTURE = "Hello World";
+ private static final CharArrayConverter CONVERTER = new CharArrayConverter();
+
+ @Test
+ public void testString() throws UnknownHostException {
+ Assert.assertEquals(CONVERTER.convert(FIXTURE), FIXTURE.toCharArray());
+ }
+
+}
diff --git a/src/test/java/com/beust/jcommander/converters/InetAddressConverterTest.java b/src/test/java/com/beust/jcommander/converters/InetAddressConverterTest.java
new file mode 100644
index 0000000..d8cf8b4
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/converters/InetAddressConverterTest.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@Test
+public class InetAddressConverterTest {
+
+ private static final InetAddressConverter INET_ADDRESS_CONVERTER = new InetAddressConverter();
+ private static final InetAddress LOOPBACK_ADDRESS = InetAddress.getLoopbackAddress();
+
+ @Test
+ public void testLocalhost() throws UnknownHostException {
+ test("localhost");
+ }
+
+ @Test
+ public void testLocalhostAddress() throws UnknownHostException {
+ test("127.0.0.1");
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testGargabeInput() throws UnknownHostException {
+ test("!@#$%");
+ }
+
+ @Test
+ public void testEmptyInput() throws UnknownHostException {
+ test("");
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testBlankInput() throws UnknownHostException {
+ test(" ");
+ }
+
+ private void test(String string) throws UnknownHostException {
+ Assert.assertEquals(INET_ADDRESS_CONVERTER.convert(string), LOOPBACK_ADDRESS);
+
+ }
+}
diff --git a/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java b/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java
index 98327bd..73e3c9e 100644
--- a/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java
+++ b/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java
@@ -51,10 +51,5 @@ public class DynamicParameterTest {
public static void main(String[] args) {
DynamicParameterTest dpt = new DynamicParameterTest();
dpt.simpleWithSpaces();
-// dpt.nonMapShouldThrow();
-// dpt.wrongSeparatorShouldThrow();
-// dpt.differentAssignment();
-// dpt.arity0();
-// dpt.usage();
}
}
diff --git a/src/test/java/test/QuotedMainTest.java b/src/test/java/test/QuotedMainTest.java
index abb97c0..68126ea 100644
--- a/src/test/java/test/QuotedMainTest.java
+++ b/src/test/java/test/QuotedMainTest.java
@@ -11,7 +11,7 @@ import com.beust.jcommander.Parameter;
public class QuotedMainTest {
@Parameter
- List<String> args = new ArrayList<String>();
+ List<String> args = new ArrayList<>();
String quoted = "\" \"";