001/* 002 Licensed to the Apache Software Foundation (ASF) under one or more 003 contributor license agreements. See the NOTICE file distributed with 004 this work for additional information regarding copyright ownership. 005 The ASF licenses this file to You under the Apache License, Version 2.0 006 (the "License"); you may not use this file except in compliance with 007 the License. You may obtain a copy of the License at 008 009 http://www.apache.org/licenses/LICENSE-2.0 010 011 Unless required by applicable law or agreed to in writing, software 012 distributed under the License is distributed on an "AS IS" BASIS, 013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 See the License for the specific language governing permissions and 015 limitations under the License. 016 */ 017 018package org.apache.commons.cli; 019 020import java.util.ArrayList; 021import java.util.Enumeration; 022import java.util.List; 023import java.util.Properties; 024import java.util.function.Consumer; 025 026/** 027 * Default parser. 028 * 029 * @since 1.3 030 */ 031public class DefaultParser implements CommandLineParser { 032 033 /** 034 * A nested builder class to create {@code DefaultParser} instances 035 * using descriptive methods. 036 * 037 * Example usage: 038 * <pre> 039 * DefaultParser parser = Option.builder() 040 * .setAllowPartialMatching(false) 041 * .setStripLeadingAndTrailingQuotes(false) 042 * .build(); 043 * </pre> 044 * 045 * @since 1.5.0 046 */ 047 public static final class Builder { 048 049 /** Flag indicating if partial matching of long options is supported. */ 050 private boolean allowPartialMatching = true; 051 052 /** 053 * The deprecated option handler. 054 * <p> 055 * If you want to serialize this field, use a serialization proxy. 056 * </p> 057 */ 058 private Consumer<Option> deprecatedHandler = CommandLine.Builder.DEPRECATED_HANDLER; 059 060 /** Flag indicating if balanced leading and trailing double quotes should be stripped from option arguments. */ 061 private Boolean stripLeadingAndTrailingQuotes; 062 063 /** 064 * Constructs a new {@code Builder} for a {@code DefaultParser} instance. 065 * 066 * Both allowPartialMatching and stripLeadingAndTrailingQuotes are true by default, 067 * mimicking the argument-less constructor. 068 */ 069 private Builder() { 070 } 071 072 /** 073 * Builds an DefaultParser with the values declared by this {@link Builder}. 074 * 075 * @return the new {@link DefaultParser} 076 * @since 1.5.0 077 */ 078 public DefaultParser build() { 079 return new DefaultParser(allowPartialMatching, stripLeadingAndTrailingQuotes, deprecatedHandler); 080 } 081 082 /** 083 * Sets if partial matching of long options is supported. 084 * 085 * By "partial matching" we mean that given the following code: 086 * 087 * <pre> 088 * { 089 * @code 090 * final Options options = new Options(); 091 * options.addOption(new Option("d", "debug", false, "Turn on debug.")); 092 * options.addOption(new Option("e", "extract", false, "Turn on extract.")); 093 * options.addOption(new Option("o", "option", true, "Turn on option with argument.")); 094 * } 095 * </pre> 096 * 097 * If "partial matching" is turned on, {@code -de} only matches the {@code "debug"} option. However, with 098 * "partial matching" disabled, {@code -de} would enable both {@code debug} as well as {@code extract} 099 * 100 * @param allowPartialMatching whether to allow partial matching of long options 101 * @return this builder, to allow method chaining 102 * @since 1.5.0 103 */ 104 public Builder setAllowPartialMatching(final boolean allowPartialMatching) { 105 this.allowPartialMatching = allowPartialMatching; 106 return this; 107 } 108 109 /** 110 * Sets the deprecated option handler. 111 * 112 * @param deprecatedHandler the deprecated option handler. 113 * @return {@code this} instance. 114 * @since 1.7.0 115 */ 116 public Builder setDeprecatedHandler(final Consumer<Option> deprecatedHandler) { 117 this.deprecatedHandler = deprecatedHandler; 118 return this; 119 } 120 121 /** 122 * Sets if balanced leading and trailing double quotes should be stripped from option arguments. 123 * 124 * If "stripping of balanced leading and trailing double quotes from option arguments" is true, 125 * the outermost balanced double quotes of option arguments values will be removed. 126 * For example, {@code -o '"x"'} getValue() will return {@code x}, instead of {@code "x"} 127 * 128 * If "stripping of balanced leading and trailing double quotes from option arguments" is null, 129 * then quotes will be stripped from option values separated by space from the option, but 130 * kept in other cases, which is the historic behavior. 131 * 132 * @param stripLeadingAndTrailingQuotes whether balanced leading and trailing double quotes should be stripped from option arguments. 133 * @return this builder, to allow method chaining 134 * @since 1.5.0 135 */ 136 public Builder setStripLeadingAndTrailingQuotes(final Boolean stripLeadingAndTrailingQuotes) { 137 this.stripLeadingAndTrailingQuotes = stripLeadingAndTrailingQuotes; 138 return this; 139 } 140 } 141 142 /** 143 * Creates a new {@link Builder} to create an {@link DefaultParser} using descriptive 144 * methods. 145 * 146 * @return a new {@link Builder} instance 147 * @since 1.5.0 148 */ 149 public static Builder builder() { 150 return new Builder(); 151 } 152 153 static int indexOfEqual(final String token) { 154 return token.indexOf(Char.EQUAL); 155 } 156 157 /** The command-line instance. */ 158 protected CommandLine cmd; 159 160 /** The current options. */ 161 protected Options options; 162 163 /** 164 * Flag indicating how unrecognized tokens are handled. {@code true} to stop the parsing and add the remaining 165 * tokens to the args list. {@code false} to throw an exception. 166 */ 167 protected boolean stopAtNonOption; 168 169 /** The token currently processed. */ 170 protected String currentToken; 171 172 /** The last option parsed. */ 173 protected Option currentOption; 174 175 /** Flag indicating if tokens should no longer be analyzed and simply added as arguments of the command line. */ 176 protected boolean skipParsing; 177 178 /** The required options and groups expected to be found when parsing the command line. */ 179 protected List expectedOpts; 180 181 /** Flag indicating if partial matching of long options is supported. */ 182 private final boolean allowPartialMatching; 183 184 /** Flag indicating if balanced leading and trailing double quotes should be stripped from option arguments. 185 * null represents the historic arbitrary behavior */ 186 private final Boolean stripLeadingAndTrailingQuotes; 187 188 /** 189 * The deprecated option handler. 190 * <p> 191 * If you want to serialize this field, use a serialization proxy. 192 * </p> 193 */ 194 private final Consumer<Option> deprecatedHandler; 195 196 /** 197 * Creates a new DefaultParser instance with partial matching enabled. 198 * 199 * By "partial matching" we mean that given the following code: 200 * 201 * <pre> 202 * { 203 * @code 204 * final Options options = new Options(); 205 * options.addOption(new Option("d", "debug", false, "Turn on debug.")); 206 * options.addOption(new Option("e", "extract", false, "Turn on extract.")); 207 * options.addOption(new Option("o", "option", true, "Turn on option with argument.")); 208 * } 209 * </pre> 210 * 211 * with "partial matching" turned on, {@code -de} only matches the {@code "debug"} option. However, with 212 * "partial matching" disabled, {@code -de} would enable both {@code debug} as well as {@code extract} 213 * options. 214 */ 215 public DefaultParser() { 216 this.allowPartialMatching = true; 217 this.stripLeadingAndTrailingQuotes = null; 218 this.deprecatedHandler = CommandLine.Builder.DEPRECATED_HANDLER; 219 } 220 221 /** 222 * Create a new DefaultParser instance with the specified partial matching policy. 223 * 224 * By "partial matching" we mean that given the following code: 225 * 226 * <pre> 227 * { 228 * @code 229 * final Options options = new Options(); 230 * options.addOption(new Option("d", "debug", false, "Turn on debug.")); 231 * options.addOption(new Option("e", "extract", false, "Turn on extract.")); 232 * options.addOption(new Option("o", "option", true, "Turn on option with argument.")); 233 * } 234 * </pre> 235 * 236 * with "partial matching" turned on, {@code -de} only matches the {@code "debug"} option. However, with 237 * "partial matching" disabled, {@code -de} would enable both {@code debug} as well as {@code extract} 238 * options. 239 * 240 * @param allowPartialMatching if partial matching of long options shall be enabled 241 */ 242 public DefaultParser(final boolean allowPartialMatching) { 243 this.allowPartialMatching = allowPartialMatching; 244 this.stripLeadingAndTrailingQuotes = null; 245 this.deprecatedHandler = CommandLine.Builder.DEPRECATED_HANDLER; 246 } 247 248 /** 249 * Creates a new DefaultParser instance with the specified partial matching and quote 250 * stripping policy. 251 * 252 * @param allowPartialMatching if partial matching of long options shall be enabled 253 * @param stripLeadingAndTrailingQuotes if balanced outer double quoutes should be stripped 254 */ 255 private DefaultParser(final boolean allowPartialMatching, final Boolean stripLeadingAndTrailingQuotes, final Consumer<Option> deprecatedHandler) { 256 this.allowPartialMatching = allowPartialMatching; 257 this.stripLeadingAndTrailingQuotes = stripLeadingAndTrailingQuotes; 258 this.deprecatedHandler = deprecatedHandler; 259 } 260 261 /** 262 * Throws a {@link MissingArgumentException} if the current option didn't receive the number of arguments expected. 263 */ 264 private void checkRequiredArgs() throws ParseException { 265 if (currentOption != null && currentOption.requiresArg()) { 266 if (isJavaProperty(currentOption.getKey()) && currentOption.getValuesList().size() == 1) { 267 return; 268 } 269 throw new MissingArgumentException(currentOption); 270 } 271 } 272 273 /** 274 * Throws a {@link MissingOptionException} if all of the required options are not present. 275 * 276 * @throws MissingOptionException if any of the required Options are not present. 277 */ 278 protected void checkRequiredOptions() throws MissingOptionException { 279 // if there are required options that have not been processed 280 if (!expectedOpts.isEmpty()) { 281 throw new MissingOptionException(expectedOpts); 282 } 283 } 284 285 /** 286 * Searches for a prefix that is the long name of an option (-Xmx512m) 287 * 288 * @param token 289 */ 290 private String getLongPrefix(final String token) { 291 final String t = Util.stripLeadingHyphens(token); 292 int i; 293 String opt = null; 294 for (i = t.length() - 2; i > 1; i--) { 295 final String prefix = t.substring(0, i); 296 if (options.hasLongOption(prefix)) { 297 opt = prefix; 298 break; 299 } 300 } 301 return opt; 302 } 303 304 /** 305 * Gets a list of matching option strings for the given token, depending on the selected partial matching policy. 306 * 307 * @param token the token (may contain leading dashes) 308 * @return the list of matching option strings or an empty list if no matching option could be found 309 */ 310 private List<String> getMatchingLongOptions(final String token) { 311 if (allowPartialMatching) { 312 return options.getMatchingOptions(token); 313 } 314 final List<String> matches = new ArrayList<>(1); 315 if (options.hasLongOption(token)) { 316 matches.add(options.getOption(token).getLongOpt()); 317 } 318 return matches; 319 } 320 321 /** 322 * Breaks {@code token} into its constituent parts using the following algorithm. 323 * 324 * <ul> 325 * <li>ignore the first character ("<b>-</b>")</li> 326 * <li>for each remaining character check if an {@link Option} exists with that id.</li> 327 * <li>if an {@link Option} does exist then add that character prepended with "<b>-</b>" to the list of processed 328 * tokens.</li> 329 * <li>if the {@link Option} can have an argument value and there are remaining characters in the token then add the 330 * remaining characters as a token to the list of processed tokens.</li> 331 * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> {@code stopAtNonOption} <b>IS</b> set then add the 332 * special token "<b>--</b>" followed by the remaining characters and also the remaining tokens directly to the 333 * processed tokens list.</li> 334 * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> {@code stopAtNonOption} <b>IS NOT</b> set then add 335 * that character prepended with "<b>-</b>".</li> 336 * </ul> 337 * 338 * @param token The current token to be <b>burst</b> at the first non-Option encountered. 339 * @throws ParseException if there are any problems encountered while parsing the command line token. 340 */ 341 protected void handleConcatenatedOptions(final String token) throws ParseException { 342 for (int i = 1; i < token.length(); i++) { 343 final String ch = String.valueOf(token.charAt(i)); 344 if (!options.hasOption(ch)) { 345 handleUnknownToken(stopAtNonOption && i > 1 ? token.substring(i) : token); 346 break; 347 } 348 handleOption(options.getOption(ch)); 349 if (currentOption != null && token.length() != i + 1) { 350 // add the trail as an argument of the option 351 currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOff(token.substring(i + 1))); 352 break; 353 } 354 } 355 } 356 357 /** 358 * Handles the following tokens: 359 * 360 * --L --L=V --L V --l 361 * 362 * @param token the command line token to handle 363 */ 364 private void handleLongOption(final String token) throws ParseException { 365 if (indexOfEqual(token) == -1) { 366 handleLongOptionWithoutEqual(token); 367 } else { 368 handleLongOptionWithEqual(token); 369 } 370 } 371 372 /** 373 * Handles the following tokens: 374 * 375 * --L=V -L=V --l=V -l=V 376 * 377 * @param token the command line token to handle 378 */ 379 private void handleLongOptionWithEqual(final String token) throws ParseException { 380 final int pos = indexOfEqual(token); 381 final String value = token.substring(pos + 1); 382 final String opt = token.substring(0, pos); 383 final List<String> matchingOpts = getMatchingLongOptions(opt); 384 if (matchingOpts.isEmpty()) { 385 handleUnknownToken(currentToken); 386 } else if (matchingOpts.size() > 1 && !options.hasLongOption(opt)) { 387 throw new AmbiguousOptionException(opt, matchingOpts); 388 } else { 389 final String key = options.hasLongOption(opt) ? opt : matchingOpts.get(0); 390 final Option option = options.getOption(key); 391 if (option.acceptsArg()) { 392 handleOption(option); 393 currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOff(value)); 394 currentOption = null; 395 } else { 396 handleUnknownToken(currentToken); 397 } 398 } 399 } 400 401 /** 402 * Handles the following tokens: 403 * 404 * --L -L --l -l 405 * 406 * @param token the command line token to handle 407 */ 408 private void handleLongOptionWithoutEqual(final String token) throws ParseException { 409 final List<String> matchingOpts = getMatchingLongOptions(token); 410 if (matchingOpts.isEmpty()) { 411 handleUnknownToken(currentToken); 412 } else if (matchingOpts.size() > 1 && !options.hasLongOption(token)) { 413 throw new AmbiguousOptionException(token, matchingOpts); 414 } else { 415 final String key = options.hasLongOption(token) ? token : matchingOpts.get(0); 416 handleOption(options.getOption(key)); 417 } 418 } 419 420 private void handleOption(final Option option) throws ParseException { 421 // check the previous option before handling the next one 422 checkRequiredArgs(); 423 final Option copy = (Option) option.clone(); 424 updateRequiredOptions(copy); 425 cmd.addOption(copy); 426 currentOption = copy.hasArg() ? copy : null; 427 } 428 429 /** 430 * Sets the values of Options using the values in {@code properties}. 431 * 432 * @param properties The value properties to be processed. 433 */ 434 private void handleProperties(final Properties properties) throws ParseException { 435 if (properties == null) { 436 return; 437 } 438 for (final Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();) { 439 final String option = e.nextElement().toString(); 440 final Option opt = options.getOption(option); 441 if (opt == null) { 442 throw new UnrecognizedOptionException("Default option wasn't defined", option); 443 } 444 // if the option is part of a group, check if another option of the group has been selected 445 final OptionGroup group = options.getOptionGroup(opt); 446 final boolean selected = group != null && group.isSelected(); 447 if (!cmd.hasOption(option) && !selected) { 448 // get the value from the properties 449 final String value = properties.getProperty(option); 450 451 if (opt.hasArg()) { 452 if (Util.isEmpty(opt.getValues())) { 453 opt.processValue(stripLeadingAndTrailingQuotesDefaultOff(value)); 454 } 455 } else if (!("yes".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value) || "1".equalsIgnoreCase(value))) { 456 // if the value is not yes, true or 1 then don't add the option to the CommandLine 457 continue; 458 } 459 handleOption(opt); 460 currentOption = null; 461 } 462 } 463 } 464 465 /** 466 * Handles the following tokens: 467 * 468 * -S -SV -S V -S=V -S1S2 -S1S2 V -SV1=V2 469 * 470 * -L -LV -L V -L=V -l 471 * 472 * @param hyphenToken the command line token to handle 473 */ 474 private void handleShortAndLongOption(final String hyphenToken) throws ParseException { 475 final String token = Util.stripLeadingHyphens(hyphenToken); 476 final int pos = indexOfEqual(token); 477 if (token.length() == 1) { 478 // -S 479 if (options.hasShortOption(token)) { 480 handleOption(options.getOption(token)); 481 } else { 482 handleUnknownToken(hyphenToken); 483 } 484 } else if (pos == -1) { 485 // no equal sign found (-xxx) 486 if (options.hasShortOption(token)) { 487 handleOption(options.getOption(token)); 488 } else if (!getMatchingLongOptions(token).isEmpty()) { 489 // -L or -l 490 handleLongOptionWithoutEqual(hyphenToken); 491 } else { 492 // look for a long prefix (-Xmx512m) 493 final String opt = getLongPrefix(token); 494 495 if (opt != null && options.getOption(opt).acceptsArg()) { 496 handleOption(options.getOption(opt)); 497 currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOff(token.substring(opt.length()))); 498 currentOption = null; 499 } else if (isJavaProperty(token)) { 500 // -SV1 (-Dflag) 501 handleOption(options.getOption(token.substring(0, 1))); 502 currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOff(token.substring(1))); 503 currentOption = null; 504 } else { 505 // -S1S2S3 or -S1S2V 506 handleConcatenatedOptions(hyphenToken); 507 } 508 } 509 } else { 510 // equal sign found (-xxx=yyy) 511 final String opt = token.substring(0, pos); 512 final String value = token.substring(pos + 1); 513 514 if (opt.length() == 1) { 515 // -S=V 516 final Option option = options.getOption(opt); 517 if (option != null && option.acceptsArg()) { 518 handleOption(option); 519 currentOption.processValue(value); 520 currentOption = null; 521 } else { 522 handleUnknownToken(hyphenToken); 523 } 524 } else if (isJavaProperty(opt)) { 525 // -SV1=V2 (-Dkey=value) 526 handleOption(options.getOption(opt.substring(0, 1))); 527 currentOption.processValue(opt.substring(1)); 528 currentOption.processValue(value); 529 currentOption = null; 530 } else { 531 // -L=V or -l=V 532 handleLongOptionWithEqual(hyphenToken); 533 } 534 } 535 } 536 537 /** 538 * Handles any command line token. 539 * 540 * @param token the command line token to handle 541 * @throws ParseException 542 */ 543 private void handleToken(final String token) throws ParseException { 544 if (token != null) { 545 currentToken = token; 546 if (skipParsing) { 547 cmd.addArg(token); 548 } else if ("--".equals(token)) { 549 skipParsing = true; 550 } else if (currentOption != null && currentOption.acceptsArg() && isArgument(token)) { 551 currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOn(token)); 552 } else if (token.startsWith("--")) { 553 handleLongOption(token); 554 } else if (token.startsWith("-") && !"-".equals(token)) { 555 handleShortAndLongOption(token); 556 } else { 557 handleUnknownToken(token); 558 } 559 if (currentOption != null && !currentOption.acceptsArg()) { 560 currentOption = null; 561 } 562 } 563 } 564 565 /** 566 * Handles an unknown token. If the token starts with a dash an UnrecognizedOptionException is thrown. Otherwise the 567 * token is added to the arguments of the command line. If the stopAtNonOption flag is set, this stops the parsing and 568 * the remaining tokens are added as-is in the arguments of the command line. 569 * 570 * @param token the command line token to handle 571 */ 572 private void handleUnknownToken(final String token) throws ParseException { 573 if (token.startsWith("-") && token.length() > 1 && !stopAtNonOption) { 574 throw new UnrecognizedOptionException("Unrecognized option: " + token, token); 575 } 576 cmd.addArg(token); 577 if (stopAtNonOption) { 578 skipParsing = true; 579 } 580 } 581 582 /** 583 * Tests if the token is a valid argument. 584 * 585 * @param token 586 */ 587 private boolean isArgument(final String token) { 588 return !isOption(token) || isNegativeNumber(token); 589 } 590 591 /** 592 * Tests if the specified token is a Java-like property (-Dkey=value). 593 */ 594 private boolean isJavaProperty(final String token) { 595 final String opt = token.isEmpty() ? null : token.substring(0, 1); 596 final Option option = options.getOption(opt); 597 return option != null && (option.getArgs() >= 2 || option.getArgs() == Option.UNLIMITED_VALUES); 598 } 599 600 /** 601 * Tests if the token looks like a long option. 602 * 603 * @param token 604 */ 605 private boolean isLongOption(final String token) { 606 if (token == null || !token.startsWith("-") || token.length() == 1) { 607 return false; 608 } 609 final int pos = indexOfEqual(token); 610 final String t = pos == -1 ? token : token.substring(0, pos); 611 if (!getMatchingLongOptions(t).isEmpty()) { 612 // long or partial long options (--L, -L, --L=V, -L=V, --l, --l=V) 613 return true; 614 } 615 if (getLongPrefix(token) != null && !token.startsWith("--")) { 616 // -LV 617 return true; 618 } 619 return false; 620 } 621 622 /** 623 * Tests if the token is a negative number. 624 * 625 * @param token 626 */ 627 private boolean isNegativeNumber(final String token) { 628 try { 629 Double.parseDouble(token); 630 return true; 631 } catch (final NumberFormatException e) { 632 return false; 633 } 634 } 635 636 /** 637 * Tests if the token looks like an option. 638 * 639 * @param token 640 */ 641 private boolean isOption(final String token) { 642 return isLongOption(token) || isShortOption(token); 643 } 644 645 /** 646 * Tests if the token looks like a short option. 647 * 648 * @param token 649 */ 650 private boolean isShortOption(final String token) { 651 // short options (-S, -SV, -S=V, -SV1=V2, -S1S2) 652 if (token == null || !token.startsWith("-") || token.length() == 1) { 653 return false; 654 } 655 // remove leading "-" and "=value" 656 final int pos = indexOfEqual(token); 657 final String optName = pos == -1 ? token.substring(1) : token.substring(1, pos); 658 if (options.hasShortOption(optName)) { 659 return true; 660 } 661 // check for several concatenated short options 662 return !optName.isEmpty() && options.hasShortOption(String.valueOf(optName.charAt(0))); 663 } 664 665 @Override 666 public CommandLine parse(final Options options, final String[] arguments) throws ParseException { 667 return parse(options, arguments, null); 668 } 669 670 @Override 671 public CommandLine parse(final Options options, final String[] arguments, final boolean stopAtNonOption) throws ParseException { 672 return parse(options, arguments, null, stopAtNonOption); 673 } 674 675 /** 676 * Parses the arguments according to the specified options and properties. 677 * 678 * @param options the specified Options 679 * @param arguments the command line arguments 680 * @param properties command line option name-value pairs 681 * @return the list of atomic option and value tokens 682 * 683 * @throws ParseException if there are any problems encountered while parsing the command line tokens. 684 */ 685 public CommandLine parse(final Options options, final String[] arguments, final Properties properties) throws ParseException { 686 return parse(options, arguments, properties, false); 687 } 688 689 /** 690 * Parses the arguments according to the specified options and properties. 691 * 692 * @param options the specified Options 693 * @param arguments the command line arguments 694 * @param properties command line option name-value pairs 695 * @param stopAtNonOption if {@code true} an unrecognized argument stops the parsing and the remaining arguments 696 * are added to the {@link CommandLine}s args list. If {@code false} an unrecognized argument triggers a 697 * ParseException. 698 * 699 * @return the list of atomic option and value tokens 700 * @throws ParseException if there are any problems encountered while parsing the command line tokens. 701 */ 702 public CommandLine parse(final Options options, final String[] arguments, final Properties properties, final boolean stopAtNonOption) 703 throws ParseException { 704 this.options = options; 705 this.stopAtNonOption = stopAtNonOption; 706 skipParsing = false; 707 currentOption = null; 708 expectedOpts = new ArrayList<>(options.getRequiredOptions()); 709 // clear the data from the groups 710 for (final OptionGroup group : options.getOptionGroups()) { 711 group.setSelected(null); 712 } 713 cmd = CommandLine.builder().setDeprecatedHandler(deprecatedHandler).build(); 714 if (arguments != null) { 715 for (final String argument : arguments) { 716 handleToken(argument); 717 } 718 } 719 // check the arguments of the last option 720 checkRequiredArgs(); 721 // add the default options 722 handleProperties(properties); 723 checkRequiredOptions(); 724 return cmd; 725 } 726 727 /** 728 * Strips balanced leading and trailing quotes if the stripLeadingAndTrailingQuotes is set 729 * If stripLeadingAndTrailingQuotes is null, then do not strip 730 * 731 * @param token a string 732 * @return token with the quotes stripped (if set) 733 */ 734 private String stripLeadingAndTrailingQuotesDefaultOff(final String token) { 735 if (stripLeadingAndTrailingQuotes != null && stripLeadingAndTrailingQuotes) { 736 return Util.stripLeadingAndTrailingQuotes(token); 737 } 738 return token; 739 } 740 741 /** 742 * Strips balanced leading and trailing quotes if the stripLeadingAndTrailingQuotes is set 743 * If stripLeadingAndTrailingQuotes is null, then do not strip 744 * 745 * @param token a string 746 * @return token with the quotes stripped (if set) 747 */ 748 private String stripLeadingAndTrailingQuotesDefaultOn(final String token) { 749 if (stripLeadingAndTrailingQuotes == null || stripLeadingAndTrailingQuotes) { 750 return Util.stripLeadingAndTrailingQuotes(token); 751 } 752 return token; 753 } 754 755 /** 756 * Removes the option or its group from the list of expected elements. 757 * 758 * @param option 759 */ 760 private void updateRequiredOptions(final Option option) throws AlreadySelectedException { 761 if (option.isRequired()) { 762 expectedOpts.remove(option.getKey()); 763 } 764 765 // if the option is in an OptionGroup make that option the selected option of the group 766 if (options.getOptionGroup(option) != null) { 767 final OptionGroup group = options.getOptionGroup(option); 768 769 if (group.isRequired()) { 770 expectedOpts.remove(group); 771 } 772 773 group.setSelected(option); 774 } 775 } 776}