Coverage Report - joptsimple.OptionParser
 
Classes in this File Line Coverage Branch Coverage Complexity
OptionParser
99%
157/158
98%
69/70
1.953
 
 1  
 /*
 2  
  The MIT License
 3  
 
 4  
  Copyright (c) 2004-2015 Paul R. Holser, Jr.
 5  
 
 6  
  Permission is hereby granted, free of charge, to any person obtaining
 7  
  a copy of this software and associated documentation files (the
 8  
  "Software"), to deal in the Software without restriction, including
 9  
  without limitation the rights to use, copy, modify, merge, publish,
 10  
  distribute, sublicense, and/or sell copies of the Software, and to
 11  
  permit persons to whom the Software is furnished to do so, subject to
 12  
  the following conditions:
 13  
 
 14  
  The above copyright notice and this permission notice shall be
 15  
  included in all copies or substantial portions of the Software.
 16  
 
 17  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 18  
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 19  
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 20  
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 21  
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 22  
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 23  
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 24  
 */
 25  
 
 26  
 package joptsimple;
 27  
 
 28  
 import java.io.IOException;
 29  
 import java.io.OutputStream;
 30  
 import java.io.OutputStreamWriter;
 31  
 import java.io.Writer;
 32  
 import java.util.ArrayList;
 33  
 import java.util.Collection;
 34  
 import java.util.HashMap;
 35  
 import java.util.HashSet;
 36  
 import java.util.LinkedHashMap;
 37  
 import java.util.List;
 38  
 import java.util.Map;
 39  
 import java.util.Set;
 40  
 
 41  
 import joptsimple.internal.AbbreviationMap;
 42  
 import joptsimple.util.KeyValuePair;
 43  
 
 44  
 import static java.util.Collections.*;
 45  
 import static joptsimple.OptionException.*;
 46  
 import static joptsimple.OptionParserState.*;
 47  
 import static joptsimple.ParserRules.*;
 48  
 
 49  
 /**
 50  
  * <p>Parses command line arguments, using a syntax that attempts to take from the best of POSIX {@code getopt()}
 51  
  * and GNU {@code getopt_long()}.</p>
 52  
  *
 53  
  * <p>This parser supports short options and long options.</p>
 54  
  *
 55  
  * <ul>
 56  
  *   <li><dfn>Short options</dfn> begin with a single hyphen ("<kbd>-</kbd>") followed by a single letter or digit,
 57  
  *   or question mark ("<kbd>?</kbd>"), or dot ("<kbd>.</kbd>"), or underscore ("<kbd>_</kbd>").</li>
 58  
  *
 59  
  *   <li>Short options can accept single arguments. The argument can be made required or optional. The option's
 60  
  *   argument can occur:
 61  
  *     <ul>
 62  
  *       <li>in the slot after the option, as in <kbd>-d /tmp</kbd></li>
 63  
  *       <li>right up against the option, as in <kbd>-d/tmp</kbd></li>
 64  
  *       <li>right up against the option separated by an equals sign (<kbd>"="</kbd>), as in <kbd>-d=/tmp</kbd></li>
 65  
  *     </ul>
 66  
  *   To specify <em>n</em> arguments for an option, specify the option <em>n</em> times, once for each argument,
 67  
  *   as in <kbd>-d /tmp -d /var -d /opt</kbd>; or, when using the
 68  
  *   {@linkplain ArgumentAcceptingOptionSpec#withValuesSeparatedBy(char) "separated values"} clause of the "fluent
 69  
  *   interface" (see below), give multiple values separated by a given character as a single argument to the
 70  
  *   option.</li>
 71  
  *
 72  
  *   <li>Short options can be clustered, so that <kbd>-abc</kbd> is treated as <kbd>-a -b -c</kbd>. If a short option
 73  
  *   in the cluster can accept an argument, the remaining characters are interpreted as the argument for that
 74  
  *   option.</li>
 75  
  *
 76  
  *   <li>An argument consisting only of two hyphens (<kbd>"--"</kbd>) signals that the remaining arguments are to be
 77  
  *   treated as non-options.</li>
 78  
  *
 79  
  *   <li>An argument consisting only of a single hyphen is considered a non-option argument (though it can be an
 80  
  *   argument of an option). Many Unix programs treat single hyphens as stand-ins for the standard input or standard
 81  
  *   output streams.</li>
 82  
  *
 83  
  *   <li><dfn>Long options</dfn> begin with two hyphens (<kbd>"--"</kbd>), followed by multiple letters, digits,
 84  
  *   hyphens, question marks, or dots. A hyphen cannot be the first character of a long option specification when
 85  
  *   configuring the parser.</li>
 86  
  *
 87  
  *   <li>You can abbreviate long options, so long as the abbreviation is unique.</li>
 88  
  *
 89  
  *   <li>Long options can accept single arguments.  The argument can be made required or optional.  The option's
 90  
  *   argument can occur:
 91  
  *     <ul>
 92  
  *       <li>in the slot after the option, as in <kbd>--directory /tmp</kbd></li>
 93  
  *       <li>right up against the option separated by an equals sign (<kbd>"="</kbd>), as in
 94  
  *       <kbd>--directory=/tmp</kbd>
 95  
  *     </ul>
 96  
  *   Specify multiple arguments for a long option in the same manner as for short options (see above).</li>
 97  
  *
 98  
  *   <li>You can use a single hyphen (<kbd>"-"</kbd>) instead of a double hyphen (<kbd>"--"</kbd>) for a long
 99  
  *   option.</li>
 100  
  *
 101  
  *   <li>The option <kbd>-W</kbd> is reserved.  If you tell the parser to {@linkplain
 102  
  *   #recognizeAlternativeLongOptions(boolean) recognize alternative long options}, then it will treat, for example,
 103  
  *   <kbd>-W foo=bar</kbd> as the long option <kbd>foo</kbd> with argument <kbd>bar</kbd>, as though you had written
 104  
  *   <kbd>--foo=bar</kbd>.</li>
 105  
  *
 106  
  *   <li>You can specify <kbd>-W</kbd> as a valid short option, or use it as an abbreviation for a long option, but
 107  
  *   {@linkplain #recognizeAlternativeLongOptions(boolean) recognizing alternative long options} will always supersede
 108  
  *   this behavior.</li>
 109  
  *
 110  
  *   <li>You can specify a given short or long option multiple times on a single command line. The parser collects
 111  
  *   any arguments specified for those options as a list.</li>
 112  
  *
 113  
  *   <li>If the parser detects an option whose argument is optional, and the next argument "looks like" an option,
 114  
  *   that argument is not treated as the argument to the option, but as a potentially valid option. If, on the other
 115  
  *   hand, the optional argument is typed as a derivative of {@link Number}, then that argument is treated as the
 116  
  *   negative number argument of the option, even if the parser recognizes the corresponding numeric option.
 117  
  *   For example:
 118  
  *   <pre><code>
 119  
  *     OptionParser parser = new OptionParser();
 120  
  *     parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
 121  
  *     parser.accepts( "2" );
 122  
  *     OptionSet options = parser.parse( "-a", "-2" );
 123  
  *   </code></pre>
 124  
  *   In this case, the option set contains <kbd>"a"</kbd> with argument <kbd>-2</kbd>, not both <kbd>"a"</kbd> and
 125  
  *   <kbd>"2"</kbd>. Swapping the elements in the <em>args</em> array gives the latter.</li>
 126  
  * </ul>
 127  
  *
 128  
  * <p>There are two ways to tell the parser what options to recognize:</p>
 129  
  *
 130  
  * <ol>
 131  
  *   <li>A "fluent interface"-style API for specifying options, available since version 2. Sentences in this fluent
 132  
  *   interface language begin with a call to {@link #accepts(String) accepts} or {@link #acceptsAll(List)
 133  
  *   acceptsAll} methods; calls on the ensuing chain of objects describe whether the options can take an argument,
 134  
  *   whether the argument is required or optional, to what type arguments of the options should be converted if any,
 135  
  *   etc. Since version 3, these calls return an instance of {@link OptionSpec}, which can subsequently be used to
 136  
  *   retrieve the arguments of the associated option in a type-safe manner.</li>
 137  
  *
 138  
  *   <li>Since version 1, a more concise way of specifying short options has been to use the special {@linkplain
 139  
  *   #OptionParser(String) constructor}. Arguments of options specified in this manner will be of type {@link String}.
 140  
  *   Here are the rules for the format of the specification strings this constructor accepts:
 141  
  *
 142  
  *     <ul>
 143  
  *       <li>Any letter or digit is treated as an option character.</li>
 144  
  *
 145  
  *       <li>An option character can be immediately followed by an asterisk (*) to indicate that the option is a
 146  
  *       "help" option.</li>
 147  
  *
 148  
  *       <li>If an option character (with possible trailing asterisk) is followed by a single colon (<kbd>":"</kbd>),
 149  
  *       then the option requires an argument.</li>
 150  
  *
 151  
  *       <li>If an option character (with possible trailing asterisk) is followed by two colons (<kbd>"::"</kbd>),
 152  
  *       then the option accepts an optional argument.</li>
 153  
  *
 154  
  *       <li>Otherwise, the option character accepts no argument.</li>
 155  
  *
 156  
  *       <li>If the option specification string begins with a plus sign (<kbd>"+"</kbd>), the parser will behave
 157  
  *       "POSIX-ly correct".</li>
 158  
  *
 159  
  *       <li>If the option specification string contains the sequence <kbd>"W;"</kbd> (capital W followed by a
 160  
  *       semicolon), the parser will recognize the alternative form of long options.</li>
 161  
  *     </ul>
 162  
  *   </li>
 163  
  * </ol>
 164  
  *
 165  
  * <p>Each of the options in a list of options given to {@link #acceptsAll(List) acceptsAll} is treated as a
 166  
  * synonym of the others.  For example:
 167  
  *   <pre>
 168  
  *     <code>
 169  
  *     OptionParser parser = new OptionParser();
 170  
  *     parser.acceptsAll( asList( "w", "interactive", "confirmation" ) );
 171  
  *     OptionSet options = parser.parse( "-w" );
 172  
  *     </code>
 173  
  *   </pre>
 174  
  * In this case, <code>options.{@link OptionSet#has(String) has}</code> would answer {@code true} when given arguments
 175  
  * <kbd>"w"</kbd>, <kbd>"interactive"</kbd>, and <kbd>"confirmation"</kbd>. The {@link OptionSet} would give the same
 176  
  * responses to these arguments for its other methods as well.</p>
 177  
  *
 178  
  * <p>By default, as with GNU {@code getopt()}, the parser allows intermixing of options and non-options. If, however,
 179  
  * the parser has been created to be "POSIX-ly correct", then the first argument that does not look lexically like an
 180  
  * option, and is not a required argument of a preceding option, signals the end of options. You can still bind
 181  
  * optional arguments to their options using the abutting (for short options) or <kbd>=</kbd> syntax.</p>
 182  
  *
 183  
  * <p>Unlike GNU {@code getopt()}, this parser does not honor the environment variable {@code POSIXLY_CORRECT}.
 184  
  * "POSIX-ly correct" parsers are configured by either:</p>
 185  
  *
 186  
  * <ol>
 187  
  *   <li>using the method {@link #posixlyCorrect(boolean)}, or</li>
 188  
  *
 189  
  *   <li>using the {@linkplain #OptionParser(String) constructor} with an argument whose first character is a plus sign
 190  
  *   (<kbd>"+"</kbd>)</li>
 191  
  * </ol>
 192  
  *
 193  
  * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
 194  
  * @see <a href="http://www.gnu.org/software/libc/manual">The GNU C Library</a>
 195  
  */
 196  
 public class OptionParser implements OptionDeclarer {
 197  
     private final AbbreviationMap<AbstractOptionSpec<?>> recognizedOptions;
 198  
     private final ArrayList<AbstractOptionSpec<?>> trainingOrder;
 199  
     private final Map<List<String>, Set<OptionSpec<?>>> requiredIf;
 200  
     private final Map<List<String>, Set<OptionSpec<?>>> requiredUnless;
 201  
     private OptionParserState state;
 202  
     private boolean posixlyCorrect;
 203  
     private boolean allowsUnrecognizedOptions;
 204  381
     private HelpFormatter helpFormatter = new BuiltinHelpFormatter();
 205  
 
 206  
     /**
 207  
      * Creates an option parser that initially recognizes no options, and does not exhibit "POSIX-ly correct"
 208  
      * behavior.
 209  
      */
 210  381
     public OptionParser() {
 211  381
         recognizedOptions = new AbbreviationMap<AbstractOptionSpec<?>>();
 212  381
         trainingOrder = new ArrayList<AbstractOptionSpec<?>>();
 213  381
         requiredIf = new HashMap<List<String>, Set<OptionSpec<?>>>();
 214  381
         requiredUnless = new HashMap<List<String>, Set<OptionSpec<?>>>();
 215  381
         state = moreOptions( false );
 216  
 
 217  381
         recognize( new NonOptionArgumentSpec<String>() );
 218  381
     }
 219  
 
 220  
     /**
 221  
      * Creates an option parser and configures it to recognize the short options specified in the given string.
 222  
      *
 223  
      * Arguments of options specified this way will be of type {@link String}.
 224  
      *
 225  
      * @param optionSpecification an option specification
 226  
      * @throws NullPointerException if {@code optionSpecification} is {@code null}
 227  
      * @throws OptionException if the option specification contains illegal characters or otherwise cannot be
 228  
      * recognized
 229  
      */
 230  
     public OptionParser( String optionSpecification ) {
 231  25
         this();
 232  
 
 233  25
         new OptionSpecTokenizer( optionSpecification ).configure( this );
 234  23
     }
 235  
 
 236  
     public OptionSpecBuilder accepts( String option ) {
 237  541
         return acceptsAll( singletonList( option ) );
 238  
     }
 239  
 
 240  
     public OptionSpecBuilder accepts( String option, String description ) {
 241  31
         return acceptsAll( singletonList( option ), description );
 242  
     }
 243  
 
 244  
     public OptionSpecBuilder acceptsAll( List<String> options ) {
 245  603
         return acceptsAll( options, "" );
 246  
     }
 247  
 
 248  
     public OptionSpecBuilder acceptsAll( List<String> options, String description ) {
 249  698
         if ( options.isEmpty() )
 250  1
             throw new IllegalArgumentException( "need at least one option" );
 251  
 
 252  695
         ensureLegalOptions( options );
 253  
 
 254  687
         return new OptionSpecBuilder( this, options, description );
 255  
     }
 256  
 
 257  
     public NonOptionArgumentSpec<String> nonOptions() {
 258  12
         NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<String>();
 259  
 
 260  12
         recognize( spec );
 261  
 
 262  12
         return spec;
 263  
     }
 264  
 
 265  
     public NonOptionArgumentSpec<String> nonOptions( String description ) {
 266  5
         NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<String>( description );
 267  
 
 268  5
         recognize( spec );
 269  
 
 270  5
         return spec;
 271  
     }
 272  
 
 273  
     public void posixlyCorrect( boolean setting ) {
 274  10
         posixlyCorrect = setting;
 275  10
         state = moreOptions( setting );
 276  10
     }
 277  
 
 278  
     boolean posixlyCorrect() {
 279  40
         return posixlyCorrect;
 280  
     }
 281  
 
 282  
     public void allowsUnrecognizedOptions() {
 283  2
         allowsUnrecognizedOptions = true;
 284  2
     }
 285  
 
 286  
     boolean doesAllowsUnrecognizedOptions() {
 287  16
         return allowsUnrecognizedOptions;
 288  
     }
 289  
 
 290  
     public void recognizeAlternativeLongOptions( boolean recognize ) {
 291  15
         if ( recognize )
 292  14
             recognize( new AlternativeLongOptionSpec() );
 293  
         else
 294  1
             recognizedOptions.remove( String.valueOf( RESERVED_FOR_EXTENSIONS ) );
 295  15
     }
 296  
 
 297  
     void recognize( AbstractOptionSpec<?> spec ) {
 298  1516
         recognizedOptions.putAll(spec.options(), spec);
 299  1516
         trainingOrder.add( spec );
 300  1516
     }
 301  
 
 302  
     /**
 303  
      * Writes information about the options this parser recognizes to the given output sink.
 304  
      *
 305  
      * The output sink is flushed, but not closed.
 306  
      *
 307  
      * @param sink the sink to write information to
 308  
      * @throws IOException if there is a problem writing to the sink
 309  
      * @throws NullPointerException if {@code sink} is {@code null}
 310  
      * @see #printHelpOn(Writer)
 311  
      */
 312  
     public void printHelpOn( OutputStream sink ) throws IOException {
 313  2
         printHelpOn( new OutputStreamWriter( sink ) );
 314  2
     }
 315  
 
 316  
     /**
 317  
      * Writes information about the options this parser recognizes to the given output sink.
 318  
      *
 319  
      * The output sink is flushed, but not closed.
 320  
      *
 321  
      * @param sink the sink to write information to
 322  
      * @throws IOException if there is a problem writing to the sink
 323  
      * @throws NullPointerException if {@code sink} is {@code null}
 324  
      * @see #printHelpOn(OutputStream)
 325  
      */
 326  
     public void printHelpOn( Writer sink ) throws IOException {
 327  66
         sink.write( helpFormatter.format( _recognizedOptions() ) );
 328  66
         sink.flush();
 329  66
     }
 330  
 
 331  
     /**
 332  
      * Tells the parser to use the given formatter when asked to {@linkplain #printHelpOn(java.io.Writer) print help}.
 333  
      *
 334  
      * @param formatter the formatter to use for printing help
 335  
      * @throws NullPointerException if the formatter is {@code null}
 336  
      */
 337  
     public void formatHelpWith( HelpFormatter formatter ) {
 338  31
         if ( formatter == null )
 339  1
             throw new NullPointerException();
 340  
 
 341  30
         helpFormatter = formatter;
 342  30
     }
 343  
 
 344  
     /**
 345  
      * Retrieves all options-spec pairings which have been configured for the parser in the same order as declared
 346  
      * during training. Option flags for specs are alphabetized by {@link OptionSpec#options()}; only the order of the
 347  
      * specs is preserved.
 348  
      *
 349  
      * (Note: prior to 4.7 the order was alphabetical across all options regardless of spec.)
 350  
      *
 351  
      * @return a map containing all the configured options and their corresponding {@link OptionSpec}
 352  
      * @since 4.6
 353  
      */
 354  
     public Map<String, OptionSpec<?>> recognizedOptions() {
 355  2
         return new LinkedHashMap<String, OptionSpec<?>>( _recognizedOptions() );
 356  
     }
 357  
 
 358  
     private Map<String, AbstractOptionSpec<?>> _recognizedOptions() {
 359  68
         Map<String, AbstractOptionSpec<?>> options = new LinkedHashMap<String, AbstractOptionSpec<?>>();
 360  68
         for ( AbstractOptionSpec<?> spec : trainingOrder ) {
 361  242
             for ( String option : spec.options() )
 362  326
                 options.put( option, spec );
 363  242
         }
 364  68
         return options;
 365  
     }
 366  
 
 367  
    /**
 368  
      * Parses the given command line arguments according to the option specifications given to the parser.
 369  
      *
 370  
      * @param arguments arguments to parse
 371  
      * @return an {@link OptionSet} describing the parsed options, their arguments, and any non-option arguments found
 372  
      * @throws OptionException if problems are detected while parsing
 373  
      * @throws NullPointerException if the argument list is {@code null}
 374  
      */
 375  
     public OptionSet parse( String... arguments ) {
 376  268
         ArgumentList argumentList = new ArgumentList( arguments );
 377  267
         OptionSet detected = new OptionSet( recognizedOptions.toJavaUtilMap() );
 378  267
         detected.add( recognizedOptions.get( NonOptionArgumentSpec.NAME ) );
 379  
 
 380  779
         while ( argumentList.hasMore() )
 381  531
             state.handleArgument( this, argumentList, detected );
 382  
 
 383  248
         reset();
 384  
 
 385  248
         ensureRequiredOptions( detected );
 386  
 
 387  236
         return detected;
 388  
     }
 389  
 
 390  
     private void ensureRequiredOptions( OptionSet options ) {
 391  248
         List<AbstractOptionSpec<?>> missingRequiredOptions = missingRequiredOptions(options);
 392  248
         boolean helpOptionPresent = isHelpOptionPresent( options );
 393  
 
 394  248
         if ( !missingRequiredOptions.isEmpty() && !helpOptionPresent )
 395  12
             throw new MissingRequiredOptionsException( missingRequiredOptions );
 396  236
     }
 397  
 
 398  
     private List<AbstractOptionSpec<?>> missingRequiredOptions(OptionSet options) {
 399  248
         List<AbstractOptionSpec<?>> missingRequiredOptions = new ArrayList<AbstractOptionSpec<?>>();
 400  
 
 401  248
         for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {
 402  898
             if ( each.isRequired() && !options.has( each ) )
 403  10
                 missingRequiredOptions.add(each);
 404  898
         }
 405  
 
 406  248
         for ( Map.Entry<List<String>, Set<OptionSpec<?>>> eachEntry : requiredIf.entrySet() ) {
 407  17
             AbstractOptionSpec<?> required = specFor( eachEntry.getKey().iterator().next() );
 408  
 
 409  17
             if ( optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) {
 410  10
                 missingRequiredOptions.add(required);
 411  
             }
 412  17
         }
 413  
 
 414  248
         for ( Map.Entry<List<String>, Set<OptionSpec<?>>> eachEntry : requiredUnless.entrySet() ) {
 415  16
             AbstractOptionSpec<?> required = specFor( eachEntry.getKey().iterator().next() );
 416  
 
 417  16
             if ( !optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) {
 418  5
                 missingRequiredOptions.add(required);
 419  
             }
 420  16
         }
 421  
 
 422  248
         return missingRequiredOptions;
 423  
     }
 424  
 
 425  
     private boolean optionsHasAnyOf( OptionSet options, Collection<OptionSpec<?>> specs ) {
 426  33
         for ( OptionSpec<?> each : specs ) {
 427  42
             if ( options.has( each ) )
 428  21
                 return true;
 429  21
         }
 430  
 
 431  12
         return false;
 432  
     }
 433  
 
 434  
     private boolean isHelpOptionPresent( OptionSet options ) {
 435  248
         boolean helpOptionPresent = false;
 436  248
         for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {
 437  879
             if ( each.isForHelp() && options.has( each ) ) {
 438  5
                 helpOptionPresent = true;
 439  5
                 break;
 440  
             }
 441  874
         }
 442  248
         return helpOptionPresent;
 443  
     }
 444  
 
 445  
     void handleLongOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {
 446  99
         KeyValuePair optionAndArgument = parseLongOptionWithArgument( candidate );
 447  
 
 448  99
         if ( !isRecognized( optionAndArgument.key ) )
 449  8
             throw unrecognizedOption( optionAndArgument.key );
 450  
 
 451  91
         AbstractOptionSpec<?> optionSpec = specFor( optionAndArgument.key );
 452  91
         optionSpec.handleOption( this, arguments, detected, optionAndArgument.value );
 453  89
     }
 454  
 
 455  
     void handleShortOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {
 456  291
         KeyValuePair optionAndArgument = parseShortOptionWithArgument( candidate );
 457  
 
 458  291
         if ( isRecognized( optionAndArgument.key ) ) {
 459  232
             specFor( optionAndArgument.key ).handleOption( this, arguments, detected, optionAndArgument.value );
 460  
         }
 461  
         else
 462  59
             handleShortOptionCluster( candidate, arguments, detected );
 463  280
     }
 464  
 
 465  
     private void handleShortOptionCluster( String candidate, ArgumentList arguments, OptionSet detected ) {
 466  59
         char[] options = extractShortOptionsFrom( candidate );
 467  59
         validateOptionCharacters( options );
 468  
 
 469  79
         for ( int i = 0; i < options.length; i++ ) {
 470  75
             AbstractOptionSpec<?> optionSpec = specFor( options[ i ] );
 471  
 
 472  75
             if ( optionSpec.acceptsArguments() && options.length > i + 1 ) {
 473  47
                 String detectedArgument = String.valueOf( options, i + 1, options.length - 1 - i );
 474  47
                 optionSpec.handleOption( this, arguments, detected, detectedArgument );
 475  47
                 break;
 476  
             }
 477  
 
 478  28
             optionSpec.handleOption( this, arguments, detected, null );
 479  
         }
 480  51
     }
 481  
 
 482  
     void handleNonOptionArgument( String candidate, ArgumentList arguments, OptionSet detectedOptions ) {
 483  136
         specFor( NonOptionArgumentSpec.NAME ).handleOption( this, arguments, detectedOptions, candidate );
 484  136
     }
 485  
 
 486  
     void noMoreOptions() {
 487  16
         state = OptionParserState.noMoreOptions();
 488  16
     }
 489  
 
 490  
     boolean looksLikeAnOption( String argument ) {
 491  57
         return isShortOptionToken( argument ) || isLongOptionToken( argument );
 492  
     }
 493  
 
 494  
     boolean isRecognized( String option ) {
 495  511
         return recognizedOptions.contains( option );
 496  
     }
 497  
 
 498  
     void requiredIf( List<String> precedentSynonyms, String required ) {
 499  18
         requiredIf( precedentSynonyms, specFor( required ) );
 500  18
     }
 501  
 
 502  
     void requiredIf( List<String> precedentSynonyms, OptionSpec<?> required ) {
 503  36
         putRequiredOption( precedentSynonyms, required, requiredIf );
 504  36
     }
 505  
 
 506  
     void requiredUnless( List<String> precedentSynonyms, String required ) {
 507  16
         requiredUnless( precedentSynonyms, specFor( required ) );
 508  16
     }
 509  
 
 510  
     void requiredUnless( List<String> precedentSynonyms, OptionSpec<?> required ) {
 511  32
         putRequiredOption( precedentSynonyms, required, requiredUnless );
 512  32
     }
 513  
 
 514  
     private void putRequiredOption( List<String> precedentSynonyms, OptionSpec<?> required,
 515  
         Map<List<String>, Set<OptionSpec<?>>> target ) {
 516  
 
 517  68
         for ( String each : precedentSynonyms ) {
 518  100
             AbstractOptionSpec<?> spec = specFor( each );
 519  100
             if ( spec == null )
 520  0
                 throw new UnconfiguredOptionException( precedentSynonyms );
 521  100
         }
 522  
 
 523  68
         Set<OptionSpec<?>> associated = target.get( precedentSynonyms );
 524  68
         if ( associated == null ) {
 525  41
             associated = new HashSet<OptionSpec<?>>();
 526  41
             target.put( precedentSynonyms, associated );
 527  
         }
 528  
 
 529  68
         associated.add( required );
 530  68
     }
 531  
 
 532  
     private AbstractOptionSpec<?> specFor( char option ) {
 533  75
         return specFor( String.valueOf( option ) );
 534  
     }
 535  
 
 536  
     private AbstractOptionSpec<?> specFor( String option ) {
 537  777
         return recognizedOptions.get( option );
 538  
     }
 539  
 
 540  
     private void reset() {
 541  248
         state = moreOptions( posixlyCorrect );
 542  248
     }
 543  
 
 544  
     private static char[] extractShortOptionsFrom( String argument ) {
 545  59
         char[] options = new char[ argument.length() - 1 ];
 546  59
         argument.getChars( 1, argument.length(), options, 0 );
 547  
 
 548  59
         return options;
 549  
     }
 550  
 
 551  
     private void validateOptionCharacters( char[] options ) {
 552  86
         for ( char each : options ) {
 553  83
             String option = String.valueOf( each );
 554  
 
 555  83
             if ( !isRecognized( option ) )
 556  7
                 throw unrecognizedOption( option );
 557  
 
 558  76
             if ( specFor( option ).acceptsArguments() )
 559  49
                 return;
 560  
         }
 561  3
     }
 562  
 
 563  
     private static KeyValuePair parseLongOptionWithArgument( String argument ) {
 564  99
         return KeyValuePair.valueOf( argument.substring( 2 ) );
 565  
     }
 566  
 
 567  
     private static KeyValuePair parseShortOptionWithArgument( String argument ) {
 568  291
         return KeyValuePair.valueOf( argument.substring( 1 ) );
 569  
     }
 570  
 }