Merge remote-tracking branch 'goog/master' into nyc-dev

Bug: 27552463
Change-Id: Icaacbb9acf1454b39974d319d30023aea6fea5f6
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5812af2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+target
+.classpath
+.project
+.settings
+test-output
+src/test/java/com/beust/jcommander/ignore
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..76f2f8b
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# 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.
+#
+
+#
+# Build support for jcommander within the Android Open Source Project
+# See https://source.android.com/source/building.html for more information
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Host JAR builds have every single file because all the standard APIs are available.
+jcommander_all_src_files := $(call all-java-files-under, src/main)
+# Filter out PathConverter since android is missing java.nio.file APIs.
+jcommander_android_src_files := $(filter-out %/PathConverter.java,$(jcommander_all_src_files))
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(jcommander_all_src_files)
+LOCAL_MODULE := jcommander-host
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(jcommander_android_src_files)
+LOCAL_MODULE := jcommander-hostdex
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
+
+# TODO: also add the tests once we have testng working.
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..05bb809
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,139 @@
+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/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100755
index 0000000..d0c18cf
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,203 @@
+
+                                 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
new file mode 100644
index 0000000..0aecbf5
--- /dev/null
+++ b/README.markdown
@@ -0,0 +1,43 @@
+JCommander
+==========
+
+This is an annotation based parameter parsing framework for Java.
+
+Here is a quick example:
+
+```java
+public class JCommanderTest {
+    @Parameter
+    public List<String> parameters = Lists.newArrayList();
+ 
+    @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity")
+    public Integer verbose = 1;
+ 
+    @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+    public String groups;
+ 
+    @Parameter(names = "-debug", description = "Debug mode")
+    public boolean debug = false;
+
+    @DynamicParameter(names = "-D", description = "Dynamic parameters go here")
+    public Map<String, String> dynamicParams = new HashMap<String, String>();
+
+}
+```
+
+and how you use it:
+
+```java
+JCommanderTest jct = new JCommanderTest();
+String[] argv = { "-log", "2", "-groups", "unit1,unit2,unit3",
+                    "-debug", "-Doption=value", "a", "b", "c" };
+new JCommander(jct, argv);
+
+Assert.assertEquals(2, jct.verbose.intValue());
+Assert.assertEquals("unit1,unit2,unit3", jct.groups);
+Assert.assertEquals(true, jct.debug);
+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
diff --git a/README.version b/README.version
new file mode 100644
index 0000000..9de0411
--- /dev/null
+++ b/README.version
@@ -0,0 +1,4 @@
+URL: https://github.com/cbeust/jcommander
+Version: 1.48 (14fbe2bc5a2c402b456ed68578a5d5dc2c343fa2)
+BugComponent: 99142
+Owners: iam
diff --git a/build-with-maven b/build-with-maven
new file mode 100755
index 0000000..0e2de5a
--- /dev/null
+++ b/build-with-maven
@@ -0,0 +1,16 @@
+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/doc/index.html b/doc/index.html
new file mode 100644
index 0000000..da0368b
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,871 @@
+<!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: January 14th, 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;String&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>
+
+<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;String&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.
+
+<h2><a class="section" name="Parameter_validation">Parameter validation</a></h2>
+
+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.
+
+
+<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;String&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>
+
+<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;String&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;String, String&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>If you are using Maven, add the following dependency to your <tt>pom.xml</tt>:
+
+  <pre class="brush: xml">
+
+<dependency>
+  &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>
+
+</body>
+
+<script type="text/javascript" src="http://beust.com/toc.js"></script>
+<script type="text/javascript"> generateToc(); </script>
+
+</html>
diff --git a/license.txt b/license.txt
new file mode 100755
index 0000000..d0c18cf
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,203 @@
+
+                                 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/notice.md b/notice.md
new file mode 100644
index 0000000..64fc84a
--- /dev/null
+++ b/notice.md
@@ -0,0 +1,5 @@
+JCommander Copyright Notices 
+============================
+
+Copyright 2010 Cedric Beust <cedric@beust.com>
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1cedb33
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,283 @@
+<!--
+
+    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/release b/release
new file mode 100644
index 0000000..2a0163c
--- /dev/null
+++ b/release
@@ -0,0 +1,8 @@
+# UI : https://oss.sonatype.org/index.html
+# Wiki: https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide
+#
+# deploy without tagging: mvn deploy -DperformRelease
+# mvn release:clean javadoc:javadoc release:prepare release:perform
+
+mvn release:perform -P sign
+
diff --git a/src/main/java/com/beust/jcommander/DynamicParameter.java b/src/main/java/com/beust/jcommander/DynamicParameter.java
new file mode 100644
index 0000000..2159c1f
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/DynamicParameter.java
@@ -0,0 +1,50 @@
+package com.beust.jcommander;
+
+import static java.lang.annotation.ElementType.FIELD;
+
+import com.beust.jcommander.validators.NoValidator;
+import com.beust.jcommander.validators.NoValueValidator;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({ FIELD })
+public @interface DynamicParameter {
+  /**
+   * An array of allowed command line parameters (e.g. "-D", "--define", etc...).
+   */
+  String[] names() default {};
+
+  /**
+   * Whether this option is required.
+   */
+  boolean required() default false;
+
+  /**
+   * A description of this option.
+   */
+  String description() default "";
+
+  /**
+   * The key used to find the string in the message bundle.
+   */
+  String descriptionKey() default "";
+
+  /**
+   * If true, this parameter won't appear in the usage().
+   */
+  boolean hidden() default false;
+
+  /**
+   * The validation class to use.
+   */
+  Class<? extends IParameterValidator> validateWith() default NoValidator.class;
+
+  /**
+   * The character(s) used to assign the values.
+   */
+  String assignment() default "=";
+
+  Class<? extends IValueValidator> validateValueWith() default NoValueValidator.class;
+}
diff --git a/src/main/java/com/beust/jcommander/FuzzyMap.java b/src/main/java/com/beust/jcommander/FuzzyMap.java
new file mode 100644
index 0000000..5f3939b
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/FuzzyMap.java
@@ -0,0 +1,61 @@
+package com.beust.jcommander;
+
+import com.beust.jcommander.internal.Maps;
+
+import java.util.Map;
+
+/**
+ * Helper class to perform fuzzy key look ups: looking up case insensitive or
+ * abbreviated keys.
+ */
+public class FuzzyMap {
+  interface IKey {
+    String getName();
+  }
+
+  public static <V> V findInMap(Map<? extends IKey, V> map, IKey name,
+      boolean caseSensitive, boolean allowAbbreviations) {
+    if (allowAbbreviations) {
+      return findAbbreviatedValue(map, name, caseSensitive);
+    } else {
+      if (caseSensitive) {
+        return map.get(name);
+      } else {
+        for (IKey c : map.keySet()) {
+          if (c.getName().equalsIgnoreCase(name.getName())) {
+            return map.get(c);
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  private static <V> V findAbbreviatedValue(Map<? extends IKey, V> map, IKey name,
+      boolean caseSensitive) {
+    String string = name.getName();
+    Map<String, V> results = Maps.newHashMap();
+    for (IKey c : map.keySet()) {
+      String n = c.getName();
+      boolean match = (caseSensitive && n.startsWith(string))
+          || ((! caseSensitive) && n.toLowerCase().startsWith(string.toLowerCase()));
+      if (match) {
+        results.put(n, map.get(c));
+      }
+    }
+
+    V result;
+    if (results.size() > 1) {
+      throw new ParameterException("Ambiguous option: " + name
+          + " matches " + results.keySet());
+    } else if (results.size() == 1) {
+      result = results.values().iterator().next();
+    } else {
+      result = null;
+    }
+
+    return result;
+  }
+
+
+}
diff --git a/src/main/java/com/beust/jcommander/IDefaultProvider.java b/src/main/java/com/beust/jcommander/IDefaultProvider.java
new file mode 100644
index 0000000..0353928
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/IDefaultProvider.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+/**
+ * Allows the specification of default values.
+ * 
+ * @author cbeust
+ */
+public interface IDefaultProvider {
+
+  /**
+   * @param optionName The name of the option as specified in the names() attribute
+   * of the @Parameter option (e.g. "-file").
+   * 
+   * @return the default value for this option.
+   */
+  String getDefaultValueFor(String optionName);
+}
diff --git a/src/main/java/com/beust/jcommander/IParameterValidator.java b/src/main/java/com/beust/jcommander/IParameterValidator.java
new file mode 100644
index 0000000..19fee0d
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/IParameterValidator.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2011 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+/**
+ * The class used to validate parameters.
+ *
+ * @author Cedric Beust <cedric@beust.com>
+ */
+public interface IParameterValidator {
+
+  /**
+   * Validate the parameter.
+   *
+   * @param name The name of the parameter (e.g. "-host").
+   * @param value The value of the parameter that we need to validate
+   *
+   * @throws ParameterException Thrown if the value of the parameter is invalid.
+   */
+  void validate(String name, String value) throws ParameterException;
+
+}
diff --git a/src/main/java/com/beust/jcommander/IParameterValidator2.java b/src/main/java/com/beust/jcommander/IParameterValidator2.java
new file mode 100644
index 0000000..77e7dd3
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/IParameterValidator2.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) 2011 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+public interface IParameterValidator2 extends IParameterValidator {
+
+  /**
+   * Validate the parameter.
+   *
+   * @param name The name of the parameter (e.g. "-host").
+   * @param value The value of the parameter that we need to validate
+   * @param pd The description of this parameter
+   *
+   * @throws ParameterException Thrown if the value of the parameter is invalid.
+   */
+  void validate(String name, String value, ParameterDescription pd) throws ParameterException;
+
+}
diff --git a/src/main/java/com/beust/jcommander/IStringConverter.java b/src/main/java/com/beust/jcommander/IStringConverter.java
new file mode 100644
index 0000000..fb51a79
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/IStringConverter.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+/**
+ * An interface that converts strings to any arbitrary type.
+ * 
+ * If your class implements a constructor that takes a String, this
+ * constructor will be used to instantiate your converter and the
+ * parameter will receive the name of the option that's being parsed,
+ * which can be useful to issue a more useful error message if the
+ * conversion fails.
+ * 
+ * You can also extend BaseConverter to make your life easier.
+ * 
+ * @author cbeust
+ */
+public interface IStringConverter<T> {
+  /**
+   * @return an object of type <T> created from the parameter value.
+   */
+  T convert(String value);
+}
diff --git a/src/main/java/com/beust/jcommander/IStringConverterFactory.java b/src/main/java/com/beust/jcommander/IStringConverterFactory.java
new file mode 100644
index 0000000..0e53ca0
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/IStringConverterFactory.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+/**
+ * A factory for IStringConverter. This interface lets you specify your
+ * converters in one place instead of having them repeated all over
+ * your argument classes.
+ * 
+ * @author cbeust
+ */
+public interface IStringConverterFactory {
+  <T> Class<? extends IStringConverter<T>> getConverter(Class<T> forType);
+}
diff --git a/src/main/java/com/beust/jcommander/IValueValidator.java b/src/main/java/com/beust/jcommander/IValueValidator.java
new file mode 100644
index 0000000..feed25d
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/IValueValidator.java
@@ -0,0 +1,14 @@
+package com.beust.jcommander;
+
+public interface IValueValidator<T> {
+  /**
+   * Validate the parameter.
+   *
+   * @param name The name of the parameter (e.g. "-host").
+   * @param value The value of the parameter that we need to validate
+   *
+   * @throws ParameterException Thrown if the value of the parameter is invalid.
+   */
+  void validate(String name, T value) throws ParameterException;
+
+}
diff --git a/src/main/java/com/beust/jcommander/IVariableArity.java b/src/main/java/com/beust/jcommander/IVariableArity.java
new file mode 100644
index 0000000..e8a40ba
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/IVariableArity.java
@@ -0,0 +1,16 @@
+package com.beust.jcommander;
+
+/**
+ * Must be implemented by argument classes that contain at least one
+ * \@Parameter with "variableArity = true".
+ */
+public interface IVariableArity {
+
+  /**
+   * @param optionName the name of the option to process.
+   * @param options the entire list of options.
+   *
+   * @return how many options were processed.
+   */
+  int processVariableArity(String optionName, String[] options);
+}
diff --git a/src/main/java/com/beust/jcommander/JCommander.java b/src/main/java/com/beust/jcommander/JCommander.java
new file mode 100644
index 0000000..2e049a1
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/JCommander.java
@@ -0,0 +1,1599 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import com.beust.jcommander.FuzzyMap.IKey;
+import com.beust.jcommander.converters.IParameterSplitter;
+import com.beust.jcommander.converters.NoConverter;
+import com.beust.jcommander.converters.StringConverter;
+import com.beust.jcommander.internal.Console;
+import com.beust.jcommander.internal.DefaultConsole;
+import com.beust.jcommander.internal.DefaultConverterFactory;
+import com.beust.jcommander.internal.JDK6Console;
+import com.beust.jcommander.internal.Lists;
+import com.beust.jcommander.internal.Maps;
+import com.beust.jcommander.internal.Nullable;
+
+/**
+ * The main class for JCommander. It's responsible for parsing the object that contains
+ * all the annotated fields, parse the command line and assign the fields with the correct
+ * values and a few other helper methods, such as usage().
+ *
+ * The object(s) you pass in the constructor are expected to have one or more
+ * \@Parameter annotations on them. You can pass either a single object, an array of objects
+ * or an instance of Iterable. In the case of an array or Iterable, JCommander will collect
+ * the \@Parameter annotations from all the objects passed in parameter.
+ *
+ * @author Cedric Beust <cedric@beust.com>
+ */
+public class JCommander {
+  public static final String DEBUG_PROPERTY = "jcommander.debug";
+
+  /**
+   * A map to look up parameter description per option name.
+   */
+  private Map<IKey, ParameterDescription> m_descriptions;
+
+  /**
+   * The objects that contain fields annotated with @Parameter.
+   */
+  private List<Object> m_objects = Lists.newArrayList();
+
+  private boolean m_firstTimeMainParameter = true;
+
+  /**
+   * This field/method will contain whatever command line parameter is not an option.
+   * It is expected to be a List<String>.
+   */
+  private Parameterized m_mainParameter = null;
+
+  /**
+   * The object on which we found the main parameter field.
+   */
+  private Object m_mainParameterObject;
+
+  /**
+   * The annotation found on the main parameter field.
+   */
+  private Parameter m_mainParameterAnnotation;
+
+  private ParameterDescription m_mainParameterDescription;
+
+  /**
+   * A set of all the parameterizeds that are required. During the reflection phase,
+   * this field receives all the fields that are annotated with required=true
+   * and during the parsing phase, all the fields that are assigned a value
+   * are removed from it. At the end of the parsing phase, if it's not empty,
+   * then some required fields did not receive a value and an exception is
+   * thrown.
+   */
+  private Map<Parameterized, ParameterDescription> m_requiredFields = Maps.newHashMap();
+
+  /**
+   * A map of all the parameterized fields/methods.
+   */
+  private Map<Parameterized, ParameterDescription> m_fields = Maps.newHashMap();
+
+  private ResourceBundle m_bundle;
+
+  /**
+   * A default provider returns default values for the parameters.
+   */
+  private IDefaultProvider m_defaultProvider;
+
+  /**
+   * List of commands and their instance.
+   */
+  private Map<ProgramName, JCommander> m_commands = Maps.newLinkedHashMap();
+
+  /**
+   * Alias database for reverse lookup
+   */
+  private Map<IKey, ProgramName> aliasMap = Maps.newLinkedHashMap();
+
+  /**
+   * The name of the command after the parsing has run.
+   */
+  private String m_parsedCommand;
+
+  /**
+   * The name of command or alias as it was passed to the
+   * command line
+   */
+  private String m_parsedAlias;
+
+  private ProgramName m_programName;
+
+  private Comparator<? super ParameterDescription> m_parameterDescriptionComparator
+      = new Comparator<ParameterDescription>() {
+        @Override
+        public int compare(ParameterDescription p0, ParameterDescription p1) {
+          return p0.getLongestName().compareTo(p1.getLongestName());
+        }
+      };
+
+  private int m_columnSize = 79;
+
+  private boolean m_helpWasSpecified;
+
+  private List<String> m_unknownArgs = Lists.newArrayList();
+  private boolean m_acceptUnknownOptions = false;
+  private boolean m_allowParameterOverwriting = false;
+  
+  private static Console m_console;
+
+  /**
+   * The factories used to look up string converters.
+   */
+  private static LinkedList<IStringConverterFactory> CONVERTER_FACTORIES = Lists.newLinkedList();
+
+  static {
+    CONVERTER_FACTORIES.addFirst(new DefaultConverterFactory());
+  };
+
+  /**
+   * Creates a new un-configured JCommander object.
+   */
+  public JCommander() {
+  }
+
+  /**
+   * @param object The arg object expected to contain {@link Parameter} annotations.
+   */
+  public JCommander(Object object) {
+    addObject(object);
+    createDescriptions();
+  }
+
+  /**
+   * @param object The arg object expected to contain {@link Parameter} annotations.
+   * @param bundle The bundle to use for the descriptions. Can be null.
+   */
+  public JCommander(Object object, @Nullable ResourceBundle bundle) {
+    addObject(object);
+    setDescriptionsBundle(bundle);
+  }
+
+  /**
+   * @param object The arg object expected to contain {@link Parameter} annotations.
+   * @param bundle The bundle to use for the descriptions. Can be null.
+   * @param args The arguments to parse (optional).
+   */
+  public JCommander(Object object, ResourceBundle bundle, String... args) {
+    addObject(object);
+    setDescriptionsBundle(bundle);
+    parse(args);
+  }
+
+  /**
+   * @param object The arg object expected to contain {@link Parameter} annotations.
+   * @param args The arguments to parse (optional).
+   */
+  public JCommander(Object object, String... args) {
+    addObject(object);
+    parse(args);
+  }
+  
+  public static Console getConsole() {
+    if (m_console == null) {
+      try {
+        Method consoleMethod = System.class.getDeclaredMethod("console", new Class<?>[0]);
+        Object console = consoleMethod.invoke(null, new Object[0]);
+        m_console = new JDK6Console(console);
+      } catch (Throwable t) {
+        m_console = new DefaultConsole();
+      }
+    }
+    return m_console;
+  }
+
+  /**
+   * Adds the provided arg object to the set of objects that this commander
+   * will parse arguments into.
+   *
+   * @param object The arg object expected to contain {@link Parameter}
+   * annotations. If <code>object</code> is an array or is {@link Iterable},
+   * the child objects will be added instead.
+   */
+  // declared final since this is invoked from constructors
+  public final void addObject(Object object) {
+    if (object instanceof Iterable) {
+      // Iterable
+      for (Object o : (Iterable<?>) object) {
+        m_objects.add(o);
+      }
+    } else if (object.getClass().isArray()) {
+      // Array
+      for (Object o : (Object[]) object) {
+        m_objects.add(o);
+      }
+    } else {
+      // Single object
+      m_objects.add(object);
+    }
+  }
+
+  /**
+   * Sets the {@link ResourceBundle} to use for looking up descriptions.
+   * Set this to <code>null</code> to use description text directly.
+   */
+  // declared final since this is invoked from constructors
+  public final void setDescriptionsBundle(ResourceBundle bundle) {
+    m_bundle = bundle;
+  }
+
+  /**
+   * Parse and validate the command line parameters.
+   */
+  public void parse(String... args) {
+    parse(true /* validate */, args);
+  }
+
+  /**
+   * Parse the command line parameters without validating them.
+   */
+  public void parseWithoutValidation(String... args) {
+    parse(false /* no validation */, args);
+  }
+
+  private void parse(boolean validate, String... args) {
+    StringBuilder sb = new StringBuilder("Parsing \"");
+    sb.append(join(args).append("\"\n  with:").append(join(m_objects.toArray())));
+    p(sb.toString());
+
+    if (m_descriptions == null) createDescriptions();
+    initializeDefaultValues();
+    parseValues(expandArgs(args), validate);
+    if (validate) validateOptions();
+  }
+
+  private StringBuilder join(Object[] args) {
+    StringBuilder result = new StringBuilder();
+    for (int i = 0; i < args.length; i++) {
+      if (i > 0) result.append(" ");
+      result.append(args[i]);
+    }
+    return result;
+  }
+
+  private void initializeDefaultValues() {
+    if (m_defaultProvider != null) {
+      for (ParameterDescription pd : m_descriptions.values()) {
+        initializeDefaultValue(pd);
+      }
+
+      for (Map.Entry<ProgramName, JCommander> entry : m_commands.entrySet()) {
+        entry.getValue().initializeDefaultValues();
+      }
+    }
+  }
+
+  /**
+   * Make sure that all the required parameters have received a value.
+   */
+  private void validateOptions() {
+    // No validation if we found a help parameter
+    if (m_helpWasSpecified) {
+      return;
+    }
+
+    if (! m_requiredFields.isEmpty()) {
+      StringBuilder missingFields = new StringBuilder();
+      for (ParameterDescription pd : m_requiredFields.values()) {
+        missingFields.append(pd.getNames()).append(" ");
+      }
+      throw new ParameterException("The following "
+            + pluralize(m_requiredFields.size(), "option is required: ", "options are required: ")
+            + missingFields);
+    }
+
+    if (m_mainParameterDescription != null) {
+      if (m_mainParameterDescription.getParameter().required() &&
+          !m_mainParameterDescription.isAssigned()) {
+        throw new ParameterException("Main parameters are required (\""
+            + m_mainParameterDescription.getDescription() + "\")");
+      }
+    }
+  }
+
+  private static String pluralize(int quantity, String singular, String plural) {
+    return quantity == 1 ? singular : plural;
+  }
+
+  /**
+   * Expand the command line parameters to take @ parameters into account.
+   * When @ is encountered, the content of the file that follows is inserted
+   * in the command line.
+   *
+   * @param originalArgv the original command line parameters
+   * @return the new and enriched command line parameters
+   */
+  private String[] expandArgs(String[] originalArgv) {
+    List<String> vResult1 = Lists.newArrayList();
+
+    //
+    // Expand @
+    //
+    for (String arg : originalArgv) {
+
+      if (arg.startsWith("@")) {
+        String fileName = arg.substring(1);
+        vResult1.addAll(readFile(fileName));
+      }
+      else {
+        List<String> expanded = expandDynamicArg(arg);
+        vResult1.addAll(expanded);
+      }
+    }
+
+    // Expand separators
+    //
+    List<String> vResult2 = Lists.newArrayList();
+    for (int i = 0; i < vResult1.size(); i++) {
+      String arg = vResult1.get(i);
+      String[] v1 = vResult1.toArray(new String[0]);
+      if (isOption(v1, arg)) {
+        String sep = getSeparatorFor(v1, arg);
+        if (! " ".equals(sep)) {
+          String[] sp = arg.split("[" + sep + "]", 2);
+          for (String ssp : sp) {
+            vResult2.add(ssp);
+          }
+        } else {
+          vResult2.add(arg);
+        }
+      } else {
+        vResult2.add(arg);
+      }
+    }
+
+    return vResult2.toArray(new String[vResult2.size()]);
+  }
+
+  private List<String> expandDynamicArg(String arg) {
+    for (ParameterDescription pd : m_descriptions.values()) {
+      if (pd.isDynamicParameter()) {
+        for (String name : pd.getParameter().names()) {
+          if (arg.startsWith(name) && !arg.equals(name)) {
+            return Arrays.asList(name, arg.substring(name.length()));
+          }
+        }
+      }
+    }
+
+    return Arrays.asList(arg);
+  }
+
+  private boolean isOption(String[] args, String arg) {
+    String prefixes = getOptionPrefixes(args, arg);
+    return arg.length() > 0 && prefixes.indexOf(arg.charAt(0)) >= 0;
+  }
+
+  private ParameterDescription getPrefixDescriptionFor(String arg) {
+    for (Map.Entry<IKey, ParameterDescription> es : m_descriptions.entrySet()) {
+      if (arg.startsWith(es.getKey().getName())) return es.getValue();
+    }
+
+    return null;
+  }
+
+  /**
+   * If arg is an option, we can look it up directly, but if it's a value,
+   * we need to find the description for the option that precedes it.
+   */
+  private ParameterDescription getDescriptionFor(String[] args, String arg) {
+    ParameterDescription result = getPrefixDescriptionFor(arg);
+    if (result != null) return result;
+
+    for (String a : args) {
+      ParameterDescription pd = getPrefixDescriptionFor(arg);
+      if (pd != null) result = pd;
+      if (a.equals(arg)) return result;
+    }
+
+    throw new ParameterException("Unknown parameter: " + arg);
+  }
+
+  private String getSeparatorFor(String[] args, String arg) {
+    ParameterDescription pd = getDescriptionFor(args, arg);
+
+    // Could be null if only main parameters were passed
+    if (pd != null) {
+      Parameters p = pd.getObject().getClass().getAnnotation(Parameters.class);
+      if (p != null) return p.separators();
+    }
+
+    return " ";
+  }
+
+  private String getOptionPrefixes(String[] args, String arg) {
+    ParameterDescription pd = getDescriptionFor(args, arg);
+
+    // Could be null if only main parameters were passed
+    if (pd != null) {
+      Parameters p = pd.getObject().getClass()
+          .getAnnotation(Parameters.class);
+      if (p != null) return p.optionPrefixes();
+    }
+    String result = Parameters.DEFAULT_OPTION_PREFIXES;
+
+    // See if any of the objects contains a @Parameters(optionPrefixes)
+    StringBuilder sb = new StringBuilder();
+    for (Object o : m_objects) {
+      Parameters p = o.getClass().getAnnotation(Parameters.class);
+      if (p != null && !Parameters.DEFAULT_OPTION_PREFIXES.equals(p.optionPrefixes())) {
+        sb.append(p.optionPrefixes());
+      }
+    }
+
+    if (! Strings.isStringEmpty(sb.toString())) {
+      result = sb.toString();
+    }
+
+    return result;
+  }
+
+  /**
+   * Reads the file specified by filename and returns the file content as a string.
+   * End of lines are replaced by a space.
+   *
+   * @param fileName the command line filename
+   * @return the file content as a string.
+   */
+  private static List<String> readFile(String fileName) {
+    List<String> result = Lists.newArrayList();
+
+    try {
+      BufferedReader bufRead = new BufferedReader(new FileReader(fileName));
+
+      String line;
+
+      // Read through file one line at time. Print line # and line
+      while ((line = bufRead.readLine()) != null) {
+        // Allow empty lines and # comments in these at files
+        if (line.length() > 0 && ! line.trim().startsWith("#")) {
+            result.add(line);
+        }
+      }
+
+      bufRead.close();
+    }
+    catch (IOException e) {
+      throw new ParameterException("Could not read file " + fileName + ": " + e);
+    }
+
+    return result;
+  }
+
+  /**
+   * Remove spaces at both ends and handle double quotes.
+   */
+  private static String trim(String string) {
+    String result = string.trim();
+    if (result.startsWith("\"") && result.endsWith("\"") && result.length() > 1) {
+      result = result.substring(1, result.length() - 1);
+    }
+    return result;
+  }
+
+  /**
+   * Create the ParameterDescriptions for all the \@Parameter found.
+   */
+  private void createDescriptions() {
+    m_descriptions = Maps.newHashMap();
+
+    for (Object object : m_objects) {
+      addDescription(object);
+    }
+  }
+
+  private void addDescription(Object object) {
+    Class<?> cls = object.getClass();
+
+    List<Parameterized> parameterizeds = Parameterized.parseArg(object);
+    for (Parameterized parameterized : parameterizeds) {
+      WrappedParameter wp = parameterized.getWrappedParameter();
+      if (wp != null && wp.getParameter() != null) {
+        Parameter annotation = wp.getParameter();
+        //
+        // @Parameter
+        //
+        Parameter p = annotation;
+        if (p.names().length == 0) {
+          p("Found main parameter:" + parameterized);
+          if (m_mainParameter != null) {
+            throw new ParameterException("Only one @Parameter with no names attribute is"
+                + " allowed, found:" + m_mainParameter + " and " + parameterized);
+          }
+          m_mainParameter = parameterized;
+          m_mainParameterObject = object;
+          m_mainParameterAnnotation = p;
+          m_mainParameterDescription =
+              new ParameterDescription(object, p, parameterized, m_bundle, this);
+        } else {
+          ParameterDescription pd =
+              new ParameterDescription(object, p, parameterized, m_bundle, this);
+          for (String name : p.names()) {
+            if (m_descriptions.containsKey(new StringKey(name))) {
+              throw new ParameterException("Found the option " + name + " multiple times");
+            }
+            p("Adding description for " + name);
+            m_fields.put(parameterized, pd);
+            m_descriptions.put(new StringKey(name), pd);
+
+            if (p.required()) m_requiredFields.put(parameterized, pd);
+          }
+        }
+      } else if (parameterized.getDelegateAnnotation() != null) {
+        //
+        // @ParametersDelegate
+        //
+        Object delegateObject = parameterized.get(object);
+        if (delegateObject == null){
+          throw new ParameterException("Delegate field '" + parameterized.getName()
+              + "' cannot be null.");
+        }
+        addDescription(delegateObject);
+      } else if (wp != null && wp.getDynamicParameter() != null) {
+        //
+        // @DynamicParameter
+        //
+        DynamicParameter dp = wp.getDynamicParameter();
+        for (String name : dp.names()) {
+          if (m_descriptions.containsKey(name)) {
+            throw new ParameterException("Found the option " + name + " multiple times");
+          }
+          p("Adding description for " + name);
+          ParameterDescription pd =
+              new ParameterDescription(object, dp, parameterized, m_bundle, this);
+          m_fields.put(parameterized, pd);
+          m_descriptions.put(new StringKey(name), pd);
+    
+          if (dp.required()) m_requiredFields.put(parameterized, pd);
+        }
+      }
+    }
+
+//    while (!Object.class.equals(cls)) {
+//      for (Field f : cls.getDeclaredFields()) {
+//        p("Field:" + cls.getSimpleName() + "." + f.getName());
+//        f.setAccessible(true);
+//        Annotation annotation = f.getAnnotation(Parameter.class);
+//        Annotation delegateAnnotation = f.getAnnotation(ParametersDelegate.class);
+//        Annotation dynamicParameter = f.getAnnotation(DynamicParameter.class);
+//        if (annotation != null) {
+//          //
+//          // @Parameter
+//          //
+//          Parameter p = (Parameter) annotation;
+//          if (p.names().length == 0) {
+//            p("Found main parameter:" + f);
+//            if (m_mainParameterField != null) {
+//              throw new ParameterException("Only one @Parameter with no names attribute is"
+//                  + " allowed, found:" + m_mainParameterField + " and " + f);
+//            }
+//            m_mainParameterField = parameterized;
+//            m_mainParameterObject = object;
+//            m_mainParameterAnnotation = p;
+//            m_mainParameterDescription = new ParameterDescription(object, p, f, m_bundle, this);
+//          } else {
+//            for (String name : p.names()) {
+//              if (m_descriptions.containsKey(name)) {
+//                throw new ParameterException("Found the option " + name + " multiple times");
+//              }
+//              p("Adding description for " + name);
+//              ParameterDescription pd = new ParameterDescription(object, p, f, m_bundle, this);
+//              m_fields.put(f, pd);
+//              m_descriptions.put(name, pd);
+//
+//              if (p.required()) m_requiredFields.put(f, pd);
+//            }
+//          }
+//        } else if (delegateAnnotation != null) {
+//          //
+//          // @ParametersDelegate
+//          //
+//          try {
+//            Object delegateObject = f.get(object);
+//            if (delegateObject == null){
+//              throw new ParameterException("Delegate field '" + f.getName() + "' cannot be null.");
+//            }
+//            addDescription(delegateObject);
+//          } catch (IllegalAccessException e) {
+//          }
+//        } else if (dynamicParameter != null) {
+//          //
+//          // @DynamicParameter
+//          //
+//          DynamicParameter dp = (DynamicParameter) dynamicParameter;
+//          for (String name : dp.names()) {
+//            if (m_descriptions.containsKey(name)) {
+//              throw new ParameterException("Found the option " + name + " multiple times");
+//            }
+//            p("Adding description for " + name);
+//            ParameterDescription pd = new ParameterDescription(object, dp, f, m_bundle, this);
+//            m_fields.put(f, pd);
+//            m_descriptions.put(name, pd);
+//
+//            if (dp.required()) m_requiredFields.put(f, pd);
+//          }
+//        }
+//      }
+//      // Traverse the super class until we find Object.class
+//      cls = cls.getSuperclass();
+//    }
+  }
+
+  private void initializeDefaultValue(ParameterDescription pd) {
+    for (String optionName : pd.getParameter().names()) {
+      String def = m_defaultProvider.getDefaultValueFor(optionName);
+      if (def != null) {
+        p("Initializing " + optionName + " with default value:" + def);
+        pd.addValue(def, true /* default */);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Main method that parses the values and initializes the fields accordingly.
+   */
+  private void parseValues(String[] args, boolean validate) {
+    // This boolean becomes true if we encounter a command, which indicates we need
+    // to stop parsing (the parsing of the command will be done in a sub JCommander
+    // object)
+    boolean commandParsed = false;
+    int i = 0;
+    boolean isDashDash = false; // once we encounter --, everything goes into the main parameter
+    while (i < args.length && ! commandParsed) {
+      String arg = args[i];
+      String a = trim(arg);
+      args[i] = a;
+      p("Parsing arg: " + a);
+
+      JCommander jc = findCommandByAlias(arg);
+      int increment = 1;
+      if (! isDashDash && ! "--".equals(a) && isOption(args, a) && jc == null) {
+        //
+        // Option
+        //
+        ParameterDescription pd = findParameterDescription(a);
+
+        if (pd != null) {
+          if (pd.getParameter().password()) {
+            //
+            // Password option, use the Console to retrieve the password
+            //
+            char[] password = readPassword(pd.getDescription(), pd.getParameter().echoInput());
+            pd.addValue(new String(password));
+            m_requiredFields.remove(pd.getParameterized());
+          } else {
+            if (pd.getParameter().variableArity()) {
+              //
+              // Variable arity?
+              //
+              increment = processVariableArity(args, i, pd);
+            } else {
+              //
+              // Regular option
+              //
+              Class<?> fieldType = pd.getParameterized().getType();
+
+              // Boolean, set to true as soon as we see it, unless it specified
+              // an arity of 1, in which case we need to read the next value
+              if ((fieldType == boolean.class || fieldType == Boolean.class)
+                  && pd.getParameter().arity() == -1) {
+                pd.addValue("true");
+                m_requiredFields.remove(pd.getParameterized());
+              } else {
+                increment = processFixedArity(args, i, pd, fieldType);
+              }
+              // If it's a help option, remember for later
+              if (pd.isHelp()) {
+                m_helpWasSpecified = true;
+              }
+            }
+          }
+        } else {
+          if (m_acceptUnknownOptions) {
+            m_unknownArgs.add(arg);
+            i++;
+            while (i < args.length && ! isOption(args, args[i])) {
+              m_unknownArgs.add(args[i++]);
+            }
+            increment = 0;
+          } else {
+            throw new ParameterException("Unknown option: " + arg);
+          }
+        }
+      }
+      else {
+        //
+        // Main parameter
+        //
+        if (! Strings.isStringEmpty(arg)) {
+          if ("--".equals(arg)) {
+              isDashDash = true;
+              a = trim(args[++i]);
+          }
+          if (m_commands.isEmpty()) {
+            //
+            // Regular (non-command) parsing
+            //
+            List mp = getMainParameter(arg);
+            String value = a; // If there's a non-quoted version, prefer that one
+            Object convertedValue = value;
+
+            if (m_mainParameter.getGenericType() instanceof ParameterizedType) {
+              ParameterizedType p = (ParameterizedType) m_mainParameter.getGenericType();
+              Type cls = p.getActualTypeArguments()[0];
+              if (cls instanceof Class) {
+                convertedValue = convertValue(m_mainParameter, (Class) cls, value);
+              }
+            }
+
+            ParameterDescription.validateParameter(m_mainParameterDescription,
+                m_mainParameterAnnotation.validateWith(),
+                "Default", value);
+
+            m_mainParameterDescription.setAssigned(true);
+            mp.add(convertedValue);
+          }
+          else {
+            //
+            // Command parsing
+            //
+            if (jc == null && validate) {
+                throw new MissingCommandException("Expected a command, got " + arg);
+            } else if (jc != null){
+                m_parsedCommand = jc.m_programName.m_name;
+                m_parsedAlias = arg; //preserve the original form
+    
+                // Found a valid command, ask it to parse the remainder of the arguments.
+                // Setting the boolean commandParsed to true will force the current
+                // loop to end.
+                jc.parse(subArray(args, i + 1));
+                commandParsed = true;
+            }
+          }
+        }
+      }
+      i += increment;
+    }
+
+    // Mark the parameter descriptions held in m_fields as assigned
+    for (ParameterDescription parameterDescription : m_descriptions.values()) {
+      if (parameterDescription.isAssigned()) {
+        m_fields.get(parameterDescription.getParameterized()).setAssigned(true);
+      }
+    }
+
+  }
+
+  private class DefaultVariableArity implements IVariableArity {
+
+    @Override
+    public int processVariableArity(String optionName, String[] options) {
+        int i = 0;
+        while (i < options.length && !isOption(options, options[i])) {
+          i++;
+        }
+        return i;
+    }
+  }
+  private final IVariableArity DEFAULT_VARIABLE_ARITY = new DefaultVariableArity();
+
+  private int m_verbose = 0;
+
+  private boolean m_caseSensitiveOptions = true;
+  private boolean m_allowAbbreviatedOptions = false;
+
+  /**
+   * @return the number of options that were processed.
+   */
+  private int processVariableArity(String[] args, int index, ParameterDescription pd) {
+    Object arg = pd.getObject();
+    IVariableArity va;
+    if (! (arg instanceof IVariableArity)) {
+        va = DEFAULT_VARIABLE_ARITY;
+    } else {
+        va = (IVariableArity) arg;
+    }
+
+    List<String> currentArgs = Lists.newArrayList();
+    for (int j = index + 1; j < args.length; j++) {
+      currentArgs.add(args[j]);
+    }
+    int arity = va.processVariableArity(pd.getParameter().names()[0],
+        currentArgs.toArray(new String[0]));
+
+    int result = processFixedArity(args, index, pd, List.class, arity);
+    return result;
+  }
+
+  private int processFixedArity(String[] args, int index, ParameterDescription pd,
+      Class<?> fieldType) {
+    // Regular parameter, use the arity to tell use how many values
+    // we need to consume
+    int arity = pd.getParameter().arity();
+    int n = (arity != -1 ? arity : 1);
+
+    return processFixedArity(args, index, pd, fieldType, n);
+  }
+
+  private int processFixedArity(String[] args, int originalIndex, ParameterDescription pd,
+                                Class<?> fieldType, int arity) {
+    int index = originalIndex;
+    String arg = args[index];
+    // Special case for boolean parameters of arity 0
+    if (arity == 0 &&
+        (Boolean.class.isAssignableFrom(fieldType)
+            || boolean.class.isAssignableFrom(fieldType))) {
+      pd.addValue("true");
+      m_requiredFields.remove(pd.getParameterized());
+    } else if (index < args.length - 1) {
+      int offset = "--".equals(args[index + 1]) ? 1 : 0;
+
+      if (index + arity < args.length) {
+        for (int j = 1; j <= arity; j++) {
+          pd.addValue(trim(args[index + j + offset]));
+          m_requiredFields.remove(pd.getParameterized());
+        }
+        index += arity + offset;
+      } else {
+        throw new ParameterException("Expected " + arity + " values after " + arg);
+      }
+    } else {
+      throw new ParameterException("Expected a value after parameter " + arg);
+    }
+
+    return arity + 1;
+  }
+
+  /**
+   * Invoke Console.readPassword through reflection to avoid depending
+   * on Java 6.
+   */
+  private char[] readPassword(String description, boolean echoInput) {
+    getConsole().print(description + ": ");
+    return getConsole().readPassword(echoInput);
+  }
+
+  private String[] subArray(String[] args, int index) {
+    int l = args.length - index;
+    String[] result = new String[l];
+    System.arraycopy(args, index, result, 0, l);
+
+    return result;
+  }
+
+  /**
+   * @return the field that's meant to receive all the parameters that are not options.
+   *
+   * @param arg the arg that we're about to add (only passed here to output a meaningful
+   * error message).
+   */
+  private List<?> getMainParameter(String arg) {
+    if (m_mainParameter == null) {
+      throw new ParameterException(
+          "Was passed main parameter '" + arg + "' but no main parameter was defined");
+    }
+
+    List<?> result = (List<?>) m_mainParameter.get(m_mainParameterObject);
+    if (result == null) {
+      result = Lists.newArrayList();
+      if (! List.class.isAssignableFrom(m_mainParameter.getType())) {
+        throw new ParameterException("Main parameter field " + m_mainParameter
+            + " needs to be of type List, not " + m_mainParameter.getType());
+      }
+      m_mainParameter.set(m_mainParameterObject, result);
+    }
+    if (m_firstTimeMainParameter) {
+      result.clear();
+      m_firstTimeMainParameter = false;
+    }
+    return result;
+  }
+
+  public String getMainParameterDescription() {
+    if (m_descriptions == null) createDescriptions();
+    return m_mainParameterAnnotation != null ? m_mainParameterAnnotation.description()
+        : null;
+  }
+
+//  private int longestName(Collection<?> objects) {
+//    int result = 0;
+//    for (Object o : objects) {
+//      int l = o.toString().length();
+//      if (l > result) result = l;
+//    }
+//
+//    return result;
+//  }
+
+  /**
+   * Set the program name (used only in the usage).
+   */
+  public void setProgramName(String name) {
+    setProgramName(name, new String[0]);
+  }
+
+  /**
+   * Set the program name
+   *
+   * @param name    program name
+   * @param aliases aliases to the program name
+   */
+  public void setProgramName(String name, String... aliases) {
+    m_programName = new ProgramName(name, Arrays.asList(aliases));
+  }
+
+  /**
+   * Display the usage for this command.
+   */
+  public void usage(String commandName) {
+    StringBuilder sb = new StringBuilder();
+    usage(commandName, sb);
+    getConsole().println(sb.toString());
+  }
+
+  /**
+   * Store the help for the command in the passed string builder.
+   */
+  public void usage(String commandName, StringBuilder out) {
+    usage(commandName, out, "");
+  }
+
+  /**
+   * Store the help for the command in the passed string builder, indenting
+   * every line with "indent".
+   */
+  public void usage(String commandName, StringBuilder out, String indent) {
+    String description = getCommandDescription(commandName);
+    JCommander jc = findCommandByAlias(commandName);
+    if (description != null) {
+      out.append(indent).append(description);
+      out.append("\n");
+    }
+    jc.usage(out, indent);
+  }
+
+  /**
+   * @return the description of the command.
+   */
+  public String getCommandDescription(String commandName) {
+    JCommander jc = findCommandByAlias(commandName);
+    if (jc == null) {
+      throw new ParameterException("Asking description for unknown command: " + commandName);
+    }
+
+    Object arg = jc.getObjects().get(0);
+    Parameters p = arg.getClass().getAnnotation(Parameters.class);
+    ResourceBundle bundle = null;
+    String result = null;
+    if (p != null) {
+      result = p.commandDescription();
+      String bundleName = p.resourceBundle();
+      if (!"".equals(bundleName)) {
+        bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
+      } else {
+        bundle = m_bundle;
+      }
+
+      if (bundle != null) {
+        result = getI18nString(bundle, p.commandDescriptionKey(), p.commandDescription());
+      }
+    }
+
+    return result;
+  }
+
+  /**
+   * @return The internationalized version of the string if available, otherwise
+   * return def.
+   */
+  private String getI18nString(ResourceBundle bundle, String key, String def) {
+    String s = bundle != null ? bundle.getString(key) : null;
+    return s != null ? s : def;
+  }
+
+  /**
+   * Display the help on System.out.
+   */
+  public void usage() {
+    StringBuilder sb = new StringBuilder();
+    usage(sb);
+    getConsole().println(sb.toString());
+  }
+
+  /**
+   * Store the help in the passed string builder.
+   */
+  public void usage(StringBuilder out) {
+    usage(out, "");
+  }
+
+  public void usage(StringBuilder out, String indent) {
+    if (m_descriptions == null) createDescriptions();
+    boolean hasCommands = !m_commands.isEmpty();
+
+    //
+    // First line of the usage
+    //
+    String programName = m_programName != null ? m_programName.getDisplayName() : "<main class>";
+    out.append(indent).append("Usage: " + programName + " [options]");
+    if (hasCommands) out.append(indent).append(" [command] [command options]");
+    if (m_mainParameterDescription != null) {
+      out.append(" " + m_mainParameterDescription.getDescription());
+    }
+    out.append("\n");
+
+    //
+    // Align the descriptions at the "longestName" column
+    //
+    int longestName = 0;
+    List<ParameterDescription> sorted = Lists.newArrayList();
+    for (ParameterDescription pd : m_fields.values()) {
+      if (! pd.getParameter().hidden()) {
+        sorted.add(pd);
+        // + to have an extra space between the name and the description
+        int length = pd.getNames().length() + 2;
+        if (length > longestName) {
+          longestName = length;
+        }
+      }
+    }
+
+    //
+    // Sort the options
+    //
+    Collections.sort(sorted, getParameterDescriptionComparator());
+
+    //
+    // Display all the names and descriptions
+    //
+    int descriptionIndent = 6;
+    if (sorted.size() > 0) out.append(indent).append("  Options:\n");
+    for (ParameterDescription pd : sorted) {
+      WrappedParameter parameter = pd.getParameter();
+      out.append(indent).append("  "
+          + (parameter.required() ? "* " : "  ")
+          + pd.getNames()
+          + "\n"
+          + indent + s(descriptionIndent));
+      int indentCount = indent.length() + descriptionIndent;
+      wrapDescription(out, indentCount, pd.getDescription());
+      Object def = pd.getDefault();
+      if (pd.isDynamicParameter()) {
+        out.append("\n" + s(indentCount + 1))
+            .append("Syntax: " + parameter.names()[0]
+                + "key" + parameter.getAssignment()
+                + "value");
+      }
+      if (def != null) {
+        String displayedDef = Strings.isStringEmpty(def.toString())
+            ? "<empty string>"
+            : def.toString();
+        out.append("\n" + s(indentCount + 1))
+            .append("Default: " + (parameter.password()?"********" : displayedDef));
+      }
+      Class<?> type =  pd.getParameterized().getType();
+      if(type.isEnum()){
+          out.append("\n" + s(indentCount + 1))
+          .append("Possible Values: " + EnumSet.allOf((Class<? extends Enum>) type));
+      }
+      out.append("\n");
+    }
+
+    //
+    // If commands were specified, show them as well
+    //
+    if (hasCommands) {
+      out.append("  Commands:\n");
+      // The magic value 3 is the number of spaces between the name of the option
+      // and its description
+      for (Map.Entry<ProgramName, JCommander> commands : m_commands.entrySet()) {
+        Object arg = commands.getValue().getObjects().get(0);
+        Parameters p = arg.getClass().getAnnotation(Parameters.class);
+        if (!p.hidden()) {
+          ProgramName progName = commands.getKey();
+          String dispName = progName.getDisplayName();
+          out.append(indent).append("    " + dispName); // + s(spaceCount) + getCommandDescription(progName.name) + "\n");
+
+          // Options for this command
+          usage(progName.getName(), out, "      ");
+          out.append("\n");
+        }
+      }
+    }
+  }
+
+  private Comparator<? super ParameterDescription> getParameterDescriptionComparator() {
+    return m_parameterDescriptionComparator;
+  }
+
+  public void setParameterDescriptionComparator(Comparator<? super ParameterDescription> c) {
+    m_parameterDescriptionComparator = c;
+  }
+
+  public void setColumnSize(int columnSize) {
+    m_columnSize = columnSize;
+  }
+
+  public int getColumnSize() {
+    return m_columnSize;
+  }
+
+  private void wrapDescription(StringBuilder out, int indent, String description) {
+    int max = getColumnSize();
+    String[] words = description.split(" ");
+    int current = indent;
+    int i = 0;
+    while (i < words.length) {
+      String word = words[i];
+      if (word.length() > max || current + word.length() <= max) {
+        out.append(" ").append(word);
+        current += word.length() + 1;
+      } else {
+        out.append("\n").append(s(indent + 1)).append(word);
+        current = indent;
+      }
+      i++;
+    }
+  }
+
+  /**
+   * @return a Collection of all the \@Parameter annotations found on the
+   * target class. This can be used to display the usage() in a different
+   * format (e.g. HTML).
+   */
+  public List<ParameterDescription> getParameters() {
+    return new ArrayList<ParameterDescription>(m_fields.values());
+  }
+
+  /**
+   * @return the main parameter description or null if none is defined.
+   */
+  public ParameterDescription getMainParameter() {
+    return m_mainParameterDescription;
+  }
+
+  private void p(String string) {
+    if (m_verbose > 0 || System.getProperty(JCommander.DEBUG_PROPERTY) != null) {
+      getConsole().println("[JCommander] " + string);
+    }
+  }
+
+  /**
+   * Define the default provider for this instance.
+   */
+  public void setDefaultProvider(IDefaultProvider defaultProvider) {
+    m_defaultProvider = defaultProvider;
+
+    for (Map.Entry<ProgramName, JCommander> entry : m_commands.entrySet()) {
+      entry.getValue().setDefaultProvider(defaultProvider);
+    }
+  }
+
+  public void addConverterFactory(IStringConverterFactory converterFactory) {
+    CONVERTER_FACTORIES.addFirst(converterFactory);
+  }
+
+  public <T> Class<? extends IStringConverter<T>> findConverter(Class<T> cls) {
+    for (IStringConverterFactory f : CONVERTER_FACTORIES) {
+      Class<? extends IStringConverter<T>> result = f.getConverter(cls);
+      if (result != null) return result;
+    }
+
+    return null;
+  }
+
+  public Object convertValue(ParameterDescription pd, String value) {
+    return convertValue(pd.getParameterized(), pd.getParameterized().getType(), value);
+  }
+
+  /**
+   * @param type The type of the actual parameter
+   * @param value The value to convert
+   */
+  public Object convertValue(Parameterized parameterized, Class type,
+      String value) {
+    Parameter annotation = parameterized.getParameter();
+
+    // Do nothing if it's a @DynamicParameter
+    if (annotation == null) return value;
+
+    Class<? extends IStringConverter<?>> converterClass = annotation.converter();
+    boolean listConverterWasSpecified = annotation.listConverter() != NoConverter.class;
+
+    //
+    // Try to find a converter on the annotation
+    //
+    if (converterClass == null || converterClass == NoConverter.class) {
+      // If no converter specified and type is enum, used enum values to convert
+      if (type.isEnum()){
+        converterClass = type;
+      } else {
+        converterClass = findConverter(type);
+      }
+    }
+
+    if (converterClass == null) {
+      Type elementType = parameterized.findFieldGenericType();
+      converterClass = elementType != null
+          ? findConverter((Class<? extends IStringConverter<?>>) elementType)
+          : StringConverter.class;
+      // Check for enum type parameter
+      if (converterClass == null && Enum.class.isAssignableFrom((Class) elementType)) {
+        converterClass = (Class<? extends IStringConverter<?>>) elementType;
+      }
+    }
+
+    IStringConverter<?> converter;
+    Object result = null;
+    try {
+      String[] names = annotation.names();
+      String optionName = names.length > 0 ? names[0] : "[Main class]";
+      if (converterClass != null && converterClass.isEnum()) {
+        try {
+          result = Enum.valueOf((Class<? extends Enum>) converterClass, value);
+        } catch (IllegalArgumentException e) {
+            try {
+                result = Enum.valueOf((Class<? extends Enum>) converterClass, value.toUpperCase());
+            } catch (IllegalArgumentException ex) {
+                throw new ParameterException("Invalid value for " + optionName + " parameter. Allowed values:" +
+                        EnumSet.allOf((Class<? extends Enum>) converterClass));
+            }
+        } catch (Exception e) {
+          throw new ParameterException("Invalid value for " + optionName + " parameter. Allowed values:" +
+                      EnumSet.allOf((Class<? extends Enum>) converterClass));
+        }
+      } else {
+        converter = instantiateConverter(optionName, converterClass);
+        if (type.isAssignableFrom(List.class)
+              && parameterized.getGenericType() instanceof ParameterizedType) {
+
+          // The field is a List
+          if (listConverterWasSpecified) {
+            // If a list converter was specified, pass the value to it
+            // for direct conversion
+            IStringConverter<?> listConverter =
+                instantiateConverter(optionName, annotation.listConverter());
+            result = listConverter.convert(value);
+          } else {
+            // No list converter: use the single value converter and pass each
+            // parsed value to it individually
+            result = convertToList(value, converter, annotation.splitter());
+          }
+        } else {
+          result = converter.convert(value);
+        }
+      }
+    } catch (InstantiationException e) {
+      throw new ParameterException(e);
+    } catch (IllegalAccessException e) {
+      throw new ParameterException(e);
+    } catch (InvocationTargetException e) {
+      throw new ParameterException(e);
+    }
+
+    return result;
+  }
+
+  /**
+   * Use the splitter to split the value into multiple values and then convert
+   * each of them individually.
+   */
+  private Object convertToList(String value, IStringConverter<?> converter,
+      Class<? extends IParameterSplitter> splitterClass)
+          throws InstantiationException, IllegalAccessException {
+    IParameterSplitter splitter = splitterClass.newInstance();
+    List<Object> result = Lists.newArrayList();
+    for (String param : splitter.split(value)) {
+      result.add(converter.convert(param));
+    }
+    return result;
+  }
+
+  private IStringConverter<?> instantiateConverter(String optionName,
+      Class<? extends IStringConverter<?>> converterClass)
+      throws IllegalArgumentException, InstantiationException, IllegalAccessException,
+      InvocationTargetException {
+    Constructor<IStringConverter<?>> ctor = null;
+    Constructor<IStringConverter<?>> stringCtor = null;
+    Constructor<IStringConverter<?>>[] ctors
+        = (Constructor<IStringConverter<?>>[]) converterClass.getDeclaredConstructors();
+    for (Constructor<IStringConverter<?>> c : ctors) {
+      Class<?>[] types = c.getParameterTypes();
+      if (types.length == 1 && types[0].equals(String.class)) {
+        stringCtor = c;
+      } else if (types.length == 0) {
+        ctor = c;
+      }
+    }
+
+    IStringConverter<?> result = stringCtor != null
+        ? stringCtor.newInstance(optionName)
+        : (ctor != null
+            ? ctor.newInstance()
+            : null);
+
+    return result;
+  }
+
+  /**
+   * Add a command object.
+   */
+  public void addCommand(String name, Object object) {
+    addCommand(name, object, new String[0]);
+  }
+
+  public void addCommand(Object object) {
+    Parameters p = object.getClass().getAnnotation(Parameters.class);
+    if (p != null && p.commandNames().length > 0) {
+      for (String commandName : p.commandNames()) {
+        addCommand(commandName, object);
+      }
+    } else {
+      throw new ParameterException("Trying to add command " + object.getClass().getName()
+          + " without specifying its names in @Parameters");
+    }
+  }
+
+  /**
+   * Add a command object and its aliases.
+   */
+  public void addCommand(String name, Object object, String... aliases) {
+    JCommander jc = new JCommander(object);
+    jc.setProgramName(name, aliases);
+    jc.setDefaultProvider(m_defaultProvider);
+    jc.setAcceptUnknownOptions(m_acceptUnknownOptions);
+    ProgramName progName = jc.m_programName;
+    m_commands.put(progName, jc);
+
+    /*
+    * Register aliases
+    */
+    //register command name as an alias of itself for reverse lookup
+    //Note: Name clash check is intentionally omitted to resemble the
+    //     original behaviour of clashing commands.
+    //     Aliases are, however, are strictly checked for name clashes.
+    aliasMap.put(new StringKey(name), progName);
+    for (String a : aliases) {
+      IKey alias = new StringKey(a);
+      //omit pointless aliases to avoid name clash exception
+      if (!alias.equals(name)) {
+        ProgramName mappedName = aliasMap.get(alias);
+        if (mappedName != null && !mappedName.equals(progName)) {
+          throw new ParameterException("Cannot set alias " + alias
+                  + " for " + name
+                  + " command because it has already been defined for "
+                  + mappedName.m_name + " command");
+        }
+        aliasMap.put(alias, progName);
+      }
+    }
+  }
+
+  public Map<String, JCommander> getCommands() {
+    Map<String, JCommander> res = Maps.newLinkedHashMap();
+    for (Map.Entry<ProgramName, JCommander> entry : m_commands.entrySet()) {
+      res.put(entry.getKey().m_name, entry.getValue());
+    }
+    return res;
+  }
+
+  public String getParsedCommand() {
+    return m_parsedCommand;
+  }
+
+  /**
+   * The name of the command or the alias in the form it was
+   * passed to the command line. <code>null</code> if no
+   * command or alias was specified.
+   *
+   * @return Name of command or alias passed to command line. If none passed: <code>null</code>.
+   */
+  public String getParsedAlias() {
+    return m_parsedAlias;
+  }
+
+  /**
+   * @return n spaces
+   */
+  private String s(int count) {
+    StringBuilder result = new StringBuilder();
+    for (int i = 0; i < count; i++) {
+      result.append(" ");
+    }
+
+    return result.toString();
+  }
+
+  /**
+   * @return the objects that JCommander will fill with the result of
+   * parsing the command line.
+   */
+  public List<Object> getObjects() {
+    return m_objects;
+  }
+
+  private ParameterDescription findParameterDescription(String arg) {
+    return FuzzyMap.findInMap(m_descriptions, new StringKey(arg), m_caseSensitiveOptions,
+        m_allowAbbreviatedOptions);
+  }
+
+  private JCommander findCommand(ProgramName name) {
+    return FuzzyMap.findInMap(m_commands, name,
+        m_caseSensitiveOptions, m_allowAbbreviatedOptions);
+//    if (! m_caseSensitiveOptions) {
+//      return m_commands.get(name);
+//    } else {
+//      for (ProgramName c : m_commands.keySet()) {
+//        if (c.getName().equalsIgnoreCase(name.getName())) {
+//          return m_commands.get(c);
+//        }
+//      }
+//    }
+//    return null;
+  }
+
+  private ProgramName findProgramName(String name) {
+    return FuzzyMap.findInMap(aliasMap, new StringKey(name),
+        m_caseSensitiveOptions, m_allowAbbreviatedOptions);
+  }
+
+  /*
+  * Reverse lookup JCommand object by command's name or its alias
+  */
+  private JCommander findCommandByAlias(String commandOrAlias) {
+    ProgramName progName = findProgramName(commandOrAlias);
+    if (progName == null) {
+      return null;
+    }
+    JCommander jc = findCommand(progName);
+    if (jc == null) {
+      throw new IllegalStateException(
+              "There appears to be inconsistency in the internal command database. " +
+                      " This is likely a bug. Please report.");
+    }
+    return jc;
+  }
+
+  /**
+   * Encapsulation of either a main application or an individual command.
+   */
+  private static final class ProgramName implements IKey {
+    private final String m_name;
+    private final List<String> m_aliases;
+
+    ProgramName(String name, List<String> aliases) {
+      m_name = name;
+      m_aliases = aliases;
+    }
+
+    @Override
+    public String getName() {
+      return m_name;
+    }
+
+    private String getDisplayName() {
+      StringBuilder sb = new StringBuilder();
+      sb.append(m_name);
+      if (!m_aliases.isEmpty()) {
+        sb.append("(");
+        Iterator<String> aliasesIt = m_aliases.iterator();
+        while (aliasesIt.hasNext()) {
+          sb.append(aliasesIt.next());
+          if (aliasesIt.hasNext()) {
+            sb.append(",");
+          }
+        }
+        sb.append(")");
+      }
+      return sb.toString();
+    }
+    
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
+      return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj)
+        return true;
+      if (obj == null)
+        return false;
+      if (getClass() != obj.getClass())
+        return false;
+      ProgramName other = (ProgramName) obj;
+      if (m_name == null) {
+        if (other.m_name != null)
+          return false;
+      } else if (!m_name.equals(other.m_name))
+        return false;
+      return true;
+    }
+
+    /*
+     * Important: ProgramName#toString() is used by longestName(Collection) function
+     * to format usage output.
+     */
+    @Override
+    public String toString() {
+      return getDisplayName();
+      
+    }
+  }
+
+  public void setVerbose(int verbose) {
+    m_verbose = verbose;
+  }
+
+  public void setCaseSensitiveOptions(boolean b) {
+    m_caseSensitiveOptions = b;
+  }
+
+  public void setAllowAbbreviatedOptions(boolean b) {
+    m_allowAbbreviatedOptions = b;
+  }
+
+  public void setAcceptUnknownOptions(boolean b) {
+    m_acceptUnknownOptions = b;
+  }
+
+  public List<String> getUnknownOptions() {
+    return m_unknownArgs;
+  }
+  public void setAllowParameterOverwriting(boolean b) {
+    m_allowParameterOverwriting = b;
+  }
+
+  public boolean isParameterOverwritingAllowed() {
+    return m_allowParameterOverwriting;
+  }
+//  public void setCaseSensitiveCommands(boolean b) {
+//    m_caseSensitiveCommands = b;
+//  }
+}
+
diff --git a/src/main/java/com/beust/jcommander/MissingCommandException.java b/src/main/java/com/beust/jcommander/MissingCommandException.java
new file mode 100644
index 0000000..1d572ab
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/MissingCommandException.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+/**
+ * Thrown when a command was expected.
+ *
+ * @author Cedric Beust <cedric@beust.com>
+ */
+@SuppressWarnings("serial")
+public class MissingCommandException extends ParameterException {
+
+  public MissingCommandException(String string) {
+    super(string);
+  }
+
+  public MissingCommandException(Throwable t) {
+    super(t);
+  }
+}
diff --git a/src/main/java/com/beust/jcommander/Parameter.java b/src/main/java/com/beust/jcommander/Parameter.java
new file mode 100644
index 0000000..d8cf87d
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/Parameter.java
@@ -0,0 +1,130 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+
+import com.beust.jcommander.converters.CommaParameterSplitter;
+import com.beust.jcommander.converters.IParameterSplitter;
+import com.beust.jcommander.converters.NoConverter;
+import com.beust.jcommander.validators.NoValidator;
+import com.beust.jcommander.validators.NoValueValidator;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({ FIELD, METHOD })
+public @interface Parameter {
+
+  /**
+   * An array of allowed command line parameters (e.g. "-d", "--outputdir", etc...).
+   * If this attribute is omitted, the field it's annotating will receive all the
+   * unparsed options. There can only be at most one such annotation.
+   */
+  String[] names() default {};
+
+  /**
+   * A description of this option.
+   */
+  String description() default "";
+
+  /**
+   * Whether this option is required.
+   */
+  boolean required() default false;
+
+  /**
+   * The key used to find the string in the message bundle.
+   */
+  String descriptionKey() default "";
+
+  /**
+   * How many parameter values this parameter will consume. For example,
+   * an arity of 2 will allow "-pair value1 value2".
+   */
+  int arity() default -1;
+
+  /**
+   * If true, this parameter is a password and it will be prompted on the console
+   * (if available).
+   */
+  boolean password() default false;
+
+  /**
+   * The string converter to use for this field. If the field is of type <tt>List</tt>
+   * and not <tt>listConverter</tt> attribute was specified, JCommander will split
+   * the input in individual values and convert each of them separately.
+   */
+  Class<? extends IStringConverter<?>> converter() default NoConverter.class;
+
+  /**
+   * The list string converter to use for this field. If it's specified, the
+   * field has to be of type <tt>List</tt> and the converter needs to return
+   * a List that's compatible with that type.
+   */
+  Class<? extends IStringConverter<?>> listConverter() default NoConverter.class;
+
+  /**
+   * If true, this parameter won't appear in the usage().
+   */
+  boolean hidden() default false;
+
+  /**
+   * Validate the parameter found on the command line.
+   */
+  Class<? extends IParameterValidator> validateWith() default NoValidator.class;
+
+  /**
+   * Validate the value for this parameter.
+   */
+  Class<? extends IValueValidator> validateValueWith() default NoValueValidator.class;
+
+  /**
+   * @return true if this parameter has a variable arity. See @{IVariableArity}
+   */
+  boolean variableArity() default false;
+
+  /**
+   * What splitter to use (applicable only on fields of type <tt>List</tt>). By default,
+   * a comma separated splitter will be used.
+   */
+  Class<? extends IParameterSplitter> splitter() default CommaParameterSplitter.class;
+  
+  /**
+   * If true, console will not echo typed input
+   * Used in conjunction with password = true
+   */
+  boolean echoInput() default false;
+
+  /**
+   * If true, this parameter is for help. If such a parameter is specified,
+   * required parameters are no longer checked for their presence.
+   */
+  boolean help() default false;
+  
+  /**
+   * If true, this parameter can be overwritten through a file or another appearance of the parameter
+   * @return 
+   */
+  boolean forceNonOverwritable() default false;
+
+  
+}
diff --git a/src/main/java/com/beust/jcommander/ParameterDescription.java b/src/main/java/com/beust/jcommander/ParameterDescription.java
new file mode 100644
index 0000000..2ef2d5f
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/ParameterDescription.java
@@ -0,0 +1,364 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import com.beust.jcommander.validators.NoValidator;
+import com.beust.jcommander.validators.NoValueValidator;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class ParameterDescription {
+  private Object m_object;
+
+  private WrappedParameter m_wrappedParameter;
+  private Parameter m_parameterAnnotation;
+  private DynamicParameter m_dynamicParameterAnnotation;
+
+  /** The field/method */
+  private Parameterized m_parameterized;
+  /** Keep track of whether a value was added to flag an error */
+  private boolean m_assigned = false;
+  private ResourceBundle m_bundle;
+  private String m_description;
+  private JCommander m_jCommander;
+  private Object m_default;
+  /** Longest of the names(), used to present usage() alphabetically */
+  private String m_longestName = "";
+
+  public ParameterDescription(Object object, DynamicParameter annotation,
+      Parameterized parameterized,
+      ResourceBundle bundle, JCommander jc) {
+    if (! Map.class.isAssignableFrom(parameterized.getType())) {
+      throw new ParameterException("@DynamicParameter " + parameterized.getName()
+          + " should be of type "
+          + "Map but is " + parameterized.getType().getName());
+    }
+
+    m_dynamicParameterAnnotation = annotation;
+    m_wrappedParameter = new WrappedParameter(m_dynamicParameterAnnotation);
+    init(object, parameterized, bundle, jc);
+  }
+
+  public ParameterDescription(Object object, Parameter annotation, Parameterized parameterized,
+      ResourceBundle bundle, JCommander jc) {
+    m_parameterAnnotation = annotation;
+    m_wrappedParameter = new WrappedParameter(m_parameterAnnotation);
+    init(object, parameterized, bundle, jc);
+  }
+
+  /**
+   * Find the resource bundle in the annotations.
+   * @return
+   */
+  @SuppressWarnings("deprecation")
+  private ResourceBundle findResourceBundle(Object o) {
+    ResourceBundle result = null;
+
+    Parameters p = o.getClass().getAnnotation(Parameters.class);
+    if (p != null && ! isEmpty(p.resourceBundle())) {
+      result = ResourceBundle.getBundle(p.resourceBundle(), Locale.getDefault());
+    } else {
+      com.beust.jcommander.ResourceBundle a = o.getClass().getAnnotation(
+          com.beust.jcommander.ResourceBundle.class);
+      if (a != null && ! isEmpty(a.value())) {
+        result = ResourceBundle.getBundle(a.value(), Locale.getDefault());
+      }
+    }
+
+    return result;
+  }
+
+  private boolean isEmpty(String s) {
+    return s == null || "".equals(s);
+  }
+
+  private void initDescription(String description, String descriptionKey, String[] names) {
+    m_description = description;
+    if (! "".equals(descriptionKey)) {
+      if (m_bundle != null) {
+        m_description = m_bundle.getString(descriptionKey);
+      } else {
+//        JCommander.getConsole().println("Warning: field " + object.getClass() + "." + field.getName()
+//            + " has a descriptionKey but no bundle was defined with @ResourceBundle, using " +
+//            "default description:'" + m_description + "'");
+      }
+    }
+
+    for (String name : names) {
+      if (name.length() > m_longestName.length()) m_longestName = name;
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private void init(Object object, Parameterized parameterized, ResourceBundle bundle,
+      JCommander jCommander) {
+    m_object = object;
+    m_parameterized = parameterized;
+    m_bundle = bundle;
+    if (m_bundle == null) {
+      m_bundle = findResourceBundle(object);
+    }
+    m_jCommander = jCommander;
+
+    if (m_parameterAnnotation != null) {
+      String description;
+      if (Enum.class.isAssignableFrom(parameterized.getType())
+          && m_parameterAnnotation.description().isEmpty()) {
+        description = "Options: " + EnumSet.allOf((Class<? extends Enum>) parameterized.getType());
+      }else {
+        description = m_parameterAnnotation.description();
+      }
+      initDescription(description, m_parameterAnnotation.descriptionKey(),
+          m_parameterAnnotation.names());
+    } else if (m_dynamicParameterAnnotation != null) {
+      initDescription(m_dynamicParameterAnnotation.description(),
+          m_dynamicParameterAnnotation.descriptionKey(),
+          m_dynamicParameterAnnotation.names());
+    } else {
+      throw new AssertionError("Shound never happen");
+    }
+
+    try {
+      m_default = parameterized.get(object);
+    } catch (Exception e) {
+    }
+
+    //
+    // Validate default values, if any and if applicable
+    //
+    if (m_default != null) {
+      if (m_parameterAnnotation != null) {
+        validateDefaultValues(m_parameterAnnotation.names());
+      }
+    }
+  }
+
+  private void validateDefaultValues(String[] names) {
+    String name = names.length > 0 ? names[0] : "";
+    validateValueParameter(name, m_default);
+  }
+
+  public String getLongestName() {
+    return m_longestName;
+  }
+
+  public Object getDefault() {
+   return m_default;
+  }
+
+  public String getDescription() {
+    return m_description;
+  }
+
+  public Object getObject() {
+    return m_object;
+  }
+
+  public String getNames() {
+    StringBuilder sb = new StringBuilder();
+    String[] names = m_wrappedParameter.names();
+    for (int i = 0; i < names.length; i++) {
+      if (i > 0) sb.append(", ");
+      sb.append(names[i]);
+    }
+    return sb.toString();
+  }
+
+  public WrappedParameter getParameter() {
+    return m_wrappedParameter;
+  }
+
+  public Parameterized getParameterized() {
+    return m_parameterized;
+  }
+
+  private boolean isMultiOption() {
+    Class<?> fieldType = m_parameterized.getType();
+    return fieldType.equals(List.class) || fieldType.equals(Set.class)
+        || m_parameterized.isDynamicParameter();
+  }
+
+  public void addValue(String value) {
+    addValue(value, false /* not default */);
+  }
+
+  /**
+   * @return true if this parameter received a value during the parsing phase.
+   */
+  public boolean isAssigned() {
+    return m_assigned;
+  }
+
+
+  public void setAssigned(boolean b) {
+    m_assigned = b;
+  }
+
+  /**
+   * Add the specified value to the field. First, validate the value if a
+   * validator was specified. Then look up any field converter, then any type
+   * converter, and if we can't find any, throw an exception.
+   */
+  public void addValue(String value, boolean isDefault) {
+    p("Adding " + (isDefault ? "default " : "") + "value:" + value
+        + " to parameter:" + m_parameterized.getName());
+    String name = m_wrappedParameter.names()[0];
+    if (m_assigned && ! isMultiOption() && !m_jCommander.isParameterOverwritingAllowed() || isNonOverwritableForced()) {
+      throw new ParameterException("Can only specify option " + name + " once.");
+    }
+
+    validateParameter(name, value);
+
+    Class<?> type = m_parameterized.getType();
+
+    Object convertedValue = m_jCommander.convertValue(this, value);
+    validateValueParameter(name, convertedValue);
+    boolean isCollection = Collection.class.isAssignableFrom(type);
+
+    if (isCollection) {
+      @SuppressWarnings("unchecked")
+      Collection<Object> l = (Collection<Object>) m_parameterized.get(m_object);
+      if (l == null || fieldIsSetForTheFirstTime(isDefault)) {
+        l = newCollection(type);
+        m_parameterized.set(m_object, l);
+      }
+      if (convertedValue instanceof Collection) {
+        l.addAll((Collection) convertedValue);
+      } else { // if (isMainParameter || m_parameterAnnotation.arity() > 1) {
+        l.add(convertedValue);
+//        } else {
+//          l.
+      }
+    } else {
+      m_wrappedParameter.addValue(m_parameterized, m_object, convertedValue);
+    }
+    if (! isDefault) m_assigned = true;
+  }
+
+  private void validateParameter(String name, String value) {
+    Class<? extends IParameterValidator> validator = m_wrappedParameter.validateWith();
+    if (validator != null) {
+      validateParameter(this, validator, name, value);
+    }
+  }
+
+  private void validateValueParameter(String name, Object value) {
+    Class<? extends IValueValidator> validator = m_wrappedParameter.validateValueWith();
+    if (validator != null) {
+      validateValueParameter(validator, name, value);
+    }
+  }
+
+  public static void validateValueParameter(Class<? extends IValueValidator> validator,
+      String name, Object value) {
+    try {
+      if (validator != NoValueValidator.class) {
+        p("Validating value parameter:" + name + " value:" + value + " validator:" + validator);
+      }
+      validator.newInstance().validate(name, value);
+    } catch (InstantiationException e) {
+      throw new ParameterException("Can't instantiate validator:" + e);
+    } catch (IllegalAccessException e) {
+      throw new ParameterException("Can't instantiate validator:" + e);
+    }
+  }
+
+  public static void validateParameter(ParameterDescription pd,
+      Class<? extends IParameterValidator> validator,
+      String name, String value) {
+    try {
+      if (validator != NoValidator.class) {
+        p("Validating parameter:" + name + " value:" + value + " validator:" + validator);
+      }
+      validator.newInstance().validate(name, value);
+      if (IParameterValidator2.class.isAssignableFrom(validator)) {
+        IParameterValidator2 instance = (IParameterValidator2) validator.newInstance();
+        instance.validate(name, value, pd);
+      }
+    } catch (InstantiationException e) {
+      throw new ParameterException("Can't instantiate validator:" + e);
+    } catch (IllegalAccessException e) {
+      throw new ParameterException("Can't instantiate validator:" + e);
+    } catch(ParameterException ex) {
+      throw ex;
+    } catch(Exception ex) {
+      throw new ParameterException(ex);
+    }
+  }
+
+  /*
+   * Creates a new collection for the field's type.
+   *
+   * Currently only List and Set are supported. Support for
+   * Queues and Stacks could be useful.
+   */
+  @SuppressWarnings("unchecked")
+  private Collection<Object> newCollection(Class<?> type) {
+    if (SortedSet.class.isAssignableFrom(type)) return new TreeSet();
+    else if (LinkedHashSet.class.isAssignableFrom(type)) return new LinkedHashSet();
+    else if (Set.class.isAssignableFrom(type)) return new HashSet();
+    else if (List.class.isAssignableFrom(type)) return new ArrayList();
+    else {
+      throw new ParameterException("Parameters of Collection type '" + type.getSimpleName()
+                                  + "' are not supported. Please use List or Set instead.");
+    }
+  }
+
+  /*
+   * Tests if its the first time a non-default value is
+   * being added to the field.
+   */
+  private boolean fieldIsSetForTheFirstTime(boolean isDefault) {
+    return (!isDefault && !m_assigned);
+  }
+
+  private static void p(String string) {
+    if (System.getProperty(JCommander.DEBUG_PROPERTY) != null) {
+      JCommander.getConsole().println("[ParameterDescription] " + string);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "[ParameterDescription " + m_parameterized.getName() + "]";
+  }
+
+  public boolean isDynamicParameter() {
+    return m_dynamicParameterAnnotation != null;
+  }
+
+  public boolean isHelp() {
+    return m_wrappedParameter.isHelp();
+  }
+  
+  public boolean isNonOverwritableForced() {
+    return m_wrappedParameter.isNonOverwritableForced();
+  }
+}
diff --git a/src/main/java/com/beust/jcommander/ParameterException.java b/src/main/java/com/beust/jcommander/ParameterException.java
new file mode 100644
index 0000000..2bba7d1
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/ParameterException.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+/**
+ * The main exception that JCommand will throw when something goes wrong while
+ * parsing parameters.
+ *
+ * @author Cedric Beust <cedric@beust.com>
+ */
+@SuppressWarnings("serial")
+public class ParameterException extends RuntimeException {
+
+  public ParameterException(Throwable t) {
+    super(t);
+  }
+
+  public ParameterException(String string) {
+    super(string);
+  }
+  
+  public ParameterException(String string, Throwable t) {
+      super(string, t);
+  }   
+
+}
diff --git a/src/main/java/com/beust/jcommander/Parameterized.java b/src/main/java/com/beust/jcommander/Parameterized.java
new file mode 100644
index 0000000..ff8753b
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/Parameterized.java
@@ -0,0 +1,244 @@
+package com.beust.jcommander;
+
+import com.beust.jcommander.internal.Lists;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Encapsulate a field or a method annotated with @Parameter or @DynamicParameter
+ */
+public class Parameterized {
+
+  // Either a method or a field
+  private Field m_field;
+  private Method m_method;
+  private Method m_getter;
+
+  // Either of these two
+  private WrappedParameter m_wrappedParameter;
+  private ParametersDelegate m_parametersDelegate;
+
+  public Parameterized(WrappedParameter wp, ParametersDelegate pd,
+      Field field, Method method) {
+    m_wrappedParameter = wp;
+    m_method = method;
+    m_field = field;
+    if (m_field != null) {
+      m_field.setAccessible(true);
+    }
+    m_parametersDelegate = pd;
+  }
+
+  public static List<Parameterized> parseArg(Object arg) {
+    List<Parameterized> result = Lists.newArrayList();
+
+    Class<? extends Object> cls = arg.getClass();
+    while (!Object.class.equals(cls)) {
+      for (Field f : cls.getDeclaredFields()) {
+        Annotation annotation = f.getAnnotation(Parameter.class);
+        Annotation delegateAnnotation = f.getAnnotation(ParametersDelegate.class);
+        Annotation dynamicParameter = f.getAnnotation(DynamicParameter.class);
+        if (annotation != null) {
+          result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
+              f, null));
+        } else if (dynamicParameter != null) {
+          result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null,
+              f, null));
+        } else if (delegateAnnotation != null) {
+          result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
+              f, null));
+        }
+      }
+      cls = cls.getSuperclass();
+    }
+
+    // Reassigning
+    cls = arg.getClass();
+    while (!Object.class.equals(cls)) {
+      for (Method m : cls.getDeclaredMethods()) {
+        Annotation annotation = m.getAnnotation(Parameter.class);
+        Annotation delegateAnnotation = m.getAnnotation(ParametersDelegate.class);
+        Annotation dynamicParameter = m.getAnnotation(DynamicParameter.class);
+        if (annotation != null) {
+          result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
+              null, m));
+        } else if (dynamicParameter != null) {
+          result.add(new Parameterized(new WrappedParameter((DynamicParameter) annotation), null,
+              null, m));
+        } else if (delegateAnnotation != null) {
+          result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
+              null, m));
+        }
+      }
+      cls = cls.getSuperclass();
+    }
+
+    return result;
+  }
+
+  public WrappedParameter getWrappedParameter() {
+    return m_wrappedParameter;
+  }
+
+  public Class<?> getType() {
+    if (m_method != null) {
+      return m_method.getParameterTypes()[0];
+    } else {
+      return m_field.getType();
+    }
+  }
+
+  public String getName() {
+    if (m_method != null) {
+      return m_method.getName();
+    } else {
+      return m_field.getName();
+    }
+  }
+
+  public Object get(Object object) {
+    try {
+      if (m_method != null) {
+        if (m_getter == null) {
+            m_getter = m_method.getDeclaringClass()
+                .getMethod("g" + m_method.getName().substring(1),
+                new Class[0]);
+        }
+        return m_getter.invoke(object);
+      } else {
+        return m_field.get(object);
+      }
+    } catch (SecurityException e) {
+      throw new ParameterException(e);
+    } catch (NoSuchMethodException e) {
+      // Try to find a field
+      String name = m_method.getName();
+      String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4);
+      Object result = null;
+      try {
+        Field field = m_method.getDeclaringClass().getDeclaredField(fieldName);
+        if (field != null) {
+          field.setAccessible(true);
+          result = field.get(object);
+        }
+      } catch(NoSuchFieldException ex) {
+        // ignore
+      } catch(IllegalAccessException ex) {
+        // ignore
+      }
+      return result;
+    } catch (IllegalArgumentException e) {
+      throw new ParameterException(e);
+    } catch (IllegalAccessException e) {
+      throw new ParameterException(e);
+    } catch (InvocationTargetException e) {
+      throw new ParameterException(e);
+    }
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((m_field == null) ? 0 : m_field.hashCode());
+    result = prime * result + ((m_method == null) ? 0 : m_method.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (obj == null)
+      return false;
+    if (getClass() != obj.getClass())
+      return false;
+    Parameterized other = (Parameterized) obj;
+    if (m_field == null) {
+      if (other.m_field != null)
+        return false;
+    } else if (!m_field.equals(other.m_field))
+      return false;
+    if (m_method == null) {
+      if (other.m_method != null)
+        return false;
+    } else if (!m_method.equals(other.m_method))
+      return false;
+    return true;
+  }
+
+  public boolean isDynamicParameter(Field field) {
+    if (m_method != null) {
+      return m_method.getAnnotation(DynamicParameter.class) != null;
+    } else {
+      return m_field.getAnnotation(DynamicParameter.class) != null;
+    }
+  }
+
+  public void set(Object object, Object value) {
+    try {
+      if (m_method != null) {
+        m_method.invoke(object, value);
+      } else {
+          m_field.set(object, value);
+      }
+    } catch (IllegalArgumentException ex) {
+      throw new ParameterException(ex);
+    } catch (IllegalAccessException ex) {
+      throw new ParameterException(ex);
+    } catch (InvocationTargetException ex) {
+      // If a ParameterException was thrown, don't wrap it into another one
+      if (ex.getTargetException() instanceof ParameterException) {
+        throw (ParameterException) ex.getTargetException();
+      } else {
+        throw new ParameterException(ex);
+      }
+    }
+  }
+
+  public ParametersDelegate getDelegateAnnotation() {
+    return m_parametersDelegate;
+  }
+
+  public Type getGenericType() {
+    if (m_method != null) {
+      return m_method.getGenericParameterTypes()[0];
+    } else {
+      return m_field.getGenericType();
+    }
+  }
+
+  public Parameter getParameter() {
+    return m_wrappedParameter.getParameter();
+  }
+
+  /**
+   * @return the generic type of the collection for this field, or null if not applicable.
+   */
+  public Type findFieldGenericType() {
+    if (m_method != null) {
+      return null;
+    } else {
+      if (m_field.getGenericType() instanceof ParameterizedType) {
+        ParameterizedType p = (ParameterizedType) m_field.getGenericType();
+        Type cls = p.getActualTypeArguments()[0];
+        if (cls instanceof Class) {
+          return cls;
+        }
+      }
+    }
+
+    return null;
+  }
+
+  public boolean isDynamicParameter() {
+    return m_wrappedParameter.getDynamicParameter() != null;
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/Parameters.java b/src/main/java/com/beust/jcommander/Parameters.java
new file mode 100644
index 0000000..f2e8c76
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/Parameters.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+/**
+ * An annotation used to specify settings for parameter parsing.
+ * 
+ * @author cbeust
+ */
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({ TYPE })
+@Inherited
+public @interface Parameters {
+
+  public static final String DEFAULT_OPTION_PREFIXES = "-";
+
+  /**
+   * The name of the resource bundle to use for this class.
+   */
+  String resourceBundle() default "";
+
+  /**
+   * The character(s) that separate options.
+   */
+  String separators() default " ";
+
+  /**
+   * What characters an option starts with.
+   */
+  String optionPrefixes() default DEFAULT_OPTION_PREFIXES;
+
+  /**
+   * If the annotated class was added to {@link JCommander} as a command with
+   * {@link JCommander#addCommand}, then this string will be displayed in the
+   * description when @{link JCommander#usage} is invoked.
+   */
+  String commandDescription() default "";
+
+  /**
+   * @return the key used to find the command description in the resource bundle.
+   */
+  String commandDescriptionKey() default "";
+
+  /**
+   * An array of allowed command names.
+   */
+  String[] commandNames() default {};
+
+  /**
+   * If true, this command won't appear in the usage().
+   */
+  boolean hidden() default false;
+}
diff --git a/src/main/java/com/beust/jcommander/ParametersDelegate.java b/src/main/java/com/beust/jcommander/ParametersDelegate.java
new file mode 100644
index 0000000..5a06f8e
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/ParametersDelegate.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+
+/**
+ * <p>When applied to a field all of its child fields annotated
+ * with {@link Parameter} will be included during arguments
+ * parsing.</p>
+ *
+ * <p>Mainly useful when creating complex command based CLI interfaces,
+ * where several commands can share a set of arguments, but using
+ * object inheritance is not enough, due to no-multiple-inheritance
+ * restriction. Using {@link ParametersDelegate} any number of
+ * command sets can be shared by using composition pattern.</p>
+ *
+ * <p>Delegations can be chained (nested).</p>
+ * 
+ * @author rodionmoiseev
+ */
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({ FIELD })
+public @interface ParametersDelegate {
+}
diff --git a/src/main/java/com/beust/jcommander/ResourceBundle.java b/src/main/java/com/beust/jcommander/ResourceBundle.java
new file mode 100644
index 0000000..806ade8
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/ResourceBundle.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @deprecated use @Parameters
+ * 
+ * @author Cedric Beust <cedric@beust.com>
+ */
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({ TYPE })
+public @interface ResourceBundle {
+  /**
+   * The name of the resource bundle to use for this class.
+   */
+  String value();
+}
diff --git a/src/main/java/com/beust/jcommander/StringKey.java b/src/main/java/com/beust/jcommander/StringKey.java
new file mode 100644
index 0000000..09d1149
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/StringKey.java
@@ -0,0 +1,48 @@
+package com.beust.jcommander;
+
+import com.beust.jcommander.FuzzyMap.IKey;
+
+public class StringKey implements IKey {
+
+  private String m_name;
+
+  public StringKey(String name) {
+    m_name = name;
+  }
+
+  @Override
+  public String getName() {
+    return m_name;
+  }
+
+  @Override
+  public String toString() {
+    return m_name;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (obj == null)
+      return false;
+    if (getClass() != obj.getClass())
+      return false;
+    StringKey other = (StringKey) obj;
+    if (m_name == null) {
+      if (other.m_name != null)
+        return false;
+    } else if (!m_name.equals(other.m_name))
+      return false;
+    return true;
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/Strings.java b/src/main/java/com/beust/jcommander/Strings.java
new file mode 100644
index 0000000..591a38a
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/Strings.java
@@ -0,0 +1,7 @@
+package com.beust.jcommander;
+
+public class Strings {
+  public static boolean isStringEmpty(String s) {
+    return s == null || "".equals(s);
+  }
+}
diff --git a/src/main/java/com/beust/jcommander/WrappedParameter.java b/src/main/java/com/beust/jcommander/WrappedParameter.java
new file mode 100644
index 0000000..f4e7d56
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/WrappedParameter.java
@@ -0,0 +1,115 @@
+package com.beust.jcommander;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Encapsulates the operations common to @Parameter and @DynamicParameter
+ */
+public class WrappedParameter {
+  private Parameter m_parameter;
+  private DynamicParameter m_dynamicParameter;
+
+  public WrappedParameter(Parameter p) {
+    m_parameter = p;
+  }
+
+  public WrappedParameter(DynamicParameter p) {
+    m_dynamicParameter = p;
+  }
+
+  public Parameter getParameter() {
+    return m_parameter;
+  }
+
+  public DynamicParameter getDynamicParameter() {
+    return m_dynamicParameter;
+  }
+
+  public int arity() {
+    return m_parameter != null ? m_parameter.arity() : 1;
+  }
+
+  public boolean hidden() {
+    return m_parameter != null ? m_parameter.hidden() : m_dynamicParameter.hidden();
+  }
+
+  public boolean required() {
+    return m_parameter != null ? m_parameter.required() : m_dynamicParameter.required();
+  }
+
+  public boolean password() {
+    return m_parameter != null ? m_parameter.password() : false;
+  }
+
+  public String[] names() {
+    return m_parameter != null ? m_parameter.names() : m_dynamicParameter.names();
+  }
+
+  public boolean variableArity() {
+    return m_parameter != null ? m_parameter.variableArity() : false;
+  }
+
+  public Class<? extends IParameterValidator> validateWith() {
+    return m_parameter != null ? m_parameter.validateWith() : m_dynamicParameter.validateWith();
+  }
+
+  public Class<? extends IValueValidator> validateValueWith() {
+    return m_parameter != null
+        ? m_parameter.validateValueWith()
+        : m_dynamicParameter.validateValueWith();
+  }
+
+  public boolean echoInput() {
+	  return m_parameter != null ? m_parameter.echoInput() : false;
+  }
+
+  public void addValue(Parameterized parameterized, Object object, Object value) {
+    if (m_parameter != null) {
+      parameterized.set(object, value);
+    } else {
+      String a = m_dynamicParameter.assignment();
+      String sv = value.toString();
+
+      int aInd = sv.indexOf(a);
+      if (aInd == -1) {
+        throw new ParameterException(
+            "Dynamic parameter expected a value of the form a" + a + "b"
+                + " but got:" + sv);
+      }
+      callPut(object, parameterized, sv.substring(0, aInd), sv.substring(aInd + 1));
+    }
+  }
+
+  private void callPut(Object object, Parameterized parameterized, String key, String value) {
+    try {
+      Method m;
+      m = findPut(parameterized.getType());
+      m.invoke(parameterized.get(object), key, value);
+    } catch (SecurityException e) {
+      e.printStackTrace();
+    } catch(IllegalAccessException e) {
+      e.printStackTrace();
+    } catch(InvocationTargetException e) {
+      e.printStackTrace();
+    } catch (NoSuchMethodException e) {
+      e.printStackTrace();
+    }
+  }
+
+  private Method findPut(Class<?> cls) throws SecurityException, NoSuchMethodException {
+    return cls.getMethod("put", Object.class, Object.class);
+  }
+
+  public String getAssignment() {
+    return m_dynamicParameter != null ? m_dynamicParameter.assignment() : "";
+  }
+
+  public boolean isHelp() {
+    return m_parameter != null && m_parameter.help();
+  }
+
+  public boolean isNonOverwritableForced() {
+      return m_parameter != null && m_parameter.forceNonOverwritable();
+  }
+}
diff --git a/src/main/java/com/beust/jcommander/converters/BaseConverter.java b/src/main/java/com/beust/jcommander/converters/BaseConverter.java
new file mode 100644
index 0000000..4287163
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/BaseConverter.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+
+/**
+ * Base class for converters that stores the name of the option.
+ * 
+ * @author cbeust
+ */
+abstract public class BaseConverter<T> implements IStringConverter<T> {
+
+  private String m_optionName;
+
+  public BaseConverter(String optionName) {
+    m_optionName = optionName;
+  }
+
+  public String getOptionName() {
+    return m_optionName;
+  }
+
+  protected String getErrorString(String value, String to) {
+    return "\"" + getOptionName() + "\": couldn't convert \"" + value + "\" to " + to;
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/BigDecimalConverter.java b/src/main/java/com/beust/jcommander/converters/BigDecimalConverter.java
new file mode 100644
index 0000000..dfbba34
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/BigDecimalConverter.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.ParameterException;
+
+import java.math.BigDecimal;
+
+/**
+ * Converts a String to a BigDecimal.
+ *
+ * @author Angus Smithson
+ */
+public class BigDecimalConverter extends BaseConverter<BigDecimal> {
+
+  public BigDecimalConverter(String optionName) {
+    super(optionName);
+  }
+
+  public BigDecimal convert(String value) {
+    try {
+      return new BigDecimal(value);
+    } catch (NumberFormatException nfe) {
+      throw new ParameterException(getErrorString(value, "a BigDecimal"));
+    }
+  }
+}
diff --git a/src/main/java/com/beust/jcommander/converters/BooleanConverter.java b/src/main/java/com/beust/jcommander/converters/BooleanConverter.java
new file mode 100644
index 0000000..5126d22
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/BooleanConverter.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.ParameterException;
+
+/**
+ * Converts a string to a boolean.
+ * 
+ * @author cbeust
+ */
+public class BooleanConverter extends BaseConverter<Boolean> {
+
+  public BooleanConverter(String optionName) {
+    super(optionName);
+  }
+
+  public Boolean convert(String value) {
+    if ("false".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value)) {
+      return Boolean.parseBoolean(value);
+    } else {
+      throw new ParameterException(getErrorString(value, "a boolean"));
+    }
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/CommaParameterSplitter.java b/src/main/java/com/beust/jcommander/converters/CommaParameterSplitter.java
new file mode 100644
index 0000000..0e3bb18
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/CommaParameterSplitter.java
@@ -0,0 +1,12 @@
+package com.beust.jcommander.converters;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class CommaParameterSplitter implements IParameterSplitter {
+
+  public List<String> split(String value) {
+    return Arrays.asList(value.split(","));
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/DoubleConverter.java b/src/main/java/com/beust/jcommander/converters/DoubleConverter.java
new file mode 100644
index 0000000..0c36c68
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/DoubleConverter.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.ParameterException;
+
+/**
+ * Convert a string to a double.
+ *
+ * @author acornejo
+ */
+public class DoubleConverter extends BaseConverter<Double> {
+
+  public DoubleConverter(String optionName) {
+    super(optionName);
+  }
+
+  public Double convert(String value) {
+    try {
+      return Double.parseDouble(value);
+    } catch(NumberFormatException ex) {
+      throw new ParameterException(getErrorString(value, "a double"));
+    }
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/FileConverter.java b/src/main/java/com/beust/jcommander/converters/FileConverter.java
new file mode 100644
index 0000000..c18b575
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/FileConverter.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+
+import java.io.File;
+
+/**
+ * Convert a string into a file.
+ * 
+ * @author cbeust
+ */
+public class FileConverter implements IStringConverter<File> {
+
+  public File convert(String value) {
+    return new File(value);
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/FloatConverter.java b/src/main/java/com/beust/jcommander/converters/FloatConverter.java
new file mode 100644
index 0000000..2e2eff8
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/FloatConverter.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.ParameterException;
+
+/**
+ * Convert a string to a float.
+ *
+ * @author acornejo
+ */
+public class FloatConverter extends BaseConverter<Float> {
+
+  public FloatConverter(String optionName) {
+    super(optionName);
+  }
+
+  public Float convert(String value) {
+    try {
+      return Float.parseFloat(value);
+    } catch(NumberFormatException ex) {
+      throw new ParameterException(getErrorString(value, "a float"));
+    }
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/IParameterSplitter.java b/src/main/java/com/beust/jcommander/converters/IParameterSplitter.java
new file mode 100644
index 0000000..5859f4a
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/IParameterSplitter.java
@@ -0,0 +1,11 @@
+package com.beust.jcommander.converters;
+
+import java.util.List;
+
+/**
+ * Convert a string representing several parameters (e.g. "a,b,c" or "d/e/f") into a
+ * list of arguments ([a,b,c] and [d,e,f]).
+ */
+public interface IParameterSplitter {
+  List<String> split(String value);
+}
diff --git a/src/main/java/com/beust/jcommander/converters/ISO8601DateConverter.java b/src/main/java/com/beust/jcommander/converters/ISO8601DateConverter.java
new file mode 100644
index 0000000..f024f5c
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/ISO8601DateConverter.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.ParameterException;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Converts a String to a Date.
+ * TODO Modify to work with all valid ISO 8601 date formats (currently only works with yyyy-MM-dd).
+ *
+ * @author Angus Smithson
+ */
+public class ISO8601DateConverter extends BaseConverter<Date> {
+
+  private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+
+  public ISO8601DateConverter(String optionName) {
+    super(optionName);
+  }
+
+  public Date convert(String value) {
+    try {
+      return DATE_FORMAT.parse(value);
+    } catch (ParseException pe) {
+      throw new ParameterException(getErrorString(value, String.format("an ISO-8601 formatted date (%s)", DATE_FORMAT.toPattern())));
+    }
+  }
+}
diff --git a/src/main/java/com/beust/jcommander/converters/IntegerConverter.java b/src/main/java/com/beust/jcommander/converters/IntegerConverter.java
new file mode 100644
index 0000000..53d1119
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/IntegerConverter.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.ParameterException;
+
+/**
+ * Convert a string to an integer.
+ * 
+ * @author cbeust
+ */
+public class IntegerConverter extends BaseConverter<Integer> {
+
+  public IntegerConverter(String optionName) {
+    super(optionName);
+  }
+
+  public Integer convert(String value) {
+    try {
+      return Integer.parseInt(value);
+    } catch(NumberFormatException ex) {
+      throw new ParameterException(getErrorString(value, "an integer"));
+    }
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/LongConverter.java b/src/main/java/com/beust/jcommander/converters/LongConverter.java
new file mode 100644
index 0000000..863956b
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/LongConverter.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.ParameterException;
+
+/**
+ * Convert a string to a long.
+ * 
+ * @author cbeust
+ */
+public class LongConverter extends BaseConverter<Long> {
+
+  public LongConverter(String optionName) {
+    super(optionName);
+  }
+
+  public Long convert(String value) {
+    try {
+      return Long.parseLong(value);
+    } catch(NumberFormatException ex) {
+      throw new ParameterException(getErrorString(value, "a long"));
+    }
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/NoConverter.java b/src/main/java/com/beust/jcommander/converters/NoConverter.java
new file mode 100644
index 0000000..618daf9
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/NoConverter.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+
+/**
+ * Default value for a converter when none is specified.
+ * 
+ * @author cbeust
+ */
+public class NoConverter implements IStringConverter<String> {
+
+  public String convert(String value) {
+    throw new UnsupportedOperationException();
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/PathConverter.java b/src/main/java/com/beust/jcommander/converters/PathConverter.java
new file mode 100644
index 0000000..b7fdafd
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/PathConverter.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Convert a string into a path.
+ * 
+ * @author samvv
+ */
+public class PathConverter implements IStringConverter<Path> {
+  
+  public Path convert(String value) {
+    return Paths.get(value);
+  }
+  
+}
diff --git a/src/main/java/com/beust/jcommander/converters/StringConverter.java b/src/main/java/com/beust/jcommander/converters/StringConverter.java
new file mode 100644
index 0000000..ea1ae38
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/StringConverter.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.IStringConverter;
+
+/**
+ * Default converter for strings.
+ * 
+ * @author cbeust
+ */
+public class StringConverter implements IStringConverter<String> {
+
+  public String convert(String value) {
+    return value;
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/converters/URIConverter.java b/src/main/java/com/beust/jcommander/converters/URIConverter.java
new file mode 100644
index 0000000..3473bf0
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/URIConverter.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.ParameterException;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Convert a string into a URI.
+ * 
+ * @author samvv
+ */
+public class URIConverter extends BaseConverter<URI> {
+  
+  public URIConverter(String optionName) {
+    super(optionName);
+  }
+  
+  public URI convert(String value) {
+    try {
+      return new URI(value);
+    } catch (URISyntaxException e) {
+      throw new ParameterException(getErrorString(value, "a RFC 2396 and RFC 2732 compliant URI"));
+    }
+  }
+  
+}
diff --git a/src/main/java/com/beust/jcommander/converters/URLConverter.java b/src/main/java/com/beust/jcommander/converters/URLConverter.java
new file mode 100644
index 0000000..1f3734b
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/converters/URLConverter.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.converters;
+
+import com.beust.jcommander.ParameterException;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Convert a string into a URI.
+ * 
+ * @author samvv
+ */
+public class URLConverter extends BaseConverter<URL> {
+
+	public URLConverter(String optionName) {
+		super(optionName);
+	}
+
+	public URL convert(String value) {
+		try {
+			return new URL(value);
+		} catch (MalformedURLException e) {
+			throw new ParameterException(
+					getErrorString(value, "a RFC 2396 and RFC 2732 compliant URL"));
+			
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java b/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java
new file mode 100644
index 0000000..d5401a1
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/defaultprovider/PropertyFileDefaultProvider.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.defaultprovider;
+
+import com.beust.jcommander.IDefaultProvider;
+import com.beust.jcommander.ParameterException;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Properties;
+
+/**
+ * A default provider that reads its default values from a property file.
+ * 
+ * @author cbeust
+ */
+public class PropertyFileDefaultProvider implements IDefaultProvider {
+  public static final String DEFAULT_FILE_NAME = "jcommander.properties";
+  private Properties m_properties;
+
+  public PropertyFileDefaultProvider() {
+    init(DEFAULT_FILE_NAME);
+  }
+
+  public PropertyFileDefaultProvider(String fileName) {
+    init(fileName);
+  }
+
+  private void init(String fileName) {
+    try {
+      m_properties = new Properties();
+      URL url = ClassLoader.getSystemResource(fileName);
+      if (url != null) {
+        m_properties.load(url.openStream());
+      } else {
+        throw new ParameterException("Could not find property file: " + fileName
+            + " on the class path");
+      }
+    }
+    catch (IOException e) {
+      throw new ParameterException("Could not open property file: " + fileName);
+    }
+  }
+  
+  public String getDefaultValueFor(String optionName) {
+    int index = 0;
+    while (index < optionName.length() && ! Character.isLetterOrDigit(optionName.charAt(index))) {
+      index++;
+    }
+    String key = optionName.substring(index);
+    return m_properties.getProperty(key);
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/internal/Console.java b/src/main/java/com/beust/jcommander/internal/Console.java
new file mode 100644
index 0000000..95eafe1
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/internal/Console.java
@@ -0,0 +1,10 @@
+package com.beust.jcommander.internal;
+
+public interface Console {
+
+  void print(String msg);
+
+  void println(String msg);
+
+  char[] readPassword(boolean echoInput);
+}
diff --git a/src/main/java/com/beust/jcommander/internal/DefaultConsole.java b/src/main/java/com/beust/jcommander/internal/DefaultConsole.java
new file mode 100644
index 0000000..8fd7d6d
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/internal/DefaultConsole.java
@@ -0,0 +1,32 @@
+package com.beust.jcommander.internal;
+
+import com.beust.jcommander.ParameterException;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class DefaultConsole implements Console {
+
+  public void print(String msg) {
+    System.out.print(msg);
+  }
+
+  public void println(String msg) {
+    System.out.println(msg);
+  }
+
+  public char[] readPassword(boolean echoInput) {
+    try {
+      // Do not close the readers since System.in should not be closed
+      InputStreamReader isr = new InputStreamReader(System.in);
+      BufferedReader in = new BufferedReader(isr);
+      String result = in.readLine();
+      return result.toCharArray();
+    }
+    catch (IOException e) {
+      throw new ParameterException(e);
+    }
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java b/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java
new file mode 100644
index 0000000..7eb5ae5
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/internal/DefaultConverterFactory.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.internal;
+
+import com.beust.jcommander.IStringConverter;
+import com.beust.jcommander.IStringConverterFactory;
+import com.beust.jcommander.converters.BigDecimalConverter;
+import com.beust.jcommander.converters.BooleanConverter;
+import com.beust.jcommander.converters.DoubleConverter;
+import com.beust.jcommander.converters.FileConverter;
+import com.beust.jcommander.converters.FloatConverter;
+import com.beust.jcommander.converters.ISO8601DateConverter;
+import com.beust.jcommander.converters.IntegerConverter;
+import com.beust.jcommander.converters.LongConverter;
+import com.beust.jcommander.converters.StringConverter;
+import com.beust.jcommander.converters.URIConverter;
+import com.beust.jcommander.converters.URLConverter;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.net.URI;
+import java.net.URL;
+import java.util.Map;
+
+public class DefaultConverterFactory implements IStringConverterFactory {
+  /**
+   * A map of converters per class.
+   */
+  private static Map<Class, Class<? extends IStringConverter<?>>> m_classConverters;
+
+  static {
+    m_classConverters = Maps.newHashMap();
+    m_classConverters.put(String.class, StringConverter.class);
+    m_classConverters.put(Integer.class, IntegerConverter.class);
+    m_classConverters.put(int.class, IntegerConverter.class);
+    m_classConverters.put(Long.class, LongConverter.class);
+    m_classConverters.put(long.class, LongConverter.class);
+    m_classConverters.put(Float.class, FloatConverter.class);
+    m_classConverters.put(float.class, FloatConverter.class);
+    m_classConverters.put(Double.class, DoubleConverter.class);
+    m_classConverters.put(double.class, DoubleConverter.class);
+    m_classConverters.put(Boolean.class, BooleanConverter.class);
+    m_classConverters.put(boolean.class, BooleanConverter.class);
+    m_classConverters.put(File.class, FileConverter.class);
+    m_classConverters.put(BigDecimal.class, BigDecimalConverter.class);
+    m_classConverters.put(Date.class, ISO8601DateConverter.class);
+    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
+    }
+    m_classConverters.put(URI.class, URIConverter.class);
+    m_classConverters.put(URL.class, URLConverter.class);
+  }
+
+  public Class<? extends IStringConverter<?>> getConverter(Class forType) {
+    return m_classConverters.get(forType);
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/internal/JDK6Console.java b/src/main/java/com/beust/jcommander/internal/JDK6Console.java
new file mode 100644
index 0000000..70cb186
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/internal/JDK6Console.java
@@ -0,0 +1,45 @@
+package com.beust.jcommander.internal;
+
+import com.beust.jcommander.ParameterException;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+
+public class JDK6Console implements Console {
+
+  private Object console;
+
+  private PrintWriter writer;
+
+  public JDK6Console(Object console) throws Exception {
+    this.console = console;
+    Method writerMethod = console.getClass().getDeclaredMethod("writer", new Class<?>[0]);
+    writer = (PrintWriter) writerMethod.invoke(console, new Object[0]);
+  }
+
+  public void print(String msg) {
+    writer.print(msg);
+  }
+
+  public void println(String msg) {
+    writer.println(msg);
+  }
+
+  public char[] readPassword(boolean echoInput) {
+    try {
+      writer.flush();
+      Method method;
+      if (echoInput) {
+          method = console.getClass().getDeclaredMethod("readLine", new Class<?>[0]);
+          return ((String) method.invoke(console, new Object[0])).toCharArray();
+      } else {
+          method = console.getClass().getDeclaredMethod("readPassword", new Class<?>[0]);
+          return (char[]) method.invoke(console, new Object[0]);
+      }
+    }
+    catch (Exception e) {
+      throw new ParameterException(e);
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/beust/jcommander/internal/Lists.java b/src/main/java/com/beust/jcommander/internal/Lists.java
new file mode 100644
index 0000000..fdbee55
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/internal/Lists.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Lists {
+
+    public static <K> List<K> newArrayList() {
+        return new ArrayList<K>();
+    }
+
+    public static <K> List<K> newArrayList(Collection<K> c) {
+        return new ArrayList<K>(c);
+    }
+
+    public static <K> List<K> newArrayList(K... c) {
+      return new ArrayList<K>(Arrays.asList(c));
+    }
+
+    public static <K> List<K> newArrayList(int size) {
+        return new ArrayList<K>(size);
+    }
+
+    public static <K> LinkedList<K> newLinkedList() {
+        return new LinkedList<K>();
+    }
+
+    public static <K> LinkedList<K> newLinkedList(Collection<K> c) {
+        return new LinkedList<K>(c);
+    }
+
+
+}
diff --git a/src/main/java/com/beust/jcommander/internal/Maps.java b/src/main/java/com/beust/jcommander/internal/Maps.java
new file mode 100644
index 0000000..e272122
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/internal/Maps.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.internal;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class Maps {
+
+  public static <K, V> Map<K,V> newHashMap() {
+    return new HashMap<K, V>();
+  }
+
+  public static <K, V> Map<K,V> newLinkedHashMap() {
+    return new LinkedHashMap<K, V>();
+  }
+
+  public static <T> Map<T, T> newHashMap(T... parameters) {
+    Map<T, T> result = Maps.newHashMap();
+    for (int i = 0; i < parameters.length; i += 2) {
+      result.put(parameters[i], parameters[i + 1]);
+    }
+    return result;
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/internal/Nullable.java b/src/main/java/com/beust/jcommander/internal/Nullable.java
new file mode 100644
index 0000000..b988373
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/internal/Nullable.java
@@ -0,0 +1,12 @@
+package com.beust.jcommander.internal;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({FIELD, PARAMETER})
+public @interface Nullable {
+}
diff --git a/src/main/java/com/beust/jcommander/internal/Sets.java b/src/main/java/com/beust/jcommander/internal/Sets.java
new file mode 100644
index 0000000..77949c3
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/internal/Sets.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.internal;
+
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class Sets {
+
+  public static <K> Set<K> newHashSet() {
+    return new HashSet<K>();
+  }
+
+  public static <K> Set<K> newLinkedHashSet() {
+    return new LinkedHashSet<K>();
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/validators/NoValidator.java b/src/main/java/com/beust/jcommander/validators/NoValidator.java
new file mode 100644
index 0000000..f1b4df2
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/validators/NoValidator.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2011 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.validators;
+
+import com.beust.jcommander.IParameterValidator;
+import com.beust.jcommander.ParameterException;
+
+/**
+ * This is the default value of the validateWith attribute.
+ *
+ * @author Cedric Beust <cedric@beust.com>
+ */
+public class NoValidator implements IParameterValidator {
+
+  public void validate(String parameterName, String parameterValue)
+      throws ParameterException {
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/validators/NoValueValidator.java b/src/main/java/com/beust/jcommander/validators/NoValueValidator.java
new file mode 100644
index 0000000..21fa820
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/validators/NoValueValidator.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2011 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.validators;
+
+import com.beust.jcommander.IValueValidator;
+import com.beust.jcommander.ParameterException;
+
+/**
+ * This is the default value of the validateValueWith attribute.
+ *
+ * @author Cedric Beust <cedric@beust.com>
+ */
+public class NoValueValidator<T> implements IValueValidator<T> {
+
+  public void validate(String parameterName, T parameterValue)
+      throws ParameterException {
+  }
+
+}
diff --git a/src/main/java/com/beust/jcommander/validators/PositiveInteger.java b/src/main/java/com/beust/jcommander/validators/PositiveInteger.java
new file mode 100644
index 0000000..7b5c1b7
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/validators/PositiveInteger.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2011 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.validators;
+
+import com.beust.jcommander.IParameterValidator;
+import com.beust.jcommander.ParameterException;
+
+/**
+ * A validator that makes sure the value of the parameter is a positive integer.
+ *
+ * @author Cedric Beust <cedric@beust.com>
+ */
+public class PositiveInteger implements IParameterValidator {
+
+  public void validate(String name, String value)
+      throws ParameterException {
+    int n = Integer.parseInt(value);
+    if (n < 0) {
+      throw new ParameterException("Parameter " + name
+          + " should be positive (found " + value +")");
+    }
+  }
+
+}
diff --git a/src/main/license/license-header.txt b/src/main/license/license-header.txt
new file mode 100644
index 0000000..4cbe379
--- /dev/null
+++ b/src/main/license/license-header.txt
@@ -0,0 +1,15 @@
+Copyright (C) 2010 the original author or authors.

+See the notice.md file distributed with this work for additional

+information regarding copyright ownership.

+

+Licensed under the Apache License, Version 2.0 (the "License");

+you may not use this file except in compliance with the License.

+You may obtain a copy of the License at

+

+    http://www.apache.org/licenses/LICENSE-2.0

+

+Unless required by applicable law or agreed to in writing, software

+distributed under the License is distributed on an "AS IS" BASIS,

+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+See the License for the specific language governing permissions and

+limitations under the License.

diff --git a/src/test/java/com/beust/jcommander/ArgsRequiredWrongMain.java b/src/test/java/com/beust/jcommander/ArgsRequiredWrongMain.java
new file mode 100644
index 0000000..c2124d9
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/ArgsRequiredWrongMain.java
@@ -0,0 +1,6 @@
+package com.beust.jcommander;
+
+public class ArgsRequiredWrongMain {
+  @Parameter(required = true)
+  public String[] file;
+}
\ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/ArgsValidate2.java b/src/test/java/com/beust/jcommander/ArgsValidate2.java
new file mode 100644
index 0000000..2b8f07b
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/ArgsValidate2.java
@@ -0,0 +1,24 @@
+package com.beust.jcommander;
+
+import com.beust.jcommander.converters.FileConverter;
+
+import java.io.File;
+
+public class ArgsValidate2 {
+  public static class FailingValidator implements IValueValidator<File> {
+
+    public void validate(String name, File value) throws ParameterException {
+      throw new ParameterException("Validation will always fail:" + name + " " + value);
+    }
+    
+  }
+
+  public static final String POSSIBLE_TEMPLATE_FILE = "mayOrMayNotExist.template";
+
+  @Parameter(names = { "-template"},
+      description = "The default file may or may not exist",
+      converter = FileConverter.class, 
+      validateValueWith = FailingValidator.class
+      )
+  public File template = new File(POSSIBLE_TEMPLATE_FILE);
+}
diff --git a/src/test/java/com/beust/jcommander/CmdTest.java b/src/test/java/com/beust/jcommander/CmdTest.java
new file mode 100644
index 0000000..6601193
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/CmdTest.java
@@ -0,0 +1,86 @@
+package com.beust.jcommander;
+
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+public class CmdTest {
+
+    @Parameters(commandNames = "--cmd-one")
+    public static class CmdOne {
+    }
+
+    @Parameters(commandNames = "--cmd-two")
+    class CmdTwo {
+        @Parameter
+        List<String> params = new java.util.LinkedList<String>();
+    }
+
+    public String parseArgs(boolean withDefault, String[] args) {
+        JCommander jc = new JCommander();
+        jc.addCommand(new CmdOne());
+        jc.addCommand(new CmdTwo());
+
+        if (withDefault) {
+            // First check if a command was given, when not prepend default
+            // command (--cmd-two")
+            // In version up to 1.23 JCommander throws an Exception in this
+            // line,
+            // which might be incorrect, at least its not reasonable if the
+            // method
+            // is named "WithoutValidation".
+            jc.parseWithoutValidation(args);
+            if (jc.getParsedCommand() == null) {
+                LinkedList<String> newArgs = new LinkedList<String>();
+                newArgs.add("--cmd-two");
+                newArgs.addAll(Arrays.asList(args));
+                jc.parse(newArgs.toArray(new String[0]));
+            }
+        } else {
+            jc.parse(args);
+        }
+        return jc.getParsedCommand();
+    }
+
+    @DataProvider
+    public Object[][] testData() {
+        return new Object[][] {
+                new Object[] { "--cmd-one", false, new String[] { "--cmd-one" } },
+                new Object[] { "--cmd-two", false, new String[] { "--cmd-two" } },
+                new Object[] { "--cmd-two", false,
+                        new String[] { "--cmd-two", "param1", "param2" } },
+                // This is the relevant test case to test default commands
+                new Object[] { "--cmd-two", true,
+                        new String[] { "param1", "param2" } } };
+    }
+
+    @Test(dataProvider = "testData")
+    public void testArgsWithoutDefaultCmd(String expected,
+            boolean requireDefault, String[] args) {
+        if (!requireDefault) {
+            Assert.assertEquals(parseArgs(false, args), expected);
+        }
+    }
+
+    @Test(dataProvider = "testData", expectedExceptions = MissingCommandException.class)
+    public void testArgsWithoutDefaultCmdFail(String expected,
+            boolean requireDefault, String[] args) {
+        if (requireDefault) {
+            parseArgs(false, args);
+        } else {
+            throw new MissingCommandException("irrelevant test case");
+        }
+    }
+
+    // We do not expect a MissingCommandException!
+    @Test(dataProvider = "testData")
+    public void testArgsWithDefaultCmd(String expected, boolean requireDefault,
+            String[] args) {
+        Assert.assertEquals(parseArgs(true, args), expected);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/ConverterFactoryTest.java b/src/test/java/com/beust/jcommander/ConverterFactoryTest.java
new file mode 100644
index 0000000..e02166e
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/ConverterFactoryTest.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import com.beust.jcommander.args.ArgsConverterFactory;
+import com.beust.jcommander.args.ArgsMainParameter1;
+import com.beust.jcommander.args.ArgsMainParameter2;
+import com.beust.jcommander.args.IHostPorts;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Test the converter factory feature.
+ * 
+ * @author cbeust
+ */
+public class ConverterFactoryTest {
+  private static final Map<Class, Class<? extends IStringConverter<?>>> MAP = new HashMap() {{
+    put(HostPort.class, HostPortConverter.class);
+  }};
+
+  private static final IStringConverterFactory CONVERTER_FACTORY = new IStringConverterFactory() {
+
+    public Class<? extends IStringConverter<?>> getConverter(Class forType) {
+      return MAP.get(forType);
+    }
+    
+  };
+
+  @Test
+  public void parameterWithHostPortParameters() {
+    ArgsConverterFactory a = new ArgsConverterFactory();
+    JCommander jc = new JCommander(a);
+    jc.addConverterFactory(CONVERTER_FACTORY);
+    jc.parse("-hostport", "example.com:8080");
+
+    Assert.assertEquals(a.hostPort.host, "example.com");
+    Assert.assertEquals(a.hostPort.port.intValue(), 8080);
+  }
+
+  /**
+   * Test that main parameters can be used with string converters,
+   * either with a factory or from the annotation.
+   */
+  private void mainWithHostPortParameters(IStringConverterFactory f, IHostPorts a) {
+    JCommander jc = new JCommander(a);
+    if (f != null) jc.addConverterFactory(f);
+    jc.parse("a.com:10", "b.com:20");
+    Assert.assertEquals(a.getHostPorts().get(0).host, "a.com");
+    Assert.assertEquals(a.getHostPorts().get(0).port.intValue(), 10);
+    Assert.assertEquals(a.getHostPorts().get(1).host, "b.com");
+    Assert.assertEquals(a.getHostPorts().get(1).port.intValue(), 20);
+  }
+
+  @Test
+  public void mainWithoutFactory() {
+    mainWithHostPortParameters(null, new ArgsMainParameter1());
+  }
+
+  @Test
+  public void mainWithFactory() {
+    mainWithHostPortParameters(CONVERTER_FACTORY, new ArgsMainParameter2());
+  }
+
+}
+
diff --git a/src/test/java/com/beust/jcommander/DefaultProviderTest.java b/src/test/java/com/beust/jcommander/DefaultProviderTest.java
new file mode 100644
index 0000000..45ab6b6
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/DefaultProviderTest.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import com.beust.jcommander.args.ArgsDefault;
+import com.beust.jcommander.defaultprovider.PropertyFileDefaultProvider;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class DefaultProviderTest {
+  private static final IDefaultProvider DEFAULT_PROVIDER = new IDefaultProvider() {
+
+    public String getDefaultValueFor(String optionName) {
+      return "-debug".equals(optionName) ? "false" : "42";
+    }
+    
+  };
+
+  private ArgsDefault defaultProvider(IDefaultProvider provider, String... args) {
+    ArgsDefault a = new ArgsDefault();
+    JCommander jc = new JCommander(a);
+    jc.setDefaultProvider(provider);
+
+    jc.parse(args);
+    return a;
+  }
+
+  @Test
+  public void defaultProvider1() {
+    ArgsDefault a = defaultProvider(DEFAULT_PROVIDER, "f");
+
+    Assert.assertEquals(a.groups, "42");
+    Assert.assertEquals(a.level, 42);
+    Assert.assertEquals(a.log.intValue(), 42);
+  }
+
+  @Test
+  public void defaultProvider2() {
+    ArgsDefault a = defaultProvider(DEFAULT_PROVIDER, "-groups", "foo", "f");
+
+    Assert.assertEquals(a.groups, "foo");
+    Assert.assertEquals(a.level, 42);
+    Assert.assertEquals(a.log.intValue(), 42);
+  }
+
+  @Test
+  public void defaultProvider3() {
+    ArgsDefault a = defaultProvider(DEFAULT_PROVIDER, "-groups", "foo", "-level", "13", "f");
+
+    Assert.assertEquals(a.groups, "foo");
+    Assert.assertEquals(a.level, 13);
+    Assert.assertEquals(a.log.intValue(), 42);
+  }
+
+  @Test
+  public void defaultProvider4() {
+    ArgsDefault a = defaultProvider(DEFAULT_PROVIDER,
+        "-log", "19", "-groups", "foo", "-level", "13", "f");
+
+    Assert.assertEquals(a.groups, "foo");
+    Assert.assertEquals(a.level, 13);
+    Assert.assertEquals(a.log.intValue(), 19);
+  }
+
+  @Test
+  public void propertyFileDefaultProvider1() {
+    ArgsDefault a = defaultProvider(new PropertyFileDefaultProvider(), "f");
+
+    Assert.assertEquals(a.groups, "unit");
+    Assert.assertEquals(a.level, 17);
+    Assert.assertEquals(a.log.intValue(), 18);
+  }
+
+  @Test
+  public void propertyFileDefaultProvider2() {
+    ArgsDefault a = defaultProvider(new PropertyFileDefaultProvider(), "-groups", "foo", "f");
+    
+    Assert.assertEquals(a.groups, "foo");
+    Assert.assertEquals(a.level, 17);
+    Assert.assertEquals(a.log.intValue(), 18);
+  }
+
+  @Test
+  public void propertyFileDefaultProvider3() {
+    ArgsDefault a = defaultProvider(new PropertyFileDefaultProvider(),
+        "-groups", "foo", "-level", "13", "f");
+
+    Assert.assertEquals(a.groups, "foo");
+    Assert.assertEquals(a.level, 13);
+    Assert.assertEquals(a.log.intValue(), 18);
+  }
+
+  @Test
+  public void propertyFileDefaultProvider4() {
+    ArgsDefault a = defaultProvider(new PropertyFileDefaultProvider(),
+        "-log", "19", "-groups", "foo", "-level", "13", "f");
+
+    Assert.assertEquals(a.groups, "foo");
+    Assert.assertEquals(a.level, 13);
+    Assert.assertEquals(a.log.intValue(), 19);
+  }
+
+}
diff --git a/src/test/java/com/beust/jcommander/DefaultValueTest.java b/src/test/java/com/beust/jcommander/DefaultValueTest.java
new file mode 100644
index 0000000..3b1f29c
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/DefaultValueTest.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import com.beust.jcommander.internal.Lists;
+import com.beust.jcommander.internal.Sets;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test behaviour of default parameter values
+ * @author rodionmoiseev
+ */
+public class DefaultValueTest {
+  @Test
+  public void emptyDefaultValueForListParameterStaysEmptyIfNotAssignedOrIsSetOtherwise() {
+    MyOptsWithEmptyDefaults opts = new MyOptsWithEmptyDefaults();
+    JCommander cmd = new JCommander(opts);
+    cmd.parse(new String[]{"-a", "anotherValue"});
+    Assert.assertEquals(opts.list.size(), 1);
+    Assert.assertEquals(opts.list.get(0), "anotherValue");
+    Assert.assertEquals(opts.set.size(), 0);
+  }
+
+  @Test
+  public void defaultValueForListParametersGetsOverwrittenWithSpecifiedValueOrStaysAsDefaultOtherwise() {
+    MyOptsWithDefaultValues opts = new MyOptsWithDefaultValues();
+    JCommander cmd = new JCommander(opts);
+    cmd.parse(new String[]{"-a", "anotherValue"});
+    Assert.assertEquals(opts.list.size(), 1);
+    Assert.assertEquals(opts.list.get(0), "anotherValue");
+    Assert.assertEquals(opts.set.size(), 1);
+    Assert.assertEquals(opts.set.iterator().next(), "defaultValue");
+  }
+
+  @Test
+  public void anyNumberOfValuesCanBeSetToListParameters_ForEmptyDefaults(){
+    MyOptsWithEmptyDefaults opts = new MyOptsWithEmptyDefaults();
+    testSettingMultipleValuesToListTypeParameters(opts);
+  }
+
+  @Test
+  public void anyNumberOfValuesCanBeSetToListParameters_ForNonEmptyDefaults(){
+    MyOptsWithDefaultValues opts = new MyOptsWithDefaultValues();
+    testSettingMultipleValuesToListTypeParameters(opts);
+  }
+
+  private void testSettingMultipleValuesToListTypeParameters(MyOpts opts) {
+    JCommander cmd = new JCommander(opts);
+    cmd.parse(new String[]{"-a", "anotherValue", "-a", "anotherValue2",
+                           "-b", "anotherValue3", "-b", "anotherValue4"});
+    Assert.assertEquals(opts.list.size(), 2);
+    Assert.assertEquals(opts.list.get(0), "anotherValue");
+    Assert.assertEquals(opts.list.get(1), "anotherValue2");
+    Assert.assertEquals(opts.set.size(), 2);
+    Iterator<String> arg2it = opts.set.iterator();
+    Assert.assertEquals(arg2it.next(), "anotherValue3");
+    Assert.assertEquals(arg2it.next(), "anotherValue4");
+  }
+
+  public static class MyOpts {
+    @Parameter(names = "-a")
+    public List<String> list;
+    @Parameter(names = "-b")
+    public Set<String> set;
+  }
+
+  public static final class MyOptsWithDefaultValues extends MyOpts {
+    public MyOptsWithDefaultValues(){
+      this.list = singletonList("defaultValue");
+      this.set = singletonSet("defaultValue");
+    }
+  }
+
+  public static final class MyOptsWithEmptyDefaults extends MyOpts {
+    public MyOptsWithEmptyDefaults(){
+      this.list = Lists.newArrayList();
+      this.set = Sets.newLinkedHashSet();
+    }
+  }
+
+  public static final List<String> singletonList(String value) {
+    List<String> list = Lists.newArrayList();
+    list.add(value);
+    return list;
+  }
+
+  public static final Set<String> singletonSet(String value){
+    Set<String> set = Sets.newLinkedHashSet();
+    set.add(value);
+    return set;
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/FinderTest.java b/src/test/java/com/beust/jcommander/FinderTest.java
new file mode 100644
index 0000000..94bf812
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/FinderTest.java
@@ -0,0 +1,97 @@
+package com.beust.jcommander;
+
+import com.beust.jcommander.JCommanderTest.BaseArgs;
+import com.beust.jcommander.JCommanderTest.ConfigureArgs;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@Test
+public class FinderTest {
+  public void caseInsensitiveOption() {
+    class Arg {
+  
+      @Parameter(names = { "-p", "--param" })
+      private String param;
+    }
+    Arg a = new Arg();
+    JCommander jc = new JCommander(a);
+    jc.setCaseSensitiveOptions(false);
+    jc.parse(new String[] { "--PARAM", "foo" });
+    Assert.assertEquals(a.param, "foo");
+  }
+
+  public void caseInsensitiveCommand() {
+    BaseArgs a = new BaseArgs();
+    ConfigureArgs conf = new ConfigureArgs();
+    JCommander jc = new JCommander(a);
+    jc.addCommand(conf);
+    jc.setCaseSensitiveOptions(false);
+//    jc.setCaseSensitiveCommands(false);
+    jc.parse("--CONFIGURE");
+    String command = jc.getParsedCommand();
+    Assert.assertEquals(command, "--configure");
+  }
+
+  public void abbreviatedOptions() {
+    class Arg {
+      @Parameter(names = { "-p", "--param" })
+      private String param;
+    }
+    Arg a = new Arg();
+    JCommander jc = new JCommander(a);
+    jc.setAllowAbbreviatedOptions(true);
+    jc.parse(new String[] { "--par", "foo" });
+    Assert.assertEquals(a.param, "foo");
+  }
+
+  public void abbreviatedOptionsCaseInsensitive() {
+    class Arg {
+      @Parameter(names = { "-p", "--param" })
+      private String param;
+    }
+    Arg a = new Arg();
+    JCommander jc = new JCommander(a);
+    jc.setCaseSensitiveOptions(false);
+    jc.setAllowAbbreviatedOptions(true);
+    jc.parse(new String[] { "--PAR", "foo" });
+    Assert.assertEquals(a.param, "foo");
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void ambiguousAbbreviatedOptions() {
+    class Arg {
+      @Parameter(names = { "--param" })
+      private String param;
+      @Parameter(names = { "--parb" })
+      private String parb;
+    }
+    Arg a = new Arg();
+    JCommander jc = new JCommander(a);
+    jc.setAllowAbbreviatedOptions(true);
+    jc.parse(new String[] { "--par", "foo" });
+    Assert.assertEquals(a.param, "foo");
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void ambiguousAbbreviatedOptionsCaseInsensitive() {
+    class Arg {
+      @Parameter(names = { "--param" })
+      private String param;
+      @Parameter(names = { "--parb" })
+      private String parb;
+    }
+    Arg a = new Arg();
+    JCommander jc = new JCommander(a);
+    jc.setCaseSensitiveOptions(false);
+    jc.setAllowAbbreviatedOptions(true);
+    jc.parse(new String[] { "--PAR", "foo" });
+    Assert.assertEquals(a.param, "foo");
+  }
+
+  @Test(enabled = false)
+  public static void main(String[] args) throws Exception {
+    new FinderTest().ambiguousAbbreviatedOptionsCaseInsensitive();
+  }
+
+}
diff --git a/src/test/java/com/beust/jcommander/HostPort.java b/src/test/java/com/beust/jcommander/HostPort.java
new file mode 100644
index 0000000..a18018e
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/HostPort.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+public class HostPort {
+  public String host;
+  public Integer port;
+  @Override
+
+  public String toString() {
+    return "[Host:" + host + " port:" + port + "]";
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/HostPortConverter.java b/src/test/java/com/beust/jcommander/HostPortConverter.java
new file mode 100644
index 0000000..f45e3ba
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/HostPortConverter.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+public class HostPortConverter implements IStringConverter<HostPort> {
+
+  public HostPort convert(String value) {
+    HostPort result = new HostPort();
+    String[] s = value.split(":");
+    result.host = s[0];
+    result.port = Integer.parseInt(s[1]);
+
+    return result;
+  }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/JCommanderTest.java b/src/test/java/com/beust/jcommander/JCommanderTest.java
new file mode 100644
index 0000000..ad2c5e8
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/JCommanderTest.java
@@ -0,0 +1,1081 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.TreeSet;
+
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.beust.jcommander.args.AlternateNamesForListArgs;
+import com.beust.jcommander.args.Args1;
+import com.beust.jcommander.args.Args1Setter;
+import com.beust.jcommander.args.Args2;
+import com.beust.jcommander.args.ArgsArityString;
+import com.beust.jcommander.args.ArgsBooleanArity;
+import com.beust.jcommander.args.ArgsBooleanArity0;
+import com.beust.jcommander.args.ArgsConverter;
+import com.beust.jcommander.args.ArgsEnum;
+import com.beust.jcommander.args.ArgsEnum.ChoiceType;
+import com.beust.jcommander.args.ArgsEquals;
+import com.beust.jcommander.args.ArgsHelp;
+import com.beust.jcommander.args.ArgsI18N1;
+import com.beust.jcommander.args.ArgsI18N2;
+import com.beust.jcommander.args.ArgsI18N2New;
+import com.beust.jcommander.args.ArgsInherited;
+import com.beust.jcommander.args.ArgsList;
+import com.beust.jcommander.args.ArgsMainParameter1;
+import com.beust.jcommander.args.ArgsMaster;
+import com.beust.jcommander.args.ArgsMultipleUnparsed;
+import com.beust.jcommander.args.ArgsOutOfMemory;
+import com.beust.jcommander.args.ArgsPrivate;
+import com.beust.jcommander.args.ArgsRequired;
+import com.beust.jcommander.args.ArgsSlave;
+import com.beust.jcommander.args.ArgsSlaveBogus;
+import com.beust.jcommander.args.ArgsValidate1;
+import com.beust.jcommander.args.ArgsWithSet;
+import com.beust.jcommander.args.Arity1;
+import com.beust.jcommander.args.SeparatorColon;
+import com.beust.jcommander.args.SeparatorEqual;
+import com.beust.jcommander.args.SeparatorMixed;
+import com.beust.jcommander.args.SlashSeparator;
+import com.beust.jcommander.args.VariableArity;
+import com.beust.jcommander.command.CommandAdd;
+import com.beust.jcommander.command.CommandCommit;
+import com.beust.jcommander.command.CommandMain;
+import com.beust.jcommander.internal.Lists;
+import com.beust.jcommander.internal.Maps;
+
+@Test
+public class JCommanderTest {
+  public void simpleArgs() throws ParseException {
+    Args1 args = new Args1();
+    String[] argv = { "-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
+            "-date", "2011-10-26", "-groups", "unit", "a", "b", "c" };
+    new JCommander(args, argv);
+
+    Assert.assertTrue(args.debug);
+    Assert.assertEquals(args.verbose.intValue(), 2);
+    Assert.assertEquals(args.groups, "unit");
+    Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
+    Assert.assertEquals(args.floa, 1.2f, 0.1f);
+    Assert.assertEquals(args.doub, 1.3f, 0.1f);
+    Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
+    Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
+  }
+
+  @DataProvider
+  public Object[][] alternateNamesListArgs() {
+    return new Object[][] {
+        new String[][] {new String[] {"--servers", "1", "-s", "2", "--servers", "3"}},
+        new String[][] {new String[] {"-s", "1", "-s", "2", "--servers", "3"}},
+        new String[][] {new String[] {"--servers", "1", "--servers", "2", "-s", "3"}},
+        new String[][] {new String[] {"-s", "1", "--servers", "2", "-s", "3"}},
+        new String[][] {new String[] {"-s", "1", "-s", "2", "--servers", "3"}},
+    };
+  }
+  
+  /**
+   *  Confirm that List<?> parameters with alternate names return the correct
+   * List regardless of how the arguments are specified
+   */
+  
+  @Test(dataProvider = "alternateNamesListArgs")
+  public void testAlternateNamesForListArguments(String[] argv) {
+      AlternateNamesForListArgs args = new AlternateNamesForListArgs();
+      
+      new JCommander(args, argv);
+      
+      Assert.assertEquals(args.serverNames.size(), 3);
+      Assert.assertEquals(args.serverNames.get(0), argv[1]);
+      Assert.assertEquals(args.serverNames.get(1), argv[3]);
+      Assert.assertEquals(args.serverNames.get(2), argv[5]);
+  }
+  
+  
+  /**
+   * Make sure that if there are args with multiple names (e.g. "-log" and "-verbose"),
+   * the usage will only display it once.
+   */
+  public void repeatedArgs() {
+    Args1 args = new Args1();
+    String[] argv = { "-log", "2" };
+    JCommander jc = new JCommander(args, argv);
+    Assert.assertEquals(jc.getParameters().size(), 8);
+  }
+
+  /**
+   * Not specifying a required option should throw an exception.
+   */
+  @Test(expectedExceptions = ParameterException.class)
+  public void requiredFields1Fail() {
+    Args1 args = new Args1();
+    String[] argv = { "-debug" };
+    new JCommander(args, argv);
+  }
+
+  /**
+   * Getting the description of a nonexistent command should throw an exception.
+   */
+  @Test(expectedExceptions = ParameterException.class)
+  public void nonexistentCommandShouldThrow() {
+    String[] argv = { };
+    JCommander jc = new JCommander(new Object(), argv);
+    jc.getCommandDescription("foo");
+  }
+
+  /**
+   * Required options with multiple names should work with all names.
+   */
+  private void multipleNames(String option) {
+    Args1 args = new Args1();
+    String[] argv = { option, "2" };
+    new JCommander(args, argv);
+    Assert.assertEquals(args.verbose.intValue(), 2);
+  }
+
+  public void multipleNames1() {
+    multipleNames("-log");
+  }
+
+  public void multipleNames2() {
+    multipleNames("-verbose");
+  }
+
+  private void i18n1(String bundleName, Locale locale, String expectedString) {
+    ResourceBundle bundle = locale != null ? ResourceBundle.getBundle(bundleName, locale)
+        : null;
+
+    ArgsI18N1 i18n = new ArgsI18N1();
+    String[] argv = { "-host", "localhost" };
+    JCommander jc = new JCommander(i18n, bundle, argv);
+//    jc.usage();
+
+    ParameterDescription pd = jc.getParameters().get(0);
+    Assert.assertEquals(pd.getDescription(), expectedString);
+  }
+
+  public void i18nNoLocale() {
+    i18n1("MessageBundle", null, "Host");
+  }
+
+  public void i18nUsLocale() {
+    i18n1("MessageBundle", new Locale("en", "US"), "Host");
+  }
+
+  public void i18nFrLocale() {
+    i18n1("MessageBundle", new Locale("fr", "FR"), "Hôte");
+  }
+
+  private void i18n2(Object i18n) {
+    String[] argv = { "-host", "localhost" };
+    Locale.setDefault(new Locale("fr", "FR"));
+    JCommander jc = new JCommander(i18n, argv);
+    ParameterDescription pd = jc.getParameters().get(0);
+    Assert.assertEquals(pd.getDescription(), "Hôte");
+  }
+
+  public void i18nWithResourceAnnotation() {
+    i18n2(new ArgsI18N2());
+  }
+
+  public void i18nWithResourceAnnotationNew() {
+    i18n2(new ArgsI18N2New());
+  }
+
+  public void noParseConstructor() {
+    JCommander jCommander = new JCommander(new ArgsMainParameter1());
+    jCommander.usage(new StringBuilder());
+    // Before fix, this parse would throw an exception, because it calls createDescription, which
+    // was already called by usage(), and can only be called once.
+    jCommander.parse();
+  }
+
+  /**
+   * Test a use case where there are required parameters, but you still want
+   * to interrogate the options which are specified.
+   */
+  public void usageWithRequiredArgsAndResourceBundle() {
+    ArgsHelp argsHelp = new ArgsHelp();
+    JCommander jc = new JCommander(new Object[]{argsHelp, new ArgsRequired()},
+        java.util.ResourceBundle.getBundle("MessageBundle"));
+    // Should be able to display usage without triggering validation
+    jc.usage(new StringBuilder());
+    try {
+      jc.parse("-h");
+      Assert.fail("Should have thrown a required parameter exception");
+    } catch (ParameterException e) {
+      Assert.assertTrue(e.getMessage().contains("are required"));
+    }
+    Assert.assertTrue(argsHelp.help);
+  }
+
+  public void multiObjects() {
+    ArgsMaster m = new ArgsMaster();
+    ArgsSlave s = new ArgsSlave();
+    String[] argv = { "-master", "master", "-slave", "slave" };
+    new JCommander(new Object[] { m , s }, argv);
+
+    Assert.assertEquals(m.master, "master");
+    Assert.assertEquals(s.slave, "slave");
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void multiObjectsWithDuplicatesFail() {
+    ArgsMaster m = new ArgsMaster();
+    ArgsSlave s = new ArgsSlaveBogus();
+    String[] argv = { "-master", "master", "-slave", "slave" };
+    new JCommander(new Object[] { m , s }, argv);
+  }
+
+  public void arityString() {
+    ArgsArityString args = new ArgsArityString();
+    String[] argv = { "-pairs", "pair0", "pair1", "rest" };
+    new JCommander(args, argv);
+
+    Assert.assertEquals(args.pairs.size(), 2);
+    Assert.assertEquals(args.pairs.get(0), "pair0");
+    Assert.assertEquals(args.pairs.get(1), "pair1");
+    Assert.assertEquals(args.rest.size(), 1);
+    Assert.assertEquals(args.rest.get(0), "rest");
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void arity2Fail() {
+    ArgsArityString args = new ArgsArityString();
+    String[] argv = { "-pairs", "pair0" };
+    new JCommander(args, argv);
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void multipleUnparsedFail() {
+    ArgsMultipleUnparsed args = new ArgsMultipleUnparsed();
+    String[] argv = { };
+    new JCommander(args, argv);
+  }
+
+  public void privateArgs() {
+    ArgsPrivate args = new ArgsPrivate();
+    new JCommander(args, "-verbose", "3");
+    Assert.assertEquals(args.getVerbose().intValue(), 3);
+  }
+
+  public void converterArgs() {
+    ArgsConverter args = new ArgsConverter();
+    String fileName = "a";
+    new JCommander(args, "-file", "/tmp/" + fileName, 
+      "-listStrings", "Tuesday,Thursday",
+      "-listInts", "-1,8",
+      "-listBigDecimals", "-11.52,100.12");
+    Assert.assertEquals(args.file.getName(), fileName);
+    Assert.assertEquals(args.listStrings.size(), 2);
+    Assert.assertEquals(args.listStrings.get(0), "Tuesday");
+    Assert.assertEquals(args.listStrings.get(1), "Thursday");
+    Assert.assertEquals(args.listInts.size(), 2);
+    Assert.assertEquals(args.listInts.get(0).intValue(), -1);
+    Assert.assertEquals(args.listInts.get(1).intValue(), 8);
+    Assert.assertEquals(args.listBigDecimals.size(), 2);
+    Assert.assertEquals(args.listBigDecimals.get(0), new BigDecimal("-11.52"));
+    Assert.assertEquals(args.listBigDecimals.get(1), new BigDecimal("100.12"));
+  }
+
+  private void argsBoolean1(String[] params, Boolean expected) {
+    ArgsBooleanArity args = new ArgsBooleanArity();
+    new JCommander(args, params);
+    Assert.assertEquals(args.debug, expected);
+  }
+
+  private void argsBoolean0(String[] params, Boolean expected) {
+    ArgsBooleanArity0 args = new ArgsBooleanArity0();
+    new JCommander(args, params);
+    Assert.assertEquals(args.debug, expected);
+  }
+
+  public void booleanArity1() {
+    argsBoolean1(new String[] {}, Boolean.FALSE);
+    argsBoolean1(new String[] { "-debug", "true" }, Boolean.TRUE);
+  }
+
+  public void booleanArity0() {
+    argsBoolean0(new String[] {}, Boolean.FALSE);
+    argsBoolean0(new String[] { "-debug"}, Boolean.TRUE);
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void badParameterShouldThrowParameter1Exception() {
+    Args1 args = new Args1();
+    String[] argv = { "-log", "foo" };
+    new JCommander(args, argv);
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void badParameterShouldThrowParameter2Exception() {
+    Args1 args = new Args1();
+    String[] argv = { "-long", "foo" };
+    new JCommander(args, argv);
+  }
+
+  public void listParameters() {
+    Args2 a = new Args2();
+    String[] argv = {"-log", "2", "-groups", "unit", "a", "b", "c", "-host", "host2"};
+    new JCommander(a, argv);
+    Assert.assertEquals(a.verbose.intValue(), 2);
+    Assert.assertEquals(a.groups, "unit");
+    Assert.assertEquals(a.hosts, Arrays.asList("host2"));
+    Assert.assertEquals(a.parameters, Arrays.asList("a", "b", "c"));
+  }
+
+  public void separatorEqual() {
+    SeparatorEqual s = new SeparatorEqual();
+    String[] argv = { "-log=3", "--longoption=10" };
+    new JCommander(s, argv);
+    Assert.assertEquals(s.log.intValue(), 3);
+    Assert.assertEquals(s.longOption.intValue(), 10);
+  }
+
+  public void separatorColon() {
+    SeparatorColon s = new SeparatorColon();
+    String[] argv = { "-verbose:true" };
+    new JCommander(s, argv);
+    Assert.assertTrue(s.verbose);
+  }
+
+  public void separatorBoth() {
+    SeparatorColon s = new SeparatorColon();
+    SeparatorEqual s2 = new SeparatorEqual();
+    String[] argv = { "-verbose:true", "-log=3" };
+    new JCommander(new Object[] { s, s2 }, argv);
+    Assert.assertTrue(s.verbose);
+    Assert.assertEquals(s2.log.intValue(), 3);
+  }
+
+  public void separatorMixed1() {
+    SeparatorMixed s = new SeparatorMixed();
+    String[] argv = { "-long:1", "-level=42" };
+    new JCommander(s, argv);
+    Assert.assertEquals(s.l.longValue(), 1l);
+    Assert.assertEquals(s.level.intValue(), 42);
+  }
+
+  public void slashParameters() {
+    SlashSeparator a = new SlashSeparator();
+    String[] argv = { "/verbose", "/file", "/tmp/a" };
+    new JCommander(a, argv);
+    Assert.assertTrue(a.verbose);
+    Assert.assertEquals(a.file, "/tmp/a");
+  }
+
+  public void inheritance() {
+    ArgsInherited args = new ArgsInherited();
+    String[] argv = { "-log", "3", "-child", "2" };
+    new JCommander(args, argv);
+    Assert.assertEquals(args.child.intValue(), 2);
+    Assert.assertEquals(args.log.intValue(), 3);
+  }
+
+  public void negativeNumber() {
+    Args1 a = new Args1();
+    String[] argv = { "-verbose", "-3" };
+    new JCommander(a, argv);
+    Assert.assertEquals(a.verbose.intValue(), -3);
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void requiredMainParameters() {
+    ArgsRequired a = new ArgsRequired();
+    String[] argv = {};
+    new JCommander(a, argv);
+  }
+
+  public void usageShouldNotChange() {
+    JCommander jc = new JCommander(new Args1(), new String[]{"-log", "1"});
+    StringBuilder sb = new StringBuilder();
+    jc.usage(sb);
+    String expected = sb.toString();
+    jc = new JCommander(new Args1(), new String[]{"-debug", "-log", "2", "-long", "5"});
+    sb = new StringBuilder();
+    jc.usage(sb);
+    String actual = sb.toString();
+    Assert.assertEquals(actual, expected);
+  }
+
+  private void verifyCommandOrdering(String[] commandNames, Object[] commands) {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+
+    for (int i = 0; i < commands.length; i++) {
+      jc.addCommand(commandNames[i], commands[i]);
+    }
+
+    Map<String, JCommander> c = jc.getCommands();
+    Assert.assertEquals(c.size(), commands.length);
+
+    Iterator<String> it = c.keySet().iterator();
+    for (int i = 0; i < commands.length; i++) {
+      Assert.assertEquals(it.next(), commandNames[i]);
+    }
+  }
+
+  public void commandsShouldBeShownInOrderOfInsertion() {
+    verifyCommandOrdering(new String[] { "add", "commit" },
+        new Object[] { new CommandAdd(), new CommandCommit() });
+    verifyCommandOrdering(new String[] { "commit", "add" },
+        new Object[] { new CommandCommit(), new CommandAdd() });
+  }
+
+  @DataProvider
+  public static Object[][] f() {
+    return new Integer[][] {
+      new Integer[] { 3, 5, 1 },
+      new Integer[] { 3, 8, 1 },
+      new Integer[] { 3, 12, 2 },
+      new Integer[] { 8, 12, 2 },
+      new Integer[] { 9, 10, 1 },
+    };
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void arity1Fail() {
+    final Arity1 arguments = new Arity1();
+    final JCommander jCommander = new JCommander(arguments);
+    final String[] commands = {
+        "-inspect"
+    };
+    jCommander.parse(commands);
+  }
+
+  public void arity1Success1() {
+    final Arity1 arguments = new Arity1();
+    final JCommander jCommander = new JCommander(arguments);
+    final String[] commands = {
+        "-inspect", "true"
+    };
+    jCommander.parse(commands);
+    Assert.assertTrue(arguments.inspect);
+  }
+
+  public void arity1Success2() {
+    final Arity1 arguments = new Arity1();
+    final JCommander jCommander = new JCommander(arguments);
+    final String[] commands = {
+        "-inspect", "false"
+    };
+    jCommander.parse(commands);
+    Assert.assertFalse(arguments.inspect);
+  }
+
+  @Parameters(commandDescription = "Help for the given commands.")
+  public static class Help {
+      public static final String NAME = "help";
+
+      @Parameter(description = "List of commands.")
+      public List<String> commands=new ArrayList<String>();
+  }
+
+  @Test(expectedExceptions = ParameterException.class,
+      description = "Verify that the main parameter's type is checked to be a List")
+  public void wrongMainTypeShouldThrow() {
+    JCommander jc = new JCommander(new ArgsRequiredWrongMain());
+    jc.parse(new String[] { "f1", "f2" });
+  }
+
+  @Test(description = "This used to run out of memory")
+  public void oom() {
+    JCommander jc = new JCommander(new ArgsOutOfMemory());
+    jc.usage(new StringBuilder());
+  }
+
+  @Test
+  public void getParametersShouldNotNpe() {
+    JCommander jc = new JCommander(new Args1());
+    List<ParameterDescription> parameters = jc.getParameters();
+  }
+
+  public void validationShouldWork1() {
+    ArgsValidate1 a = new ArgsValidate1();
+    JCommander jc = new JCommander(a);
+    jc.parse(new String[] { "-age", "2 "});
+    Assert.assertEquals(a.age, new Integer(2));
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void validationShouldWorkWithDefaultValues() {
+    ArgsValidate2 a = new ArgsValidate2();
+    new JCommander(a);
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void validationShouldWork2() {
+    ArgsValidate1 a = new ArgsValidate1();
+    JCommander jc = new JCommander(a);
+    jc.parse(new String[] { "-age", "-2 "});
+  }
+
+  public void atFileCanContainEmptyLines() throws IOException {
+    File f = File.createTempFile("JCommander", null);
+    f.deleteOnExit();
+    FileWriter fw = new FileWriter(f);
+    fw.write("-log\n");
+    fw.write("\n");
+    fw.write("2\n");
+    fw.close();
+    new JCommander(new Args1(), "@" + f.getAbsolutePath());
+  }
+
+  public void handleEqualSigns() {
+    ArgsEquals a = new ArgsEquals();
+    JCommander jc = new JCommander(a);
+    jc.parse(new String[] { "-args=a=b,b=c" });
+    Assert.assertEquals(a.args, "a=b,b=c");
+  }
+
+  @SuppressWarnings("serial")
+  public void handleSets() {
+    ArgsWithSet a = new ArgsWithSet();
+    new JCommander(a, new String[] { "-s", "3,1,2" });
+    Assert.assertEquals(a.set, new TreeSet<Integer>() {{ add(1); add(2); add(3); }});
+  }
+
+  private static final List<String> V = Arrays.asList("a", "b", "c", "d");
+
+  @DataProvider
+  public Object[][] variable() {
+    return new Object[][] {
+        new Object[] { 0, V.subList(0, 0), V },
+        new Object[] { 1, V.subList(0, 1), V.subList(1, 4) },
+        new Object[] { 2, V.subList(0, 2), V.subList(2, 4) },
+        new Object[] { 3, V.subList(0, 3), V.subList(3, 4) },
+        new Object[] { 4, V.subList(0, 4), V.subList(4, 4) },
+    };
+  }
+
+  @Test(dataProvider = "variable")
+  public void variableArity(int count, List<String> var, List<String> main) {
+    VariableArity va = new VariableArity(count);
+    new JCommander(va).parse("-variable", "a", "b", "c", "d");
+    Assert.assertEquals(var, va.var);
+    Assert.assertEquals(main, va.main);
+  }
+
+  public void enumArgs() {
+    ArgsEnum args = new ArgsEnum();
+    String[] argv = { "-choice", "ONE", "-choices", "ONE", "Two" };
+    JCommander jc = new JCommander(args, argv);
+
+    Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
+
+    List<ChoiceType> expected = Arrays.asList(ChoiceType.ONE, ChoiceType.Two);
+    Assert.assertEquals(expected, args.choices);
+    Assert.assertEquals(jc.getParameters().get(0).getDescription(),
+        "Options: " + EnumSet.allOf((Class<? extends Enum>) ArgsEnum.ChoiceType.class));
+
+  }
+
+  public void enumArgsCaseInsensitive() {
+      ArgsEnum args = new ArgsEnum();
+      String[] argv = { "-choice", "one"};
+      JCommander jc = new JCommander(args, argv);
+
+      Assert.assertEquals(args.choice, ArgsEnum.ChoiceType.ONE);
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void enumArgsFail() {
+    ArgsEnum args = new ArgsEnum();
+    String[] argv = { "-choice", "A" };
+    new JCommander(args, argv);
+  }
+
+  public void testListAndSplitters() {
+    ArgsList al = new ArgsList();
+    JCommander j = new JCommander(al);
+    j.parse("-groups", "a,b", "-ints", "41,42", "-hp", "localhost:1000;example.com:1001",
+        "-hp2", "localhost:1000,example.com:1001", "-uppercase", "ab,cd");
+    Assert.assertEquals(al.groups.get(0), "a");
+    Assert.assertEquals(al.groups.get(1), "b");
+    Assert.assertEquals(al.ints.get(0).intValue(), 41);
+    Assert.assertEquals(al.ints.get(1).intValue(), 42);
+    Assert.assertEquals(al.hostPorts.get(0).host, "localhost");
+    Assert.assertEquals(al.hostPorts.get(0).port.intValue(), 1000);
+    Assert.assertEquals(al.hostPorts.get(1).host, "example.com");
+    Assert.assertEquals(al.hostPorts.get(1).port.intValue(), 1001);
+    Assert.assertEquals(al.hp2.get(1).host, "example.com");
+    Assert.assertEquals(al.hp2.get(1).port.intValue(), 1001);
+    Assert.assertEquals(al.uppercase.get(0), "AB");
+    Assert.assertEquals(al.uppercase.get(1), "CD");
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void shouldThrowIfUnknownOption() {
+    class A {
+      @Parameter(names = "-long")
+      public long l;
+    }
+    A a = new A();
+    new JCommander(a).parse("-lon", "32");
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void mainParameterShouldBeValidate() {
+    class V implements IParameterValidator {
+
+      @Override
+      public void validate(String name, String value) throws ParameterException {
+        Assert.assertEquals("a", value);
+      }
+    }
+
+    class A {
+      @Parameter(validateWith = V.class)
+      public List<String> m;
+    }
+
+    A a = new A();
+    new JCommander(a).parse("b");
+  }
+
+  @Parameters(commandNames = { "--configure" })
+  public static class ConfigureArgs {
+  }
+
+  public static class BaseArgs {
+    @Parameter(names = { "-h", "--help" }, description = "Show this help screen")
+    private boolean help = false;
+
+    @Parameter(names = { "--version", "-version" }, description = "Show the program version")
+    private boolean version;
+  }
+
+  public void commandsWithSamePrefixAsOptionsShouldWork() {
+    BaseArgs a = new BaseArgs();
+    ConfigureArgs conf = new ConfigureArgs();
+    JCommander jc = new JCommander(a);
+    jc.addCommand(conf);
+    jc.parse("--configure");
+  }
+
+  // Tests:
+  // required unparsed parameter
+  @Test(enabled = false,
+      description = "For some reason, this test still asks the password on stdin")
+  public void askedRequiredPassword() {
+    class A {     
+        @Parameter(names = { "--password", "-p" }, description = "Private key password", 
+            password = true, required = true)
+        public String password;
+
+        @Parameter(names = { "--port", "-o" }, description = "Port to bind server to",
+            required = true)
+        public int port;
+    }
+    A a = new A();
+    InputStream stdin = System.in;
+    try {
+      System.setIn(new ByteArrayInputStream("password".getBytes()));      
+      new JCommander(a,new String[]{"--port", "7","--password"});
+      Assert.assertEquals(a.port, 7);
+      Assert.assertEquals(a.password, "password");
+    } finally {
+      System.setIn(stdin);
+    }
+  }
+
+  public void dynamicParameters() {
+    class Command {
+      @DynamicParameter(names = {"-P"}, description = "Additional command parameters")
+      private Map<String, String> params = Maps.newHashMap();
+    }
+    JCommander commander = new JCommander();
+    Command c = new Command();
+    commander.addCommand("command", c);
+    commander.parse(new String[] { "command", "-Pparam='name=value'" });
+    Assert.assertEquals(c.params.get("param"), "'name=value'");
+  }
+
+  public void exeParser() {
+      class Params {
+        @Parameter( names= "-i")
+        private String inputFile;
+      }
+
+      String args[] = { "-i", "" };
+      Params p = new Params();
+      new JCommander(p, args);
+  }
+
+  public void multiVariableArityList() {
+    class Params {
+      @Parameter(names = "-paramA", description = "ParamA", variableArity = true)
+      private List<String> paramA = Lists.newArrayList();
+
+      @Parameter(names = "-paramB", description = "ParamB", variableArity = true)
+      private List<String> paramB = Lists.newArrayList();
+    }
+
+    {
+      String args[] = { "-paramA", "a1", "a2", "-paramB", "b1", "b2", "b3" };
+      Params p = new Params();
+      new JCommander(p, args).parse();
+      Assert.assertEquals(p.paramA, Arrays.asList(new String[] { "a1", "a2" }));
+      Assert.assertEquals(p.paramB, Arrays.asList(new String[] { "b1", "b2", "b3" }));
+    }
+
+    {
+      String args[] = { "-paramA", "a1", "a2", "-paramB", "b1", "-paramA", "a3" };
+      Params p = new Params();
+      new JCommander(p, args).parse();
+      Assert.assertEquals(p.paramA, Arrays.asList(new String[] { "a1", "a2", "a3" }));
+      Assert.assertEquals(p.paramB, Arrays.asList(new String[] { "b1" }));
+    }
+  }
+
+  @Test(enabled = false,
+      description = "Need to double check that the command description is i18n'ed in the usage")
+  public void commandKey() {
+    @Parameters(resourceBundle = "MessageBundle", commandDescriptionKey = "command")
+    class Args {
+      @Parameter(names="-myoption", descriptionKey="myoption")
+      private boolean option; 
+    }
+    JCommander j = new JCommander();
+    Args a = new Args();
+    j.addCommand("comm", a);
+    j.usage();
+  }
+
+  public void tmp() {
+    class A {
+      @Parameter(names = "-b")
+      public String b;
+    }
+    new JCommander(new A()).parse("");
+  }
+
+  public void unknownOptionWithDifferentPrefix() {
+    @Parameters(optionPrefixes = "/")
+    class SlashSeparator {
+
+     @Parameter(names = "/verbose")
+     public boolean verbose = false;
+
+     @Parameter(names = "/file")
+     public String file;
+    }
+    SlashSeparator ss = new SlashSeparator();
+    try {
+      new JCommander(ss).parse("/notAParam");
+    } catch (ParameterException ex) {
+      boolean result = ex.getMessage().contains("Unknown option");
+      Assert.assertTrue(result);
+    }
+  }
+
+  public void equalSeparator() {
+    @Parameters(separators = "=", commandDescription = "My command")
+    class MyClass {
+
+       @Parameter(names = { "-p", "--param" }, required = true, description = "param desc...")
+       private String param;
+    }
+    MyClass c = new MyClass();
+    String expected = "\"hello\"world";
+    new JCommander(c).parse("--param=" + expected);
+    Assert.assertEquals(expected, c.param);
+  }
+
+  public void simpleArgsSetter() throws ParseException {
+    Args1Setter args = new Args1Setter();
+    String[] argv = { "-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4",
+            "-date", "2011-10-26", "-groups", "unit", "a", "b", "c" };
+    new JCommander(args, argv);
+
+    Assert.assertTrue(args.debug);
+    Assert.assertEquals(args.verbose.intValue(), 2);
+    Assert.assertEquals(args.groups, "unit");
+    Assert.assertEquals(args.parameters, Arrays.asList("a", "b", "c"));
+    Assert.assertEquals(args.floa, 1.2f, 0.1f);
+    Assert.assertEquals(args.doub, 1.3f, 0.1f);
+    Assert.assertEquals(args.bigd, new BigDecimal("1.4"));
+    Assert.assertEquals(args.date, new SimpleDateFormat("yyyy-MM-dd").parse("2011-10-26"));
+  }
+
+  public void verifyHelp() {
+    class Arg {
+      @Parameter(names = "--help", help = true)
+      public boolean help = false;
+
+      @Parameter(names = "file", required = true)
+      public String file;
+    }
+    Arg arg = new Arg();
+    String[] argv = { "--help" };
+    new JCommander(arg, argv);
+
+    Assert.assertTrue(arg.help);
+  }
+
+  public void helpTest() {
+    class Arg {
+      @Parameter(names = { "?", "-help", "--help" }, description = "Shows help", help = true)
+      private boolean help = false;
+    }
+    Arg arg = new Arg();
+    JCommander jc = new JCommander(arg);
+    jc.parse(new String[] { "-help" });
+//    System.out.println("helpTest:" + arg.help);
+  }
+
+  @Test(enabled = false, description = "Should only be enable once multiple parameters are allowed")
+  public void duplicateParameterNames() {
+    class ArgBase {
+      @Parameter(names = { "-host" })
+      protected String host;
+    }
+
+    class Arg1 extends ArgBase {}
+    Arg1 arg1 = new Arg1();
+
+    class Arg2 extends ArgBase {}
+    Arg2 arg2 = new Arg2();
+
+    JCommander jc = new JCommander(new Object[] { arg1, arg2});
+    jc.parse(new String[] { "-host", "foo" });
+    Assert.assertEquals(arg1.host, "foo");
+    Assert.assertEquals(arg2.host, "foo");
+  }
+
+  public void parameterWithOneDoubleQuote() {
+    @Parameters(separators = "=")
+    class Arg {
+      @Parameter(names = { "-p", "--param" })
+      private String param;
+    }
+    JCommander jc = new JCommander(new MyClass());
+    jc.parse("-p=\"");
+  }
+
+  public void emptyStringAsDefault() {
+    class Arg {
+      @Parameter(names = "-x")
+      String s = "";
+    }
+    Arg a = new Arg();
+    StringBuilder sb = new StringBuilder();
+    new JCommander(a).usage(sb);
+    Assert.assertTrue(sb.toString().contains("Default: <empty string>"));
+  }
+
+  public void spaces() {
+    class Arg {
+      @Parameter(names = "-rule", description = "rule")
+      private List<String> rules = new ArrayList<String>();
+    }
+    Arg a = new Arg();
+    new JCommander(a, "-rule", "some test");
+    Assert.assertEquals(a.rules, Arrays.asList("some test"));
+  }
+
+  static class V2 implements IParameterValidator2 {
+    final static List<String> names =  Lists.newArrayList();
+    static boolean validateCalled = false;
+
+    @Override
+    public void validate(String name, String value) throws ParameterException {
+      validateCalled = true;
+    }
+
+    @Override
+    public void validate(String name, String value, ParameterDescription pd)
+        throws ParameterException {
+      names.addAll(Arrays.asList(pd.getParameter().names()));
+    }
+  }
+
+  public void validator2() {
+    class Arg {
+      @Parameter(names = { "-h", "--host" }, validateWith = V2.class)
+      String host;
+    }
+    Arg a = new Arg();
+    V2.names.clear();
+    V2.validateCalled = false;
+    JCommander jc = new JCommander(a, "--host", "h");
+    jc.setAcceptUnknownOptions(true);
+    Assert.assertEquals(V2.names, Arrays.asList(new String[] { "-h", "--host" }));
+    Assert.assertTrue(V2.validateCalled);
+  }
+  
+  public void usageCommandsUnderUsage() {
+    class Arg {
+    }
+    @Parameters(commandDescription = "command a")
+    class ArgCommandA {
+      @Parameter(description = "command a parameters")
+      List<String> parameters;
+    }
+    @Parameters(commandDescription = "command b")
+    class ArgCommandB {
+      @Parameter(description = "command b parameters")
+      List<String> parameters;
+    }
+    
+    Arg a = new Arg();
+    
+    JCommander c = new JCommander(a);
+    c.addCommand("a", new ArgCommandA());
+    c.addCommand("b", new ArgCommandB());
+    
+    StringBuilder sb = new StringBuilder();
+    c.usage(sb);
+    Assert.assertTrue(sb.toString().contains("[command options]\n  Commands:"));
+  }
+
+  public void usageWithEmpytLine() {
+    class Arg {
+    }
+    @Parameters(commandDescription = "command a")
+    class ArgCommandA {
+      @Parameter(description = "command a parameters")
+      List<String> parameters;
+    }
+    @Parameters(commandDescription = "command b")
+    class ArgCommandB {
+      @Parameter(description = "command b parameters")
+      List<String> parameters;
+    }
+    
+    Arg a = new Arg();
+    
+    JCommander c = new JCommander(a);
+    c.addCommand("a", new ArgCommandA());
+    c.addCommand("b", new ArgCommandB());
+    
+    StringBuilder sb = new StringBuilder();
+    c.usage(sb);
+    Assert.assertTrue(sb.toString().contains("command a parameters\n\n    b"));
+  }
+
+  public void partialValidation() {
+    class Arg {
+      @Parameter(names = { "-h", "--host" })
+      String host;
+    }
+    Arg a = new Arg();
+    JCommander jc = new JCommander();
+    jc.setAcceptUnknownOptions(true);
+    jc.addObject(a);
+    jc.parse("-a", "foo", "-h", "host");
+    Assert.assertEquals(a.host, "host");
+    Assert.assertEquals(jc.getUnknownOptions(), Lists.newArrayList("-a", "foo"));
+  }
+
+  /**
+   * GITHUB-137.
+   */
+  public void listArgShouldBeCleared() {
+    class Args {
+      @Parameter(description = "[endpoint]")
+      public List<String> endpoint = Lists.newArrayList("prod");
+    }
+    Args a = new Args();
+    new JCommander(a, new String[] { "dev" });
+    Assert.assertEquals(a.endpoint, Lists.newArrayList("dev"));
+  }
+
+  public void dashDashParameter() {
+    class Arguments {
+        @Parameter(names = { "-name" })
+        public String name;
+        @Parameter
+        public List<String> mainParameters;
+    }
+
+    Arguments a = new Arguments();
+    new JCommander(a, new String[] {
+        "-name", "theName", "--", "param1", "param2"}
+    );
+    Assert.assertEquals(a.name, "theName");
+    Assert.assertEquals(a.mainParameters.size(), 2);
+    Assert.assertEquals(a.mainParameters.get(0), "param1");
+    Assert.assertEquals(a.mainParameters.get(1), "param2");
+  }
+
+  public void dashDashParameter2() {
+    class Arguments {
+        @Parameter(names = { "-name" })
+        public String name;
+        @Parameter
+        public List<String> mainParameters;
+    }
+
+    Arguments a = new Arguments();
+    new JCommander(a, new String[] {
+        "param1", "param2", "--", "param3", "-name", "theName"}
+    );
+    Assert.assertNull(a.name);
+    Assert.assertEquals(a.mainParameters.size(), 5);
+    Assert.assertEquals(a.mainParameters.get(0), "param1");
+    Assert.assertEquals(a.mainParameters.get(1), "param2");
+    Assert.assertEquals(a.mainParameters.get(2), "param3");
+    Assert.assertEquals(a.mainParameters.get(3), "-name");
+    Assert.assertEquals(a.mainParameters.get(4), "theName");
+  }
+
+  @Test(enabled = false)
+  public static void main(String[] args) throws Exception {
+    new JCommanderTest().enumArgsFail();
+//    class A {
+//      @Parameter(names = "-short", required = true)
+//      List<String> parameters;
+//
+//      @Parameter(names = "-long", required = true)
+//      public long l;
+//    }
+//    A a = new A();
+//    new JCommander(a).parse();
+//    System.out.println(a.l);
+//    System.out.println(a.parameters);
+//    ArgsList al = new ArgsList();
+//    JCommander j = new JCommander(al);
+//    j.setColumnSize(40);
+//    j.usage();
+//    new JCommanderTest().testListAndSplitters();
+//    new JCommanderTest().converterArgs();
+  }
+
+  // Tests:
+  // required unparsed parameter
+}
diff --git a/src/test/java/com/beust/jcommander/MethodSetterTest.java b/src/test/java/com/beust/jcommander/MethodSetterTest.java
new file mode 100644
index 0000000..f995ad6
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/MethodSetterTest.java
@@ -0,0 +1,100 @@
+package com.beust.jcommander;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+/**
+ * Tests for @Parameter on top of methods.
+ */
+@Test
+public class MethodSetterTest {
+  public void arityStringsSetter() {
+    class ArgsArityStringSetter {
+
+      @Parameter(names = "-pairs", arity = 2, description = "Pairs")
+      public void setPairs(List<String> pairs) {
+        this.pairs = pairs;
+      }
+      public List<String> getPairs() {
+        return this.pairs;
+      }
+      public List<String> pairs;
+
+      @Parameter(description = "Rest")
+      public void setRest(List<String> rest) {
+        this.rest = rest;
+      }
+//      public List<String> getRest() {
+//        return this.rest;
+//      }
+      public List<String> rest;
+    }
+    ArgsArityStringSetter args = new ArgsArityStringSetter();
+    String[] argv = { "-pairs", "pair0", "pair1", "rest" };
+    new JCommander(args, argv);
+
+    Assert.assertEquals(args.pairs.size(), 2);
+    Assert.assertEquals(args.pairs.get(0), "pair0");
+    Assert.assertEquals(args.pairs.get(1), "pair1");
+    Assert.assertEquals(args.rest.size(), 1);
+    Assert.assertEquals(args.rest.get(0), "rest");
+  }
+
+  public void setterThatThrows() {
+    class Arg {
+      @Parameter(names = "--host")
+      public void setHost(String host) {
+        throw new ParameterException("Illegal host");
+      }
+    }
+    boolean passed = false;
+    try {
+      new JCommander(new Arg(), new String[] { "--host", "host" });
+    } catch(ParameterException ex) {
+      Assert.assertEquals(ex.getCause(), null);
+      passed = true;
+    }
+    Assert.assertTrue(passed, "Should have thrown an exception");
+  }
+
+  public void getterReturningNonString() {
+    class Arg {
+      private Integer port;
+
+      @Parameter(names = "--port")
+      public void setPort(String port) {
+        this.port = Integer.parseInt(port);
+      }
+
+      public Integer getPort() {
+        return port;
+      }
+    }
+    Arg arg = new Arg();
+    new JCommander(arg, new String[] { "--port", "42" });
+
+    Assert.assertEquals(arg.port, new Integer(42));
+  }
+
+  public void noGetterButWithField() {
+    class Arg {
+      private Integer port = 43;
+
+      @Parameter(names = "--port")
+      public void setPort(String port) {
+        this.port = Integer.parseInt(port);
+      }
+    }
+    Arg arg = new Arg();
+    JCommander jc = new JCommander(arg, new String[] { "--port", "42" });
+    ParameterDescription pd = jc.getParameters().get(0);
+    Assert.assertEquals(pd.getDefault(), 43);
+  }
+
+  @Test(enabled = false)
+  public static void main(String[] args) throws Exception {
+    new MethodSetterTest().noGetterButWithField();
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/MyClass.java b/src/test/java/com/beust/jcommander/MyClass.java
new file mode 100644
index 0000000..c2d3371
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/MyClass.java
@@ -0,0 +1,24 @@
+package com.beust.jcommander;
+
+import org.testng.Assert;
+
+
+@Parameters(separators = "=")
+public class MyClass {
+
+  @Parameter(names = { "-p", "--param" }, validateWith = MyValidator.class)
+  private String param;
+
+  public static void main(String[] args) {
+    JCommander jCommander = new JCommander(new MyClass());
+    jCommander.parse("--param=value");
+  }
+
+  public static class MyValidator implements IParameterValidator {
+    @Override
+    public void validate(String name, String value) throws ParameterException {
+      Assert.assertEquals(value, "\"");
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/ParametersDelegateTest.java b/src/test/java/com/beust/jcommander/ParametersDelegateTest.java
new file mode 100644
index 0000000..46c7c6a
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/ParametersDelegateTest.java
@@ -0,0 +1,227 @@
+package com.beust.jcommander;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author rodionmoiseev
+ */
+public class ParametersDelegateTest {
+
+  @Test
+  public void delegatingEmptyClassHasNoEffect() {
+    class EmptyDelegate {
+      public String nonParamString = "a";
+    }
+    class MainParams {
+      @Parameter(names = "-a")
+      public boolean isA;
+      @Parameter(names = {"-b", "--long-b"})
+      public String bValue = "";
+      @ParametersDelegate
+      public EmptyDelegate delegate = new EmptyDelegate();
+    }
+
+    MainParams p = new MainParams();
+    JCommander cmd = new JCommander(p);
+    cmd.parse("-a", "-b", "someValue");
+    Assert.assertTrue(p.isA);
+    Assert.assertEquals(p.bValue, "someValue");
+    Assert.assertEquals(p.delegate.nonParamString, "a");
+  }
+
+  @Test
+  public void delegatingSetsFieldsOnBothMainParamsAndTheDelegatedParams() {
+    class ComplexDelegate {
+      @Parameter(names = "-c")
+      public boolean isC;
+      @Parameter(names = {"-d", "--long-d"})
+      public Integer d;
+    }
+    class MainParams {
+      @Parameter(names = "-a")
+      public boolean isA;
+      @Parameter(names = {"-b", "--long-b"})
+      public String bValue = "";
+      @ParametersDelegate
+      public ComplexDelegate delegate = new ComplexDelegate();
+    }
+
+    MainParams p = new MainParams();
+    JCommander cmd = new JCommander(p);
+    cmd.parse("-c", "--long-d", "123", "--long-b", "bValue");
+    Assert.assertFalse(p.isA);
+    Assert.assertEquals(p.bValue, "bValue");
+    Assert.assertTrue(p.delegate.isC);
+    Assert.assertEquals(p.delegate.d, Integer.valueOf(123));
+  }
+
+  @Test
+  public void combinedAndNestedDelegates() {
+    abstract class LeafAbstractDelegate {
+      abstract float getFloat();
+    }
+    class LeafDelegate {
+      @Parameter(names = "--list")
+      public List<String> list = new ArrayList<String>() {{
+        add("value1");
+        add("value2");
+      }};
+      @Parameter(names = "--bool")
+      public boolean bool;
+    }
+    class NestedDelegate1 {
+      @ParametersDelegate
+      public LeafDelegate leafDelegate = new LeafDelegate();
+      @Parameter(names = {"-d", "--long-d"})
+      public Integer d;
+    }
+    class NestedDelegate2 {
+      @Parameter(names = "-c")
+      public boolean isC;
+      @ParametersDelegate
+      public NestedDelegate1 nestedDelegate1 = new NestedDelegate1();
+      @ParametersDelegate
+      public LeafAbstractDelegate anonymousDelegate = new LeafAbstractDelegate() {
+        @Parameter(names = "--anon-float")
+        public float anon = 999f;
+
+        @Override
+        float getFloat() {
+          return anon;
+        }
+      };
+    }
+    class MainParams {
+      @Parameter(names = "-a")
+      public boolean isA;
+      @Parameter(names = {"-b", "--long-b"})
+      public String bValue = "";
+      @ParametersDelegate
+      public NestedDelegate2 nestedDelegate2 = new NestedDelegate2();
+    }
+
+    MainParams p = new MainParams();
+    JCommander cmd = new JCommander(p);
+    cmd.parse("--anon-float 1.2 -d 234 --list a --list b -a".split(" "));
+    Assert.assertEquals(p.nestedDelegate2.anonymousDelegate.getFloat(), 1.2f);
+    Assert.assertEquals(p.nestedDelegate2.nestedDelegate1.leafDelegate.list, new ArrayList<String>() {{
+      add("a");
+      add("b");
+    }});
+    Assert.assertFalse(p.nestedDelegate2.nestedDelegate1.leafDelegate.bool);
+    Assert.assertEquals(p.nestedDelegate2.nestedDelegate1.d, Integer.valueOf(234));
+    Assert.assertFalse(p.nestedDelegate2.isC);
+    Assert.assertTrue(p.isA);
+    Assert.assertEquals(p.bValue, "");
+  }
+
+  @Test
+  public void commandTest() {
+    class Delegate {
+      @Parameter(names = "-a")
+      public String a = "b";
+    }
+    class Command {
+      @ParametersDelegate
+      public Delegate delegate = new Delegate();
+    }
+
+    Command c = new Command();
+
+    JCommander cmd = new JCommander();
+    cmd.addCommand("command", c);
+
+    cmd.parse("command -a a".split(" "));
+    Assert.assertEquals(c.delegate.a, "a");
+  }
+
+  @Test
+  public void mainParametersTest() {
+    class Delegate {
+      @Parameter
+      public List<String> mainParams = new ArrayList<String>();
+    }
+    class Command {
+      @ParametersDelegate
+      public Delegate delegate = new Delegate();
+    }
+
+    Command c = new Command();
+
+    JCommander cmd = new JCommander();
+    cmd.addCommand("command", c);
+
+    cmd.parse("command main params".split(" "));
+    Assert.assertEquals(c.delegate.mainParams, new ArrayList<String>() {{
+      add("main");
+      add("params");
+    }});
+  }
+
+  @Test(expectedExceptions = ParameterException.class,
+          expectedExceptionsMessageRegExp = ".*delegate.*null.*")
+  public void nullDelegatesAreProhibited() {
+    class ComplexDelegate {
+    }
+    class MainParams {
+      @ParametersDelegate
+      public ComplexDelegate delegate;
+    }
+
+    MainParams p = new MainParams();
+    JCommander cmd = new JCommander(p);
+    cmd.parse();
+  }
+
+  @Test(expectedExceptions = ParameterException.class,
+          expectedExceptionsMessageRegExp = ".*-a.*")
+  public void duplicateDelegateThrowDuplicateOptionException() {
+    class Delegate {
+      @Parameter(names = "-a")
+      public String a;
+    }
+    class MainParams {
+      @ParametersDelegate
+      public Delegate d1 = new Delegate();
+      @ParametersDelegate
+      public Delegate d2 = new Delegate();
+    }
+
+    MainParams p = new MainParams();
+    JCommander cmd = new JCommander(p);
+    cmd.parse("-a value".split(" "));
+  }
+
+  @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = "Only one.*is allowed.*")
+  public void duplicateMainParametersAreNotAllowed() {
+    class Delegate1 {
+      @Parameter
+      public List<String> mainParams1 = new ArrayList<String>();
+    }
+    class Delegate2 {
+      @Parameter
+      public List<String> mainParams2 = new ArrayList<String>();
+    }
+    class Command {
+      @ParametersDelegate
+      public Delegate1 delegate1 = new Delegate1();
+      @ParametersDelegate
+      public Delegate2 delegate2 = new Delegate2();
+    }
+
+    Command c = new Command();
+
+    JCommander cmd = new JCommander();
+    cmd.addCommand("command", c);
+
+    cmd.parse("command main params".split(" "));
+  }
+
+  public static void main(String[] args) {
+    new ParametersDelegateTest().commandTest();
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/PositiveIntegerTest.java b/src/test/java/com/beust/jcommander/PositiveIntegerTest.java
new file mode 100644
index 0000000..ec7d273
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/PositiveIntegerTest.java
@@ -0,0 +1,65 @@
+package com.beust.jcommander;
+
+import com.beust.jcommander.validators.PositiveInteger;
+
+import org.testng.annotations.Test;
+
+public class PositiveIntegerTest {
+
+  @Test
+  public void validateTest() {
+    class Arg {
+      @Parameter(names = { "-p", "--port" }, description = "Shows help", validateWith = PositiveInteger.class)
+      private int port = 0;
+    }
+    Arg arg = new Arg();
+    JCommander jc = new JCommander(arg);
+    jc.parse(new String[] { "-p", "8080" });
+
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void validateTest2() {
+    class Arg {
+      @Parameter(names = { "-p", "--port" }, description = "Shows help", validateWith = PositiveInteger.class)
+      private int port = 0;
+    }
+    Arg arg = new Arg();
+    JCommander jc = new JCommander(arg);
+    jc.parse(new String[] { "-p", "" });
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void validateTest3() {
+    class Arg {
+      @Parameter(names = { "-p", "--port" }, description = "Shows help", validateWith = PositiveInteger.class)
+      private int port = 0;
+    }
+    Arg arg = new Arg();
+    JCommander jc = new JCommander(arg);
+    jc.parse(new String[] { "-p", "-1" });
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void validateTest4() {
+    class Arg {
+      @Parameter(names = { "-p", "--port" }, description = "Port Number", validateWith = PositiveInteger.class)
+      private int port = 0;
+    }
+    Arg arg = new Arg();
+    JCommander jc = new JCommander(arg);
+    jc.parse(new String[] { "-p", "abc" });
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void validateTest5() {
+    class Arg {
+      @Parameter(names = { "-p", "--port" }, description = "Port Number", validateWith = PositiveInteger.class)
+      private int port = 0;
+    }
+
+    Arg arg = new Arg();
+    JCommander jc = new JCommander(arg);
+    jc.parse(new String[] { "--port", " " });
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/SetConverter.java b/src/test/java/com/beust/jcommander/SetConverter.java
new file mode 100644
index 0000000..c19df11
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/SetConverter.java
@@ -0,0 +1,16 @@
+package com.beust.jcommander;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class SetConverter implements IStringConverter<SortedSet<Integer>> {
+ 
+  public SortedSet<Integer> convert(String value) {
+    SortedSet<Integer> set = new TreeSet<Integer>();
+    String[] values = value.split(",");
+    for (String num : values) {
+      set.add(Integer.parseInt(num));
+    }
+    return set;
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java b/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java
new file mode 100644
index 0000000..6a3a98f
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/ValidatePropertiesWhenParsingTest.java
@@ -0,0 +1,42 @@
+package com.beust.jcommander;
+
+import org.testng.annotations.Test;
+
+public class ValidatePropertiesWhenParsingTest {
+  @Test
+  public void f()
+      throws Exception {
+
+    JCommander cmd = new JCommander();
+
+    cmd.addCommand("a", new A());
+//    cmd.addCommand("b", new B());
+
+    cmd.parse(new String[] { "a", "-path", "myPathToHappiness" });
+  }
+
+  public static class MyPathValidator implements IParameterValidator {
+
+    public void validate(String name, String value) throws ParameterException {
+      throw new RuntimeException("I shouldn't be called for command A!");
+    }
+  }
+
+  @Parameters
+  public static class A {
+
+    @Parameter(names = "-path")
+    private String path = "W";
+  }
+
+  @Parameters
+  public static class B {
+
+    @Parameter(names = "-path", validateWith = MyPathValidator.class)
+    private String path = "W";
+  }
+
+  public static void main(String[] args) throws Exception {
+    new ValidatePropertiesWhenParsingTest().f();
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/VariableArityTest.java b/src/test/java/com/beust/jcommander/VariableArityTest.java
new file mode 100644
index 0000000..a90392f
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/VariableArityTest.java
@@ -0,0 +1,66 @@
+package com.beust.jcommander;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+public class VariableArityTest {
+
+  public static class ModelGenerationConfig {
+
+    @Parameter(names = { "-m", "--matrixData" }, variableArity = true,
+        description = "File containing a list of instances and their runtimes on various configurations", required = false)
+    public List<String> modelMatrixFile = new LinkedList<String>();
+
+    @Parameter(names = { "-f", "--featureData" }, variableArity = true,
+        description = "File containing a list of instances and their corresponding features", required = true)
+    public List<String> featureFile = new LinkedList<String>();
+
+    @Parameter(names = { "-c", "--configData" }, variableArity = true,
+        description = "File containing a list of configuration parameter values")
+    public List<String> configFile = new LinkedList<String>();
+
+    @Parameter(names = { "-o", "--outputFile" },
+        description = "File to output the resulting data to. Defaults to ./matrix-generation.zip", required = false)
+    public String outputFile = "matrix-generation.zip";
+
+    @Parameter(names = { "--seed" }, description = "Seed used for PRNG [0 means don't use a Seed]")
+    public long seed = 0;
+
+    public void print() {
+      System.out.println("modelMatrixFile: " + modelMatrixFile);
+      System.out.println("featureData: " + featureFile);
+      System.out.println("configFile: " + configFile);
+      System.out.println("output:  " + outputFile);
+      System.out.println("seed: " + seed);
+
+    }
+  }
+
+  @Test
+  public void verifyVariableArity() {
+    String input = "-m foo --seed 1024 -c foo -o foo -f foo ";
+
+    String[] split = input.split("\\s+");
+
+    ModelGenerationConfig config = new ModelGenerationConfig();
+    JCommander com = new JCommander(config);
+    com.setProgramName("modelgen");
+
+    com.parse(split);
+
+//    config.print();
+    Assert.assertNotEquals(config.seed, 0);
+    Assert.assertEquals(config.modelMatrixFile, Arrays.asList(new String[] { "foo" }));
+    Assert.assertEquals(config.featureFile, Arrays.asList(new String[] { "foo" }));
+    Assert.assertEquals(config.seed, 1024);
+    Assert.assertEquals(config.outputFile, "foo");
+  }
+
+  public static void main(String[] args) {
+    new VariableArityTest().verifyVariableArity();
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/args/AlternateNamesForListArgs.java b/src/test/java/com/beust/jcommander/args/AlternateNamesForListArgs.java
new file mode 100644
index 0000000..18a1655
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/AlternateNamesForListArgs.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.internal.Lists;
+import java.util.List;
+
+/**
+ *
+ * @author Andy Law <andy.law@roslin.ed.ac.uk>
+ */
+public class AlternateNamesForListArgs {
+
+    @Parameter(names = {"-s", "--servers"}, description = "blah")
+    public List<String> serverNames = Lists.newLinkedList();
+}
diff --git a/src/test/java/com/beust/jcommander/args/Args1.java b/src/test/java/com/beust/jcommander/args/Args1.java
new file mode 100644
index 0000000..093abec
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/Args1.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Date;
+
+import org.testng.collections.Lists;
+
+import com.beust.jcommander.Parameter;
+
+public class Args1 {
+  @Parameter
+  public List<String> parameters = Lists.newArrayList();
+
+  @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity", required = true)
+  public Integer verbose = 1;
+
+  @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+  public String groups;
+
+  @Parameter(names = "-debug", description = "Debug mode")
+  public boolean debug = false;
+
+  @Parameter(names = "-long", description = "A long number")
+  public long l;
+
+  @Parameter(names = "-double", description = "A double number")
+  public double doub;
+
+  @Parameter(names = "-float", description = "A float number")
+  public float floa;
+
+  @Parameter(names = "-bigdecimal", description = "A BigDecimal number")
+  public BigDecimal bigd;
+
+  @Parameter(names = "-date", description = "An ISO 8601 formatted date.")
+  public Date date;
+}
diff --git a/src/test/java/com/beust/jcommander/args/Args1Setter.java b/src/test/java/com/beust/jcommander/args/Args1Setter.java
new file mode 100644
index 0000000..10f9ef3
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/Args1Setter.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.internal.Lists;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+public class Args1Setter {
+  @Parameter
+  public void setParameters(List<String> p) {
+    parameters = p;
+  }
+
+  public List<String> getParameters() {
+    return this.parameters;
+  }
+  public List<String> parameters = Lists.newArrayList();
+
+  @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity", required = true)
+  public void setVerbose(Integer v) {
+    verbose = v;
+  }
+  public Integer verbose = 1;
+
+  @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+  public void setGroups(String g) {
+    groups = g;
+  }
+
+  public String groups;
+
+  @Parameter(names = "-debug", description = "Debug mode")
+  public void setDebug(boolean d) {
+    debug = d;
+  }
+
+  public boolean debug = false;
+
+  @Parameter(names = "-long", description = "A long number")
+  public void setLong(long ll) {
+    l = ll;
+  }
+
+  public long l;
+
+  @Parameter(names = "-double", description = "A double number")
+  public void setDouble(double d) {
+    doub = d;
+  }
+
+  public double doub;
+
+  @Parameter(names = "-float", description = "A float number")
+  public void setFloat(float f) {
+    floa = f;
+  }
+
+  public float floa;
+
+  @Parameter(names = "-bigdecimal", description = "A BigDecimal number")
+  public void setBigDecimal(BigDecimal bd) {
+    bigd = bd;
+  }
+
+  public BigDecimal bigd;
+
+  @Parameter(names = "-date", description = "An ISO 8601 formatted date.")
+  public void setDate(Date d) {
+    date = d;
+  }
+
+  public Date date;
+}
diff --git a/src/test/java/com/beust/jcommander/args/Args2.java b/src/test/java/com/beust/jcommander/args/Args2.java
new file mode 100644
index 0000000..89f622b
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/Args2.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.internal.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Args2 {
+  @Parameter(description = "List of parameters")
+  public List parameters = Lists.newArrayList();
+
+  @Parameter(names = {"-log", "-verbose"}, description = "Level of verbosity")
+  public Integer verbose = 1;
+
+  @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+  public String groups;
+
+  @Parameter(names = "-debug", description = "Debug mode")
+  public boolean debug = false;
+
+  @Parameter(names = "-host", description = "The host")
+  public List hosts = new ArrayList();
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsArityInteger.java b/src/test/java/com/beust/jcommander/args/ArgsArityInteger.java
new file mode 100644
index 0000000..9c1611f
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsArityInteger.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+import java.util.List;
+
+/**
+ * Test parameter arity.
+ * 
+ * @author cbeust
+ */
+public class ArgsArityInteger {
+
+  @Parameter(names = "-pairs", arity = 2, description = "Pairs")
+  public List<Integer> pairs;
+
+  @Parameter(description = "Rest")
+  public List<String> rest;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsArityString.java b/src/test/java/com/beust/jcommander/args/ArgsArityString.java
new file mode 100644
index 0000000..056ae85
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsArityString.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+import java.util.List;
+
+/**
+ * Test parameter arity.
+ * 
+ * @author cbeust
+ */
+public class ArgsArityString {
+
+  @Parameter(names = "-pairs", arity = 2, description = "Pairs")
+  public List<String> pairs;
+
+  @Parameter(description = "Rest")
+  public List<String> rest;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsBooleanArity.java b/src/test/java/com/beust/jcommander/args/ArgsBooleanArity.java
new file mode 100644
index 0000000..242e347
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsBooleanArity.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class ArgsBooleanArity {
+  @Parameter(names = "-debug", arity = 1)
+  public Boolean debug = false;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsBooleanArity0.java b/src/test/java/com/beust/jcommander/args/ArgsBooleanArity0.java
new file mode 100644
index 0000000..62895ec
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsBooleanArity0.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class ArgsBooleanArity0 {
+  @Parameter(names = "-debug", arity = 0)
+  public Boolean debug = false;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsConverter.java b/src/test/java/com/beust/jcommander/args/ArgsConverter.java
new file mode 100644
index 0000000..677cf9b
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsConverter.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.converters.FileConverter;
+import com.beust.jcommander.converters.PathConverter;
+import com.beust.jcommander.converters.URIConverter;
+import com.beust.jcommander.converters.URLConverter;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.List;
+
+public class ArgsConverter {
+
+  @Parameter(names = "-file", converter = FileConverter.class)
+  public File file;
+  
+  @Parameter(names = "-url", converter = URLConverter.class)
+  public URL url;
+  
+  @Parameter(names = "-uri", converter = URIConverter.class)
+  public URI uri;
+  
+  @Parameter(names = "-path", converter = PathConverter.class)
+  public Path path;
+  
+  @Parameter(names = "-listStrings")
+  public List<String> listStrings;
+
+  @Parameter(names = "-listInts")
+  public List<Integer> listInts;
+
+  @Parameter(names = "-listBigDecimals")
+  public List<BigDecimal> listBigDecimals;
+  
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsConverterFactory.java b/src/test/java/com/beust/jcommander/args/ArgsConverterFactory.java
new file mode 100644
index 0000000..f8463a0
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsConverterFactory.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.HostPort;
+import com.beust.jcommander.Parameter;
+
+public class ArgsConverterFactory {
+
+  @Parameter(names = "-hostport")
+  public HostPort hostPort;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsDefault.java b/src/test/java/com/beust/jcommander/args/ArgsDefault.java
new file mode 100644
index 0000000..1adcd73
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsDefault.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+import org.testng.collections.Lists;
+
+import java.util.List;
+
+public class ArgsDefault {
+  @Parameter
+  public List<String> parameters = Lists.newArrayList();
+
+  @Parameter(names = "-log", description = "Level of verbosity")
+  public Integer log = 1;
+
+  @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+  public String groups;
+
+  @Parameter(names = "-debug", description = "Debug mode")
+  public boolean debug = false;
+
+  @Parameter(names = "-level", description = "A long number")
+  public long level;
+
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsEnum.java b/src/test/java/com/beust/jcommander/args/ArgsEnum.java
new file mode 100644
index 0000000..bef663b
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsEnum.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+import org.testng.Assert;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.Parameter;
+
+/**
+ * Test enums.
+ *
+ * @author Adrian Muraru
+ */
+public class ArgsEnum {
+
+  public enum ChoiceType { ONE, Two, THREE };
+  @Parameter(names = "-choice")
+  public ChoiceType choice = ChoiceType.ONE;
+  
+  @Parameter(names = "-choices", variableArity = true)
+  public List<ChoiceType> choices = new ArrayList<ChoiceType>();
+
+  public static void main(String[] args1) {
+    ArgsEnum args = new ArgsEnum();
+    String[] argv = { "-choice", "ONE"};
+    JCommander jc = new JCommander(args, argv);
+    jc.usage();
+    Assert.assertEquals(jc.getParameters().get(0).getDescription(),
+        "Options: " + EnumSet.allOf((Class<? extends Enum>) ArgsEnum.ChoiceType.class));
+  }
+
+}
+
+
diff --git a/src/test/java/com/beust/jcommander/args/ArgsEquals.java b/src/test/java/com/beust/jcommander/args/ArgsEquals.java
new file mode 100644
index 0000000..016166d
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsEquals.java
@@ -0,0 +1,11 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+@Parameters(separators = "=")
+public class ArgsEquals {
+
+  @Parameter(names = "-args")
+  public String args;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsHelp.java b/src/test/java/com/beust/jcommander/args/ArgsHelp.java
new file mode 100644
index 0000000..4baaddd
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsHelp.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+/**
+ * Test a help option which overrides other options and option validations
+ */
+public class ArgsHelp {
+
+  @Parameter(names = "-h", description = "Display help")
+  public boolean help;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsI18N1.java b/src/test/java/com/beust/jcommander/args/ArgsI18N1.java
new file mode 100644
index 0000000..80e29e2
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsI18N1.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class ArgsI18N1 {
+
+  @Parameter(names = "-host", description = "Host", descriptionKey = "host")
+  String hostName;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsI18N2.java b/src/test/java/com/beust/jcommander/args/ArgsI18N2.java
new file mode 100644
index 0000000..1ccfd43
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsI18N2.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.ResourceBundle;
+
+@SuppressWarnings("deprecation")
+@ResourceBundle("MessageBundle")
+public class ArgsI18N2 {
+
+  @Parameter(names = "-host", description = "Host", descriptionKey = "host")
+  String hostName;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsI18N2New.java b/src/test/java/com/beust/jcommander/args/ArgsI18N2New.java
new file mode 100644
index 0000000..c760973
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsI18N2New.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+import java.util.List;
+
+@Parameters(resourceBundle = "MessageBundle")
+public class ArgsI18N2New {
+
+  @Parameter(names = "-host", description = "Host", descriptionKey = "host")
+  String hostName;
+
+  @Parameter(descriptionKey = "files")
+  List<String> files;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsInherited.java b/src/test/java/com/beust/jcommander/args/ArgsInherited.java
new file mode 100644
index 0000000..abc0e82
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsInherited.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class ArgsInherited extends ArgsDefault {
+
+    @Parameter(names = "-child", description = "Child parameter")
+    public Integer child = 1;
+
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsList.java b/src/test/java/com/beust/jcommander/args/ArgsList.java
new file mode 100644
index 0000000..e827773
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsList.java
@@ -0,0 +1,49 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.HostPort;
+import com.beust.jcommander.HostPortConverter;
+import com.beust.jcommander.IStringConverter;
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.converters.IParameterSplitter;
+
+import org.testng.collections.Lists;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ArgsList {
+  @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+  public List<String> groups;
+
+  @Parameter(names = "-ints")
+  public List<Integer> ints;
+
+  @Parameter(names = "-hp", converter = HostPortConverter.class, splitter = SemiColonSplitter.class)
+  public List<HostPort> hostPorts;
+
+  @Parameter(names = "-hp2", converter = HostPortConverter.class)
+  public List<HostPort> hp2;
+
+  @Parameter(names = "-uppercase", listConverter = UppercaseConverter.class)
+  public List<String> uppercase;
+
+  public static class UppercaseConverter implements IStringConverter<List<String>> {
+    public List<String> convert(String value) {
+      List<String> result = Lists.newArrayList();
+      String[] s = value.split(",");
+      for (String p : s) {
+        result.add(p.toUpperCase());
+      }
+      return result;
+    }
+  }
+
+  public static class SemiColonSplitter implements IParameterSplitter {
+
+    public List<String> split(String value) {
+      return Arrays.asList(value.split(";"));
+    }
+    
+  }
+
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsLongDescription.java b/src/test/java/com/beust/jcommander/args/ArgsLongDescription.java
new file mode 100644
index 0000000..e961016
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsLongDescription.java
@@ -0,0 +1,25 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class ArgsLongDescription {
+
+  @Parameter(names = "--classpath", description = "The classpath. This is a very long "
+      + "description in order to test the line wrapping. Let's see how this works."
+      + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"
+      + " incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis "
+      + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.")
+  public String classpath = "/tmp";
+
+  @Parameter(names = { "-c", "--convention" }, description = "The convention", required = true)
+  public String convention = "Java";
+
+  @Parameter(names = { "-d", "--destination" }, description = "The destination to go to")
+  public String destination;
+
+  @Parameter(names = "--configure", description = "How to configure")
+  public String configure;
+
+  @Parameter(names = "--filespec")
+  public String filespec;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsMainParameter1.java b/src/test/java/com/beust/jcommander/args/ArgsMainParameter1.java
new file mode 100644
index 0000000..cd5c52d
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsMainParameter1.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.HostPort;
+import com.beust.jcommander.Parameter;
+
+import org.testng.collections.Lists;
+
+import java.util.List;
+
+/**
+ * A class with main parameter that is not a List<String>
+ * 
+ * @author cbeust
+ */
+public class ArgsMainParameter1 implements IHostPorts {
+  @Parameter
+  public List<HostPort> parameters = Lists.newArrayList();
+
+  public List<HostPort> getHostPorts() {
+    return parameters;
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsMainParameter2.java b/src/test/java/com/beust/jcommander/args/ArgsMainParameter2.java
new file mode 100644
index 0000000..57422e3
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsMainParameter2.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.HostPort;
+import com.beust.jcommander.HostPortConverter;
+import com.beust.jcommander.Parameter;
+
+import org.testng.collections.Lists;
+
+import java.util.List;
+
+/**
+ * A class with main parameter that is not a List<String>
+ * 
+ * @author cbeust
+ */
+public class ArgsMainParameter2 implements IHostPorts {
+  @Parameter(converter = HostPortConverter.class)
+  public List<HostPort> parameters = Lists.newArrayList();
+
+  public List<HostPort> getHostPorts() {
+    return parameters;
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsMaster.java b/src/test/java/com/beust/jcommander/args/ArgsMaster.java
new file mode 100644
index 0000000..ebcba67
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsMaster.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+/**
+ * Test multi-object parsing, along with ArgsSlave.
+ * 
+ * @author cbeust
+ */
+public class ArgsMaster {
+  @Parameter(names = "-master")
+  public String master;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsMultipleUnparsed.java b/src/test/java/com/beust/jcommander/args/ArgsMultipleUnparsed.java
new file mode 100644
index 0000000..f7b0628
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsMultipleUnparsed.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+/**
+ * Error case if multiple unparsed (without a names attribute) arguments are defined.
+ * 
+ * @author cbeust
+ */
+public class ArgsMultipleUnparsed {
+
+  @Parameter(description = "Bogus1")
+  public String unparsed1;
+
+  @Parameter(description = "Bogus2")
+  public String unparsed2;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsOutOfMemory.java b/src/test/java/com/beust/jcommander/args/ArgsOutOfMemory.java
new file mode 100644
index 0000000..f059ebb
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsOutOfMemory.java
@@ -0,0 +1,13 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class ArgsOutOfMemory
+{
+  @Parameter(names = { "-p", "--pattern"  },
+      description = "pattern used by 'tail'. See http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout and http://logback.qos.ch/manual/layouts.html#AccessPatternLayout")
+  public String pattern;
+
+  @Parameter(names = "-q", description = "Filler arg")
+  public String filler;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsPassword.java b/src/test/java/com/beust/jcommander/args/ArgsPassword.java
new file mode 100644
index 0000000..06c468e
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsPassword.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class ArgsPassword {
+  @Parameter(names = "-password", description = "Connection password", password = true)
+  public String password;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsPrivate.java b/src/test/java/com/beust/jcommander/args/ArgsPrivate.java
new file mode 100644
index 0000000..16e4b4e
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsPrivate.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class ArgsPrivate {
+  @Parameter(names = "-verbose")
+  private Integer verbose = 1;
+
+  public Integer getVerbose() {
+    return verbose;
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsRequired.java b/src/test/java/com/beust/jcommander/args/ArgsRequired.java
new file mode 100644
index 0000000..69cb814
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsRequired.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.internal.Lists;
+
+import java.util.List;
+
+public class ArgsRequired {
+
+  @Parameter(description = "List of files", required = true)
+  public List<String> parameters = Lists.newArrayList();
+
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsSlave.java b/src/test/java/com/beust/jcommander/args/ArgsSlave.java
new file mode 100644
index 0000000..dabeb57
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsSlave.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+/**
+ * Test multi-object parsing, along with ArgsSlave.
+ * 
+ * @author cbeust
+ */
+public class ArgsSlave {
+  @Parameter(names = "-slave")
+  public String slave;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsSlaveBogus.java b/src/test/java/com/beust/jcommander/args/ArgsSlaveBogus.java
new file mode 100644
index 0000000..1abdf1f
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsSlaveBogus.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+/**
+ * Same as ArgsMaster class, should cause an error.
+ * 
+ * @author cbeust
+ */
+public class ArgsSlaveBogus extends ArgsSlave {
+  @Parameter(names = "-master")
+  public String master;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsValidate1.java b/src/test/java/com/beust/jcommander/args/ArgsValidate1.java
new file mode 100644
index 0000000..5274220
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsValidate1.java
@@ -0,0 +1,10 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.validators.PositiveInteger;
+
+public class ArgsValidate1 {
+
+  @Parameter(names = "-age", validateWith = PositiveInteger.class)
+  public Integer age;
+}
diff --git a/src/test/java/com/beust/jcommander/args/ArgsWithSet.java b/src/test/java/com/beust/jcommander/args/ArgsWithSet.java
new file mode 100644
index 0000000..1e41cd3
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/ArgsWithSet.java
@@ -0,0 +1,11 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.SetConverter;
+
+import java.util.SortedSet;
+
+public class ArgsWithSet {
+  @Parameter(names = "-s", converter = SetConverter.class)
+  public SortedSet<Integer> set;
+}
\ No newline at end of file
diff --git a/src/test/java/com/beust/jcommander/args/Arity1.java b/src/test/java/com/beust/jcommander/args/Arity1.java
new file mode 100644
index 0000000..8cbf086
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/Arity1.java
@@ -0,0 +1,9 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+
+public class Arity1
+{
+  @Parameter(arity = 1, names = "-inspect", description = "", required = false)
+  public boolean inspect;
+}
diff --git a/src/test/java/com/beust/jcommander/args/CommandLineArgs.java b/src/test/java/com/beust/jcommander/args/CommandLineArgs.java
new file mode 100644
index 0000000..7c8414f
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/CommandLineArgs.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.internal.Lists;
+
+import java.util.List;
+
+public class CommandLineArgs {
+
+  @Parameter(description = "The XML suite files to run")
+  public List<String> suiteFiles = Lists.newArrayList();
+
+  @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity")
+  public Integer verbose;
+
+  @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
+  public String groups;
+
+  @Parameter(names = "-excludedgroups", description ="Comma-separated list of group names to be " +
+      "run")
+  public String excludedGroups;
+  
+  @Parameter(names = "-d", description ="Output directory")
+  public String outputDirectory;
+  
+  @Parameter(names = "-junit", description ="JUnit mode")
+  public Boolean junit = Boolean.FALSE;
+
+  @Parameter(names = "-listener", description = "List of .class files or list of class names" +
+      " implementing ITestListener or ISuiteListener")
+  public String listener;
+
+  @Parameter(names = "-methodselectors", description = "List of .class files or list of class " +
+      "names implementing IMethodSelector")
+  public String methodSelectors;
+
+  @Parameter(names = "-objectfactory", description = "List of .class files or list of class " +
+      "names implementing ITestRunnerFactory")
+  public String objectFactory;
+
+  @Parameter(names = "-parallel", description = "Parallel mode (methods, tests or classes)")
+  public String parallelMode;
+  
+  @Parameter(names = "-configfailurepolicy", description = "Configuration failure policy (skip or continue)")
+  public String configFailurePolicy;
+
+  @Parameter(names = "-threadcount", description = "Number of threads to use when running tests " +
+      "in parallel")
+  public Integer threadCount;
+
+  @Parameter(names = "-dataproviderthreadcount", description = "Number of threads to use when " +
+      "running data providers")
+  public Integer dataProviderThreadCount;
+
+  @Parameter(names = "-suitename", description = "Default name of test suite, if not specified " +
+      "in suite definition file or source code")
+  public String suiteName;
+
+  @Parameter(names = "-testname", description = "Default name of test, if not specified in suite" +
+      "definition file or source code")
+  public String testName;
+
+  @Parameter(names = "-reporter", description = "Extended configuration for custom report listener")
+  public String reporter;
+
+  /**
+   * Used as map key for the complete list of report listeners provided with the above argument
+   */
+  @Parameter(names = "-reporterslist")
+  public String reportersList;
+
+  @Parameter(names = "-usedefaultlisteners", description = "Whether to use the default listeners")
+  public String useDefaultListeners = "true";
+
+  @Parameter(names = "-skipfailedinvocationcounts")
+  public Boolean skipFailedInvocationCounts;
+
+  @Parameter(names = "-testclass", description = "The list of test classes")
+  public String testClass;
+
+  @Parameter(names = "-testnames", description = "The list of test names to run")
+  public String testNames;
+
+  @Parameter(names = "-testjar", description = "")
+  public String testJar;
+
+  @Parameter(names = "-testRunFactory", description = "")
+  public String testRunFactory;
+
+  @Parameter(names = "-port", description = "The port")
+  public Integer port;
+
+  @Parameter(names = "-host", description = "The host")
+  public String host;
+
+  @Parameter(names = "-master", description ="Host where the master is")
+  public String master;
+
+  @Parameter(names = "-slave", description ="Host where the slave is")
+  public String slave;
+
+}
diff --git a/src/test/java/com/beust/jcommander/args/CommandLineArgs2.java b/src/test/java/com/beust/jcommander/args/CommandLineArgs2.java
new file mode 100644
index 0000000..ac62792
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/CommandLineArgs2.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.converters.FileConverter;
+
+import java.io.File;
+import java.util.List;
+
+public class CommandLineArgs2 {
+  @Parameter(description = "list of files")
+  List<String> list;
+
+  @Parameter(names = { "-v", "--verbose" }, description = "print verbose log messages.", arity = 1)
+  public boolean verbose = false;
+
+  @Parameter(names = { "-h", "--help" }, description = "show this help.")
+  public boolean showHelp = false;
+
+  @Parameter(names = { "-F", "--flush-preferences" }, description = "flush gui preferences.")
+  public boolean flushPreferences = false;
+
+  @Parameter(names = { "-L", "--flush-licensed" }, description = "flush licensed.")
+  public boolean flushLicensed = false;
+
+  @Parameter(names = { "-I", "--index-file" }, description = "indexes the given file.")
+  public Long indexFile;
+
+  @Parameter(names = { "-b", "--bonjour" }, description = "enable Bonjour.")
+  public boolean enableBonjour = false;
+
+  @Parameter(names = { "-m", "--md5" }, description = "create an MD5 checksum for the given file.", converter = FileConverter.class)
+  public File md5File;
+
+  @Parameter(names = { "-c", "--cat" }, description = "'cat' the given Lilith logfile.", converter = FileConverter.class)
+  public File catFile;
+
+  @Parameter(names = { "-t", "--tail" }, description = "'tail' the given Lilith logfile.", converter = FileConverter.class)
+  public File tailFile;
+
+  @Parameter(names = { "-p", "--pattern" }, description = "pattern used by 'cat' or 'tail'.")
+  public String pattern;
+
+  @Parameter(names = { "-f", "--keep-running" }, description = "keep tailing the given Lilith logfile.")
+  public boolean keepRunning = false;
+
+  @Parameter(names = { "-n", "--number-of-lines" }, description = "number of entries printed by cat or tail")
+  public Integer numberOfLines = -1;
+
+  @Parameter(names = { "-e", "--export-preferences" }, description = "export preferences into the given file.")
+  public String exportPreferencesFile;
+
+  @Parameter(names = { "-i", "--import-preferences" }, description = "import preferences from the given file.")
+  public String importPreferencesFile;
+}
diff --git a/src/test/java/com/beust/jcommander/args/IHostPorts.java b/src/test/java/com/beust/jcommander/args/IHostPorts.java
new file mode 100644
index 0000000..2020c77
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/IHostPorts.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.HostPort;
+
+import java.util.List;
+
+public interface IHostPorts {
+  List<HostPort> getHostPorts();
+}
diff --git a/src/test/java/com/beust/jcommander/args/SeparatorColon.java b/src/test/java/com/beust/jcommander/args/SeparatorColon.java
new file mode 100644
index 0000000..ab454fc
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/SeparatorColon.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+@Parameters(separators = ":")
+public class SeparatorColon {
+
+  @Parameter(names = "-verbose", arity = 1)
+  public boolean verbose = false;
+}
diff --git a/src/test/java/com/beust/jcommander/args/SeparatorEqual.java b/src/test/java/com/beust/jcommander/args/SeparatorEqual.java
new file mode 100644
index 0000000..a135207
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/SeparatorEqual.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+@Parameters(separators = "=")
+public class SeparatorEqual {
+
+  @Parameter(names = "-log")
+  public Integer log = 2;
+
+  @Parameter(names = "--longoption")
+  public Integer longOption;
+}
diff --git a/src/test/java/com/beust/jcommander/args/SeparatorMixed.java b/src/test/java/com/beust/jcommander/args/SeparatorMixed.java
new file mode 100644
index 0000000..aa2641f
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/SeparatorMixed.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+@Parameters(separators = ":=")
+public class SeparatorMixed {
+
+  @Parameter(names = "-level")
+  public Integer level = 0;
+
+  @Parameter(names = "-long")
+  public Long l = 0l;
+}
diff --git a/src/test/java/com/beust/jcommander/args/SlashSeparator.java b/src/test/java/com/beust/jcommander/args/SlashSeparator.java
new file mode 100644
index 0000000..64d3930
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/SlashSeparator.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+@Parameters(optionPrefixes = "/")
+public class SlashSeparator {
+
+  @Parameter(names = "/verbose")
+  public boolean verbose = false;
+
+  @Parameter(names = "/file")
+  public String file;
+}
diff --git a/src/test/java/com/beust/jcommander/args/VariableArity.java b/src/test/java/com/beust/jcommander/args/VariableArity.java
new file mode 100644
index 0000000..21a861d
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/args/VariableArity.java
@@ -0,0 +1,26 @@
+package com.beust.jcommander.args;
+
+import com.beust.jcommander.IVariableArity;
+import com.beust.jcommander.Parameter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class VariableArity implements IVariableArity {
+
+  private int m_count;
+
+  public VariableArity(int count) {
+    m_count = count;
+  }
+
+  @Parameter
+  public List<String> main = new ArrayList<String>();
+
+  @Parameter(names = "-variable", variableArity = true)
+  public List<String> var = new ArrayList<String>();
+
+  public int processVariableArity(String optionName, String[] options) {
+    return m_count;
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/command/CommandAdd.java b/src/test/java/com/beust/jcommander/command/CommandAdd.java
new file mode 100644
index 0000000..733e490
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/command/CommandAdd.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.command;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+import java.util.List;
+
+@Parameters(commandDescription = "Add file contents to the index")
+public class CommandAdd {
+
+  @Parameter(description = "Patterns of files to be added")
+  public List<String> patterns;
+
+  @Parameter(names = "-i")
+  public Boolean interactive = false;
+
+}
diff --git a/src/test/java/com/beust/jcommander/command/CommandAliasTest.java b/src/test/java/com/beust/jcommander/command/CommandAliasTest.java
new file mode 100644
index 0000000..4eee7f2
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/command/CommandAliasTest.java
@@ -0,0 +1,175 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.command;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.ParameterException;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Tests command alias functionality
+ *
+ * @author rodionmoiseev
+ */
+public class CommandAliasTest {
+  @Test
+  public void oneCommandWithSingleAlias() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandAdd add = new CommandAdd();
+    jc.addCommand("add", add, "a");
+    jc.parse("a", "-i", "A.java");
+
+    Assert.assertEquals(jc.getParsedCommand(), "add");
+    Assert.assertEquals(jc.getParsedAlias(), "a");
+    Assert.assertEquals(add.interactive.booleanValue(), true);
+    Assert.assertEquals(add.patterns, Arrays.asList("A.java"));
+  }
+
+  @Test
+  public void oneCommandWithMultipleAliases_commit_ci() {
+    testCommitWithAlias("ci");
+  }
+
+  @Test
+  public void oneCommandWithMultipleAliases_commit_cmt() {
+    testCommitWithAlias("cmt");
+  }
+
+  private void testCommitWithAlias(String alias) {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandCommit commit = new CommandCommit();
+    jc.addCommand("commit", commit, "ci", "cmt");
+    jc.parse(alias, "--amend", "--author", "jack", "file1.txt");
+
+    Assert.assertEquals(jc.getParsedCommand(), "commit");
+    Assert.assertEquals(jc.getParsedAlias(), alias);
+    Assert.assertEquals(commit.amend.booleanValue(), true);
+    Assert.assertEquals(commit.author, "jack");
+    Assert.assertEquals(commit.files, Arrays.asList("file1.txt"));
+  }
+
+  @Test
+  public void twoCommandsWithAliases() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandAdd add = new CommandAdd();
+    jc.addCommand("add", add, "a");
+    CommandCommit commit = new CommandCommit();
+    jc.addCommand("commit", commit, "ci", "cmt");
+    jc.parse("a", "-i", "A.java");
+
+    Assert.assertEquals(jc.getParsedCommand(), "add");
+    Assert.assertEquals(add.interactive.booleanValue(), true);
+    Assert.assertEquals(add.patterns, Arrays.asList("A.java"));
+  }
+
+  @Test
+  public void clashingAliasesAreNotAllowed() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandAdd add = new CommandAdd();
+    jc.addCommand("add", add, "xx");
+    CommandCommit commit = new CommandCommit();
+    try {
+      jc.addCommand("commit", commit, "ci", "xx");
+      Assert.fail("Should not be able to register clashing alias 'xx'");
+    } catch (ParameterException pe) {
+      //Make sure the message mentions that "xx" aliases is already
+      //defined for "add" command
+      Assert.assertTrue(pe.getMessage().contains("xx"));
+      Assert.assertTrue(pe.getMessage().contains("add"));
+    }
+  }
+
+  @Test
+  public void mainCommandReturnsNullsForGetCommandAndGetParsedAlias() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    Assert.assertNull(jc.getParsedCommand());
+    Assert.assertNull(jc.getParsedAlias());
+  }
+
+  @Test
+  public void usageCanBeRetrievedWithBothCommandAndAlias() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandCommit commit = new CommandCommit();
+    jc.addCommand("commit", commit, "ci", "cmt");
+    StringBuilder out = new StringBuilder();
+    jc.usage("commit", out);
+    patternMatchesTimes("commit\\(ci,cmt\\)", out.toString(), 1);
+
+    out = new StringBuilder();
+    jc.usage("ci", out);
+    patternMatchesTimes("commit\\(ci,cmt\\)", out.toString(), 1);
+
+    out = new StringBuilder();
+    jc.usage("cmt", out);
+    patternMatchesTimes("commit\\(ci,cmt\\)", out.toString(), 1);
+  }
+
+  @Test
+  public void usageDisplaysCommandWithAliasesOnlyOnce() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandCommit commit = new CommandCommit();
+    jc.addCommand("commit", commit, "ci", "cmt");
+    StringBuilder out = new StringBuilder();
+    jc.usage(out);
+    // The usage should display this string twice: one as the command name
+    // and one after Usage:
+    patternMatchesTimes("commit\\(ci,cmt\\)", out.toString(), 2);
+  }
+
+  /**
+   * Visually test the formatting for "prettiness"
+   */
+  @Test(enabled = false, description = "TODO: test the output instead of displaying it")
+  public void formattingLooksNice(){
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandAdd add = new CommandAdd();
+    jc.addCommand("add", add, "a");
+    CommandCommit commit = new CommandCommit();
+    jc.addCommand("commit", commit, "ci", "cmt");
+    StringBuilder sb = new StringBuilder();
+    jc.usage(sb);
+    System.out.println("--- usage() formatting ---");
+    System.out.println(sb.toString());
+
+    sb = new StringBuilder();
+    jc.usage("commit", sb);
+    System.out.println("--- usage('commit') formatting ---");
+    System.out.println(sb.toString());
+  }
+
+  private void patternMatchesTimes(String pattern, String input, int times) {
+    Matcher m = Pattern.compile(pattern).matcher(input);
+    int matches = 0;
+    while (m.find()) matches++;
+    Assert.assertEquals(matches, times);
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/command/CommandCommit.java b/src/test/java/com/beust/jcommander/command/CommandCommit.java
new file mode 100644
index 0000000..2934a89
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/command/CommandCommit.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.command;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+import java.util.List;
+
+@Parameters(separators = "=", commandDescription = "Record changes to the repository")
+public class CommandCommit {
+
+  @Parameter(description = "List of files")
+  public List<String> files;
+
+  @Parameter(names = "--amend", description = "Amend")
+  public Boolean amend = false;
+
+  @Parameter(names = "--author")
+  public String author;
+}
diff --git a/src/test/java/com/beust/jcommander/command/CommandHidden.java b/src/test/java/com/beust/jcommander/command/CommandHidden.java
new file mode 100644
index 0000000..a3fc4fa
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/command/CommandHidden.java
@@ -0,0 +1,17 @@
+package com.beust.jcommander.command;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+import java.util.List;
+
+@Parameters(commandNames = "add", commandDescription = "Hidden command to add file contents to the index", hidden = true)
+public class CommandHidden {
+
+    @Parameter(description = "Patterns of files to be added")
+    public List<String> patterns;
+
+    @Parameter(names = "-i")
+    public Boolean interactive = false;
+
+}
diff --git a/src/test/java/com/beust/jcommander/command/CommandMain.java b/src/test/java/com/beust/jcommander/command/CommandMain.java
new file mode 100644
index 0000000..c205116
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/command/CommandMain.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.command;
+
+import com.beust.jcommander.Parameter;
+
+public class CommandMain {
+
+  @Parameter(names = "-v", description = "Verbose mode")
+  public Boolean verbose = false;
+}
diff --git a/src/test/java/com/beust/jcommander/command/CommandTest.java b/src/test/java/com/beust/jcommander/command/CommandTest.java
new file mode 100644
index 0000000..cf921bd
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/command/CommandTest.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright (C) 2010 the original author or authors.
+ * See the notice.md file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.beust.jcommander.command;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.ParameterException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+
+public class CommandTest {
+  @Test
+  public void namedCommandTest1() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    NamedCommandAdd add = new NamedCommandAdd();
+    jc.addCommand(add);
+    CommandCommit commit = new CommandCommit();
+    jc.addCommand("commit", commit);
+    jc.parse("add", "-i", "A.java");
+
+    Assert.assertEquals(jc.getParsedCommand(), "add");
+    Assert.assertEquals(add.interactive.booleanValue(), true);
+    Assert.assertEquals(add.patterns, Arrays.asList("A.java"));
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void shouldComplainIfNoAnnotations() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandAdd add = new CommandAdd();
+    jc.addCommand(add);
+  }
+
+  @Test
+  public void commandTest1() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandAdd add = new CommandAdd();
+    jc.addCommand("add", add);
+    CommandCommit commit = new CommandCommit();
+    jc.addCommand("commit", commit);
+    jc.parse("add", "-i", "A.java");
+
+    Assert.assertEquals(jc.getParsedCommand(), "add");
+    Assert.assertEquals(add.interactive.booleanValue(), true);
+    Assert.assertEquals(add.patterns, Arrays.asList("A.java"));
+  }
+
+  @Test
+  public void commandTest2() {
+    CommandMain cm = new CommandMain();
+    JCommander jc = new JCommander(cm);
+    CommandAdd add = new CommandAdd();
+    jc.addCommand("add", add);
+    CommandCommit commit = new CommandCommit();
+    jc.addCommand("commit", commit);
+    jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java");
+
+//    jc.setProgramName("TestCommander");
+//    jc.usage();
+//    jc.usage("add");
+//    jc.usage("commit");
+
+    Assert.assertTrue(cm.verbose);
+    Assert.assertEquals(jc.getParsedCommand(), "commit");
+    Assert.assertTrue(commit.amend);
+    Assert.assertEquals(commit.author, "cbeust");
+    Assert.assertEquals(commit.files, Arrays.asList("A.java", "B.java"));
+  }
+
+    @Test
+    public void hiddenCommandTest() {
+        CommandMain cm = new CommandMain();
+        JCommander jc = new JCommander(cm);
+        CommandAdd add = new CommandAdd();
+        jc.addCommand("add", add);
+        CommandHidden hidden = new CommandHidden();
+        jc.addCommand("hidden", hidden);
+        jc.parse("hidden", "-i", "A.java");
+
+        Assert.assertEquals(jc.getParsedCommand(), "hidden");
+        Assert.assertEquals(hidden.interactive.booleanValue(), true);
+        Assert.assertEquals(hidden.patterns, Arrays.asList("A.java"));
+
+        jc.setProgramName("TestCommander");
+        StringBuilder out = new StringBuilder();
+        jc.usage(out);
+
+        Assert.assertTrue(out.toString().contains("add      Add file contents to the index"));
+        Assert.assertFalse(out.toString().contains("hidden      Hidden command to add file contents to the index"));
+    }
+
+  public static void main(String[] args) {
+    new CommandTest().shouldComplainIfNoAnnotations();
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/command/NamedCommandAdd.java b/src/test/java/com/beust/jcommander/command/NamedCommandAdd.java
new file mode 100644
index 0000000..0773e18
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/command/NamedCommandAdd.java
@@ -0,0 +1,17 @@
+package com.beust.jcommander.command;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+import java.util.List;
+
+@Parameters(commandNames = "add", commandDescription = "Add file contents to the index")
+public class NamedCommandAdd {
+
+  @Parameter(description = "Patterns of files to be added")
+  public List<String> patterns;
+
+  @Parameter(names = "-i")
+  public Boolean interactive = false;
+
+}
diff --git a/src/test/java/com/beust/jcommander/dynamic/DSimple.java b/src/test/java/com/beust/jcommander/dynamic/DSimple.java
new file mode 100644
index 0000000..deb5a2b
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/dynamic/DSimple.java
@@ -0,0 +1,16 @@
+package com.beust.jcommander.dynamic;
+
+import com.beust.jcommander.DynamicParameter;
+
+import org.testng.collections.Maps;
+
+import java.util.Map;
+
+public class DSimple {
+
+  @DynamicParameter(names = "-D", description = "Dynamic parameters go here")
+  public Map<String, String> params = Maps.newHashMap();
+
+  @DynamicParameter(names = "-A", assignment = "@")
+  public Map<String, String> params2 = Maps.newHashMap();
+}
diff --git a/src/test/java/com/beust/jcommander/dynamic/DSimpleBad.java b/src/test/java/com/beust/jcommander/dynamic/DSimpleBad.java
new file mode 100644
index 0000000..f859029
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/dynamic/DSimpleBad.java
@@ -0,0 +1,11 @@
+package com.beust.jcommander.dynamic;
+
+import com.beust.jcommander.DynamicParameter;
+
+import java.util.List;
+
+public class DSimpleBad {
+
+  @DynamicParameter(names = "-D")
+  public List<String> params;
+}
diff --git a/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java b/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java
new file mode 100644
index 0000000..98327bd
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/dynamic/DynamicParameterTest.java
@@ -0,0 +1,60 @@
+package com.beust.jcommander.dynamic;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.ParameterException;
+import com.beust.jcommander.internal.Maps;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@Test
+public class DynamicParameterTest {
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void nonMapShouldThrow() {
+    new JCommander(new DSimpleBad()).parse("-D", "a=b", "-D", "c=d");
+  }
+
+  @Test(expectedExceptions = ParameterException.class)
+  public void wrongSeparatorShouldThrow() {
+    DSimple ds = new DSimple();
+    new JCommander(ds).parse("-D", "a:b", "-D", "c=d");
+  }
+
+  private void simple(String... parameters) {
+    DSimple ds = new DSimple();
+    new JCommander(ds).parse(parameters);
+    Assert.assertEquals(ds.params, Maps.newHashMap("a", "b", "c", "d"));
+  }
+
+  public void simpleWithSpaces() {
+    simple("-D", "a=b", "-D", "c=d");
+  }
+
+  public void simpleWithoutSpaces() {
+    simple("-Da=b", "-Dc=d");
+  }
+
+  public void usage() {
+    DSimple ds = new DSimple();
+    new JCommander(ds).usage(new StringBuilder());
+  }
+
+  public void differentAssignment() {
+    DSimple ds = new DSimple();
+    new JCommander(ds).parse("-D", "a=b", "-A", "c@d");
+    Assert.assertEquals(ds.params, Maps.newHashMap("a", "b"));
+    Assert.assertEquals(ds.params2, Maps.newHashMap("c", "d"));
+  }
+
+  @Test(enabled = false)
+  public static void main(String[] args) {
+    DynamicParameterTest dpt = new DynamicParameterTest();
+    dpt.simpleWithSpaces();
+//    dpt.nonMapShouldThrow();
+//    dpt.wrongSeparatorShouldThrow();
+//    dpt.differentAssignment();
+//    dpt.arity0();
+//    dpt.usage();
+  }
+}
diff --git a/src/test/java/com/beust/jcommander/internal/DefaultConsoleTest.java b/src/test/java/com/beust/jcommander/internal/DefaultConsoleTest.java
new file mode 100644
index 0000000..e101439
--- /dev/null
+++ b/src/test/java/com/beust/jcommander/internal/DefaultConsoleTest.java
@@ -0,0 +1,64 @@
+package com.beust.jcommander.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@Test
+public class DefaultConsoleTest {
+  public void readPasswordCanBeCalledMultipleTimes() {
+    final InputStream inBackup = System.in;
+    try {
+      final StringInputStream in = new StringInputStream();
+      System.setIn(in);
+      final Console console = new DefaultConsole();
+
+      in.setData("password1\n");
+      char[] password = console.readPassword(false);
+      Assert.assertEquals(password, "password1".toCharArray());
+      Assert.assertFalse(in.isClosedCalled(), "System.in stream shouldn't be closed");
+
+      in.setData("password2\n");
+      password = console.readPassword(false);
+      Assert.assertEquals(password, "password2".toCharArray());
+      Assert.assertFalse(in.isClosedCalled(), "System.in stream shouldn't be closed");
+    } finally {
+      System.setIn(inBackup);
+    }
+  }
+
+  private static class StringInputStream extends InputStream {
+    private byte[] data = new byte[0];
+    private int offset = 0;
+    private boolean closedCalled;
+
+    StringInputStream() {
+      super();
+    }
+
+    void setData(final String strData) {
+      data = strData.getBytes();
+      offset = 0;
+    }
+
+    boolean isClosedCalled() {
+      return closedCalled;
+    }
+
+    @Override
+    public int read() throws IOException {
+      if (offset >= data.length) {
+        return -1;
+      }
+      return 0xFFFF & data[offset++];
+    }
+
+    @Override
+    public void close() throws IOException {
+      closedCalled = true;
+      super.close();
+    }
+  }
+}
diff --git a/src/test/java/test/QuotedMainTest.java b/src/test/java/test/QuotedMainTest.java
new file mode 100644
index 0000000..abb97c0
--- /dev/null
+++ b/src/test/java/test/QuotedMainTest.java
@@ -0,0 +1,29 @@
+package test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.Parameter;
+
+public class QuotedMainTest {
+  @Parameter
+  List<String> args = new ArrayList<String>();
+  
+  String quoted = "\" \"";
+
+  @Test
+  public void testMain() {
+    JCommander jc = new JCommander(this);
+    jc.parse(quoted);
+    Assert.assertEquals(args.size(), 1);
+    Assert.assertEquals(args.get(0), " ");
+  }
+  
+  public static void main(String[] args) {
+    new QuotedMainTest().testMain();
+  }
+}
diff --git a/src/test/resources/MessageBundle_en_US.properties b/src/test/resources/MessageBundle_en_US.properties
new file mode 100644
index 0000000..11e19e4
--- /dev/null
+++ b/src/test/resources/MessageBundle_en_US.properties
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2010 the original author or authors.
+# See the notice.md file distributed with this work for additional
+# information regarding copyright ownership.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+host = Host
+command = Command from the bundle
+myoption = My option
\ No newline at end of file
diff --git a/src/test/resources/MessageBundle_fr_FR.properties b/src/test/resources/MessageBundle_fr_FR.properties
new file mode 100644
index 0000000..4ae83a9
--- /dev/null
+++ b/src/test/resources/MessageBundle_fr_FR.properties
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2010 the original author or authors.
+# See the notice.md file distributed with this work for additional
+# information regarding copyright ownership.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+host = Hôte
+files = Les fichiers
diff --git a/src/test/resources/jcommander.properties b/src/test/resources/jcommander.properties
new file mode 100644
index 0000000..3cea84b
--- /dev/null
+++ b/src/test/resources/jcommander.properties
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2010 the original author or authors.
+# See the notice.md file distributed with this work for additional
+# information regarding copyright ownership.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+groups=unit
+level=17
+log=18
\ No newline at end of file
diff --git a/src/test/resources/testng-single.xml b/src/test/resources/testng-single.xml
new file mode 100644
index 0000000..cc0602c
--- /dev/null
+++ b/src/test/resources/testng-single.xml
@@ -0,0 +1,19 @@
+<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
+  
+<suite name="JCommander" verbose="2">
+
+  <test name="JCommander tests">
+    <classes>
+<!-- 
+      <class name="com.beust.jcommander.FinderTest" />
+-->
+      <class name="com.beust.jcommander.JCommanderTest">
+        <methods>
+          <include name="partialValidation"></include>
+        </methods>
+      </class>
+    </classes>
+  </test>
+
+</suite>
+
diff --git a/src/test/resources/testng.xml b/src/test/resources/testng.xml
new file mode 100644
index 0000000..f804418
--- /dev/null
+++ b/src/test/resources/testng.xml
@@ -0,0 +1,24 @@
+<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
+  
+<suite name="JCommander">
+
+  <test name="JCommander tests">
+    <classes>
+      <class name="com.beust.jcommander.command.CommandTest" />
+      <class name="com.beust.jcommander.command.CommandAliasTest" />
+      <class name="com.beust.jcommander.ConverterFactoryTest" />
+      <class name="com.beust.jcommander.DefaultProviderTest" />
+      <class name="com.beust.jcommander.DefaultValueTest" />
+      <class name="com.beust.jcommander.JCommanderTest" />
+      <class name="com.beust.jcommander.ParametersDelegateTest" />
+      <class name="com.beust.jcommander.dynamic.DynamicParameterTest" />
+      <class name="com.beust.jcommander.VariableArityTest" />
+      <class name="com.beust.jcommander.MethodSetterTest" />
+      <class name="com.beust.jcommander.PositiveIntegerTest" />
+      <class name="com.beust.jcommander.FinderTest" />
+      <class name="com.beust.jcommander.CmdTest" />
+    </classes>
+  </test>
+
+</suite>
+
diff --git a/upload b/upload
new file mode 100755
index 0000000..ee46516
--- /dev/null
+++ b/upload
@@ -0,0 +1,5 @@
+scp doc/index.html  target/jcommander-1.27.jar beust.com@beust.com:domains/jcommander.org/html
+mvn javadoc:javadoc
+scp -r target/site/apidocs beust.com@beust.com:domains/jcommander.org/html
+
+