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 this. 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.getSelected() != null; 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 (opt.getValues() == null || opt.getValues().length == 0) { 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 token the command line token to handle 473 */ 474 private void handleShortAndLongOption(final String token) throws ParseException { 475 final String t = Util.stripLeadingHyphens(token); 476 final int pos = indexOfEqual(t); 477 if (t.length() == 1) { 478 // -S 479 if (options.hasShortOption(t)) { 480 handleOption(options.getOption(t)); 481 } else { 482 handleUnknownToken(token); 483 } 484 } else if (pos == -1) { 485 // no equal sign found (-xxx) 486 if (options.hasShortOption(t)) { 487 handleOption(options.getOption(t)); 488 } else if (!getMatchingLongOptions(t).isEmpty()) { 489 // -L or -l 490 handleLongOptionWithoutEqual(token); 491 } else { 492 // look for a long prefix (-Xmx512m) 493 final String opt = getLongPrefix(t); 494 495 if (opt != null && options.getOption(opt).acceptsArg()) { 496 handleOption(options.getOption(opt)); 497 currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOff(t.substring(opt.length()))); 498 currentOption = null; 499 } else if (isJavaProperty(t)) { 500 // -SV1 (-Dflag) 501 handleOption(options.getOption(t.substring(0, 1))); 502 currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOff(t.substring(1))); 503 currentOption = null; 504 } else { 505 // -S1S2S3 or -S1S2V 506 handleConcatenatedOptions(token); 507 } 508 } 509 } else { 510 // equal sign found (-xxx=yyy) 511 final String opt = t.substring(0, pos); 512 final String value = t.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(token); 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(token); 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 currentToken = token; 545 if (skipParsing) { 546 cmd.addArg(token); 547 } else if ("--".equals(token)) { 548 skipParsing = true; 549 } else if (currentOption != null && currentOption.acceptsArg() && isArgument(token)) { 550 currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOn(token)); 551 } else if (token.startsWith("--")) { 552 handleLongOption(token); 553 } else if (token.startsWith("-") && !"-".equals(token)) { 554 handleShortAndLongOption(token); 555 } else { 556 handleUnknownToken(token); 557 } 558 if (currentOption != null && !currentOption.acceptsArg()) { 559 currentOption = null; 560 } 561 } 562 563 /** 564 * Handles an unknown token. If the token starts with a dash an UnrecognizedOptionException is thrown. Otherwise the 565 * token is added to the arguments of the command line. If the stopAtNonOption flag is set, this stops the parsing and 566 * the remaining tokens are added as-is in the arguments of the command line. 567 * 568 * @param token the command line token to handle 569 */ 570 private void handleUnknownToken(final String token) throws ParseException { 571 if (token.startsWith("-") && token.length() > 1 && !stopAtNonOption) { 572 throw new UnrecognizedOptionException("Unrecognized option: " + token, token); 573 } 574 cmd.addArg(token); 575 if (stopAtNonOption) { 576 skipParsing = true; 577 } 578 } 579 580 /** 581 * Tests if the token is a valid argument. 582 * 583 * @param token 584 */ 585 private boolean isArgument(final String token) { 586 return !isOption(token) || isNegativeNumber(token); 587 } 588 589 /** 590 * Tests if the specified token is a Java-like property (-Dkey=value). 591 */ 592 private boolean isJavaProperty(final String token) { 593 final String opt = token.isEmpty() ? null : token.substring(0, 1); 594 final Option option = options.getOption(opt); 595 return option != null && (option.getArgs() >= 2 || option.getArgs() == Option.UNLIMITED_VALUES); 596 } 597 598 /** 599 * Tests if the token looks like a long option. 600 * 601 * @param token 602 */ 603 private boolean isLongOption(final String token) { 604 if (token == null || !token.startsWith("-") || token.length() == 1) { 605 return false; 606 } 607 final int pos = indexOfEqual(token); 608 final String t = pos == -1 ? token : token.substring(0, pos); 609 if (!getMatchingLongOptions(t).isEmpty()) { 610 // long or partial long options (--L, -L, --L=V, -L=V, --l, --l=V) 611 return true; 612 } 613 if (getLongPrefix(token) != null && !token.startsWith("--")) { 614 // -LV 615 return true; 616 } 617 return false; 618 } 619 620 /** 621 * Tests if the token is a negative number. 622 * 623 * @param token 624 */ 625 private boolean isNegativeNumber(final String token) { 626 try { 627 Double.parseDouble(token); 628 return true; 629 } catch (final NumberFormatException e) { 630 return false; 631 } 632 } 633 634 /** 635 * Tests if the token looks like an option. 636 * 637 * @param token 638 */ 639 private boolean isOption(final String token) { 640 return isLongOption(token) || isShortOption(token); 641 } 642 643 /** 644 * Tests if the token looks like a short option. 645 * 646 * @param token 647 */ 648 private boolean isShortOption(final String token) { 649 // short options (-S, -SV, -S=V, -SV1=V2, -S1S2) 650 if (token == null || !token.startsWith("-") || token.length() == 1) { 651 return false; 652 } 653 // remove leading "-" and "=value" 654 final int pos = indexOfEqual(token); 655 final String optName = pos == -1 ? token.substring(1) : token.substring(1, pos); 656 if (options.hasShortOption(optName)) { 657 return true; 658 } 659 // check for several concatenated short options 660 return !optName.isEmpty() && options.hasShortOption(String.valueOf(optName.charAt(0))); 661 } 662 663 @Override 664 public CommandLine parse(final Options options, final String[] arguments) throws ParseException { 665 return parse(options, arguments, null); 666 } 667 668 @Override 669 public CommandLine parse(final Options options, final String[] arguments, final boolean stopAtNonOption) throws ParseException { 670 return parse(options, arguments, null, stopAtNonOption); 671 } 672 673 /** 674 * Parses the arguments according to the specified options and properties. 675 * 676 * @param options the specified Options 677 * @param arguments the command line arguments 678 * @param properties command line option name-value pairs 679 * @return the list of atomic option and value tokens 680 * 681 * @throws ParseException if there are any problems encountered while parsing the command line tokens. 682 */ 683 public CommandLine parse(final Options options, final String[] arguments, final Properties properties) throws ParseException { 684 return parse(options, arguments, properties, false); 685 } 686 687 /** 688 * Parses the arguments according to the specified options and properties. 689 * 690 * @param options the specified Options 691 * @param arguments the command line arguments 692 * @param properties command line option name-value pairs 693 * @param stopAtNonOption if {@code true} an unrecognized argument stops the parsing and the remaining arguments 694 * are added to the {@link CommandLine}s args list. If {@code false} an unrecognized argument triggers a 695 * ParseException. 696 * 697 * @return the list of atomic option and value tokens 698 * @throws ParseException if there are any problems encountered while parsing the command line tokens. 699 */ 700 public CommandLine parse(final Options options, final String[] arguments, final Properties properties, final boolean stopAtNonOption) 701 throws ParseException { 702 this.options = options; 703 this.stopAtNonOption = stopAtNonOption; 704 skipParsing = false; 705 currentOption = null; 706 expectedOpts = new ArrayList<>(options.getRequiredOptions()); 707 // clear the data from the groups 708 for (final OptionGroup group : options.getOptionGroups()) { 709 group.setSelected(null); 710 } 711 cmd = CommandLine.builder().setDeprecatedHandler(deprecatedHandler).build(); 712 if (arguments != null) { 713 for (final String argument : arguments) { 714 handleToken(argument); 715 } 716 } 717 // check the arguments of the last option 718 checkRequiredArgs(); 719 // add the default options 720 handleProperties(properties); 721 checkRequiredOptions(); 722 return cmd; 723 } 724 725 /** 726 * Strips balanced leading and trailing quotes if the stripLeadingAndTrailingQuotes is set 727 * If stripLeadingAndTrailingQuotes is null, then do not strip 728 * 729 * @param token a string 730 * @return token with the quotes stripped (if set) 731 */ 732 private String stripLeadingAndTrailingQuotesDefaultOff(final String token) { 733 if (stripLeadingAndTrailingQuotes != null && stripLeadingAndTrailingQuotes) { 734 return Util.stripLeadingAndTrailingQuotes(token); 735 } 736 return token; 737 } 738 739 /** 740 * Strips balanced leading and trailing quotes if the stripLeadingAndTrailingQuotes is set 741 * If stripLeadingAndTrailingQuotes is null, then do not strip 742 * 743 * @param token a string 744 * @return token with the quotes stripped (if set) 745 */ 746 private String stripLeadingAndTrailingQuotesDefaultOn(final String token) { 747 if (stripLeadingAndTrailingQuotes == null || stripLeadingAndTrailingQuotes) { 748 return Util.stripLeadingAndTrailingQuotes(token); 749 } 750 return token; 751 } 752 753 /** 754 * Removes the option or its group from the list of expected elements. 755 * 756 * @param option 757 */ 758 private void updateRequiredOptions(final Option option) throws AlreadySelectedException { 759 if (option.isRequired()) { 760 expectedOpts.remove(option.getKey()); 761 } 762 763 // if the option is in an OptionGroup make that option the selected option of the group 764 if (options.getOptionGroup(option) != null) { 765 final OptionGroup group = options.getOptionGroup(option); 766 767 if (group.isRequired()) { 768 expectedOpts.remove(group); 769 } 770 771 group.setSelected(option); 772 } 773 } 774}