Path: blob/master/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java
40948 views
/*1* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* This file is available under and governed by the GNU General Public27* License version 2 only, as published by the Free Software Foundation.28* However, the following notice accompanied the original version of this29* file:30*31* The MIT License32*33* Copyright (c) 2004-2015 Paul R. Holser, Jr.34*35* Permission is hereby granted, free of charge, to any person obtaining36* a copy of this software and associated documentation files (the37* "Software"), to deal in the Software without restriction, including38* without limitation the rights to use, copy, modify, merge, publish,39* distribute, sublicense, and/or sell copies of the Software, and to40* permit persons to whom the Software is furnished to do so, subject to41* the following conditions:42*43* The above copyright notice and this permission notice shall be44* included in all copies or substantial portions of the Software.45*46* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,47* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF48* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND49* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE50* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION51* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION52* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.53*/5455package jdk.internal.joptsimple;5657import java.io.IOException;58import java.io.OutputStream;59import java.io.OutputStreamWriter;60import java.io.Writer;61import java.util.*;6263import jdk.internal.joptsimple.internal.AbbreviationMap;64import jdk.internal.joptsimple.internal.SimpleOptionNameMap;65import jdk.internal.joptsimple.internal.OptionNameMap;66import jdk.internal.joptsimple.util.KeyValuePair;6768import static java.util.Collections.*;69import static jdk.internal.joptsimple.OptionException.*;70import static jdk.internal.joptsimple.OptionParserState.*;71import static jdk.internal.joptsimple.ParserRules.*;7273/**74* <p>Parses command line arguments, using a syntax that attempts to take from the best of POSIX {@code getopt()}75* and GNU {@code getopt_long()}.</p>76*77* <p>This parser supports short options and long options.</p>78*79* <ul>80* <li><dfn>Short options</dfn> begin with a single hyphen ("{@code -}") followed by a single letter or digit,81* or question mark ("{@code ?}"), or dot ("{@code .}"), or underscore ("{@code _}").</li>82*83* <li>Short options can accept single arguments. The argument can be made required or optional. The option's84* argument can occur:85* <ul>86* <li>in the slot after the option, as in {@code -d /tmp}</li>87* <li>right up against the option, as in {@code -d/tmp}</li>88* <li>right up against the option separated by an equals sign ({@code "="}), as in {@code -d=/tmp}</li>89* </ul>90* To specify <em>n</em> arguments for an option, specify the option <em>n</em> times, once for each argument,91* as in {@code -d /tmp -d /var -d /opt}; or, when using the92* {@linkplain ArgumentAcceptingOptionSpec#withValuesSeparatedBy(char) "separated values"} clause of the "fluent93* interface" (see below), give multiple values separated by a given character as a single argument to the94* option.</li>95*96* <li>Short options can be clustered, so that {@code -abc} is treated as {@code -a -b -c}. If a short option97* in the cluster can accept an argument, the remaining characters are interpreted as the argument for that98* option.</li>99*100* <li>An argument consisting only of two hyphens ({@code "--"}) signals that the remaining arguments are to be101* treated as non-options.</li>102*103* <li>An argument consisting only of a single hyphen is considered a non-option argument (though it can be an104* argument of an option). Many Unix programs treat single hyphens as stand-ins for the standard input or standard105* output streams.</li>106*107* <li><dfn>Long options</dfn> begin with two hyphens ({@code "--"}), followed by multiple letters, digits,108* hyphens, question marks, or dots. A hyphen cannot be the first character of a long option specification when109* configuring the parser.</li>110*111* <li>You can abbreviate long options, so long as the abbreviation is unique. Suppress this behavior if112* you wish using {@linkplain OptionParser#OptionParser(boolean) this constructor}.</li>113*114* <li>Long options can accept single arguments. The argument can be made required or optional. The option's115* argument can occur:116* <ul>117* <li>in the slot after the option, as in {@code --directory /tmp}</li>118* <li>right up against the option separated by an equals sign ({@code "="}), as in119* {@code --directory=/tmp}120* </ul>121* Specify multiple arguments for a long option in the same manner as for short options (see above).</li>122*123* <li>You can use a single hyphen ({@code "-"}) instead of a double hyphen ({@code "--"}) for a long124* option.</li>125*126* <li>The option {@code -W} is reserved. If you tell the parser to {@linkplain127* #recognizeAlternativeLongOptions(boolean) recognize alternative long options}, then it will treat, for example,128* {@code -W foo=bar} as the long option {@code foo} with argument {@code bar}, as though you had written129* {@code --foo=bar}.</li>130*131* <li>You can specify {@code -W} as a valid short option, or use it as an abbreviation for a long option, but132* {@linkplain #recognizeAlternativeLongOptions(boolean) recognizing alternative long options} will always supersede133* this behavior.</li>134*135* <li>You can specify a given short or long option multiple times on a single command line. The parser collects136* any arguments specified for those options as a list.</li>137*138* <li>If the parser detects an option whose argument is optional, and the next argument "looks like" an option,139* that argument is not treated as the argument to the option, but as a potentially valid option. If, on the other140* hand, the optional argument is typed as a derivative of {@link Number}, then that argument is treated as the141* negative number argument of the option, even if the parser recognizes the corresponding numeric option.142* For example:143* <pre><code>144* OptionParser parser = new OptionParser();145* parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );146* parser.accepts( "2" );147* OptionSet options = parser.parse( "-a", "-2" );148* </code></pre>149* In this case, the option set contains {@code "a"} with argument {@code -2}, not both {@code "a"} and150* {@code "2"}. Swapping the elements in the <em>args</em> array gives the latter.</li>151* </ul>152*153* <p>There are two ways to tell the parser what options to recognize:</p>154*155* <ol>156* <li>A "fluent interface"-style API for specifying options, available since version 2. Sentences in this fluent157* interface language begin with a call to {@link #accepts(String) accepts} or {@link #acceptsAll(List)158* acceptsAll} methods; calls on the ensuing chain of objects describe whether the options can take an argument,159* whether the argument is required or optional, to what type arguments of the options should be converted if any,160* etc. Since version 3, these calls return an instance of {@link OptionSpec}, which can subsequently be used to161* retrieve the arguments of the associated option in a type-safe manner.</li>162*163* <li>Since version 1, a more concise way of specifying short options has been to use the special {@linkplain164* #OptionParser(String) constructor}. Arguments of options specified in this manner will be of type {@link String}.165* Here are the rules for the format of the specification strings this constructor accepts:166*167* <ul>168* <li>Any letter or digit is treated as an option character.</li>169*170* <li>An option character can be immediately followed by an asterisk ({@code *)} to indicate that171* the option is a "help" option.</li>172*173* <li>If an option character (with possible trailing asterisk) is followed by a single colon ({@code ":"}),174* then the option requires an argument.</li>175*176* <li>If an option character (with possible trailing asterisk) is followed by two colons ({@code "::"}),177* then the option accepts an optional argument.</li>178*179* <li>Otherwise, the option character accepts no argument.</li>180*181* <li>If the option specification string begins with a plus sign ({@code "+" }), the parser will behave182* "POSIX-ly correct".</li>183*184* <li>If the option specification string contains the sequence {@code "W;"} (capital W followed by a185* semicolon), the parser will recognize the alternative form of long options.</li>186* </ul>187* </li>188* </ol>189*190* <p>Each of the options in a list of options given to {@link #acceptsAll(List) acceptsAll} is treated as a191* synonym of the others. For example:</p>192* <pre>193* <code>194* OptionParser parser = new OptionParser();195* parser.acceptsAll( asList( "w", "interactive", "confirmation" ) );196* OptionSet options = parser.parse( "-w" );197* </code>198* </pre>199* <p>In this case, <code>options.{@link OptionSet#has(String) has}</code> would answer {@code true} when given arguments200* {@code "w"}, {@code "interactive"}, and {@code "confirmation"}. The {@link OptionSet} would give the same201* responses to these arguments for its other methods as well.</p>202*203* <p>By default, as with GNU {@code getopt()}, the parser allows intermixing of options and non-options. If, however,204* the parser has been created to be "POSIX-ly correct", then the first argument that does not look lexically like an205* option, and is not a required argument of a preceding option, signals the end of options. You can still bind206* optional arguments to their options using the abutting (for short options) or {@code =} syntax.</p>207*208* <p>Unlike GNU {@code getopt()}, this parser does not honor the environment variable {@code POSIXLY_CORRECT}.209* "POSIX-ly correct" parsers are configured by either:</p>210*211* <ol>212* <li>using the method {@link #posixlyCorrect(boolean)}, or</li>213*214* <li>using the {@linkplain #OptionParser(String) constructor} with an argument whose first character is a plus sign215* ({@code "+"})</li>216* </ol>217*218* @author <a href="mailto:[email protected]">Paul Holser</a>219* @see <a href="http://www.gnu.org/software/libc/manual">The GNU C Library</a>220*/221public class OptionParser implements OptionDeclarer {222private final OptionNameMap<AbstractOptionSpec<?>> recognizedOptions;223private final ArrayList<AbstractOptionSpec<?>> trainingOrder;224private final Map<List<String>, Set<OptionSpec<?>>> requiredIf;225private final Map<List<String>, Set<OptionSpec<?>>> requiredUnless;226private final Map<List<String>, Set<OptionSpec<?>>> availableIf;227private final Map<List<String>, Set<OptionSpec<?>>> availableUnless;228229private OptionParserState state;230private boolean posixlyCorrect;231private boolean allowsUnrecognizedOptions;232private HelpFormatter helpFormatter = new BuiltinHelpFormatter();233234/**235* Creates an option parser that initially recognizes no options, and does not exhibit "POSIX-ly correct"236* behavior.237*/238public OptionParser() {239this(true);240}241242/**243* Creates an option parser that initially recognizes no options, and does not exhibit "POSIX-ly correct"244* behavior.245*246* @param allowAbbreviations whether unambiguous abbreviations of long options should be recognized247* by the parser248*/249public OptionParser( boolean allowAbbreviations ) {250trainingOrder = new ArrayList<>();251requiredIf = new HashMap<>();252requiredUnless = new HashMap<>();253availableIf = new HashMap<>();254availableUnless = new HashMap<>();255state = moreOptions( false );256257recognizedOptions = allowAbbreviations258? new AbbreviationMap<AbstractOptionSpec<?>>()259: new SimpleOptionNameMap<AbstractOptionSpec<?>>();260261recognize( new NonOptionArgumentSpec<String>() );262}263264/**265* Creates an option parser and configures it to recognize the short options specified in the given string.266*267* Arguments of options specified this way will be of type {@link String}.268*269* @param optionSpecification an option specification270* @throws NullPointerException if {@code optionSpecification} is {@code null}271* @throws OptionException if the option specification contains illegal characters or otherwise cannot be272* recognized273*/274public OptionParser( String optionSpecification ) {275this();276277new OptionSpecTokenizer( optionSpecification ).configure( this );278}279280public OptionSpecBuilder accepts( String option ) {281return acceptsAll( singletonList( option ) );282}283284public OptionSpecBuilder accepts( String option, String description ) {285return acceptsAll( singletonList( option ), description );286}287288public OptionSpecBuilder acceptsAll( List<String> options ) {289return acceptsAll( options, "" );290}291292public OptionSpecBuilder acceptsAll( List<String> options, String description ) {293if ( options.isEmpty() )294throw new IllegalArgumentException( "need at least one option" );295296ensureLegalOptions( options );297298return new OptionSpecBuilder( this, options, description );299}300301public NonOptionArgumentSpec<String> nonOptions() {302NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<>();303304recognize( spec );305306return spec;307}308309public NonOptionArgumentSpec<String> nonOptions( String description ) {310NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<>( description );311312recognize( spec );313314return spec;315}316317public void posixlyCorrect( boolean setting ) {318posixlyCorrect = setting;319state = moreOptions( setting );320}321322boolean posixlyCorrect() {323return posixlyCorrect;324}325326public void allowsUnrecognizedOptions() {327allowsUnrecognizedOptions = true;328}329330boolean doesAllowsUnrecognizedOptions() {331return allowsUnrecognizedOptions;332}333334public void recognizeAlternativeLongOptions( boolean recognize ) {335if ( recognize )336recognize( new AlternativeLongOptionSpec() );337else338recognizedOptions.remove( String.valueOf( RESERVED_FOR_EXTENSIONS ) );339}340341void recognize( AbstractOptionSpec<?> spec ) {342recognizedOptions.putAll( spec.options(), spec );343trainingOrder.add( spec );344}345346/**347* Writes information about the options this parser recognizes to the given output sink.348*349* The output sink is flushed, but not closed.350*351* @param sink the sink to write information to352* @throws IOException if there is a problem writing to the sink353* @throws NullPointerException if {@code sink} is {@code null}354* @see #printHelpOn(Writer)355*/356public void printHelpOn( OutputStream sink ) throws IOException {357printHelpOn( new OutputStreamWriter( sink ) );358}359360/**361* Writes information about the options this parser recognizes to the given output sink.362*363* The output sink is flushed, but not closed.364*365* @param sink the sink to write information to366* @throws IOException if there is a problem writing to the sink367* @throws NullPointerException if {@code sink} is {@code null}368* @see #printHelpOn(OutputStream)369*/370public void printHelpOn( Writer sink ) throws IOException {371sink.write( helpFormatter.format( _recognizedOptions() ) );372sink.flush();373}374375/**376* Tells the parser to use the given formatter when asked to {@linkplain #printHelpOn(java.io.Writer) print help}.377*378* @param formatter the formatter to use for printing help379* @throws NullPointerException if the formatter is {@code null}380*/381public void formatHelpWith( HelpFormatter formatter ) {382if ( formatter == null )383throw new NullPointerException();384385helpFormatter = formatter;386}387388/**389* Retrieves all options-spec pairings which have been configured for the parser in the same order as declared390* during training. Option flags for specs are alphabetized by {@link OptionSpec#options()}; only the order of the391* specs is preserved.392*393* (Note: prior to 4.7 the order was alphabetical across all options regardless of spec.)394*395* @return a map containing all the configured options and their corresponding {@link OptionSpec}396* @since 4.6397*/398public Map<String, OptionSpec<?>> recognizedOptions() {399return new LinkedHashMap<String, OptionSpec<?>>( _recognizedOptions() );400}401402private Map<String, AbstractOptionSpec<?>> _recognizedOptions() {403Map<String, AbstractOptionSpec<?>> options = new LinkedHashMap<>();404for ( AbstractOptionSpec<?> spec : trainingOrder ) {405for ( String option : spec.options() )406options.put( option, spec );407}408return options;409}410411/**412* Parses the given command line arguments according to the option specifications given to the parser.413*414* @param arguments arguments to parse415* @return an {@link OptionSet} describing the parsed options, their arguments, and any non-option arguments found416* @throws OptionException if problems are detected while parsing417* @throws NullPointerException if the argument list is {@code null}418*/419public OptionSet parse( String... arguments ) {420ArgumentList argumentList = new ArgumentList( arguments );421OptionSet detected = new OptionSet( recognizedOptions.toJavaUtilMap() );422detected.add( recognizedOptions.get( NonOptionArgumentSpec.NAME ) );423424while ( argumentList.hasMore() )425state.handleArgument( this, argumentList, detected );426427reset();428429ensureRequiredOptions( detected );430ensureAllowedOptions( detected );431432return detected;433}434435/**436* Mandates mutual exclusiveness for the options built by the specified builders.437*438* @param specs descriptors for options that should be mutually exclusive on a command line.439* @throws NullPointerException if {@code specs} is {@code null}440*/441public void mutuallyExclusive( OptionSpecBuilder... specs ) {442for ( int i = 0; i < specs.length; i++ ) {443for ( int j = 0; j < specs.length; j++ ) {444if ( i != j )445specs[i].availableUnless( specs[j] );446}447}448}449450private void ensureRequiredOptions( OptionSet options ) {451List<AbstractOptionSpec<?>> missingRequiredOptions = missingRequiredOptions(options);452boolean helpOptionPresent = isHelpOptionPresent( options );453454if ( !missingRequiredOptions.isEmpty() && !helpOptionPresent )455throw new MissingRequiredOptionsException( missingRequiredOptions );456}457458private void ensureAllowedOptions( OptionSet options ) {459List<AbstractOptionSpec<?>> forbiddenOptions = unavailableOptions( options );460boolean helpOptionPresent = isHelpOptionPresent( options );461462if ( !forbiddenOptions.isEmpty() && !helpOptionPresent )463throw new UnavailableOptionException( forbiddenOptions );464}465466private List<AbstractOptionSpec<?>> missingRequiredOptions( OptionSet options ) {467List<AbstractOptionSpec<?>> missingRequiredOptions = new ArrayList<>();468469for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {470if ( each.isRequired() && !options.has( each ) )471missingRequiredOptions.add(each);472}473474for ( Map.Entry<List<String>, Set<OptionSpec<?>>> each : requiredIf.entrySet() ) {475AbstractOptionSpec<?> required = specFor( each.getKey().iterator().next() );476477if ( optionsHasAnyOf( options, each.getValue() ) && !options.has( required ) )478missingRequiredOptions.add( required );479}480481for ( Map.Entry<List<String>, Set<OptionSpec<?>>> each : requiredUnless.entrySet() ) {482AbstractOptionSpec<?> required = specFor(each.getKey().iterator().next());483484if ( !optionsHasAnyOf( options, each.getValue() ) && !options.has( required ) )485missingRequiredOptions.add( required );486}487488return missingRequiredOptions;489}490491private List<AbstractOptionSpec<?>> unavailableOptions(OptionSet options) {492List<AbstractOptionSpec<?>> unavailableOptions = new ArrayList<>();493494for ( Map.Entry<List<String>, Set<OptionSpec<?>>> eachEntry : availableIf.entrySet() ) {495AbstractOptionSpec<?> forbidden = specFor( eachEntry.getKey().iterator().next() );496497if ( !optionsHasAnyOf( options, eachEntry.getValue() ) && options.has( forbidden ) ) {498unavailableOptions.add(forbidden);499}500}501502for ( Map.Entry<List<String>, Set<OptionSpec<?>>> eachEntry : availableUnless.entrySet() ) {503AbstractOptionSpec<?> forbidden = specFor( eachEntry.getKey().iterator().next() );504505if ( optionsHasAnyOf( options, eachEntry.getValue() ) && options.has( forbidden ) ) {506unavailableOptions.add(forbidden);507}508}509510return unavailableOptions;511}512513private boolean optionsHasAnyOf( OptionSet options, Collection<OptionSpec<?>> specs ) {514for ( OptionSpec<?> each : specs ) {515if ( options.has( each ) )516return true;517}518519return false;520}521522private boolean isHelpOptionPresent( OptionSet options ) {523boolean helpOptionPresent = false;524525for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {526if ( each.isForHelp() && options.has( each ) ) {527helpOptionPresent = true;528break;529}530}531532return helpOptionPresent;533}534535void handleLongOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {536KeyValuePair optionAndArgument = parseLongOptionWithArgument( candidate );537538if ( !isRecognized( optionAndArgument.key ) )539throw unrecognizedOption( optionAndArgument.key );540541AbstractOptionSpec<?> optionSpec = specFor( optionAndArgument.key );542optionSpec.handleOption( this, arguments, detected, optionAndArgument.value );543}544545void handleShortOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {546KeyValuePair optionAndArgument = parseShortOptionWithArgument( candidate );547548if ( isRecognized( optionAndArgument.key ) ) {549specFor( optionAndArgument.key ).handleOption( this, arguments, detected, optionAndArgument.value );550}551else552handleShortOptionCluster( candidate, arguments, detected );553}554555private void handleShortOptionCluster( String candidate, ArgumentList arguments, OptionSet detected ) {556char[] options = extractShortOptionsFrom( candidate );557validateOptionCharacters( options );558559for ( int i = 0; i < options.length; i++ ) {560AbstractOptionSpec<?> optionSpec = specFor( options[ i ] );561562if ( optionSpec.acceptsArguments() && options.length > i + 1 ) {563String detectedArgument = String.valueOf( options, i + 1, options.length - 1 - i );564optionSpec.handleOption( this, arguments, detected, detectedArgument );565break;566}567568optionSpec.handleOption( this, arguments, detected, null );569}570}571572void handleNonOptionArgument( String candidate, ArgumentList arguments, OptionSet detectedOptions ) {573specFor( NonOptionArgumentSpec.NAME ).handleOption( this, arguments, detectedOptions, candidate );574}575576void noMoreOptions() {577state = OptionParserState.noMoreOptions();578}579580boolean looksLikeAnOption( String argument ) {581return isShortOptionToken( argument ) || isLongOptionToken( argument );582}583584boolean isRecognized( String option ) {585return recognizedOptions.contains( option );586}587588void requiredIf( List<String> precedentSynonyms, String required ) {589requiredIf( precedentSynonyms, specFor( required ) );590}591592void requiredIf( List<String> precedentSynonyms, OptionSpec<?> required ) {593putDependentOption( precedentSynonyms, required, requiredIf );594}595596void requiredUnless( List<String> precedentSynonyms, String required ) {597requiredUnless( precedentSynonyms, specFor( required ) );598}599600void requiredUnless( List<String> precedentSynonyms, OptionSpec<?> required ) {601putDependentOption( precedentSynonyms, required, requiredUnless );602}603604void availableIf( List<String> precedentSynonyms, String available ) {605availableIf( precedentSynonyms, specFor( available ) );606}607608void availableIf( List<String> precedentSynonyms, OptionSpec<?> available) {609putDependentOption( precedentSynonyms, available, availableIf );610}611612void availableUnless( List<String> precedentSynonyms, String available ) {613availableUnless( precedentSynonyms, specFor( available ) );614}615616void availableUnless( List<String> precedentSynonyms, OptionSpec<?> available ) {617putDependentOption( precedentSynonyms, available, availableUnless );618}619620private void putDependentOption( List<String> precedentSynonyms, OptionSpec<?> required,621Map<List<String>, Set<OptionSpec<?>>> target ) {622623for ( String each : precedentSynonyms ) {624AbstractOptionSpec<?> spec = specFor( each );625if ( spec == null )626throw new UnconfiguredOptionException( precedentSynonyms );627}628629Set<OptionSpec<?>> associated = target.get( precedentSynonyms );630if ( associated == null ) {631associated = new HashSet<>();632target.put( precedentSynonyms, associated );633}634635associated.add( required );636}637638private AbstractOptionSpec<?> specFor( char option ) {639return specFor( String.valueOf( option ) );640}641642private AbstractOptionSpec<?> specFor( String option ) {643return recognizedOptions.get( option );644}645646private void reset() {647state = moreOptions( posixlyCorrect );648}649650private static char[] extractShortOptionsFrom( String argument ) {651char[] options = new char[ argument.length() - 1 ];652argument.getChars( 1, argument.length(), options, 0 );653654return options;655}656657private void validateOptionCharacters( char[] options ) {658for ( char each : options ) {659String option = String.valueOf( each );660661if ( !isRecognized( option ) )662throw unrecognizedOption( option );663664if ( specFor( option ).acceptsArguments() )665return;666}667}668669private static KeyValuePair parseLongOptionWithArgument( String argument ) {670return KeyValuePair.valueOf( argument.substring( 2 ) );671}672673private static KeyValuePair parseShortOptionWithArgument( String argument ) {674return KeyValuePair.valueOf( argument.substring( 1 ) );675}676}677678679