1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.sdkmanager; 18 19 import com.android.sdklib.ISdkLog; 20 21 import java.util.HashMap; 22 import java.util.Map.Entry; 23 24 /** 25 * Parses the command-line and stores flags needed or requested. 26 * <p/> 27 * This is a base class. To be useful you want to: 28 * <ul> 29 * <li>override it. 30 * <li>pass an action array to the constructor. 31 * <li>define flags for your actions. 32 * </ul> 33 * <p/> 34 * To use, call {@link #parseArgs(String[])} and then 35 * call {@link #getValue(String, String, String)}. 36 */ 37 class CommandLineProcessor { 38 39 /* 40 * Steps needed to add a new action: 41 * - Each action is defined as a "verb object" followed by parameters. 42 * - Either reuse a VERB_ constant or define a new one. 43 * - Either reuse an OBJECT_ constant or define a new one. 44 * - Add a new entry to mAction with a one-line help summary. 45 * - In the constructor, add a define() call for each parameter (either mandatory 46 * or optional) for the given action. 47 */ 48 49 /** Internal verb name for internally hidden flags. */ 50 public final static String GLOBAL_FLAG_VERB = "@@internal@@"; 51 52 /** String to use when the verb doesn't need any object. */ 53 public final static String NO_VERB_OBJECT = ""; 54 55 /** The global help flag. */ 56 public static final String KEY_HELP = "help"; 57 /** The global verbose flag. */ 58 public static final String KEY_VERBOSE = "verbose"; 59 /** The global silent flag. */ 60 public static final String KEY_SILENT = "silent"; 61 62 /** Verb requested by the user. Null if none specified, which will be an error. */ 63 private String mVerbRequested; 64 /** Direct object requested by the user. Can be null. */ 65 private String mDirectObjectRequested; 66 67 /** 68 * Action definitions. 69 * <p/> 70 * This list serves two purposes: first it is used to know which verb/object 71 * actions are acceptable on the command-line; second it provides a summary 72 * for each action that is printed in the help. 73 * <p/> 74 * Each entry is a string array with: 75 * <ul> 76 * <li> the verb. 77 * <li> a direct object (use {@link #NO_VERB_OBJECT} if there's no object). 78 * <li> a description. 79 * <li> an alternate form for the object (e.g. plural). 80 * </ul> 81 */ 82 private final String[][] mActions; 83 84 private static final int ACTION_VERB_INDEX = 0; 85 private static final int ACTION_OBJECT_INDEX = 1; 86 private static final int ACTION_DESC_INDEX = 2; 87 private static final int ACTION_ALT_OBJECT_INDEX = 3; 88 89 /** 90 * The map of all defined arguments. 91 * <p/> 92 * The key is a string "verb/directObject/longName". 93 */ 94 private final HashMap<String, Arg> mArguments = new HashMap<String, Arg>(); 95 /** Logger */ 96 private final ISdkLog mLog; 97 98 /** 99 * Constructs a new command-line processor. 100 * 101 * @param logger An SDK logger object. Must not be null. 102 * @param actions The list of actions recognized on the command-line. 103 * See the javadoc of {@link #mActions} for more details. 104 * 105 * @see #mActions 106 */ 107 public CommandLineProcessor(ISdkLog logger, String[][] actions) { 108 mLog = logger; 109 mActions = actions; 110 111 define(Mode.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "v", KEY_VERBOSE, 112 "Verbose mode: errors, warnings and informational messages are printed.", 113 false); 114 define(Mode.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "s", KEY_SILENT, 115 "Silent mode: only errors are printed out.", 116 false); 117 define(Mode.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "h", KEY_HELP, 118 "Help on a specific command.", 119 false); 120 } 121 122 /** 123 * Indicates if this command-line can work when no verb is specified. 124 * The default is false, which generates an error when no verb/object is specified. 125 * Derived implementations can set this to true if they can deal with a lack 126 * of verb/action. 127 */ 128 public boolean acceptLackOfVerb() { 129 return false; 130 } 131 132 133 //------------------ 134 // Helpers to get flags values 135 136 /** Helper that returns true if --verbose was requested. */ 137 public boolean isVerbose() { 138 return ((Boolean) getValue(GLOBAL_FLAG_VERB, NO_VERB_OBJECT, KEY_VERBOSE)).booleanValue(); 139 } 140 141 /** Helper that returns true if --silent was requested. */ 142 public boolean isSilent() { 143 return ((Boolean) getValue(GLOBAL_FLAG_VERB, NO_VERB_OBJECT, KEY_SILENT)).booleanValue(); 144 } 145 146 /** Helper that returns true if --help was requested. */ 147 public boolean isHelpRequested() { 148 return ((Boolean) getValue(GLOBAL_FLAG_VERB, NO_VERB_OBJECT, KEY_HELP)).booleanValue(); 149 } 150 151 /** Returns the verb name from the command-line. Can be null. */ 152 public String getVerb() { 153 return mVerbRequested; 154 } 155 156 /** Returns the direct object name from the command-line. Can be null. */ 157 public String getDirectObject() { 158 return mDirectObjectRequested; 159 } 160 161 //------------------ 162 163 /** 164 * Raw access to parsed parameter values. 165 * <p/> 166 * The default is to scan all parameters. Parameters that have been explicitly set on the 167 * command line are returned first. Otherwise one with a non-null value is returned. 168 * <p/> 169 * Both a verb and a direct object filter can be specified. When they are non-null they limit 170 * the scope of the search. 171 * <p/> 172 * If nothing has been found, return the last default value seen matching the filter. 173 * 174 * @param verb The verb name, including {@link #GLOBAL_FLAG_VERB}. If null, all possible 175 * verbs that match the direct object condition will be examined and the first 176 * value set will be used. 177 * @param directObject The direct object name, including {@link #NO_VERB_OBJECT}. If null, 178 * all possible direct objects that match the verb condition will be examined and 179 * the first value set will be used. 180 * @param longFlagName The long flag name for the given action. Mandatory. Cannot be null. 181 * @return The current value object stored in the parameter, which depends on the argument mode. 182 */ 183 public Object getValue(String verb, String directObject, String longFlagName) { 184 185 if (verb != null && directObject != null) { 186 String key = verb + "/" + directObject + "/" + longFlagName; 187 Arg arg = mArguments.get(key); 188 return arg.getCurrentValue(); 189 } 190 191 Object lastDefault = null; 192 for (Arg arg : mArguments.values()) { 193 if (arg.getLongArg().equals(longFlagName)) { 194 if (verb == null || arg.getVerb().equals(verb)) { 195 if (directObject == null || arg.getDirectObject().equals(directObject)) { 196 if (arg.isInCommandLine()) { 197 return arg.getCurrentValue(); 198 } 199 if (arg.getCurrentValue() != null) { 200 lastDefault = arg.getCurrentValue(); 201 } 202 } 203 } 204 } 205 } 206 207 return lastDefault; 208 } 209 210 /** 211 * Internal setter for raw parameter value. 212 * @param verb The verb name, including {@link #GLOBAL_FLAG_VERB}. 213 * @param directObject The direct object name, including {@link #NO_VERB_OBJECT}. 214 * @param longFlagName The long flag name for the given action. 215 * @param value The new current value object stored in the parameter, which depends on the 216 * argument mode. 217 */ 218 protected void setValue(String verb, String directObject, String longFlagName, Object value) { 219 String key = verb + "/" + directObject + "/" + longFlagName; 220 Arg arg = mArguments.get(key); 221 arg.setCurrentValue(value); 222 } 223 224 /** 225 * Parses the command-line arguments. 226 * <p/> 227 * This method will exit and not return if a parsing error arise. 228 * 229 * @param args The arguments typically received by a main method. 230 */ 231 public void parseArgs(String[] args) { 232 String needsHelp = null; 233 String verb = null; 234 String directObject = null; 235 236 try { 237 int n = args.length; 238 for (int i = 0; i < n; i++) { 239 Arg arg = null; 240 String a = args[i]; 241 if (a.startsWith("--")) { 242 arg = findLongArg(verb, directObject, a.substring(2)); 243 } else if (a.startsWith("-")) { 244 arg = findShortArg(verb, directObject, a.substring(1)); 245 } 246 247 // No matching argument name found 248 if (arg == null) { 249 // Does it looks like a dashed parameter? 250 if (a.startsWith("-")) { 251 if (verb == null || directObject == null) { 252 // It looks like a dashed parameter and we don't have a a verb/object 253 // set yet, the parameter was just given too early. 254 255 needsHelp = String.format( 256 "Flag '%1$s' is not a valid global flag. Did you mean to specify it after the verb/object name?", 257 a); 258 return; 259 } else { 260 // It looks like a dashed parameter and but it is unknown by this 261 // verb-object combination 262 263 needsHelp = String.format( 264 "Flag '%1$s' is not valid for '%2$s %3$s'.", 265 a, verb, directObject); 266 return; 267 } 268 } 269 270 if (verb == null) { 271 // Fill verb first. Find it. 272 for (String[] actionDesc : mActions) { 273 if (actionDesc[ACTION_VERB_INDEX].equals(a)) { 274 verb = a; 275 break; 276 } 277 } 278 279 // Error if it was not a valid verb 280 if (verb == null) { 281 needsHelp = String.format( 282 "Expected verb after global parameters but found '%1$s' instead.", 283 a); 284 return; 285 } 286 287 } else if (directObject == null) { 288 // Then fill the direct object. Find it. 289 for (String[] actionDesc : mActions) { 290 if (actionDesc[ACTION_VERB_INDEX].equals(verb)) { 291 if (actionDesc[ACTION_OBJECT_INDEX].equals(a)) { 292 directObject = a; 293 break; 294 } else if (actionDesc.length > ACTION_ALT_OBJECT_INDEX && 295 actionDesc[ACTION_ALT_OBJECT_INDEX].equals(a)) { 296 // if the alternate form exist and is used, we internally 297 // only memorize the default direct object form. 298 directObject = actionDesc[ACTION_OBJECT_INDEX]; 299 break; 300 } 301 } 302 } 303 304 // Error if it was not a valid object for that verb 305 if (directObject == null) { 306 needsHelp = String.format( 307 "Expected verb after global parameters but found '%1$s' instead.", 308 a); 309 return; 310 311 } 312 } 313 } else if (arg != null) { 314 // This argument was present on the command line 315 arg.setInCommandLine(true); 316 317 // Process keyword 318 String error = null; 319 if (arg.getMode().needsExtra()) { 320 if (++i >= n) { 321 needsHelp = String.format("Missing argument for flag %1$s.", a); 322 return; 323 } 324 325 error = arg.getMode().process(arg, args[i]); 326 } else { 327 error = arg.getMode().process(arg, null); 328 329 // If we just toggled help, we want to exit now without printing any error. 330 // We do this test here only when a Boolean flag is toggled since booleans 331 // are the only flags that don't take parameters and help is a boolean. 332 if (isHelpRequested()) { 333 printHelpAndExit(null); 334 // The call above should terminate however in unit tests we override 335 // it so we still need to return here. 336 return; 337 } 338 } 339 340 if (error != null) { 341 needsHelp = String.format("Invalid usage for flag %1$s: %2$s.", a, error); 342 return; 343 } 344 } 345 } 346 347 if (needsHelp == null) { 348 if (verb == null && !acceptLackOfVerb()) { 349 needsHelp = "Missing verb name."; 350 } else if (verb != null) { 351 if (directObject == null) { 352 // Make sure this verb has an optional direct object 353 for (String[] actionDesc : mActions) { 354 if (actionDesc[ACTION_VERB_INDEX].equals(verb) && 355 actionDesc[ACTION_OBJECT_INDEX].equals(NO_VERB_OBJECT)) { 356 directObject = NO_VERB_OBJECT; 357 break; 358 } 359 } 360 361 if (directObject == null) { 362 needsHelp = String.format("Missing object name for verb '%1$s'.", verb); 363 return; 364 } 365 } 366 367 // Validate that all mandatory arguments are non-null for this action 368 String missing = null; 369 boolean plural = false; 370 for (Entry<String, Arg> entry : mArguments.entrySet()) { 371 Arg arg = entry.getValue(); 372 if (arg.getVerb().equals(verb) && 373 arg.getDirectObject().equals(directObject)) { 374 if (arg.isMandatory() && arg.getCurrentValue() == null) { 375 if (missing == null) { 376 missing = "--" + arg.getLongArg(); 377 } else { 378 missing += ", --" + arg.getLongArg(); 379 plural = true; 380 } 381 } 382 } 383 } 384 385 if (missing != null) { 386 needsHelp = String.format( 387 "The %1$s %2$s must be defined for action '%3$s %4$s'", 388 plural ? "parameters" : "parameter", 389 missing, 390 verb, 391 directObject); 392 } 393 394 mVerbRequested = verb; 395 mDirectObjectRequested = directObject; 396 } 397 } 398 } finally { 399 if (needsHelp != null) { 400 printHelpAndExitForAction(verb, directObject, needsHelp); 401 } 402 } 403 } 404 405 /** 406 * Finds an {@link Arg} given an action name and a long flag name. 407 * @return The {@link Arg} found or null. 408 */ 409 protected Arg findLongArg(String verb, String directObject, String longName) { 410 if (verb == null) { 411 verb = GLOBAL_FLAG_VERB; 412 } 413 if (directObject == null) { 414 directObject = NO_VERB_OBJECT; 415 } 416 String key = verb + "/" + directObject + "/" + longName; 417 return mArguments.get(key); 418 } 419 420 /** 421 * Finds an {@link Arg} given an action name and a short flag name. 422 * @return The {@link Arg} found or null. 423 */ 424 protected Arg findShortArg(String verb, String directObject, String shortName) { 425 if (verb == null) { 426 verb = GLOBAL_FLAG_VERB; 427 } 428 if (directObject == null) { 429 directObject = NO_VERB_OBJECT; 430 } 431 432 for (Entry<String, Arg> entry : mArguments.entrySet()) { 433 Arg arg = entry.getValue(); 434 if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) { 435 if (shortName.equals(arg.getShortArg())) { 436 return arg; 437 } 438 } 439 } 440 441 return null; 442 } 443 444 /** 445 * Prints the help/usage and exits. 446 * 447 * @param errorFormat Optional error message to print prior to usage using String.format 448 * @param args Arguments for String.format 449 */ 450 public void printHelpAndExit(String errorFormat, Object... args) { 451 printHelpAndExitForAction(null /*verb*/, null /*directObject*/, errorFormat, args); 452 } 453 454 /** 455 * Prints the help/usage and exits. 456 * 457 * @param verb If null, displays help for all verbs. If not null, display help only 458 * for that specific verb. In all cases also displays general usage and action list. 459 * @param directObject If null, displays help for all verb objects. 460 * If not null, displays help only for that specific action 461 * In all cases also display general usage and action list. 462 * @param errorFormat Optional error message to print prior to usage using String.format 463 * @param args Arguments for String.format 464 */ 465 public void printHelpAndExitForAction(String verb, String directObject, 466 String errorFormat, Object... args) { 467 if (errorFormat != null) { 468 stderr(errorFormat, args); 469 } 470 471 /* 472 * usage should fit in 80 columns 473 * 12345678901234567890123456789012345678901234567890123456789012345678901234567890 474 */ 475 stdout("\n" + 476 "Usage:\n" + 477 " android [global options] action [action options]\n" + 478 "\n" + 479 "Global options:"); 480 listOptions(GLOBAL_FLAG_VERB, NO_VERB_OBJECT); 481 482 if (verb == null || directObject == null) { 483 stdout("\nValid actions are composed of a verb and an optional direct object:"); 484 for (String[] action : mActions) { 485 486 stdout("- %1$6s %2$-12s: %3$s", 487 action[ACTION_VERB_INDEX], 488 action[ACTION_OBJECT_INDEX], 489 action[ACTION_DESC_INDEX]); 490 } 491 } 492 493 for (String[] action : mActions) { 494 if (verb == null || verb.equals(action[ACTION_VERB_INDEX])) { 495 if (directObject == null || directObject.equals(action[ACTION_OBJECT_INDEX])) { 496 stdout("\nAction \"%1$s %2$s\":", 497 action[ACTION_VERB_INDEX], 498 action[ACTION_OBJECT_INDEX]); 499 stdout(" %1$s", action[ACTION_DESC_INDEX]); 500 stdout("Options:"); 501 listOptions(action[ACTION_VERB_INDEX], action[ACTION_OBJECT_INDEX]); 502 } 503 } 504 } 505 506 exit(); 507 } 508 509 /** 510 * Internal helper to print all the option flags for a given action name. 511 */ 512 protected void listOptions(String verb, String directObject) { 513 int numOptions = 0; 514 for (Entry<String, Arg> entry : mArguments.entrySet()) { 515 Arg arg = entry.getValue(); 516 if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) { 517 518 String value = ""; 519 String required = ""; 520 if (arg.isMandatory()) { 521 required = " [required]"; 522 523 } else { 524 if (arg.getDefaultValue() instanceof String[]) { 525 for (String v : (String[]) arg.getDefaultValue()) { 526 if (value.length() > 0) { 527 value += ", "; 528 } 529 value += v; 530 } 531 } else if (arg.getDefaultValue() != null) { 532 Object v = arg.getDefaultValue(); 533 if (arg.getMode() != Mode.BOOLEAN || v.equals(Boolean.TRUE)) { 534 value = v.toString(); 535 } 536 } 537 if (value.length() > 0) { 538 value = " [Default: " + value + "]"; 539 } 540 } 541 542 stdout(" -%1$s %2$-10s %3$s%4$s%5$s", 543 arg.getShortArg(), 544 "--" + arg.getLongArg(), 545 arg.getDescription(), 546 value, 547 required); 548 numOptions++; 549 } 550 } 551 552 if (numOptions == 0) { 553 stdout(" No options"); 554 } 555 } 556 557 //---- 558 559 /** 560 * The mode of an argument specifies the type of variable it represents, 561 * whether an extra parameter is required after the flag and how to parse it. 562 */ 563 static enum Mode { 564 /** Argument value is a Boolean. Default value is a Boolean. */ 565 BOOLEAN { 566 @Override 567 public boolean needsExtra() { 568 return false; 569 } 570 @Override 571 public String process(Arg arg, String extra) { 572 // Toggle the current value 573 arg.setCurrentValue(! ((Boolean) arg.getCurrentValue()).booleanValue()); 574 return null; 575 } 576 }, 577 578 /** Argument value is an Integer. Default value is an Integer. */ 579 INTEGER { 580 @Override 581 public boolean needsExtra() { 582 return true; 583 } 584 @Override 585 public String process(Arg arg, String extra) { 586 try { 587 arg.setCurrentValue(Integer.parseInt(extra)); 588 return null; 589 } catch (NumberFormatException e) { 590 return String.format("Failed to parse '%1$s' as an integer: %2%s", 591 extra, e.getMessage()); 592 } 593 } 594 }, 595 596 /** Argument value is a String. Default value is a String[]. */ 597 ENUM { 598 @Override 599 public boolean needsExtra() { 600 return true; 601 } 602 @Override 603 public String process(Arg arg, String extra) { 604 StringBuilder desc = new StringBuilder(); 605 String[] values = (String[]) arg.getDefaultValue(); 606 for (String value : values) { 607 if (value.equals(extra)) { 608 arg.setCurrentValue(extra); 609 return null; 610 } 611 612 if (desc.length() != 0) { 613 desc.append(", "); 614 } 615 desc.append(value); 616 } 617 618 return String.format("'%1$s' is not one of %2$s", extra, desc.toString()); 619 } 620 }, 621 622 /** Argument value is a String. Default value is a null. */ 623 STRING { 624 @Override 625 public boolean needsExtra() { 626 return true; 627 } 628 @Override 629 public String process(Arg arg, String extra) { 630 arg.setCurrentValue(extra); 631 return null; 632 } 633 }; 634 635 /** 636 * Returns true if this mode requires an extra parameter. 637 */ 638 public abstract boolean needsExtra(); 639 640 /** 641 * Processes the flag for this argument. 642 * 643 * @param arg The argument being processed. 644 * @param extra The extra parameter. Null if {@link #needsExtra()} returned false. 645 * @return An error string or null if there's no error. 646 */ 647 public abstract String process(Arg arg, String extra); 648 } 649 650 /** 651 * An argument accepted by the command-line, also called "a flag". 652 * Arguments must have a short version (one letter), a long version name and a description. 653 * They can have a default value, or it can be null. 654 * Depending on the {@link Mode}, the default value can be a Boolean, an Integer, a String 655 * or a String array (in which case the first item is the current by default.) 656 */ 657 static class Arg { 658 /** Verb for that argument. Never null. */ 659 private final String mVerb; 660 /** Direct Object for that argument. Never null, but can be empty string. */ 661 private final String mDirectObject; 662 /** The 1-letter short name of the argument, e.g. -v. */ 663 private final String mShortName; 664 /** The long name of the argument, e.g. --verbose. */ 665 private final String mLongName; 666 /** A description. Never null. */ 667 private final String mDescription; 668 /** A default value. Can be null. */ 669 private final Object mDefaultValue; 670 /** The argument mode (type + process method). Never null. */ 671 private final Mode mMode; 672 /** True if this argument is mandatory for this verb/directobject. */ 673 private final boolean mMandatory; 674 /** Current value. Initially set to the default value. */ 675 private Object mCurrentValue; 676 /** True if the argument has been used on the command line. */ 677 private boolean mInCommandLine; 678 679 /** 680 * Creates a new argument flag description. 681 * 682 * @param mode The {@link Mode} for the argument. 683 * @param mandatory True if this argument is mandatory for this action. 684 * @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG. 685 * @param shortName The one-letter short argument name. Cannot be empty nor null. 686 * @param longName The long argument name. Cannot be empty nor null. 687 * @param description The description. Cannot be null. 688 * @param defaultValue The default value (or values), which depends on the selected {@link Mode}. 689 */ 690 public Arg(Mode mode, 691 boolean mandatory, 692 String verb, 693 String directObject, 694 String shortName, 695 String longName, 696 String description, 697 Object defaultValue) { 698 mMode = mode; 699 mMandatory = mandatory; 700 mVerb = verb; 701 mDirectObject = directObject; 702 mShortName = shortName; 703 mLongName = longName; 704 mDescription = description; 705 mDefaultValue = defaultValue; 706 mInCommandLine = false; 707 if (defaultValue instanceof String[]) { 708 mCurrentValue = ((String[])defaultValue)[0]; 709 } else { 710 mCurrentValue = mDefaultValue; 711 } 712 } 713 714 /** Return true if this argument is mandatory for this verb/directobject. */ 715 public boolean isMandatory() { 716 return mMandatory; 717 } 718 719 /** Returns the 1-letter short name of the argument, e.g. -v. */ 720 public String getShortArg() { 721 return mShortName; 722 } 723 724 /** Returns the long name of the argument, e.g. --verbose. */ 725 public String getLongArg() { 726 return mLongName; 727 } 728 729 /** Returns the description. Never null. */ 730 public String getDescription() { 731 return mDescription; 732 } 733 734 /** Returns the verb for that argument. Never null. */ 735 public String getVerb() { 736 return mVerb; 737 } 738 739 /** Returns the direct Object for that argument. Never null, but can be empty string. */ 740 public String getDirectObject() { 741 return mDirectObject; 742 } 743 744 /** Returns the default value. Can be null. */ 745 public Object getDefaultValue() { 746 return mDefaultValue; 747 } 748 749 /** Returns the current value. Initially set to the default value. Can be null. */ 750 public Object getCurrentValue() { 751 return mCurrentValue; 752 } 753 754 /** Sets the current value. Can be null. */ 755 public void setCurrentValue(Object currentValue) { 756 mCurrentValue = currentValue; 757 } 758 759 /** Returns the argument mode (type + process method). Never null. */ 760 public Mode getMode() { 761 return mMode; 762 } 763 764 /** Returns true if the argument has been used on the command line. */ 765 public boolean isInCommandLine() { 766 return mInCommandLine; 767 } 768 769 /** Sets if the argument has been used on the command line. */ 770 public void setInCommandLine(boolean inCommandLine) { 771 mInCommandLine = inCommandLine; 772 } 773 } 774 775 /** 776 * Internal helper to define a new argument for a give action. 777 * 778 * @param mode The {@link Mode} for the argument. 779 * @param verb The verb name. Can be #INTERNAL_VERB. 780 * @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG. 781 * @param shortName The one-letter short argument name. Cannot be empty nor null. 782 * @param longName The long argument name. Cannot be empty nor null. 783 * @param description The description. Cannot be null. 784 * @param defaultValue The default value (or values), which depends on the selected {@link Mode}. 785 */ 786 protected void define(Mode mode, 787 boolean mandatory, 788 String verb, 789 String directObject, 790 String shortName, String longName, 791 String description, Object defaultValue) { 792 assert(!(mandatory && mode == Mode.BOOLEAN)); // a boolean mode cannot be mandatory 793 794 if (directObject == null) { 795 directObject = NO_VERB_OBJECT; 796 } 797 798 String key = verb + "/" + directObject + "/" + longName; 799 mArguments.put(key, new Arg(mode, mandatory, 800 verb, directObject, shortName, longName, description, defaultValue)); 801 } 802 803 /** 804 * Exits in case of error. 805 * This is protected so that it can be overridden in unit tests. 806 */ 807 protected void exit() { 808 System.exit(1); 809 } 810 811 /** 812 * Prints a line to stdout. 813 * This is protected so that it can be overridden in unit tests. 814 * 815 * @param format The string to be formatted. Cannot be null. 816 * @param args Format arguments. 817 */ 818 protected void stdout(String format, Object...args) { 819 mLog.printf(format + "\n", args); 820 } 821 822 /** 823 * Prints a line to stderr. 824 * This is protected so that it can be overridden in unit tests. 825 * 826 * @param format The string to be formatted. Cannot be null. 827 * @param args Format arguments. 828 */ 829 protected void stderr(String format, Object...args) { 830 mLog.error(null, format, args); 831 } 832 } 833