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 "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 */ 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 errorMsg = 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 errorMsg = 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 but it is unknown by this 261 // verb-object combination 262 263 errorMsg = 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 errorMsg = 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 errorMsg = String.format( 307 "Expected verb after global parameters but found '%1$s' instead.", 308 a); 309 return; 310 311 } 312 } else { 313 // The argument is not a dashed parameter and we already 314 // have a verb/object. Must be some extra unknown argument. 315 errorMsg = String.format( 316 "Argument '%1$s' is not recognized.", 317 a); 318 } 319 } else if (arg != null) { 320 // This argument was present on the command line 321 arg.setInCommandLine(true); 322 323 // Process keyword 324 String error = null; 325 if (arg.getMode().needsExtra()) { 326 if (++i >= n) { 327 errorMsg = String.format("Missing argument for flag %1$s.", a); 328 return; 329 } 330 String b = args[i]; 331 332 Arg dummyArg = null; 333 if (b.startsWith("--")) { 334 dummyArg = findLongArg(verb, directObject, b.substring(2)); 335 } else if (b.startsWith("-")) { 336 dummyArg = findShortArg(verb, directObject, b.substring(1)); 337 } 338 if (dummyArg != null) { 339 errorMsg = String.format( 340 "Oops, it looks like you didn't provide an argument for '%1$s'.\n'%2$s' was found instead.", 341 a, b); 342 return; 343 } 344 345 error = arg.getMode().process(arg, b); 346 } else { 347 error = arg.getMode().process(arg, null); 348 349 if (isHelpRequested()) { 350 // The --help flag was requested. We'll continue the usual processing 351 // so that we can find the optional verb/object words. Those will be 352 // used to print specific help. 353 // Setting a non-null error message triggers printing the help, however 354 // there is no specific error to print. 355 errorMsg = ""; 356 } 357 } 358 359 if (error != null) { 360 errorMsg = String.format("Invalid usage for flag %1$s: %2$s.", a, error); 361 return; 362 } 363 } 364 } 365 366 if (errorMsg == null) { 367 if (verb == null && !acceptLackOfVerb()) { 368 errorMsg = "Missing verb name."; 369 } else if (verb != null) { 370 if (directObject == null) { 371 // Make sure this verb has an optional direct object 372 for (String[] actionDesc : mActions) { 373 if (actionDesc[ACTION_VERB_INDEX].equals(verb) && 374 actionDesc[ACTION_OBJECT_INDEX].equals(NO_VERB_OBJECT)) { 375 directObject = NO_VERB_OBJECT; 376 break; 377 } 378 } 379 380 if (directObject == null) { 381 errorMsg = String.format("Missing object name for verb '%1$s'.", verb); 382 return; 383 } 384 } 385 386 // Validate that all mandatory arguments are non-null for this action 387 String missing = null; 388 boolean plural = false; 389 for (Entry<String, Arg> entry : mArguments.entrySet()) { 390 Arg arg = entry.getValue(); 391 if (arg.getVerb().equals(verb) && 392 arg.getDirectObject().equals(directObject)) { 393 if (arg.isMandatory() && arg.getCurrentValue() == null) { 394 if (missing == null) { 395 missing = "--" + arg.getLongArg(); 396 } else { 397 missing += ", --" + arg.getLongArg(); 398 plural = true; 399 } 400 } 401 } 402 } 403 404 if (missing != null) { 405 errorMsg = String.format( 406 "The %1$s %2$s must be defined for action '%3$s %4$s'", 407 plural ? "parameters" : "parameter", 408 missing, 409 verb, 410 directObject); 411 } 412 413 mVerbRequested = verb; 414 mDirectObjectRequested = directObject; 415 } 416 } 417 } finally { 418 if (errorMsg != null) { 419 printHelpAndExitForAction(verb, directObject, errorMsg); 420 } 421 } 422 } 423 424 /** 425 * Finds an {@link Arg} given an action name and a long flag name. 426 * @return The {@link Arg} found or null. 427 */ findLongArg(String verb, String directObject, String longName)428 protected Arg findLongArg(String verb, String directObject, String longName) { 429 if (verb == null) { 430 verb = GLOBAL_FLAG_VERB; 431 } 432 if (directObject == null) { 433 directObject = NO_VERB_OBJECT; 434 } 435 String key = verb + "/" + directObject + "/" + longName; 436 return mArguments.get(key); 437 } 438 439 /** 440 * Finds an {@link Arg} given an action name and a short flag name. 441 * @return The {@link Arg} found or null. 442 */ findShortArg(String verb, String directObject, String shortName)443 protected Arg findShortArg(String verb, String directObject, String shortName) { 444 if (verb == null) { 445 verb = GLOBAL_FLAG_VERB; 446 } 447 if (directObject == null) { 448 directObject = NO_VERB_OBJECT; 449 } 450 451 for (Entry<String, Arg> entry : mArguments.entrySet()) { 452 Arg arg = entry.getValue(); 453 if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) { 454 if (shortName.equals(arg.getShortArg())) { 455 return arg; 456 } 457 } 458 } 459 460 return null; 461 } 462 463 /** 464 * Prints the help/usage and exits. 465 * 466 * @param errorFormat Optional error message to print prior to usage using String.format 467 * @param args Arguments for String.format 468 */ printHelpAndExit(String errorFormat, Object... args)469 public void printHelpAndExit(String errorFormat, Object... args) { 470 printHelpAndExitForAction(null /*verb*/, null /*directObject*/, errorFormat, args); 471 } 472 473 /** 474 * Prints the help/usage and exits. 475 * 476 * @param verb If null, displays help for all verbs. If not null, display help only 477 * for that specific verb. In all cases also displays general usage and action list. 478 * @param directObject If null, displays help for all verb objects. 479 * If not null, displays help only for that specific action 480 * In all cases also display general usage and action list. 481 * @param errorFormat Optional error message to print prior to usage using String.format 482 * @param args Arguments for String.format 483 */ printHelpAndExitForAction(String verb, String directObject, String errorFormat, Object... args)484 public void printHelpAndExitForAction(String verb, String directObject, 485 String errorFormat, Object... args) { 486 if (errorFormat != null && errorFormat.length() > 0) { 487 stderr(errorFormat, args); 488 } 489 490 /* 491 * usage should fit in 80 columns 492 * 12345678901234567890123456789012345678901234567890123456789012345678901234567890 493 */ 494 stdout("\n" + 495 "Usage:\n" + 496 " android [global options] %s [action options]\n" + 497 "\n" + 498 "Global options:", 499 verb == null ? "action" : 500 verb + (directObject == null ? "" : " " + directObject)); 501 listOptions(GLOBAL_FLAG_VERB, NO_VERB_OBJECT); 502 503 if (verb == null || directObject == null) { 504 stdout("\nValid actions are composed of a verb and an optional direct object:"); 505 for (String[] action : mActions) { 506 if (verb == null || verb.equals(action[ACTION_VERB_INDEX])) { 507 stdout("- %1$6s %2$-14s: %3$s", 508 action[ACTION_VERB_INDEX], 509 action[ACTION_OBJECT_INDEX], 510 action[ACTION_DESC_INDEX]); 511 } 512 } 513 } 514 515 // Only print details if a verb/object is requested 516 if (verb != null) { 517 for (String[] action : mActions) { 518 if (verb == null || verb.equals(action[ACTION_VERB_INDEX])) { 519 if (directObject == null || directObject.equals(action[ACTION_OBJECT_INDEX])) { 520 stdout("\nAction \"%1$s %2$s\":", 521 action[ACTION_VERB_INDEX], 522 action[ACTION_OBJECT_INDEX]); 523 stdout(" %1$s", action[ACTION_DESC_INDEX]); 524 stdout("Options:"); 525 listOptions(action[ACTION_VERB_INDEX], action[ACTION_OBJECT_INDEX]); 526 } 527 } 528 } 529 } 530 531 exit(); 532 } 533 534 /** 535 * Internal helper to print all the option flags for a given action name. 536 */ listOptions(String verb, String directObject)537 protected void listOptions(String verb, String directObject) { 538 int numOptions = 0; 539 for (Entry<String, Arg> entry : mArguments.entrySet()) { 540 Arg arg = entry.getValue(); 541 if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) { 542 543 String value = ""; 544 String required = ""; 545 if (arg.isMandatory()) { 546 required = " [required]"; 547 548 } else { 549 if (arg.getDefaultValue() instanceof String[]) { 550 for (String v : (String[]) arg.getDefaultValue()) { 551 if (value.length() > 0) { 552 value += ", "; 553 } 554 value += v; 555 } 556 } else if (arg.getDefaultValue() != null) { 557 Object v = arg.getDefaultValue(); 558 if (arg.getMode() != Mode.BOOLEAN || v.equals(Boolean.TRUE)) { 559 value = v.toString(); 560 } 561 } 562 if (value.length() > 0) { 563 value = " [Default: " + value + "]"; 564 } 565 } 566 567 stdout(" -%1$s %2$-10s %3$s%4$s%5$s", 568 arg.getShortArg(), 569 "--" + arg.getLongArg(), 570 arg.getDescription(), 571 value, 572 required); 573 numOptions++; 574 } 575 } 576 577 if (numOptions == 0) { 578 stdout(" No options"); 579 } 580 } 581 582 //---- 583 584 /** 585 * The mode of an argument specifies the type of variable it represents, 586 * whether an extra parameter is required after the flag and how to parse it. 587 */ 588 static enum Mode { 589 /** Argument value is a Boolean. Default value is a Boolean. */ 590 BOOLEAN { 591 @Override needsExtra()592 public boolean needsExtra() { 593 return false; 594 } 595 @Override process(Arg arg, String extra)596 public String process(Arg arg, String extra) { 597 // Toggle the current value 598 arg.setCurrentValue(! ((Boolean) arg.getCurrentValue()).booleanValue()); 599 return null; 600 } 601 }, 602 603 /** Argument value is an Integer. Default value is an Integer. */ 604 INTEGER { 605 @Override needsExtra()606 public boolean needsExtra() { 607 return true; 608 } 609 @Override process(Arg arg, String extra)610 public String process(Arg arg, String extra) { 611 try { 612 arg.setCurrentValue(Integer.parseInt(extra)); 613 return null; 614 } catch (NumberFormatException e) { 615 return String.format("Failed to parse '%1$s' as an integer: %2%s", 616 extra, e.getMessage()); 617 } 618 } 619 }, 620 621 /** Argument value is a String. Default value is a String[]. */ 622 ENUM { 623 @Override needsExtra()624 public boolean needsExtra() { 625 return true; 626 } 627 @Override process(Arg arg, String extra)628 public String process(Arg arg, String extra) { 629 StringBuilder desc = new StringBuilder(); 630 String[] values = (String[]) arg.getDefaultValue(); 631 for (String value : values) { 632 if (value.equals(extra)) { 633 arg.setCurrentValue(extra); 634 return null; 635 } 636 637 if (desc.length() != 0) { 638 desc.append(", "); 639 } 640 desc.append(value); 641 } 642 643 return String.format("'%1$s' is not one of %2$s", extra, desc.toString()); 644 } 645 }, 646 647 /** Argument value is a String. Default value is a null. */ 648 STRING { 649 @Override needsExtra()650 public boolean needsExtra() { 651 return true; 652 } 653 @Override process(Arg arg, String extra)654 public String process(Arg arg, String extra) { 655 arg.setCurrentValue(extra); 656 return null; 657 } 658 }; 659 660 /** 661 * Returns true if this mode requires an extra parameter. 662 */ needsExtra()663 public abstract boolean needsExtra(); 664 665 /** 666 * Processes the flag for this argument. 667 * 668 * @param arg The argument being processed. 669 * @param extra The extra parameter. Null if {@link #needsExtra()} returned false. 670 * @return An error string or null if there's no error. 671 */ process(Arg arg, String extra)672 public abstract String process(Arg arg, String extra); 673 } 674 675 /** 676 * An argument accepted by the command-line, also called "a flag". 677 * Arguments must have a short version (one letter), a long version name and a description. 678 * They can have a default value, or it can be null. 679 * Depending on the {@link Mode}, the default value can be a Boolean, an Integer, a String 680 * or a String array (in which case the first item is the current by default.) 681 */ 682 static class Arg { 683 /** Verb for that argument. Never null. */ 684 private final String mVerb; 685 /** Direct Object for that argument. Never null, but can be empty string. */ 686 private final String mDirectObject; 687 /** The 1-letter short name of the argument, e.g. -v. */ 688 private final String mShortName; 689 /** The long name of the argument, e.g. --verbose. */ 690 private final String mLongName; 691 /** A description. Never null. */ 692 private final String mDescription; 693 /** A default value. Can be null. */ 694 private final Object mDefaultValue; 695 /** The argument mode (type + process method). Never null. */ 696 private final Mode mMode; 697 /** True if this argument is mandatory for this verb/directobject. */ 698 private final boolean mMandatory; 699 /** Current value. Initially set to the default value. */ 700 private Object mCurrentValue; 701 /** True if the argument has been used on the command line. */ 702 private boolean mInCommandLine; 703 704 /** 705 * Creates a new argument flag description. 706 * 707 * @param mode The {@link Mode} for the argument. 708 * @param mandatory True if this argument is mandatory for this action. 709 * @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG. 710 * @param shortName The one-letter short argument name. Cannot be empty nor null. 711 * @param longName The long argument name. Cannot be empty nor null. 712 * @param description The description. Cannot be null. 713 * @param defaultValue The default value (or values), which depends on the selected {@link Mode}. 714 */ Arg(Mode mode, boolean mandatory, String verb, String directObject, String shortName, String longName, String description, Object defaultValue)715 public Arg(Mode mode, 716 boolean mandatory, 717 String verb, 718 String directObject, 719 String shortName, 720 String longName, 721 String description, 722 Object defaultValue) { 723 mMode = mode; 724 mMandatory = mandatory; 725 mVerb = verb; 726 mDirectObject = directObject; 727 mShortName = shortName; 728 mLongName = longName; 729 mDescription = description; 730 mDefaultValue = defaultValue; 731 mInCommandLine = false; 732 if (defaultValue instanceof String[]) { 733 mCurrentValue = ((String[])defaultValue)[0]; 734 } else { 735 mCurrentValue = mDefaultValue; 736 } 737 } 738 739 /** Return true if this argument is mandatory for this verb/directobject. */ isMandatory()740 public boolean isMandatory() { 741 return mMandatory; 742 } 743 744 /** Returns the 1-letter short name of the argument, e.g. -v. */ getShortArg()745 public String getShortArg() { 746 return mShortName; 747 } 748 749 /** Returns the long name of the argument, e.g. --verbose. */ getLongArg()750 public String getLongArg() { 751 return mLongName; 752 } 753 754 /** Returns the description. Never null. */ getDescription()755 public String getDescription() { 756 return mDescription; 757 } 758 759 /** Returns the verb for that argument. Never null. */ getVerb()760 public String getVerb() { 761 return mVerb; 762 } 763 764 /** Returns the direct Object for that argument. Never null, but can be empty string. */ getDirectObject()765 public String getDirectObject() { 766 return mDirectObject; 767 } 768 769 /** Returns the default value. Can be null. */ getDefaultValue()770 public Object getDefaultValue() { 771 return mDefaultValue; 772 } 773 774 /** Returns the current value. Initially set to the default value. Can be null. */ getCurrentValue()775 public Object getCurrentValue() { 776 return mCurrentValue; 777 } 778 779 /** Sets the current value. Can be null. */ setCurrentValue(Object currentValue)780 public void setCurrentValue(Object currentValue) { 781 mCurrentValue = currentValue; 782 } 783 784 /** Returns the argument mode (type + process method). Never null. */ getMode()785 public Mode getMode() { 786 return mMode; 787 } 788 789 /** Returns true if the argument has been used on the command line. */ isInCommandLine()790 public boolean isInCommandLine() { 791 return mInCommandLine; 792 } 793 794 /** Sets if the argument has been used on the command line. */ setInCommandLine(boolean inCommandLine)795 public void setInCommandLine(boolean inCommandLine) { 796 mInCommandLine = inCommandLine; 797 } 798 } 799 800 /** 801 * Internal helper to define a new argument for a give action. 802 * 803 * @param mode The {@link Mode} for the argument. 804 * @param verb The verb name. Can be #INTERNAL_VERB. 805 * @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG. 806 * @param shortName The one-letter short argument name. Cannot be empty nor null. 807 * @param longName The long argument name. Cannot be empty nor null. 808 * @param description The description. Cannot be null. 809 * @param defaultValue The default value (or values), which depends on the selected {@link Mode}. 810 */ define(Mode mode, boolean mandatory, String verb, String directObject, String shortName, String longName, String description, Object defaultValue)811 protected void define(Mode mode, 812 boolean mandatory, 813 String verb, 814 String directObject, 815 String shortName, String longName, 816 String description, Object defaultValue) { 817 assert(!(mandatory && mode == Mode.BOOLEAN)); // a boolean mode cannot be mandatory 818 819 if (directObject == null) { 820 directObject = NO_VERB_OBJECT; 821 } 822 823 String key = verb + "/" + directObject + "/" + longName; 824 mArguments.put(key, new Arg(mode, mandatory, 825 verb, directObject, shortName, longName, description, defaultValue)); 826 } 827 828 /** 829 * Exits in case of error. 830 * This is protected so that it can be overridden in unit tests. 831 */ exit()832 protected void exit() { 833 System.exit(1); 834 } 835 836 /** 837 * Prints a line to stdout. 838 * This is protected so that it can be overridden in unit tests. 839 * 840 * @param format The string to be formatted. Cannot be null. 841 * @param args Format arguments. 842 */ stdout(String format, Object...args)843 protected void stdout(String format, Object...args) { 844 mLog.printf(format + "\n", args); 845 } 846 847 /** 848 * Prints a line to stderr. 849 * This is protected so that it can be overridden in unit tests. 850 * 851 * @param format The string to be formatted. Cannot be null. 852 * @param args Format arguments. 853 */ stderr(String format, Object...args)854 protected void stderr(String format, Object...args) { 855 mLog.error(null, format, args); 856 } 857 } 858