Merge pull request #189 from samvv/master

Added converters for URIs, URLs and Paths
diff --git a/build-with-maven b/build-with-maven
index 176ca1a..0e2de5a 100755
--- a/build-with-maven
+++ b/build-with-maven
@@ -2,11 +2,12 @@
 
 
 #v=6.5.2beta
-export TESTNG=`echo ../testng/target/testng-6.9beta.jar`
+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"
+run="java -classpath \"target/classes;target/test-classes;${TESTNG};$CLASSPATH\" org.testng.TestNG src/test/resources/testng.xml"
 echo "Launching tests: ${run}"
-java -classpath target/classes:target/test-classes:${TESTNG}:$CLASSPATH org.testng.TestNG src/test/resources/testng.xml
+$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"
diff --git a/doc/index.html b/doc/index.html
index a92a969..da0368b 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -30,6 +30,19 @@
     <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="[email protected]">
+  <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>
@@ -39,7 +52,7 @@
   </tr>
   <tr>
     <td align="right">
-      Last updated: October 25th, 2012
+      Last updated: January 14th, 2015
     </td>
   </tr>
   <tr><td align="right"><a href="mailto:[email protected]">C&eacute;dric Beust</a></td></tr>
@@ -795,9 +808,28 @@
 }
 </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 command line, here is <a href="http://github.com/cbeust/testng/blob/master/src/main/java/org/testng/CommandLineArgs.java">its definition file</a>.
+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>
 
diff --git a/pom.xml b/pom.xml
index 2d5ff92..cc70277 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
   <artifactId>jcommander</artifactId>
   <packaging>jar</packaging>
   <name>JCommander</name>
-  <version>1.36-SNAPSHOT</version>
+  <version>1.48-SNAPSHOT</version>
   <description>A Java framework to parse command line options with annotations.</description>
   <url>http://beust.com/jcommander</url>
   <licenses>
@@ -40,6 +40,14 @@
     <url>[email protected]: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>
@@ -155,6 +163,39 @@
         </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>
diff --git a/src/main/java/com/beust/jcommander/JCommander.java b/src/main/java/com/beust/jcommander/JCommander.java
index 3884976..98f112e 100644
--- a/src/main/java/com/beust/jcommander/JCommander.java
+++ b/src/main/java/com/beust/jcommander/JCommander.java
@@ -154,6 +154,7 @@
 
   private List<String> m_unknownArgs = Lists.newArrayList();
   private boolean m_acceptUnknownOptions = false;
+  private boolean m_allowParameterOverwriting = false;
   
   private static Console m_console;
 
@@ -485,8 +486,10 @@
 
       // Read through file one line at time. Print line # and line
       while ((line = bufRead.readLine()) != null) {
-        // Allow empty lines in these at files
-        if (line.length() > 0) result.add(line);
+        // Allow empty lines and # comments in these at files
+        if (line.length() > 0 && ! line.trim().startsWith("#")) {
+            result.add(line);
+        }
       }
 
       bufRead.close();
@@ -677,6 +680,7 @@
     // 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);
@@ -685,7 +689,7 @@
 
       JCommander jc = findCommandByAlias(arg);
       int increment = 1;
-      if (! "--".equals(a) && isOption(args, a) && jc == null) {
+      if (! isDashDash && ! "--".equals(a) && isOption(args, a) && jc == null) {
         //
         // Option
         //
@@ -745,6 +749,7 @@
         //
         if (! Strings.isStringEmpty(arg)) {
           if ("--".equals(arg)) {
+              isDashDash = true;
               a = trim(args[++i]);
           }
           if (m_commands.isEmpty()) {
@@ -1119,13 +1124,17 @@
       // 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()) {
-        ProgramName progName = commands.getKey();
-        String dispName = progName.getDisplayName();
-        out.append(indent).append("    " + dispName); // + s(spaceCount) + getCommandDescription(progName.name) + "\n");
+        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");
+          // Options for this command
+          usage(progName.getName(), out, "      ");
+          out.append("\n");
+        }
       }
     }
   }
@@ -1259,11 +1268,16 @@
       if (converterClass != null && converterClass.isEnum()) {
         try {
           result = Enum.valueOf((Class<? extends Enum>) converterClass, value);
-        } catch ( IllegalArgumentException e ) {
-          result = Enum.valueOf((Class<? extends Enum>) converterClass, value.toUpperCase());
+        } 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));
+                      EnumSet.allOf((Class<? extends Enum>) converterClass));
         }
       } else {
         converter = instantiateConverter(optionName, converterClass);
@@ -1364,6 +1378,7 @@
     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);
 
@@ -1565,7 +1580,13 @@
   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/Parameter.java b/src/main/java/com/beust/jcommander/Parameter.java
index 974eeaa..d8cf87d 100644
--- a/src/main/java/com/beust/jcommander/Parameter.java
+++ b/src/main/java/com/beust/jcommander/Parameter.java
@@ -119,4 +119,12 @@
    * 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
index 33574a9..2ef2d5f 100644
--- a/src/main/java/com/beust/jcommander/ParameterDescription.java
+++ b/src/main/java/com/beust/jcommander/ParameterDescription.java
@@ -186,7 +186,6 @@
     String[] names = m_wrappedParameter.names();
     for (int i = 0; i < names.length; i++) {
       if (i > 0) sb.append(", ");
-      if (names.length == 1 && names[i].startsWith("--")) sb.append("    ");
       sb.append(names[i]);
     }
     return sb.toString();
@@ -231,7 +230,7 @@
     p("Adding " + (isDefault ? "default " : "") + "value:" + value
         + " to parameter:" + m_parameterized.getName());
     String name = m_wrappedParameter.names()[0];
-    if (m_assigned && ! isMultiOption()) {
+    if (m_assigned && ! isMultiOption() && !m_jCommander.isParameterOverwritingAllowed() || isNonOverwritableForced()) {
       throw new ParameterException("Can only specify option " + name + " once.");
     }
 
@@ -358,4 +357,8 @@
   public boolean isHelp() {
     return m_wrappedParameter.isHelp();
   }
+  
+  public boolean isNonOverwritableForced() {
+    return m_wrappedParameter.isNonOverwritableForced();
+  }
 }
diff --git a/src/main/java/com/beust/jcommander/Parameters.java b/src/main/java/com/beust/jcommander/Parameters.java
index 9834ea0..f2e8c76 100644
--- a/src/main/java/com/beust/jcommander/Parameters.java
+++ b/src/main/java/com/beust/jcommander/Parameters.java
@@ -67,4 +67,9 @@
    * 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/WrappedParameter.java b/src/main/java/com/beust/jcommander/WrappedParameter.java
index 52cafc4..f4e7d56 100644
--- a/src/main/java/com/beust/jcommander/WrappedParameter.java
+++ b/src/main/java/com/beust/jcommander/WrappedParameter.java
@@ -109,4 +109,7 @@
     return m_parameter != null && m_parameter.help();
   }
 
+  public boolean isNonOverwritableForced() {
+      return m_parameter != null && m_parameter.forceNonOverwritable();
+  }
 }
diff --git a/src/test/java/com/beust/jcommander/JCommanderTest.java b/src/test/java/com/beust/jcommander/JCommanderTest.java
index 342b858..ad2c5e8 100644
--- a/src/test/java/com/beust/jcommander/JCommanderTest.java
+++ b/src/test/java/com/beust/jcommander/JCommanderTest.java
@@ -1056,7 +1056,7 @@
 
   @Test(enabled = false)
   public static void main(String[] args) throws Exception {
-//    new JCommanderTest().a();
+    new JCommanderTest().enumArgsFail();
 //    class A {
 //      @Parameter(names = "-short", required = true)
 //      List<String> parameters;
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/CommandTest.java b/src/test/java/com/beust/jcommander/command/CommandTest.java
index 97e0007..cf921bd 100644
--- a/src/test/java/com/beust/jcommander/command/CommandTest.java
+++ b/src/test/java/com/beust/jcommander/command/CommandTest.java
@@ -87,6 +87,28 @@
     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();
   }