1 /* 2 Licensed to the Apache Software Foundation (ASF) under one or more 3 contributor license agreements. See the NOTICE file distributed with 4 this work for additional information regarding copyright ownership. 5 The ASF licenses this file to You under the Apache License, Version 2.0 6 (the "License"); you may not use this file except in compliance with 7 the License. You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18 package org.apache.commons.cli; 19 20 import java.io.Serializable; 21 import java.util.ArrayList; 22 import java.util.Iterator; 23 import java.util.LinkedList; 24 import java.util.List; 25 import java.util.Objects; 26 import java.util.Properties; 27 import java.util.function.Consumer; 28 import java.util.function.Supplier; 29 30 /** 31 * Represents list of arguments parsed against a {@link Options} descriptor. 32 * <p> 33 * It allows querying of a boolean {@link #hasOption(String opt)}, in addition to retrieving the 34 * {@link #getOptionValue(String opt)} for options requiring arguments. 35 * </p> 36 * <p> 37 * Additionally, any left-over or unrecognized arguments, are available for further processing. 38 * </p> 39 */ 40 public class CommandLine implements Serializable { 41 42 /** 43 * A nested builder class to create {@code CommandLine} instance using descriptive methods. 44 * 45 * @since 1.4 46 */ 47 public static final class Builder { 48 49 /** 50 * Prints an Option to {@link System#out}. 51 */ 52 static final Consumer<Option> DEPRECATED_HANDLER = o -> System.out.println(o.toDeprecatedString()); 53 54 /** The unrecognized options/arguments */ 55 private final List<String> args = new LinkedList<>(); 56 57 /** The processed options */ 58 private final List<Option> options = new ArrayList<>(); 59 60 /** 61 * Deprecated Option handler. 62 */ 63 private Consumer<Option> deprecatedHandler = DEPRECATED_HANDLER; 64 65 /** 66 * Adds left-over unrecognized option/argument. 67 * 68 * @param arg the unrecognized option/argument. 69 * 70 * @return this Builder instance for method chaining. 71 */ 72 public Builder addArg(final String arg) { 73 if (arg != null) { 74 args.add(arg); 75 } 76 return this; 77 } 78 79 /** 80 * Adds an option to the command line. The values of the option are stored. 81 * 82 * @param opt the processed option. 83 * 84 * @return this Builder instance for method chaining. 85 */ 86 public Builder addOption(final Option opt) { 87 if (opt != null) { 88 options.add(opt); 89 } 90 return this; 91 } 92 93 /** 94 * Creates the new instance. 95 * 96 * @return the new instance. 97 */ 98 public CommandLine build() { 99 return new CommandLine(args, options, deprecatedHandler); 100 } 101 102 /** 103 * Sets the deprecated option handler. 104 * 105 * @param deprecatedHandler the deprecated option handler. 106 * @return {@code this} instance. 107 * @since 1.7.0 108 */ 109 public Builder setDeprecatedHandler(final Consumer<Option> deprecatedHandler) { 110 this.deprecatedHandler = deprecatedHandler; 111 return this; 112 } 113 } 114 115 /** The serial version UID. */ 116 private static final long serialVersionUID = 1L; 117 118 /** 119 * Creates a new builder. 120 * 121 * @return a new builder. 122 * @since 1.7.0 123 */ 124 public static Builder builder() { 125 return new Builder(); 126 } 127 128 /** The unrecognized options/arguments */ 129 private final List<String> args; 130 131 /** The processed options */ 132 private final List<Option> options; 133 134 /** 135 * The deprecated option handler. 136 * <p> 137 * If you want to serialize this field, use a serialization proxy. 138 * </p> 139 */ 140 private final transient Consumer<Option> deprecatedHandler; 141 142 /** 143 * Creates a command line. 144 */ 145 protected CommandLine() { 146 this(new LinkedList<>(), new ArrayList<>(), Builder.DEPRECATED_HANDLER); 147 } 148 149 /** 150 * Creates a command line. 151 */ 152 private CommandLine(final List<String> args, final List<Option> options, final Consumer<Option> deprecatedHandler) { 153 this.args = Objects.requireNonNull(args, "args"); 154 this.options = Objects.requireNonNull(options, "options"); 155 this.deprecatedHandler = deprecatedHandler; 156 } 157 158 /** 159 * Adds left-over unrecognized option/argument. 160 * 161 * @param arg the unrecognized option/argument. 162 */ 163 protected void addArg(final String arg) { 164 if (arg != null) { 165 args.add(arg); 166 } 167 } 168 169 /** 170 * Adds an option to the command line. The values of the option are stored. 171 * 172 * @param opt the processed option. 173 */ 174 protected void addOption(final Option opt) { 175 if (opt != null) { 176 options.add(opt); 177 } 178 } 179 180 private <T> T get(final Supplier<T> supplier) { 181 return supplier == null ? null : supplier.get(); 182 } 183 184 /** 185 * Gets any left-over non-recognized options and arguments 186 * 187 * @return remaining items passed in but not parsed as a {@code List}. 188 */ 189 public List<String> getArgList() { 190 return args; 191 } 192 193 /** 194 * Gets any left-over non-recognized options and arguments 195 * 196 * @return remaining items passed in but not parsed as an array. 197 */ 198 public String[] getArgs() { 199 return args.toArray(Util.EMPTY_STRING_ARRAY); 200 } 201 202 /** 203 * Gets the {@code Object} type of this {@code Option}. 204 * 205 * @deprecated due to System.err message. Instead use getParsedOptionValue(char) 206 * @param opt the name of the option. 207 * @return the type of opt. 208 */ 209 @Deprecated 210 public Object getOptionObject(final char opt) { 211 return getOptionObject(String.valueOf(opt)); 212 } 213 214 /** 215 * Gets the {@code Object} type of this {@code Option}. 216 * 217 * @param opt the name of the option. 218 * @return the type of this {@code Option}. 219 * @deprecated due to System.err message. Instead use getParsedOptionValue(String) 220 */ 221 @Deprecated 222 public Object getOptionObject(final String opt) { 223 try { 224 return getParsedOptionValue(opt); 225 } catch (final ParseException pe) { 226 System.err.println("Exception found converting " + opt + " to desired type: " + pe.getMessage()); 227 return null; 228 } 229 } 230 231 /** 232 * Gets the map of values associated to the option. This is convenient for options specifying Java properties like 233 * <code>-Dparam1=value1 234 * -Dparam2=value2</code>. All odd numbered values are property keys 235 * and even numbered values are property values. If there are an odd number of values 236 * the last value is assumed to be a boolean flag and the value is "true". 237 * 238 * @param option name of the option. 239 * @return The Properties mapped by the option, never {@code null} even if the option doesn't exists. 240 * @since 1.5.0 241 */ 242 public Properties getOptionProperties(final Option option) { 243 final Properties props = new Properties(); 244 for (final Option processedOption : options) { 245 if (processedOption.equals(option)) { 246 processPropertiesFromValues(props, processedOption.getValuesList()); 247 } 248 } 249 return props; 250 } 251 252 /** 253 * Gets the map of values associated to the option. This is convenient for options specifying Java properties like 254 * <code>-Dparam1=value1 255 * -Dparam2=value2</code>. The first argument of the option is the key, and the 2nd argument is the value. If the option 256 * has only one argument ({@code -Dfoo}) it is considered as a boolean flag and the value is {@code "true"}. 257 * 258 * @param opt name of the option. 259 * @return The Properties mapped by the option, never {@code null} even if the option doesn't exists. 260 * @since 1.2 261 */ 262 public Properties getOptionProperties(final String opt) { 263 final Properties props = new Properties(); 264 for (final Option option : options) { 265 if (opt.equals(option.getOpt()) || opt.equals(option.getLongOpt())) { 266 processPropertiesFromValues(props, option.getValuesList()); 267 } 268 } 269 return props; 270 } 271 272 /** 273 * Gets an array of the processed {@link Option}s. 274 * 275 * @return an array of the processed {@link Option}s. 276 */ 277 public Option[] getOptions() { 278 return options.toArray(Option.EMPTY_ARRAY); 279 } 280 281 /** 282 * Gets the first argument, if any, of this option. 283 * 284 * @param opt the character name of the option. 285 * @return Value of the argument if option is set, and has an argument, otherwise null. 286 */ 287 public String getOptionValue(final char opt) { 288 return getOptionValue(String.valueOf(opt)); 289 } 290 291 /** 292 * Gets the argument, if any, of an option. 293 * 294 * @param opt character name of the option 295 * @param defaultValue is the default value to be returned if the option is not specified. 296 * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}. 297 */ 298 public String getOptionValue(final char opt, final String defaultValue) { 299 return getOptionValue(String.valueOf(opt), () -> defaultValue); 300 } 301 302 /** 303 * Gets the argument, if any, of an option. 304 * 305 * @param opt character name of the option 306 * @param defaultValue is a supplier for the default value to be returned if the option is not specified. 307 * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}. 308 * @since 1.7.0 309 */ 310 public String getOptionValue(final char opt, final Supplier<String> defaultValue) { 311 return getOptionValue(String.valueOf(opt), defaultValue); 312 } 313 314 /** 315 * Gets the first argument, if any, of this option. 316 * 317 * @param option the name of the option. 318 * @return Value of the argument if option is set, and has an argument, otherwise null. 319 * @since 1.5.0 320 */ 321 public String getOptionValue(final Option option) { 322 final String[] values = getOptionValues(option); 323 return values == null ? null : values[0]; 324 } 325 326 /** 327 * Gets the first argument, if any, of an option. 328 * 329 * @param option name of the option. 330 * @param defaultValue is the default value to be returned if the option is not specified. 331 * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}. 332 * @since 1.5.0 333 */ 334 public String getOptionValue(final Option option, final String defaultValue) { 335 return getOptionValue(option, () -> defaultValue); 336 } 337 338 /** 339 * Gets the first argument, if any, of an option. 340 * 341 * @param option name of the option. 342 * @param defaultValue is a supplier for the default value to be returned if the option is not specified. 343 * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}. 344 * @since 1.7.0 345 */ 346 public String getOptionValue(final Option option, final Supplier<String> defaultValue) { 347 final String answer = getOptionValue(option); 348 return answer != null ? answer : get(defaultValue); 349 } 350 351 /** 352 * Gets the first argument, if any, of this option. 353 * 354 * @param opt the name of the option. 355 * @return Value of the argument if option is set, and has an argument, otherwise null. 356 */ 357 public String getOptionValue(final String opt) { 358 return getOptionValue(resolveOption(opt)); 359 } 360 361 /** 362 * Gets the first argument, if any, of an option. 363 * 364 * @param opt name of the option. 365 * @param defaultValue is the default value to be returned if the option is not specified. 366 * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}. 367 */ 368 public String getOptionValue(final String opt, final String defaultValue) { 369 return getOptionValue(resolveOption(opt), () -> defaultValue); 370 } 371 372 /** 373 * Gets the first argument, if any, of an option. 374 * 375 * @param opt name of the option. 376 * @param defaultValue is a supplier for the default value to be returned if the option is not specified. 377 * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}. 378 * @since 1.7.0 379 */ 380 public String getOptionValue(final String opt, final Supplier<String> defaultValue) { 381 return getOptionValue(resolveOption(opt), defaultValue); 382 } 383 384 385 /** 386 * Gets the array of values, if any, of an option. 387 * 388 * @param opt character name of the option. 389 * @return Values of the argument if option is set, and has an argument, otherwise null. 390 */ 391 public String[] getOptionValues(final char opt) { 392 return getOptionValues(String.valueOf(opt)); 393 } 394 395 /** 396 * Gets the array of values, if any, of an option. 397 * 398 * @param option string name of the option. 399 * @return Values of the argument if option is set, and has an argument, otherwise null. 400 * @since 1.5.0 401 */ 402 public String[] getOptionValues(final Option option) { 403 if (option == null) { 404 return null; 405 } 406 if (option.isDeprecated()) { 407 handleDeprecated(option); 408 } 409 final List<String> values = new ArrayList<>(); 410 for (final Option processedOption : options) { 411 if (processedOption.equals(option)) { 412 values.addAll(processedOption.getValuesList()); 413 } 414 } 415 return values.isEmpty() ? null : values.toArray(Util.EMPTY_STRING_ARRAY); 416 } 417 418 /** 419 * Gets the array of values, if any, of an option. 420 * 421 * @param opt string name of the option. 422 * @return Values of the argument if option is set, and has an argument, otherwise null. 423 */ 424 public String[] getOptionValues(final String opt) { 425 return getOptionValues(resolveOption(opt)); 426 } 427 428 /** 429 * Gets a version of this {@code Option} converted to a particular type. 430 * 431 * @param opt the name of the option. 432 * @param <T> The return type for the method. 433 * @return the value parsed into a particular object. 434 * @throws ParseException if there are problems turning the option value into the desired type 435 * @see PatternOptionBuilder 436 * @since 1.5.0 437 */ 438 public <T> T getParsedOptionValue(final char opt) throws ParseException { 439 return getParsedOptionValue(String.valueOf(opt)); 440 } 441 442 /** 443 * Gets a version of this {@code Option} converted to a particular type. 444 * 445 * @param opt the name of the option. 446 * @param defaultValue the default value to return if opt is not set. 447 * @param <T> The return type for the method. 448 * @return the value parsed into a particular object. 449 * @throws ParseException if there are problems turning the option value into the desired type 450 * @see PatternOptionBuilder 451 * @since 1.7.0 452 */ 453 public <T> T getParsedOptionValue(final char opt, final Supplier<T> defaultValue) throws ParseException { 454 return getParsedOptionValue(String.valueOf(opt), defaultValue); 455 } 456 457 /** 458 * Gets a version of this {@code Option} converted to a particular type. 459 * 460 * @param opt the name of the option. 461 * @param defaultValue the default value to return if opt is not set. 462 * @param <T> The return type for the method. 463 * @return the value parsed into a particular object. 464 * @throws ParseException if there are problems turning the option value into the desired type 465 * @see PatternOptionBuilder 466 * @since 1.7.0 467 */ 468 public <T> T getParsedOptionValue(final char opt, final T defaultValue) throws ParseException { 469 return getParsedOptionValue(String.valueOf(opt), defaultValue); 470 } 471 472 /** 473 * Gets a version of this {@code Option} converted to a particular type. 474 * 475 * @param option the name of the option. 476 * @param <T> The return type for the method. 477 * @return the value parsed into a particular object. 478 * @throws ParseException if there are problems turning the option value into the desired type 479 * @see PatternOptionBuilder 480 * @since 1.5.0 481 */ 482 public <T> T getParsedOptionValue(final Option option) throws ParseException { 483 return getParsedOptionValue(option, () -> null); 484 } 485 486 /** 487 * Gets a version of this {@code Option} converted to a particular type. 488 * 489 * @param option the name of the option. 490 * @param defaultValue the default value to return if opt is not set. 491 * @param <T> The return type for the method. 492 * @return the value parsed into a particular object. 493 * @throws ParseException if there are problems turning the option value into the desired type 494 * @see PatternOptionBuilder 495 * @since 1.7.0 496 */ 497 @SuppressWarnings("unchecked") 498 public <T> T getParsedOptionValue(final Option option, final Supplier<T> defaultValue) throws ParseException { 499 if (option == null) { 500 return get(defaultValue); 501 } 502 final String res = getOptionValue(option); 503 try { 504 if (res == null) { 505 return get(defaultValue); 506 } 507 return (T) option.getConverter().apply(res); 508 } catch (final Throwable e) { 509 throw ParseException.wrap(e); 510 } 511 } 512 513 /** 514 * Gets a version of this {@code Option} converted to a particular type. 515 * 516 * @param option the name of the option. 517 * @param defaultValue the default value to return if opt is not set. 518 * @param <T> The return type for the method. 519 * @return the value parsed into a particular object. 520 * @throws ParseException if there are problems turning the option value into the desired type 521 * @see PatternOptionBuilder 522 * @since 1.7.0 523 */ 524 public <T> T getParsedOptionValue(final Option option, final T defaultValue) throws ParseException { 525 return getParsedOptionValue(option, () -> defaultValue); 526 } 527 528 /** 529 * Gets a version of this {@code Option} converted to a particular type. 530 * 531 * @param opt the name of the option. 532 * @param <T> The return type for the method. 533 * @return the value parsed into a particular object. 534 * @throws ParseException if there are problems turning the option value into the desired type 535 * @see PatternOptionBuilder 536 * @since 1.2 537 */ 538 public <T> T getParsedOptionValue(final String opt) throws ParseException { 539 return getParsedOptionValue(resolveOption(opt)); 540 } 541 542 /** 543 * Gets a version of this {@code Option} converted to a particular type. 544 * 545 * @param opt the name of the option. 546 * @param defaultValue the default value to return if opt is not set. 547 * @param <T> The return type for the method. 548 * @return the value parsed into a particular object. 549 * @throws ParseException if there are problems turning the option value into the desired type 550 * @see PatternOptionBuilder 551 * @since 1.7.0 552 */ 553 public <T> T getParsedOptionValue(final String opt, final Supplier<T> defaultValue) throws ParseException { 554 return getParsedOptionValue(resolveOption(opt), defaultValue); 555 } 556 557 /** 558 * Gets a version of this {@code Option} converted to a particular type. 559 * 560 * @param opt the name of the option. 561 * @param defaultValue the default value to return if opt is not set. 562 * @param <T> The return type for the method. 563 * @return the value parsed into a particular object. 564 * @throws ParseException if there are problems turning the option value into the desired type 565 * @see PatternOptionBuilder 566 * @since 1.7.0 567 */ 568 public <T> T getParsedOptionValue(final String opt, final T defaultValue) throws ParseException { 569 return getParsedOptionValue(resolveOption(opt), defaultValue); 570 } 571 572 /** 573 * Handles deprecated options. 574 * 575 * @param option a deprecated option. 576 */ 577 private void handleDeprecated(final Option option) { 578 if (deprecatedHandler != null) { 579 deprecatedHandler.accept(option); 580 } 581 } 582 583 /** 584 * jkeyes - commented out until it is implemented properly 585 * <p> 586 * Dump state, suitable for debugging. 587 * </p> 588 * 589 * @return Stringified form of this object. 590 */ 591 592 /* 593 * public String toString() { StringBuilder buf = new StringBuilder(); 594 * 595 * buf.append("[ CommandLine: [ options: "); buf.append(options.toString()); buf.append(" ] [ args: "); 596 * buf.append(args.toString()); buf.append(" ] ]"); 597 * 598 * return buf.toString(); } 599 */ 600 601 /** 602 * Tests to see if an option has been set. 603 * 604 * @param opt character name of the option. 605 * @return true if set, false if not. 606 */ 607 public boolean hasOption(final char opt) { 608 return hasOption(String.valueOf(opt)); 609 } 610 611 /** 612 * Tests to see if an option has been set. 613 * 614 * @param opt the option to check. 615 * @return true if set, false if not. 616 * @since 1.5.0 617 */ 618 public boolean hasOption(final Option opt) { 619 final boolean result = options.contains(opt); 620 if (result && opt.isDeprecated()) { 621 handleDeprecated(opt); 622 } 623 return result; 624 } 625 626 /** 627 * Tests to see if an option has been set. 628 * 629 * @param opt Short name of the option. 630 * @return true if set, false if not. 631 */ 632 public boolean hasOption(final String opt) { 633 return hasOption(resolveOption(opt)); 634 } 635 636 /** 637 * Returns an iterator over the Option members of CommandLine. 638 * 639 * @return an {@code Iterator} over the processed {@link Option} members of this {@link CommandLine}. 640 */ 641 public Iterator<Option> iterator() { 642 return options.iterator(); 643 } 644 645 /** 646 * Parses a list of values as properties. All odd numbered values are property keys 647 * and even numbered values are property values. If there are an odd number of values 648 * the last value is assumed to be a boolean with a value of "true". 649 * @param props the properties to update. 650 * @param values the list of values to parse. 651 */ 652 private void processPropertiesFromValues(final Properties props, final List<String> values) { 653 for (int i = 0; i < values.size(); i += 2) { 654 if (i + 1 < values.size()) { 655 props.put(values.get(i), values.get(i + 1)); 656 } else { 657 props.put(values.get(i), "true"); 658 } 659 } 660 } 661 662 /** 663 * Retrieves the option object given the long or short option as a String 664 * 665 * @param opt short or long name of the option, may be null. 666 * @return Canonicalized option. 667 */ 668 private Option resolveOption(final String opt) { 669 final String actual = Util.stripLeadingHyphens(opt); 670 if (actual != null) { 671 for (final Option option : options) { 672 if (actual.equals(option.getOpt()) || actual.equals(option.getLongOpt())) { 673 return option; 674 } 675 } 676 } 677 return null; 678 } 679 }