Clover coverage report - PLT Utilities Test Coverage (plt-20120304-r5436)
Coverage timestamp: Sat Mar 3 2012 22:01:56 CST
file stats: LOC: 296   Methods: 19
NCLOC: 144   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
ArgumentParser.java 73.9% 82.8% 89.5% 80.9%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK*
 2   
 3    PLT Utilities BSD License
 4   
 5    Copyright (c) 2007-2010 JavaPLT group at Rice University
 6    All rights reserved.
 7   
 8    Developed by: Java Programming Languages Team
 9    Rice University
 10    http://www.cs.rice.edu/~javaplt/
 11   
 12    Redistribution and use in source and binary forms, with or without modification, are permitted
 13    provided that the following conditions are met:
 14   
 15    - Redistributions of source code must retain the above copyright notice, this list of conditions
 16    and the following disclaimer.
 17    - Redistributions in binary form must reproduce the above copyright notice, this list of
 18    conditions and the following disclaimer in the documentation and/or other materials provided
 19    with the distribution.
 20    - Neither the name of the JavaPLT group, Rice University, nor the names of the library's
 21    contributors may be used to endorse or promote products derived from this software without
 22    specific prior written permission.
 23   
 24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 25    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 26    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND
 27    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 29    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 30    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 31    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32   
 33    *END_COPYRIGHT_BLOCK*/
 34   
 35    package edu.rice.cs.plt.text;
 36   
 37    import java.util.Map;
 38    import java.util.HashMap;
 39    import java.util.List;
 40    import java.util.LinkedList;
 41    import edu.rice.cs.plt.iter.IterUtil;
 42    import edu.rice.cs.plt.tuple.Pair;
 43    import edu.rice.cs.plt.tuple.Triple;
 44    import edu.rice.cs.plt.tuple.Quad;
 45   
 46    /**
 47    * A simple utility class for processing command-line argument arrays. Argument strings
 48    * are assumed to be made up of <em>options</em>, <em>option arguments</em>, and
 49    * <em>parameters</em>. All argument string beginning with the character {@code '-'} are
 50    * interpreted as options. Clients should specify the names and arities (and, optionally,
 51    * default values) of their supported options. Aliases may also be specified, allowing
 52    * multiple names to be interpreted as the same option. {@link #parse} may then be invoked on
 53    * an argument array, producing errors for unrecognized options or options with the wrong
 54    * number of arguments, and interpreting all remaining strings as parameters.
 55    */
 56    public class ArgumentParser {
 57   
 58    private final Map<String, Integer> _supportedOptions;
 59    private final HashMap<String, Iterable<String>> _defaults; // HashMap implements Cloneable
 60    private final Map<String, String> _aliases;
 61    private boolean _strictOrder;
 62    private int _requiredParams;
 63   
 64    /** Create an argument parser with an initially-empty set of supported options. */
 65  4 public ArgumentParser() {
 66  4 _supportedOptions = new HashMap<String, Integer>();
 67  4 _defaults = new HashMap<String, Iterable<String>>();
 68  4 _aliases = new HashMap<String, String>();
 69  4 _strictOrder = false;
 70  4 _requiredParams = 0;
 71    }
 72   
 73    /** Require all options to precede any parameters (not required by default). */
 74  1 public void requireStrictOrder() { _strictOrder = true; }
 75   
 76    /** Require at least the given number of parameters (0 by default). */
 77  2 public void requireParams(int count) { _requiredParams = count; }
 78   
 79    /** Determine whether the given option is supported. */
 80  8 public boolean supportsOption(String name) {
 81  8 return _supportedOptions.containsKey(name) || _aliases.containsKey(name);
 82    }
 83   
 84    /**
 85    * Add the given option name to the list of supported options.
 86    * @param name The option name, excluding the "-" prefix.
 87    * @param arity The number of arguments to follow the option.
 88    * @throws IllegalArgumentException If the option is already supported.
 89    */
 90  7 public void supportOption(String name, int arity) {
 91  0 if (supportsOption(name)) { throw new IllegalArgumentException(name + " is already supported"); }
 92  7 _supportedOptions.put(name, arity);
 93    }
 94   
 95    /**
 96    * Add the given option name to the list of supported options and record the given default argument set.
 97    * @param name The option name, excluding the "-" prefix.
 98    * @param defaultArguments The default arguments associated with the option. Implicitly defines the arity
 99    * of the option as well. If the length is 0, no default will be set (otherwise,
 100    * the nullary option would always appear to be present).
 101    * @throws IllegalArgumentException If the option is already supported.
 102    */
 103  3 public void supportOption(String name, String... defaultArguments) {
 104  3 supportOption(name, defaultArguments.length);
 105  3 if (defaultArguments.length > 0) {
 106  2 _defaults.put(name, IterUtil.asIterable(defaultArguments));
 107    }
 108    }
 109   
 110    /**
 111    * Add the given option name to the list of supported options. An arbitrary number
 112    * of arguments, terminated by another option or the end of the argument list, may follow the option.
 113    * @param name The option name, excluding the "-" prefix.
 114    * @throws IllegalArgumentException If the option is already supported.
 115    */
 116  1 public void supportVarargOption(String name) {
 117  0 if (supportsOption(name)) { throw new IllegalArgumentException(name + " is already supported"); }
 118  1 _supportedOptions.put(name, -1);
 119    }
 120   
 121    /**
 122    * Add the given option name to the list of supported options and record the given default argument set.
 123    * An arbitrary number of arguments, terminated by another option or the end of the argument list, may
 124    * follow the option. (Use a 0-length array to set a 0-length default. Otherwise,
 125    * {@link #supportVarargOption(String)} will be invoked instead.)
 126    *
 127    * @param name The option name, excluding the "-" prefix.
 128    * @param defaultArguments The default arguments associated with the option.
 129    * @throws IllegalArgumentException If the option is already supported.
 130    */
 131  0 public void supportVarargOption(String name, String... defaultArguments) {
 132  0 supportVarargOption(name);
 133  0 _defaults.put(name, IterUtil.asIterable(defaultArguments));
 134    }
 135   
 136    /**
 137    * Create an alias: {@code aliasName} is an alias for {@code optionName}. Parsed matches will appear
 138    * under the referenced option name in the result.
 139    * @throws IllegalArgumentException If the alias name is already supported, or if the option name is
 140    * <em>not</em> supported.
 141    */
 142  0 public void supportAlias(String aliasName, String optionName) {
 143  0 if (supportsOption(aliasName)) { throw new IllegalArgumentException(aliasName + " is already supported"); }
 144  0 if (!supportsOption(optionName)) { throw new IllegalArgumentException(optionName + " is not supported"); }
 145  0 if (_aliases.containsKey(optionName)) { optionName = _aliases.get(optionName); }
 146  0 _aliases.put(aliasName, optionName);
 147    }
 148   
 149   
 150    /** Parse an array of arguments based on the previously-defined supported options. */
 151  34 public Result parse(String... args) throws IllegalArgumentException {
 152  34 @SuppressWarnings("unchecked")
 153    Map<String, Iterable<String>> options = (Map<String, Iterable<String>>) _defaults.clone();
 154   
 155  34 List<String> params = new LinkedList<String>();
 156   
 157  34 String currentOption = null;
 158  34 List<String> currentOptionArgs = null;
 159  34 int optionArgsCount = -1;
 160  34 boolean allowMoreOptions = true;
 161   
 162  34 for (String s : args) {
 163  115 if (currentOption != null && optionArgsCount == 0) {
 164    // convert to a SizedIterable to work around a Retroweaver bug (tests will fail)
 165  24 options.put(currentOption, IterUtil.asSizedIterable(currentOptionArgs));
 166  24 currentOption = null;
 167    }
 168   
 169  115 if (s.startsWith("-")) {
 170  46 String opt = s.substring(1);
 171  0 if (_aliases.containsKey(opt)) { opt = _aliases.get(opt); }
 172  46 if (optionArgsCount > 0) {
 173  1 throw new IllegalArgumentException("Expected " + optionArgsCount + " more argument(s) for option " +
 174    currentOption);
 175    }
 176  45 else if (!allowMoreOptions) {
 177  1 throw new IllegalArgumentException("Unexpected option: " + opt);
 178    }
 179  44 else if (!_supportedOptions.containsKey(opt)) {
 180  3 throw new IllegalArgumentException("Unrecognized option: " + opt);
 181    }
 182    else {
 183  41 if (currentOption != null) {
 184    // convert to a SizedIterable to work around a Retroweaver bug (tests will fail)
 185  1 options.put(currentOption, IterUtil.asSizedIterable(currentOptionArgs));
 186    }
 187  41 currentOption = opt;
 188  41 currentOptionArgs = new LinkedList<String>();
 189  41 optionArgsCount = _supportedOptions.get(opt);
 190    }
 191    }
 192   
 193    else {
 194  69 if (currentOption == null) {
 195  2 if (_strictOrder) { allowMoreOptions = false; }
 196  31 params.add(s);
 197    }
 198    else {
 199  38 currentOptionArgs.add(s);
 200  33 if (optionArgsCount > 0) { optionArgsCount--; }
 201    }
 202    }
 203    }
 204  29 if (optionArgsCount > 0) {
 205  3 throw new IllegalArgumentException("Expected " + optionArgsCount + " more argument(s) for option " +
 206    currentOption);
 207    }
 208  26 if (currentOption != null) {
 209    // convert to a SizedIterable to work around a Retroweaver bug (tests will fail)
 210  12 options.put(currentOption, IterUtil.asSizedIterable(currentOptionArgs));
 211    }
 212   
 213  26 if (params.size() < _requiredParams) {
 214  4 throw new IllegalArgumentException("Expected at least " + _requiredParams + " parameter(s)");
 215    }
 216  22 return new Result(options, params);
 217    }
 218   
 219   
 220    /** A collection of the options and parameters parsed from an array of arguments. */
 221    public static class Result {
 222    private final Map<String, Iterable<String>> _options;
 223    private final Iterable<String> _params;
 224   
 225  22 public Result(Map<String, Iterable<String>> options, Iterable<String> params) {
 226  22 _options = options;
 227  22 _params = params;
 228    }
 229   
 230    /** Get the parameters (non-option strings associated with no option) that were parsed. */
 231  22 public Iterable<String> params() { return _params; }
 232   
 233    /** Test whether the given option was defined (or was given a default value). */
 234  13 public boolean hasOption(String opt) { return _options.containsKey(opt); }
 235   
 236    /**
 237    * Get the arguments associated with the given option, or {@code null} if it is undefined.
 238    * @param opt The option name, excluding the "-" prefix.
 239    */
 240  13 public Iterable<String> getOption(String opt) { return _options.get(opt); }
 241   
 242    /**
 243    * Test whether the given option was defined with 0 arguments.
 244    * @param opt The option name, excluding the "-" prefix.
 245    */
 246  12 public boolean hasNullaryOption(String opt) {
 247  12 return _options.containsKey(opt) && IterUtil.isEmpty(_options.get(opt));
 248    }
 249   
 250    /**
 251    * Get the single argument associated with the given option, or {@code null} if it is undefined
 252    * or has a different number of arguments.
 253    * @param opt The option name, excluding the "-" prefix.
 254    */
 255  11 public String getUnaryOption(String opt) {
 256  11 Iterable<String> result = _options.get(opt);
 257  0 if (result == null || IterUtil.sizeOf(result) != 1) { return null; }
 258  11 else { return IterUtil.first(result); }
 259    }
 260   
 261    /**
 262    * Get the 2 arguments associated with the given option, or {@code null} if it is undefined
 263    * or has a different number of arguments.
 264    * @param opt The option name, excluding the "-" prefix.
 265    */
 266  12 public Pair<String, String> getBinaryOption(String opt) {
 267  12 Iterable<String> result = _options.get(opt);
 268  1 if (result == null || IterUtil.sizeOf(result) != 2) { return null; }
 269  11 else { return IterUtil.makePair(result); }
 270    }
 271   
 272    /**
 273    * Get the 3 arguments associated with the given option, or {@code null} if it is undefined
 274    * or has a different number of arguments.
 275    * @param opt The option name, excluding the "-" prefix.
 276    */
 277  1 public Triple<String, String, String> getTernaryOption(String opt) {
 278  1 Iterable<String> result = _options.get(opt);
 279  1 if (result == null || IterUtil.sizeOf(result) != 3) { return null; }
 280  0 else { return IterUtil.makeTriple(result); }
 281    }
 282   
 283    /**
 284    * Get the 4 arguments associated with the given option, or {@code null} if it is undefined
 285    * or has a different number of arguments.
 286    * @param opt The option name, excluding the "-" prefix.
 287    */
 288  1 public Quad<String, String, String, String> getQuaternaryOption(String opt) {
 289  1 Iterable<String> result = _options.get(opt);
 290  1 if (result == null || IterUtil.sizeOf(result) != 4) { return null; }
 291  0 else { return IterUtil.makeQuad(result); }
 292    }
 293   
 294    }
 295   
 296    }