You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by eb...@apache.org on 2009/05/30 15:43:44 UTC

svn commit: r780264 - in /commons/proper/cli/trunk: src/java/org/apache/commons/cli/ src/test/org/apache/commons/cli/ xdocs/

Author: ebourg
Date: Sat May 30 13:43:44 2009
New Revision: 780264

URL: http://svn.apache.org/viewvc?rev=780264&view=rev
Log:
New unified parser (CLI-181, also fixes CLI-160, CLI-161, CLI-167, CLI-184)

Added:
    commons/proper/cli/trunk/src/java/org/apache/commons/cli/DefaultParser.java   (with props)
    commons/proper/cli/trunk/src/test/org/apache/commons/cli/DefaultParserTest.java   (with props)
Modified:
    commons/proper/cli/trunk/src/java/org/apache/commons/cli/Option.java
    commons/proper/cli/trunk/src/java/org/apache/commons/cli/Options.java
    commons/proper/cli/trunk/src/test/org/apache/commons/cli/BasicParserTest.java
    commons/proper/cli/trunk/src/test/org/apache/commons/cli/GnuParserTest.java
    commons/proper/cli/trunk/src/test/org/apache/commons/cli/ParserTestCase.java
    commons/proper/cli/trunk/src/test/org/apache/commons/cli/PosixParserTest.java
    commons/proper/cli/trunk/xdocs/changes.xml

Added: commons/proper/cli/trunk/src/java/org/apache/commons/cli/DefaultParser.java
URL: http://svn.apache.org/viewvc/commons/proper/cli/trunk/src/java/org/apache/commons/cli/DefaultParser.java?rev=780264&view=auto
==============================================================================
--- commons/proper/cli/trunk/src/java/org/apache/commons/cli/DefaultParser.java (added)
+++ commons/proper/cli/trunk/src/java/org/apache/commons/cli/DefaultParser.java Sat May 30 13:43:44 2009
@@ -0,0 +1,590 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.cli;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * Default parser.
+ * 
+ * @author Emmanuel Bourg
+ * @version $Revision$, $Date$
+ * @since 1.3
+ */
+public class DefaultParser implements CommandLineParser
+{    
+    protected CommandLine cmd;
+    protected Options options;
+    protected boolean stopAtNonOption;
+    
+    /** The token currently processed. */
+    protected String currentToken;
+    
+    /** The last option parsed. */
+    protected Option currentOption;
+    
+    /** Flag indicating if tokens should no longer be analysed and simply added as arguments of the command line. */
+    protected boolean skipParsing;
+    
+    /** The required options expected to be found when parsing the command line. */
+    protected List expectedOpts;
+    
+    public CommandLine parse(Options options, String[] arguments) throws ParseException
+    {
+        return parse(options, arguments, false);
+    }
+
+    public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException
+    {
+        this.options = options;
+        this.stopAtNonOption = stopAtNonOption;
+        skipParsing = false;
+        currentOption = null;
+        expectedOpts = new ArrayList(options.getRequiredOptions());
+        
+        // clear the data from the groups
+        for (Iterator it = options.getOptionGroups().iterator(); it.hasNext();)
+        {
+            OptionGroup group = (OptionGroup) it.next();
+            group.setSelected(null);
+        }
+        
+        cmd = new CommandLine();
+
+        if (arguments != null)
+        {
+            for (int i = 0; i < arguments.length; i++)
+            {
+                handleToken(arguments[i]);
+            }
+        }
+        
+        // check the arguments of the last option
+        checkRequiredArgs();
+        
+        checkRequiredOptions();
+        
+        return cmd;
+    }
+
+    /**
+     * Throws a {@link MissingOptionException} if all of the required options
+     * are not present.
+     *
+     * @throws MissingOptionException if any of the required Options
+     * are not present.
+     */
+    private void checkRequiredOptions() throws MissingOptionException
+    {       
+        // if there are required options that have not been processsed
+        if (!expectedOpts.isEmpty())
+        {
+            throw new MissingOptionException(expectedOpts);
+        }
+    }
+
+    /**
+     * Throw a {@link MissingArgumentException} if the current option
+     * didn't receive the number of arguments expected.
+     */
+    private void checkRequiredArgs() throws ParseException
+    {
+        if (currentOption != null && currentOption.requiresArg())
+        {
+            throw new MissingArgumentException(currentOption);
+        }
+    }
+
+    /**
+     * Handle any command line token.
+     * 
+     * @param token the command line token to handle
+     * @throws ParseException
+     */
+    private void handleToken(String token) throws ParseException
+    {
+        currentToken = token;
+        
+        if (skipParsing)
+        {
+            cmd.addArg(token);
+        }
+        else if ("--".equals(token))
+        {
+            skipParsing = true;
+        }
+        else if (currentOption != null && currentOption.acceptsArg() && isArgument(token))
+        {
+            currentOption.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(token));
+        }
+        else if (token.startsWith("--"))
+        {
+            handleLongOption(token);
+        }
+        else if (token.startsWith("-") && !"-".equals(token))
+        {
+            handleShortAndLongOption(token);
+        }
+        else
+        {
+            handleUnknownToken(token);
+        }
+        
+        if (currentOption != null && !currentOption.acceptsArg())
+        {
+            currentOption = null;
+        }
+    }
+
+    /**
+     * Returns true is the token is a valid argument.
+     * 
+     * @param token
+     */
+    private boolean isArgument(String token)
+    {
+        return !isOption(token) || isNegativeNumber(token);
+    }
+
+    /**
+     * Check if the token is a negative number.
+     * 
+     * @param token
+     */
+    private boolean isNegativeNumber(String token)
+    {
+        try
+        {
+            Double.parseDouble(token);
+            return true;
+        }
+        catch (NumberFormatException e)
+        {
+            return false;
+        }
+    }
+
+    /**
+     * Tells if the token looks like an option.
+     * 
+     * @param token
+     */
+    private boolean isOption(String token)
+    {
+        return isLongOption(token) || isShortOption(token);
+    }
+
+    /**
+     * Tells if the token looks like a short option.
+     * 
+     * @param token
+     */
+    private boolean isShortOption(String token)
+    {
+        // short options (-S, -SV, -S=V, -SV1=V2, -S1S2)
+        return token.startsWith("-") && token.length() >= 2 && options.hasShortOption(token.substring(1, 2));
+    }
+
+    /**
+     * Tells if the token looks like a long option.
+     * 
+     * @param token
+     */
+    private boolean isLongOption(String token)
+    {
+        if (!token.startsWith("-") || token.length() == 1)
+        {
+            return false;
+        }
+
+        int pos = token.indexOf("=");
+        String t = pos == -1 ? token : token.substring(0, pos);
+        
+        if (!options.getMatchingOptions(t).isEmpty())
+        {
+            // long or partial long options (--L, -L, --L=V, -L=V, --l, --l=V)
+            return true;
+        }
+        else if (getLongPrefix(token) != null && !token.startsWith("--"))
+        {
+            // -LV
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Handles an unknown token. If the token starts with a dash an 
+     * UnrecognizedOptionException is thrown. Otherwise the token is added 
+     * to the arguments of the command line. If the stopAtNonOption flag 
+     * is set, this stops the parsing and the remaining tokens are added 
+     * as-is in the arguments of the command line.
+     * 
+     * @param token the command line token to handle
+     */
+    private void handleUnknownToken(String token) throws ParseException
+    {
+        if (token.startsWith("-") && token.length() > 1 && !stopAtNonOption)
+        {
+            throw new UnrecognizedOptionException("Unrecognized option: " + token, token);
+        }
+        
+        cmd.addArg(token);
+        if (stopAtNonOption)
+        {
+            skipParsing = true;
+        }        
+    }
+
+    /**
+     * Handles the following tokens:
+     * 
+     * --L
+     * --L=V
+     * --L V
+     * --l
+     * 
+     * @param token the command line token to handle
+     */
+    private void handleLongOption(String token) throws ParseException
+    {
+        if (token.indexOf('=') == -1)
+        {
+            handleLongOptionWithoutEqual(token);
+        }
+        else
+        {
+            handleLongOptionWithEqual(token);                                   
+        }
+    }
+
+    /**
+     * Handles the following tokens:
+     * 
+     * --L
+     * -L
+     * --l
+     * -l
+     * 
+     * @param token the command line token to handle
+     */
+    private void handleLongOptionWithoutEqual(String token) throws ParseException
+    {
+        List matchingOpts = options.getMatchingOptions(token);
+        if (matchingOpts.isEmpty())
+        {
+            handleUnknownToken(currentToken);
+        }
+        else if (matchingOpts.size() > 1)
+        {
+            throw new AmbiguousOptionException(token, matchingOpts);
+        }
+        else
+        {
+            handleOption(options.getOption((String) matchingOpts.get(0)));
+        }
+    }
+
+    /**
+     * Handles the following tokens:
+     * 
+     * --L=V
+     * -L=V
+     * --l=V
+     * -l=V
+     * 
+     * @param token the command line token to handle
+     */
+    private void handleLongOptionWithEqual(String token) throws ParseException
+    {
+        int pos = token.indexOf('=');
+
+        String value = token.substring(pos + 1);
+        
+        String opt = token.substring(0, pos);
+        
+        List matchingOpts = options.getMatchingOptions(opt);
+        if (matchingOpts.isEmpty())
+        {
+            handleUnknownToken(currentToken);
+        }
+        else if (matchingOpts.size() > 1)
+        {
+            throw new AmbiguousOptionException(opt, matchingOpts);
+        }
+        else
+        {
+            Option option = options.getOption((String) matchingOpts.get(0));
+            
+            if (option.acceptsArg())
+            {
+                handleOption(option);
+                currentOption.addValueForProcessing(value);
+                currentOption = null;
+            }
+            else
+            {
+                handleUnknownToken(currentToken);
+            }
+        }
+    }
+
+    /**
+     * Handles the following tokens:
+     * 
+     * -S
+     * -SV
+     * -S V
+     * -S=V
+     * -S1S2
+     * -S1S2 V
+     * -SV1=V2
+     * 
+     * -L
+     * -LV
+     * -L V
+     * -L=V
+     * -l
+     * 
+     * @param token the command line token to handle
+     */
+    private void handleShortAndLongOption(String token) throws ParseException
+    {
+        String t = Util.stripLeadingHyphens(token);
+        
+        int pos = t.indexOf('=');
+        
+        if (t.length() == 1)
+        {
+            // -S
+            if (options.hasShortOption(t))
+            {
+                handleOption(options.getOption(t));
+            }
+            else
+            {
+                handleUnknownToken(token);
+            }
+        }
+        else if (pos == -1)
+        {
+            if (options.hasShortOption(t))
+            {
+                handleOption(options.getOption(t));
+            }
+            else if (!options.getMatchingOptions(t).isEmpty())
+            {
+                // -L or -l
+                handleLongOptionWithoutEqual(token);
+            }
+            else
+            {
+                // look for a long prefix (-Xmx512m)
+                String opt = getLongPrefix(t);
+                
+                if (opt != null && options.getOption(opt).acceptsArg())
+                {
+                    handleOption(options.getOption(opt));
+                    currentOption.addValueForProcessing(t.substring(opt.length()));
+                    currentOption = null;
+                }
+                else if (isJavaProperty(t))
+                {
+                    // -SV1 (-Dflag)
+                    handleOption(options.getOption(t.substring(0, 1)));
+                    currentOption.addValueForProcessing(t.substring(1));
+                    currentOption = null;
+                }
+                else
+                {
+                    // -S1S2S3 or -S1S2V
+                    handleConcatenatedOptions(token);
+                }
+            }
+        }
+        else
+        {
+            String opt = t.substring(0, pos);
+            String value = t.substring(pos + 1);
+            
+            if (opt.length() == 1)
+            {
+                // -S=V
+                Option option = options.getOption(opt);
+                if (option != null && option.acceptsArg())
+                {
+                    handleOption(option);
+                    currentOption.addValueForProcessing(value);
+                    currentOption = null;
+                }
+                else
+                {
+                    handleUnknownToken(token);
+                }
+            }
+            else if (isJavaProperty(opt))
+            {
+                // -SV1=V2 (-Dkey=value)
+                handleOption(options.getOption(opt.substring(0, 1)));
+                currentOption.addValueForProcessing(opt.substring(1));
+                currentOption.addValueForProcessing(value);
+                currentOption = null;
+            }
+            else
+            {
+                // -L=V or -l=V
+                handleLongOptionWithEqual(token);
+            }
+        }
+    }
+
+    /**
+     * Search for a prefix that is the long name of an option (-Xmx512m)
+     * 
+     * @param token
+     */
+    private String getLongPrefix(String token)
+    {
+        String t = Util.stripLeadingHyphens(token);
+        
+        int i;
+        String opt = null;
+        for (i = t.length() - 2; i > 1; i--)
+        {
+            String prefix = t.substring(0, i);
+            if (options.hasLongOption(prefix))
+            {
+                opt = prefix;
+                break;
+            }
+        }
+        
+        return opt;
+    }
+
+    /**
+     * Check if the specified token is a Java-like property (-Dkey=value).
+     */
+    private boolean isJavaProperty(String token)
+    {
+        String opt = token.substring(0, 1);
+        Option option = options.getOption(opt);
+        
+        return option != null && (option.getArgs() >= 2 || option.getArgs() == Option.UNLIMITED_VALUES);
+    }
+
+    private void handleOption(Option option) throws ParseException
+    {
+        // check the previous option before handling the next one
+        checkRequiredArgs();
+        
+        option = (Option) option.clone();
+        
+        updateRequiredOptions(option);
+        
+        cmd.addOption(option);
+        
+        if (option.hasArg())
+        {
+            currentOption = option;            
+        }
+        else
+        {
+            currentOption = null;
+        }
+    }
+
+    /**
+     * Removes the option or its group from the list of expected elements.
+     * 
+     * @param option
+     */
+    private void updateRequiredOptions(Option option) throws AlreadySelectedException
+    {
+        if (option.isRequired())
+        {
+            expectedOpts.remove(option.getKey());
+        }
+
+        // if the option is in an OptionGroup make that option the selected option of the group
+        if (options.getOptionGroup(option) != null)
+        {
+            OptionGroup group = options.getOptionGroup(option);
+
+            if (group.isRequired())
+            {
+                expectedOpts.remove(group);
+            }
+
+            group.setSelected(option);
+        }
+    }
+
+    /**
+     * Breaks <code>token</code> into its constituent parts
+     * using the following algorithm.
+     *
+     * <ul>
+     *  <li>ignore the first character ("<b>-</b>")</li>
+     *  <li>foreach remaining character check if an {@link Option}
+     *  exists with that id.</li>
+     *  <li>if an {@link Option} does exist then add that character
+     *  prepended with "<b>-</b>" to the list of processed tokens.</li>
+     *  <li>if the {@link Option} can have an argument value and there
+     *  are remaining characters in the token then add the remaining
+     *  characters as a token to the list of processed tokens.</li>
+     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
+     *  <code>stopAtNonOption</code> <b>IS</b> set then add the special token
+     *  "<b>--</b>" followed by the remaining characters and also
+     *  the remaining tokens directly to the processed tokens list.</li>
+     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
+     *  <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
+     *  character prepended with "<b>-</b>".</li>
+     * </ul>
+     *
+     * @param token The current token to be <b>burst</b>
+     * at the first non-Option encountered.
+     */
+    protected void handleConcatenatedOptions(String token) throws ParseException
+    {
+        for (int i = 1; i < token.length(); i++)
+        {
+            String ch = String.valueOf(token.charAt(i));
+
+            if (options.hasOption(ch))
+            {
+                handleOption(options.getOption(ch));
+                
+                if (currentOption != null && (token.length() != (i + 1)))
+                {
+                    // add the trail as an argument of the option
+                    currentOption.addValueForProcessing(token.substring(i + 1));
+                    break;
+                }
+            }
+            else
+            {                
+                handleUnknownToken(stopAtNonOption && i > 1 ? token.substring(i) : token);
+                break;
+            }
+        }
+    }
+}

Propchange: commons/proper/cli/trunk/src/java/org/apache/commons/cli/DefaultParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/cli/trunk/src/java/org/apache/commons/cli/DefaultParser.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: commons/proper/cli/trunk/src/java/org/apache/commons/cli/Option.java
URL: http://svn.apache.org/viewvc/commons/proper/cli/trunk/src/java/org/apache/commons/cli/Option.java?rev=780264&r1=780263&r2=780264&view=diff
==============================================================================
--- commons/proper/cli/trunk/src/java/org/apache/commons/cli/Option.java (original)
+++ commons/proper/cli/trunk/src/java/org/apache/commons/cli/Option.java Sat May 30 13:43:44 2009
@@ -465,7 +465,7 @@
      */
     private void add(String value)
     {
-        if ((numberOfArgs > 0) && (values.size() > (numberOfArgs - 1)))
+        if (!acceptsArg())
         {
             throw new RuntimeException("Cannot add value, list full.");
         }
@@ -671,4 +671,36 @@
                 + "Subclasses should use the addValueForProcessing method instead. ");
     }
 
+    /**
+     * Tells if the option can accept more arguments.
+     * 
+     * @return false if the maximum number of arguments is reached
+     * @since 1.3
+     */
+    boolean acceptsArg()
+    {
+        return (hasArg() || hasArgs() || hasOptionalArg()) && (numberOfArgs <= 0 || values.size() < numberOfArgs);
+    }
+
+    /**
+     * Tells if the option requires more arguments to be valid.
+     * 
+     * @return false if the option doesn't require more arguments
+     * @since 1.3
+     */
+    boolean requiresArg()
+    {
+        if (optionalArg)
+        {
+            return false;
+        }
+        if (numberOfArgs == UNLIMITED_VALUES)
+        {
+            return values.size() < 1;
+        }
+        else
+        {
+            return acceptsArg();
+        }
+    }
 }

Modified: commons/proper/cli/trunk/src/java/org/apache/commons/cli/Options.java
URL: http://svn.apache.org/viewvc/commons/proper/cli/trunk/src/java/org/apache/commons/cli/Options.java?rev=780264&r1=780263&r2=780264&view=diff
==============================================================================
--- commons/proper/cli/trunk/src/java/org/apache/commons/cli/Options.java (original)
+++ commons/proper/cli/trunk/src/java/org/apache/commons/cli/Options.java Sat May 30 13:43:44 2009
@@ -243,8 +243,7 @@
      * Returns whether the named {@link Option} is a member of this {@link Options}.
      *
      * @param opt short or long name of the {@link Option}
-     * @return true if the named {@link Option} is a member
-     * of this {@link Options}
+     * @return true if the named {@link Option} is a member of this {@link Options}
      */
     public boolean hasOption(String opt)
     {
@@ -254,6 +253,34 @@
     }
 
     /**
+     * Returns whether the named {@link Option} is a member of this {@link Options}.
+     *
+     * @param opt long name of the {@link Option}
+     * @return true if the named {@link Option} is a member of this {@link Options}
+     * @since 1.3
+     */
+    public boolean hasLongOption(String opt)
+    {
+        opt = Util.stripLeadingHyphens(opt);
+
+        return longOpts.containsKey(opt);
+    }
+
+    /**
+     * Returns whether the named {@link Option} is a member of this {@link Options}.
+     *
+     * @param opt short name of the {@link Option}
+     * @return true if the named {@link Option} is a member of this {@link Options}
+     * @since 1.3
+     */
+    public boolean hasShortOption(String opt)
+    {
+        opt = Util.stripLeadingHyphens(opt);
+
+        return shortOpts.containsKey(opt);
+    }
+
+    /**
      * Returns the OptionGroup the <code>opt</code> belongs to.
      * @param opt the option whose OptionGroup is being queried.
      *

Modified: commons/proper/cli/trunk/src/test/org/apache/commons/cli/BasicParserTest.java
URL: http://svn.apache.org/viewvc/commons/proper/cli/trunk/src/test/org/apache/commons/cli/BasicParserTest.java?rev=780264&r1=780263&r2=780264&view=diff
==============================================================================
--- commons/proper/cli/trunk/src/test/org/apache/commons/cli/BasicParserTest.java (original)
+++ commons/proper/cli/trunk/src/test/org/apache/commons/cli/BasicParserTest.java Sat May 30 13:43:44 2009
@@ -29,6 +29,26 @@
         parser = new BasicParser();
     }
 
+    public void testDoubleDash2() throws Exception
+    {
+        // not supported by the BasicParser
+    }
+
+    public void testLongWithoutEqualSingleDash() throws Exception
+    {
+        // not supported by the BasicParser
+    }
+    
+    public void testAmbiguousLongWithoutEqualSingleDash() throws Exception
+    {
+        // not supported by the basicParser
+    }
+    
+    public void testNegativeOption() throws Exception
+    {
+        // not supported by the BasicParser (CLI-184)
+    }
+
     public void testPropertiesOption1() throws Exception
     {
         // not supported by the BasicParser

Added: commons/proper/cli/trunk/src/test/org/apache/commons/cli/DefaultParserTest.java
URL: http://svn.apache.org/viewvc/commons/proper/cli/trunk/src/test/org/apache/commons/cli/DefaultParserTest.java?rev=780264&view=auto
==============================================================================
--- commons/proper/cli/trunk/src/test/org/apache/commons/cli/DefaultParserTest.java (added)
+++ commons/proper/cli/trunk/src/test/org/apache/commons/cli/DefaultParserTest.java Sat May 30 13:43:44 2009
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.cli;
+
+/**
+ * @author Emmanuel Bourg
+ * @version $Revision$, $Date$
+ */
+public class DefaultParserTest extends ParserTestCase {
+
+    public void setUp() {
+        super.setUp();
+        parser = new DefaultParser();
+    }
+}

Propchange: commons/proper/cli/trunk/src/test/org/apache/commons/cli/DefaultParserTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/cli/trunk/src/test/org/apache/commons/cli/DefaultParserTest.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: commons/proper/cli/trunk/src/test/org/apache/commons/cli/GnuParserTest.java
URL: http://svn.apache.org/viewvc/commons/proper/cli/trunk/src/test/org/apache/commons/cli/GnuParserTest.java?rev=780264&r1=780263&r2=780264&view=diff
==============================================================================
--- commons/proper/cli/trunk/src/test/org/apache/commons/cli/GnuParserTest.java (original)
+++ commons/proper/cli/trunk/src/test/org/apache/commons/cli/GnuParserTest.java Sat May 30 13:43:44 2009
@@ -25,6 +25,41 @@
         parser = new GnuParser();
     }
 
+    public void testDoubleDash2() throws Exception
+    {
+        // not supported by the GnuParser
+    }
+    
+    public void testLongWithoutEqualSingleDash() throws Exception
+    {
+        // not supported by the GnuParser
+    }
+
+    public void testAmbiguousLongWithoutEqualSingleDash() throws Exception
+    {
+        // not supported by the GnuParser
+    }
+
+    public void testNegativeOption() throws Exception
+    {
+        // not supported by the GnuParser (CLI-184)
+    }
+
+    public void testLongWithUnexpectedArgument1() throws Exception 
+    {
+        // not supported by the GnuParser
+    }
+
+    public void testLongWithUnexpectedArgument2() throws Exception 
+    {
+        // not supported by the GnuParser
+    }
+
+    public void testShortWithUnexpectedArgument() throws Exception 
+    {
+        // not supported by the GnuParser
+    }
+
     public void testUnambiguousPartialLongOption1() throws Exception
     {
         // not supported by the GnuParser

Modified: commons/proper/cli/trunk/src/test/org/apache/commons/cli/ParserTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/cli/trunk/src/test/org/apache/commons/cli/ParserTestCase.java?rev=780264&r1=780263&r2=780264&view=diff
==============================================================================
--- commons/proper/cli/trunk/src/test/org/apache/commons/cli/ParserTestCase.java (original)
+++ commons/proper/cli/trunk/src/test/org/apache/commons/cli/ParserTestCase.java Sat May 30 13:43:44 2009
@@ -157,6 +157,24 @@
         assertTrue("Confirm 2 extra args: " + cl.getArgList().size(), cl.getArgList().size() == 2);
     }
 
+    public void testDoubleDash2() throws Exception
+    {
+        Options options = new Options();
+        options.addOption(OptionBuilder.hasArg().create('n'));
+        options.addOption(OptionBuilder.create('m'));
+
+        try
+        {
+            parser.parse(options, new String[]{"-n", "--", "-m"});
+            fail("MissingArgumentException not thrown for option -n");
+        }
+        catch (MissingArgumentException e)
+        {
+            assertNotNull("option null", e.getOption());
+            assertEquals("n", e.getOption().getOpt());
+        }
+    }
+    
     public void testSingleDash() throws Exception
     {
         String[] args = new String[] { "--copt",
@@ -228,6 +246,16 @@
         assertEquals("-1", cl.getOptionValue("b"));
     }
 
+    public void testNegativeOption() throws Exception
+    {
+        String[] args = new String[] { "-b", "-1"} ;
+        
+        options.addOption("1", false, null);
+
+        CommandLine cl = parser.parse(options, args);
+        assertEquals("-1", cl.getOptionValue("b"));
+    }
+    
     public void testArgumentStartingWithHyphen() throws Exception
     {
         String[] args = new String[]{"-b", "-foo"};
@@ -284,6 +312,105 @@
         assertEquals("bar", cl.getOptionValue("foo"));
     }
 
+    public void testLongWithoutEqualSingleDash() throws Exception
+    {
+        String[] args = new String[] { "-foobar" };
+
+        Options options = new Options();
+        options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
+
+        CommandLine cl = parser.parse(options, args);
+
+        assertEquals("bar", cl.getOptionValue("foo"));
+    }
+    
+    public void testAmbiguousLongWithoutEqualSingleDash() throws Exception
+    {
+        String[] args = new String[] { "-b", "-foobar" };
+
+        Options options = new Options();
+        options.addOption(OptionBuilder.withLongOpt("foo").hasOptionalArg().create('f'));
+        options.addOption(OptionBuilder.withLongOpt("bar").hasOptionalArg().create('b'));
+
+        CommandLine cl = parser.parse(options, args);
+
+        assertTrue(cl.hasOption("b"));
+        assertTrue(cl.hasOption("f"));
+        assertEquals("bar", cl.getOptionValue("foo"));
+    }
+
+    public void testLongWithoutEqualDoubleDash() throws Exception
+    {
+        String[] args = new String[] { "--foobar" };
+
+        Options options = new Options();
+        options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
+
+        CommandLine cl = parser.parse(options, args, true);
+
+        assertFalse(cl.hasOption("foo")); // foo isn't expected to be recognized with a double dash
+    }
+
+    public void testLongWithUnexpectedArgument1() throws Exception
+    {
+        String[] args = new String[] { "--foo=bar" };
+
+        Options options = new Options();
+        options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
+
+        try
+        {
+            parser.parse(options, args);
+        }
+        catch (UnrecognizedOptionException e)
+        {
+            assertEquals("--foo=bar", e.getOption());
+            return;
+        }
+
+        fail("UnrecognizedOptionException not thrown");
+    }
+
+    public void testLongWithUnexpectedArgument2() throws Exception
+    {
+        String[] args = new String[] { "-foobar" };
+
+        Options options = new Options();
+        options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
+
+        try
+        {
+            parser.parse(options, args);
+        }
+        catch (UnrecognizedOptionException e)
+        {
+            assertEquals("-foobar", e.getOption());
+            return;
+        }
+
+        fail("UnrecognizedOptionException not thrown");
+    }
+
+    public void testShortWithUnexpectedArgument() throws Exception
+    {
+        String[] args = new String[] { "-f=bar" };
+
+        Options options = new Options();
+        options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
+
+        try
+        {
+            parser.parse(options, args);
+        }
+        catch (UnrecognizedOptionException e)
+        {
+            assertEquals("-f=bar", e.getOption());
+            return;
+        }
+
+        fail("UnrecognizedOptionException not thrown");
+    }
+    
     public void testPropertiesOption1() throws Exception
     {
         String[] args = new String[] { "-Jsource=1.5", "-J", "target", "1.5", "foo" };
@@ -726,4 +853,20 @@
         assertTrue("Confirm  1 extra arg: " + cl.getArgList().size(), cl.getArgList().size() == 1);
         assertTrue("Confirm  value of extra arg: " + cl.getArgList().get(0), cl.getArgList().get(0).equals("foobar"));
     }
+
+    public void testUnlimitedArgs() throws Exception
+    {
+        String[] args = new String[]{"-e", "one", "two", "-f", "alpha"};
+
+        Options options = new Options();
+        options.addOption(OptionBuilder.hasArgs().create("e"));
+        options.addOption(OptionBuilder.hasArgs().create("f"));
+
+        CommandLine cl = parser.parse(options, args);
+
+        assertTrue("Confirm -e is set", cl.hasOption("e"));
+        assertEquals("number of arg for -e", 2, cl.getOptionValues("e").length);
+        assertTrue("Confirm -f is set", cl.hasOption("f"));
+        assertEquals("number of arg for -f", 1, cl.getOptionValues("f").length);
+    }
 }

Modified: commons/proper/cli/trunk/src/test/org/apache/commons/cli/PosixParserTest.java
URL: http://svn.apache.org/viewvc/commons/proper/cli/trunk/src/test/org/apache/commons/cli/PosixParserTest.java?rev=780264&r1=780263&r2=780264&view=diff
==============================================================================
--- commons/proper/cli/trunk/src/test/org/apache/commons/cli/PosixParserTest.java (original)
+++ commons/proper/cli/trunk/src/test/org/apache/commons/cli/PosixParserTest.java Sat May 30 13:43:44 2009
@@ -30,6 +30,31 @@
         parser = new PosixParser();
     }
 
+    public void testDoubleDash2() throws Exception
+    {
+        // not supported by the PosixParser
+    }
+    
+    public void testLongWithoutEqualSingleDash() throws Exception
+    {
+        // not supported by the PosixParser
+    }
+
+    public void testAmbiguousLongWithoutEqualSingleDash() throws Exception
+    {
+        // not supported by the PosixParser
+    }
+    
+    public void testNegativeOption() throws Exception
+    {
+        // not supported by the PosixParser (CLI-184)
+    }
+
+    public void testLongWithUnexpectedArgument1() throws Exception
+    {
+        // not supported by the PosixParser
+    }
+
     public void testLongWithEqualSingleDash() throws Exception
     {
         // not supported by the PosixParser

Modified: commons/proper/cli/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/cli/trunk/xdocs/changes.xml?rev=780264&r1=780263&r2=780264&view=diff
==============================================================================
--- commons/proper/cli/trunk/xdocs/changes.xml (original)
+++ commons/proper/cli/trunk/xdocs/changes.xml Sat May 30 13:43:44 2009
@@ -23,6 +23,11 @@
   <body>
     
     <release version="1.3" date="in SVN">
+      <action type="add" dev="ebourg" issue="CLI-181">
+        A new parser is available: DefaultParser. It combines the features of the GnuParser and the PosixParser.
+        It also provides additional features like partial matching for the long options, and long options without
+        separator (i.e like the JVM memory settings: -Xmx512m). This new parser deprecates the previous ones.
+      </action>
       <action type="fix" dev="ebourg" issue="CLI-183">
         OptionGroups no longer throw an AlreadySelectedException when reused for several parsings.
       </action>