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