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