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 */ CommandLineProcessor(ISdkLog logger, String[][] actions)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 "This help.", 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 */ acceptLackOfVerb()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. */ isVerbose()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. */ isSilent()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. */ isHelpRequested()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. */ getVerb()152 public String getVerb() { 153 return mVerbRequested; 154 } 155 156 /** Returns the direct object name from the command-line. Can be null. */ getDirectObject()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 */ getValue(String verb, String directObject, String longFlagName)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 */ setValue(String verb, String directObject, String longFlagName, Object value)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 */ parseArgs(String[] args)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 */ findLongArg(String verb, String directObject, String longName)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 */ findShortArg(String verb, String directObject, String shortName)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 */ printHelpAndExit(String errorFormat, Object... args)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 */ printHelpAndExitForAction(String verb, String directObject, String errorFormat, Object... args)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 */ listOptions(String verb, String directObject)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 needsExtra()567 public boolean needsExtra() { 568 return false; 569 } 570 @Override process(Arg arg, String extra)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 needsExtra()581 public boolean needsExtra() { 582 return true; 583 } 584 @Override process(Arg arg, String extra)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 needsExtra()599 public boolean needsExtra() { 600 return true; 601 } 602 @Override process(Arg arg, String extra)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 needsExtra()625 public boolean needsExtra() { 626 return true; 627 } 628 @Override process(Arg arg, String extra)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 */ needsExtra()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 */ process(Arg arg, String extra)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 */ Arg(Mode mode, boolean mandatory, String verb, String directObject, String shortName, String longName, String description, Object defaultValue)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. */ isMandatory()715 public boolean isMandatory() { 716 return mMandatory; 717 } 718 719 /** Returns the 1-letter short name of the argument, e.g. -v. */ getShortArg()720 public String getShortArg() { 721 return mShortName; 722 } 723 724 /** Returns the long name of the argument, e.g. --verbose. */ getLongArg()725 public String getLongArg() { 726 return mLongName; 727 } 728 729 /** Returns the description. Never null. */ getDescription()730 public String getDescription() { 731 return mDescription; 732 } 733 734 /** Returns the verb for that argument. Never null. */ getVerb()735 public String getVerb() { 736 return mVerb; 737 } 738 739 /** Returns the direct Object for that argument. Never null, but can be empty string. */ getDirectObject()740 public String getDirectObject() { 741 return mDirectObject; 742 } 743 744 /** Returns the default value. Can be null. */ getDefaultValue()745 public Object getDefaultValue() { 746 return mDefaultValue; 747 } 748 749 /** Returns the current value. Initially set to the default value. Can be null. */ getCurrentValue()750 public Object getCurrentValue() { 751 return mCurrentValue; 752 } 753 754 /** Sets the current value. Can be null. */ setCurrentValue(Object currentValue)755 public void setCurrentValue(Object currentValue) { 756 mCurrentValue = currentValue; 757 } 758 759 /** Returns the argument mode (type + process method). Never null. */ getMode()760 public Mode getMode() { 761 return mMode; 762 } 763 764 /** Returns true if the argument has been used on the command line. */ isInCommandLine()765 public boolean isInCommandLine() { 766 return mInCommandLine; 767 } 768 769 /** Sets if the argument has been used on the command line. */ setInCommandLine(boolean inCommandLine)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 */ define(Mode mode, boolean mandatory, String verb, String directObject, String shortName, String longName, String description, Object defaultValue)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 */ exit()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 */ stdout(String format, Object...args)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 */ stderr(String format, Object...args)829 protected void stderr(String format, Object...args) { 830 mLog.error(null, format, args); 831 } 832 } 833