001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.util.args; 028 import org.opends.messages.Message; 029 030 031 032 import java.io.File; 033 import java.io.FileInputStream; 034 import java.io.IOException; 035 import java.io.OutputStream; 036 import java.util.ArrayList; 037 import java.util.Enumeration; 038 import java.util.HashMap; 039 import java.util.LinkedList; 040 import java.util.Properties; 041 import java.util.TreeSet; 042 import java.util.Set; 043 044 import org.opends.server.core.DirectoryServer; 045 import org.opends.server.util.SetupUtils; 046 047 import static org.opends.messages.UtilityMessages.*; 048 import static org.opends.server.util.ServerConstants.*; 049 import static org.opends.server.util.StaticUtils.*; 050 import static org.opends.server.tools.ToolConstants.*; 051 import static org.opends.messages.ToolMessages.*; 052 053 import org.opends.messages.MessageBuilder; 054 055 056 /** 057 * This class defines a utility that can be used to deal with command-line 058 * arguments for applications in a CLIP-compliant manner using either short 059 * one-character or longer word-based arguments. It is also integrated with the 060 * Directory Server message catalog so that it can display messages in an 061 * internationalizeable format, can automatically generate usage information, 062 * can detect conflicts between arguments, and can interact with a properties 063 * file to obtain default values for arguments there if they are not specified 064 * on the command-line. 065 */ 066 public class ArgumentParser 067 { 068 /** 069 * The argument that will be used to indicate the file properties. 070 */ 071 private StringArgument filePropertiesPathArgument; 072 073 /** 074 * The argument that will be used to indicate that we'll not look for 075 * default properties file. 076 */ 077 private BooleanArgument noPropertiesFileArgument; 078 079 // The argument that will be used to trigger the display of usage information. 080 private Argument usageArgument; 081 082 // The argument that will be used to trigger the display of the OpenDS 083 // version. 084 private Argument versionArgument; 085 086 // The set of unnamed trailing arguments that were provided for this parser. 087 private ArrayList<String> trailingArguments; 088 089 // Indicates whether this parser will allow additional unnamed arguments at 090 // the end of the list. 091 private boolean allowsTrailingArguments; 092 093 // Indicates whether long arguments should be treated in a case-sensitive 094 // manner. 095 private boolean longArgumentsCaseSensitive; 096 097 // Indicates whether the usage or version information has been displayed. 098 private boolean usageOrVersionDisplayed; 099 100 // Indicates whether the version argument was provided. 101 private boolean versionPresent; 102 103 // The set of arguments defined for this parser, referenced by short ID. 104 private HashMap<Character,Argument> shortIDMap; 105 106 // The set of arguments defined for this parser, referenced by argument name. 107 private HashMap<String,Argument> argumentMap; 108 109 // The set of arguments defined for this parser, referenced by long ID. 110 private HashMap<String,Argument> longIDMap; 111 112 // The maximum number of unnamed trailing arguments that may be provided. 113 private int maxTrailingArguments; 114 115 // The minimum number of unnamed trailing arguments that may be provided. 116 private int minTrailingArguments; 117 118 // The total set of arguments defined for this parser. 119 private LinkedList<Argument> argumentList; 120 121 // The output stream to which usage information should be printed. 122 private OutputStream usageOutputStream; 123 124 // The fully-qualified name of the Java class that should be invoked to launch 125 // the program with which this argument parser is associated. 126 private String mainClassName; 127 128 // A human-readable description for the tool, which will be included when 129 // displaying usage information. 130 private Message toolDescription; 131 132 // The display name that will be used for the trailing arguments in the usage 133 // information. 134 private String trailingArgsDisplayName; 135 136 // The raw set of command-line arguments that were provided. 137 private String[] rawArguments; 138 139 /** Set of argument groups. */ 140 protected Set<ArgumentGroup> argumentGroups; 141 142 143 /** 144 * Group for arguments that have not been explicitly grouped. 145 * These will appear at the top of the usage statement without 146 * a header. 147 */ 148 protected ArgumentGroup defaultArgGroup = new ArgumentGroup( 149 Message.EMPTY, Integer.MAX_VALUE); 150 151 152 /** 153 * Group for arguments that are related to utility input/output like 154 * verbose, quite, no-prompt etc. These will appear toward the bottom 155 * of the usage statement. 156 */ 157 protected ArgumentGroup ldapArgGroup = new ArgumentGroup( 158 INFO_DESCRIPTION_LDAP_CONNECTION_ARGS.get(), Integer.MIN_VALUE + 2); 159 160 161 /** 162 * Group for arguments that are related to utility input/output like 163 * verbose, quite, no-prompt etc. These will appear toward the bottom 164 * of the usage statement. 165 */ 166 protected ArgumentGroup ioArgGroup = new ArgumentGroup( 167 INFO_DESCRIPTION_IO_ARGS.get(), Integer.MIN_VALUE + 1); 168 169 170 /** 171 * Group for arguments that are general like help, version etc. 172 * These will appear at the end of the usage statement. 173 */ 174 protected ArgumentGroup generalArgGroup = new ArgumentGroup( 175 INFO_DESCRIPTION_GENERAL_ARGS.get(), Integer.MIN_VALUE); 176 177 178 private final static String INDENT = " "; 179 private final static int MAX_LENGTH = SetupUtils.isWindows() ? 79 : 80; 180 181 /** 182 * Creates a new instance of this argument parser with no arguments. 183 * Unnamed trailing arguments will not be allowed. 184 * 185 * @param mainClassName The fully-qualified name of the Java 186 * class that should be invoked to launch 187 * the program with which this argument 188 * parser is associated. 189 * @param toolDescription A human-readable description for the 190 * tool, which will be included when 191 * displaying usage information. 192 * @param longArgumentsCaseSensitive Indicates whether long arguments should 193 * be treated in a case-sensitive manner. 194 */ 195 public ArgumentParser(String mainClassName, Message toolDescription, 196 boolean longArgumentsCaseSensitive) 197 { 198 this.mainClassName = mainClassName; 199 this.toolDescription = toolDescription; 200 this.longArgumentsCaseSensitive = longArgumentsCaseSensitive; 201 202 argumentList = new LinkedList<Argument>(); 203 argumentMap = new HashMap<String,Argument>(); 204 shortIDMap = new HashMap<Character,Argument>(); 205 longIDMap = new HashMap<String,Argument>(); 206 allowsTrailingArguments = false; 207 usageOrVersionDisplayed = false; 208 versionPresent = false; 209 trailingArgsDisplayName = null; 210 maxTrailingArguments = 0; 211 minTrailingArguments = 0; 212 trailingArguments = new ArrayList<String>(); 213 rawArguments = null; 214 usageArgument = null; 215 filePropertiesPathArgument = null; 216 noPropertiesFileArgument = null; 217 usageOutputStream = System.out; 218 initGroups(); 219 } 220 221 222 223 224 225 /** 226 * Creates a new instance of this argument parser with no arguments that may 227 * or may not be allowed to have unnamed trailing arguments. 228 * 229 * @param mainClassName The fully-qualified name of the Java 230 * class that should be invoked to launch 231 * the program with which this argument 232 * parser is associated. 233 * @param toolDescription A human-readable description for the 234 * tool, which will be included when 235 * displaying usage information. 236 * @param longArgumentsCaseSensitive Indicates whether long arguments should 237 * be treated in a case-sensitive manner. 238 * @param allowsTrailingArguments Indicates whether this parser allows 239 * unnamed trailing arguments to be 240 * provided. 241 * @param minTrailingArguments The minimum number of unnamed trailing 242 * arguments that must be provided. A 243 * value less than or equal to zero 244 * indicates that no minimum will be 245 * enforced. 246 * @param maxTrailingArguments The maximum number of unnamed trailing 247 * arguments that may be provided. A 248 * value less than or equal to zero 249 * indicates that no maximum will be 250 * enforced. 251 * @param trailingArgsDisplayName The display name that should be used 252 * as a placeholder for unnamed trailing 253 * arguments in the generated usage 254 * information. 255 */ 256 public ArgumentParser(String mainClassName, Message toolDescription, 257 boolean longArgumentsCaseSensitive, 258 boolean allowsTrailingArguments, 259 int minTrailingArguments, int maxTrailingArguments, 260 String trailingArgsDisplayName) 261 { 262 this.mainClassName = mainClassName; 263 this.toolDescription = toolDescription; 264 this.longArgumentsCaseSensitive = longArgumentsCaseSensitive; 265 this.allowsTrailingArguments = allowsTrailingArguments; 266 this.minTrailingArguments = minTrailingArguments; 267 this.maxTrailingArguments = maxTrailingArguments; 268 this.trailingArgsDisplayName = trailingArgsDisplayName; 269 270 argumentList = new LinkedList<Argument>(); 271 argumentMap = new HashMap<String,Argument>(); 272 shortIDMap = new HashMap<Character,Argument>(); 273 longIDMap = new HashMap<String,Argument>(); 274 trailingArguments = new ArrayList<String>(); 275 usageOrVersionDisplayed = false; 276 versionPresent = false; 277 rawArguments = null; 278 usageArgument = null; 279 usageOutputStream = System.out; 280 initGroups(); 281 } 282 283 284 285 /** 286 * Retrieves the fully-qualified name of the Java class that should be invoked 287 * to launch the program with which this argument parser is associated. 288 * 289 * @return The fully-qualified name of the Java class that should be invoked 290 * to launch the program with which this argument parser is 291 * associated. 292 */ 293 public String getMainClassName() 294 { 295 return mainClassName; 296 } 297 298 299 300 /** 301 * Retrieves a human-readable description for this tool, which should be 302 * included at the top of the command-line usage information. 303 * 304 * @return A human-readable description for this tool, or {@code null} if 305 * none is available. 306 */ 307 public Message getToolDescription() 308 { 309 return toolDescription; 310 } 311 312 313 314 /** 315 * Indicates whether this parser will allow unnamed trailing arguments. These 316 * will be arguments at the end of the list that are not preceded by either a 317 * long or short identifier and will need to be manually parsed by the 318 * application using this parser. Note that once an unnamed trailing argument 319 * has been identified, all remaining arguments will be classified as such. 320 * 321 * @return <CODE>true</CODE> if this parser allows unnamed trailing 322 * arguments, or <CODE>false</CODE> if it does not. 323 */ 324 public boolean allowsTrailingArguments() 325 { 326 return allowsTrailingArguments; 327 } 328 329 330 331 /** 332 * Retrieves the minimum number of unnamed trailing arguments that must be 333 * provided. 334 * 335 * @return The minimum number of unnamed trailing arguments that must be 336 * provided, or a value less than or equal to zero if no minimum will 337 * be enforced. 338 */ 339 public int getMinTrailingArguments() 340 { 341 return minTrailingArguments; 342 } 343 344 345 346 /** 347 * Retrieves the maximum number of unnamed trailing arguments that may be 348 * provided. 349 * 350 * @return The maximum number of unnamed trailing arguments that may be 351 * provided, or a value less than or equal to zero if no maximum will 352 * be enforced. 353 */ 354 public int getMaxTrailingArguments() 355 { 356 return maxTrailingArguments; 357 } 358 359 360 361 /** 362 * Retrieves the list of all arguments that have been defined for this 363 * argument parser. 364 * 365 * @return The list of all arguments that have been defined for this argument 366 * parser. 367 */ 368 public LinkedList<Argument> getArgumentList() 369 { 370 return argumentList; 371 } 372 373 374 375 /** 376 * Retrieves the argument with the specified name. 377 * 378 * @param name The name of the argument to retrieve. 379 * 380 * @return The argument with the specified name, or <CODE>null</CODE> if 381 * there is no such argument. 382 */ 383 public Argument getArgument(String name) 384 { 385 return argumentMap.get(name); 386 } 387 388 389 390 /** 391 * Retrieves the set of arguments mapped by the short identifier that may be 392 * used to reference them. Note that arguments that do not have a short 393 * identifier will not be present in this list. 394 * 395 * @return The set of arguments mapped by the short identifier that may be 396 * used to reference them. 397 */ 398 public HashMap<Character,Argument> getArgumentsByShortID() 399 { 400 return shortIDMap; 401 } 402 403 404 405 /** 406 * Retrieves the argument with the specified short identifier. 407 * 408 * @param shortID The short ID for the argument to retrieve. 409 * 410 * @return The argument with the specified short identifier, or 411 * <CODE>null</CODE> if there is no such argument. 412 */ 413 public Argument getArgumentForShortID(Character shortID) 414 { 415 return shortIDMap.get(shortID); 416 } 417 418 419 420 /** 421 * Retrieves the set of arguments mapped by the long identifier that may be 422 * used to reference them. Note that arguments that do not have a long 423 * identifier will not be present in this list. 424 * 425 * @return The set of arguments mapped by the long identifier that may be 426 * used to reference them. 427 */ 428 public HashMap<String,Argument> getArgumentsByLongID() 429 { 430 return longIDMap; 431 } 432 433 434 435 /** 436 * Retrieves the argument with the specified long identifier. 437 * 438 * @param longID The long identifier of the argument to retrieve. 439 * 440 * @return The argument with the specified long identifier, or 441 * <CODE>null</CODE> if there is no such argument. 442 */ 443 public Argument getArgumentForLongID(String longID) 444 { 445 return longIDMap.get(longID); 446 } 447 448 449 450 /** 451 * Retrieves the set of unnamed trailing arguments that were provided on the 452 * command line. 453 * 454 * @return The set of unnamed trailing arguments that were provided on the 455 * command line. 456 */ 457 public ArrayList<String> getTrailingArguments() 458 { 459 return trailingArguments; 460 } 461 462 463 464 /** 465 * Retrieves the raw set of arguments that were provided. 466 * 467 * @return The raw set of arguments that were provided, or <CODE>null</CODE> 468 * if the argument list has not yet been parsed. 469 */ 470 public String[] getRawArguments() 471 { 472 return rawArguments; 473 } 474 475 476 /** 477 * Sets the usage group description for the default argument group. 478 * 479 * @param description for the default group 480 */ 481 public void setDefaultArgumentGroupDescription(Message description) 482 { 483 this.defaultArgGroup.setDescription(description); 484 } 485 486 487 /** 488 * Sets the usage group description for the LDAP argument group. 489 * 490 * @param description for the LDAP group 491 */ 492 public void setLdapArgumentGroupDescription(Message description) 493 { 494 this.ldapArgGroup.setDescription(description); 495 } 496 497 498 /** 499 * Sets the usage group description for the input/output argument group. 500 * 501 * @param description for the input/output group 502 */ 503 public void setInputOutputArgumentGroupDescription(Message description) 504 { 505 this.ioArgGroup.setDescription(description); 506 } 507 508 509 /** 510 * Sets the usage group description for the general argument group. 511 * 512 * @param description for the general group 513 */ 514 public void setGeneralArgumentGroupDescription(Message description) 515 { 516 this.generalArgGroup.setDescription(description); 517 } 518 519 520 /** 521 * Adds the provided argument to the set of arguments handled by this parser. 522 * 523 * @param argument The argument to be added. 524 * 525 * @throws ArgumentException If the provided argument conflicts with another 526 * argument that has already been defined. 527 */ 528 public void addArgument(Argument argument) 529 throws ArgumentException 530 { 531 addArgument(argument, null); 532 } 533 534 /** 535 * Adds the provided argument to the set of arguments handled by this parser 536 * and puts the arguement in the default group. 537 * 538 * @param argument The argument to be added. 539 * 540 * @throws ArgumentException If the provided argument conflicts with another 541 * argument that has already been defined. 542 */ 543 public void addDefaultArgument(Argument argument) 544 throws ArgumentException 545 { 546 addArgument(argument, defaultArgGroup); 547 } 548 549 /** 550 * Adds the provided argument to the set of arguments handled by this parser 551 * and puts the argument in the LDAP connection group. 552 * 553 * @param argument The argument to be added. 554 * 555 * @throws ArgumentException If the provided argument conflicts with another 556 * argument that has already been defined. 557 */ 558 public void addLdapConnectionArgument(Argument argument) 559 throws ArgumentException 560 { 561 addArgument(argument, ldapArgGroup); 562 } 563 564 /** 565 * Adds the provided argument to the set of arguments handled by this parser 566 * and puts the argument in the input/output group. 567 * 568 * @param argument The argument to be added. 569 * 570 * @throws ArgumentException If the provided argument conflicts with another 571 * argument that has already been defined. 572 */ 573 public void addInputOutputArgument(Argument argument) 574 throws ArgumentException 575 { 576 addArgument(argument, ioArgGroup); 577 } 578 579 /** 580 * Adds the provided argument to the set of arguments handled by this parser 581 * and puts the arguement in the general group. 582 * 583 * @param argument The argument to be added. 584 * 585 * @throws ArgumentException If the provided argument conflicts with another 586 * argument that has already been defined. 587 */ 588 public void addGeneralArgument(Argument argument) 589 throws ArgumentException 590 { 591 addArgument(argument, generalArgGroup); 592 } 593 594 /** 595 * Adds the provided argument to the set of arguments handled by this parser. 596 * 597 * @param argument The argument to be added. 598 * @param group The argument group to which the argument belongs. 599 * 600 * @throws ArgumentException If the provided argument conflicts with another 601 * argument that has already been defined. 602 */ 603 public void addArgument(Argument argument, ArgumentGroup group) 604 throws ArgumentException 605 { 606 607 Character shortID = argument.getShortIdentifier(); 608 if ((shortID != null) && shortIDMap.containsKey(shortID)) 609 { 610 String conflictingName = shortIDMap.get(shortID).getName(); 611 612 Message message = ERR_ARGPARSER_DUPLICATE_SHORT_ID.get( 613 argument.getName(), String.valueOf(shortID), conflictingName); 614 throw new ArgumentException(message); 615 } 616 617 if (versionArgument != null) 618 { 619 if (shortID == versionArgument.getShortIdentifier()) 620 { 621 // Update the version argument to not display its short identifier. 622 try { 623 versionArgument = new BooleanArgument( 624 OPTION_LONG_PRODUCT_VERSION, 625 null, 626 OPTION_LONG_PRODUCT_VERSION, 627 INFO_DESCRIPTION_PRODUCT_VERSION.get()); 628 this.generalArgGroup.addArgument(versionArgument); 629 } catch (ArgumentException e) { 630 // ignore 631 } 632 } 633 } 634 635 String longID = argument.getLongIdentifier(); 636 if (longID != null) 637 { 638 if (! longArgumentsCaseSensitive) 639 { 640 longID = toLowerCase(longID); 641 } 642 if (longIDMap.containsKey(longID)) 643 { 644 String conflictingName = longIDMap.get(longID).getName(); 645 646 Message message = ERR_ARGPARSER_DUPLICATE_LONG_ID.get( 647 argument.getName(), argument.getLongIdentifier(), conflictingName); 648 throw new ArgumentException(message); 649 } 650 } 651 652 if (shortID != null) 653 { 654 shortIDMap.put(shortID, argument); 655 } 656 657 if (longID != null) 658 { 659 longIDMap.put(longID, argument); 660 } 661 662 argumentList.add(argument); 663 664 if (group == null) { 665 group = getStandardGroup(argument); 666 } 667 group.addArgument(argument); 668 argumentGroups.add(group); 669 } 670 671 672 673 /** 674 * Sets the provided argument as one which will automatically trigger the 675 * output of usage information if it is provided on the command line and no 676 * further argument validation will be performed. Note that the caller will 677 * still need to add this argument to the parser with the 678 * <CODE>addArgument</CODE> method, and the argument should not be required 679 * and should not take a value. Also, the caller will still need to check 680 * for the presence of the usage argument after calling 681 * <CODE>parseArguments</CODE> to know that no further processing will be 682 * required. 683 * 684 * @param argument The argument whose presence should automatically 685 * trigger the display of usage information. 686 */ 687 public void setUsageArgument(Argument argument) 688 { 689 usageArgument = argument; 690 usageOutputStream = System.out; 691 } 692 693 694 695 /** 696 * Sets the provided argument as one which will automatically trigger the 697 * output of usage information if it is provided on the command line and no 698 * further argument validation will be performed. Note that the caller will 699 * still need to add this argument to the parser with the 700 * <CODE>addArgument</CODE> method, and the argument should not be required 701 * and should not take a value. Also, the caller will still need to check 702 * for the presence of the usage argument after calling 703 * <CODE>parseArguments</CODE> to know that no further processing will be 704 * required. 705 * 706 * @param argument The argument whose presence should automatically 707 * trigger the display of usage information. 708 * @param outputStream The output stream to which the usage information 709 * should be written. 710 */ 711 public void setUsageArgument(Argument argument, OutputStream outputStream) 712 { 713 usageArgument = argument; 714 usageOutputStream = outputStream; 715 } 716 717 718 /** 719 * Sets the provided argument which will be used to identify the 720 * file properties. 721 * 722 * @param argument 723 * The argument which will be used to identify the file 724 * properties. 725 */ 726 public void setFilePropertiesArgument(StringArgument argument) 727 { 728 filePropertiesPathArgument= argument; 729 } 730 731 /** 732 * Sets the provided argument which will be used to identify the 733 * file properties. 734 * 735 * @param argument 736 * The argument which will be used to indicate if we have to 737 * look for properties file. 738 */ 739 public void setNoPropertiesFileArgument(BooleanArgument argument) 740 { 741 noPropertiesFileArgument = argument; 742 } 743 744 /** 745 * Parses the provided set of arguments and updates the information associated 746 * with this parser accordingly. 747 * 748 * @param rawArguments The raw set of arguments to parse. 749 * 750 * @throws ArgumentException If a problem was encountered while parsing the 751 * provided arguments. 752 */ 753 public void parseArguments(String[] rawArguments) 754 throws ArgumentException 755 { 756 parseArguments(rawArguments, null); 757 } 758 759 760 761 /** 762 * Parses the provided set of arguments and updates the information associated 763 * with this parser accordingly. Default values for unspecified arguments 764 * may be read from the specified properties file. 765 * 766 * @param rawArguments The set of raw arguments to parse. 767 * @param propertiesFile The path to the properties file to use to 768 * obtain default values for unspecified 769 * properties. 770 * @param requirePropertiesFile Indicates whether the parsing should fail if 771 * the provided properties file does not exist 772 * or is not accessible. 773 * 774 * @throws ArgumentException If a problem was encountered while parsing the 775 * provided arguments or interacting with the 776 * properties file. 777 */ 778 public void parseArguments(String[] rawArguments, String propertiesFile, 779 boolean requirePropertiesFile) 780 throws ArgumentException 781 { 782 this.rawArguments = rawArguments; 783 784 Properties argumentProperties = null; 785 786 try 787 { 788 Properties p = new Properties(); 789 FileInputStream fis = new FileInputStream(propertiesFile); 790 p.load(fis); 791 fis.close(); 792 argumentProperties = p; 793 } 794 catch (Exception e) 795 { 796 if (requirePropertiesFile) 797 { 798 Message message = ERR_ARGPARSER_CANNOT_READ_PROPERTIES_FILE.get( 799 String.valueOf(propertiesFile), getExceptionMessage(e)); 800 throw new ArgumentException(message, e); 801 } 802 } 803 804 parseArguments(rawArguments, argumentProperties); 805 } 806 807 808 809 /** 810 * Parses the provided set of arguments and updates the information associated 811 * with this parser accordingly. Default values for unspecified arguments may 812 * be read from the specified properties if any are provided. 813 * 814 * @param rawArguments The set of raw arguments to parse. 815 * @param argumentProperties A set of properties that may be used to provide 816 * default values for arguments not included in 817 * the given raw arguments. 818 * 819 * @throws ArgumentException If a problem was encountered while parsing the 820 * provided arguments. 821 */ 822 public void parseArguments(String[] rawArguments, 823 Properties argumentProperties) 824 throws ArgumentException 825 { 826 this.rawArguments = rawArguments; 827 828 boolean inTrailingArgs = false; 829 830 int numArguments = rawArguments.length; 831 for (int i=0; i < numArguments; i++) 832 { 833 String arg = rawArguments[i]; 834 835 if (inTrailingArgs) 836 { 837 trailingArguments.add(arg); 838 if ((maxTrailingArguments > 0) && 839 (trailingArguments.size() > maxTrailingArguments)) 840 { 841 Message message = 842 ERR_ARGPARSER_TOO_MANY_TRAILING_ARGS.get(maxTrailingArguments); 843 throw new ArgumentException(message); 844 } 845 846 continue; 847 } 848 849 if (arg.equals("--")) 850 { 851 // This is a special indicator that we have reached the end of the named 852 // arguments and that everything that follows after this should be 853 // considered trailing arguments. 854 inTrailingArgs = true; 855 } 856 else if (arg.startsWith("--")) 857 { 858 // This indicates that we are using the long name to reference the 859 // argument. It may be in any of the following forms: 860 // --name 861 // --name value 862 // --name=value 863 864 String argName = arg.substring(2); 865 String argValue = null; 866 int equalPos = argName.indexOf('='); 867 if (equalPos < 0) 868 { 869 // This is fine. The value is not part of the argument name token. 870 } 871 else if (equalPos == 0) 872 { 873 // The argument starts with "--=", which is not acceptable. 874 Message message = ERR_ARGPARSER_LONG_ARG_WITHOUT_NAME.get(arg); 875 throw new ArgumentException(message); 876 } 877 else 878 { 879 // The argument is in the form --name=value, so parse them both out. 880 argValue = argName.substring(equalPos+1); 881 argName = argName.substring(0, equalPos); 882 } 883 884 // If we're not case-sensitive, then convert the name to lowercase. 885 String origArgName = argName; 886 if (! longArgumentsCaseSensitive) 887 { 888 argName = toLowerCase(argName); 889 } 890 891 // Get the argument with the specified name. 892 Argument a = longIDMap.get(argName); 893 if (a == null) 894 { 895 if (argName.equals(OPTION_LONG_HELP)) 896 { 897 // "--help" will always be interpreted as requesting usage 898 // information. 899 try 900 { 901 getUsage(usageOutputStream); 902 } catch (Exception e) {} 903 904 return; 905 } 906 else 907 if (argName.equals(OPTION_LONG_PRODUCT_VERSION)) 908 { 909 // "--version" will always be interpreted as requesting version 910 // information. 911 usageOrVersionDisplayed = true; 912 versionPresent = true; 913 try 914 { 915 DirectoryServer.printVersion(usageOutputStream); 916 } catch (Exception e) {} 917 918 return; 919 } 920 else 921 { 922 // There is no such argument registered. 923 Message message = 924 ERR_ARGPARSER_NO_ARGUMENT_WITH_LONG_ID.get(origArgName); 925 throw new ArgumentException(message); 926 } 927 } 928 else 929 { 930 a.setPresent(true); 931 932 // If this is the usage argument, then immediately stop and print 933 // usage information. 934 if ((usageArgument != null) && 935 usageArgument.getName().equals(a.getName())) 936 { 937 try 938 { 939 getUsage(usageOutputStream); 940 } catch (Exception e) {} 941 942 return; 943 } 944 } 945 946 // See if the argument takes a value. If so, then make sure one was 947 // provided. If not, then make sure none was provided. 948 if (a.needsValue()) 949 { 950 if (argValue == null) 951 { 952 if ((i+1) == numArguments) 953 { 954 Message message = 955 ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_LONG_ID.get( 956 origArgName); 957 throw new ArgumentException(message); 958 } 959 960 argValue = rawArguments[++i]; 961 } 962 963 MessageBuilder invalidReason = new MessageBuilder(); 964 if (! a.valueIsAcceptable(argValue, invalidReason)) 965 { 966 Message message = ERR_ARGPARSER_VALUE_UNACCEPTABLE_FOR_LONG_ID.get( 967 argValue, origArgName, invalidReason.toString()); 968 throw new ArgumentException(message); 969 } 970 971 // If the argument already has a value, then make sure it is 972 // acceptable to have more than one. 973 if (a.hasValue() && (! a.isMultiValued())) 974 { 975 Message message = 976 ERR_ARGPARSER_NOT_MULTIVALUED_FOR_LONG_ID.get(origArgName); 977 throw new ArgumentException(message); 978 } 979 980 a.addValue(argValue); 981 } 982 else 983 { 984 if (argValue != null) 985 { 986 Message message = 987 ERR_ARGPARSER_ARG_FOR_LONG_ID_DOESNT_TAKE_VALUE.get( 988 origArgName); 989 throw new ArgumentException(message); 990 } 991 } 992 } 993 else if (arg.startsWith("-")) 994 { 995 // This indicates that we are using the 1-character name to reference 996 // the argument. It may be in any of the following forms: 997 // -n 998 // -nvalue 999 // -n value 1000 if (arg.equals("-")) 1001 { 1002 Message message = ERR_ARGPARSER_INVALID_DASH_AS_ARGUMENT.get(); 1003 throw new ArgumentException(message); 1004 } 1005 1006 char argCharacter = arg.charAt(1); 1007 String argValue; 1008 if (arg.length() > 2) 1009 { 1010 argValue = arg.substring(2); 1011 } 1012 else 1013 { 1014 argValue = null; 1015 } 1016 1017 1018 // Get the argument with the specified short ID. 1019 Argument a = shortIDMap.get(argCharacter); 1020 if (a == null) 1021 { 1022 if (argCharacter == '?') 1023 { 1024 // "-?" will always be interpreted as requesting usage information. 1025 try 1026 { 1027 getUsage(usageOutputStream); 1028 } catch (Exception e) {} 1029 1030 return; 1031 } 1032 else 1033 if ( (argCharacter == OPTION_SHORT_PRODUCT_VERSION) 1034 && 1035 ( ! shortIDMap.containsKey(OPTION_SHORT_PRODUCT_VERSION))) 1036 { 1037 // "-V" will always be interpreted as requesting 1038 // version information except if it's already defined (e.g in 1039 // ldap tools). 1040 usageOrVersionDisplayed = true ; 1041 versionPresent = true; 1042 try 1043 { 1044 DirectoryServer.printVersion(usageOutputStream); 1045 } catch (Exception e) {} 1046 return; 1047 } 1048 else 1049 { 1050 // There is no such argument registered. 1051 Message message = ERR_ARGPARSER_NO_ARGUMENT_WITH_SHORT_ID.get( 1052 String.valueOf(argCharacter)); 1053 throw new ArgumentException(message); 1054 } 1055 } 1056 else 1057 { 1058 a.setPresent(true); 1059 1060 // If this is the usage argument, then immediately stop and print 1061 // usage information. 1062 if ((usageArgument != null) && 1063 usageArgument.getName().equals(a.getName())) 1064 { 1065 try 1066 { 1067 getUsage(usageOutputStream); 1068 } catch (Exception e) {} 1069 1070 return; 1071 } 1072 } 1073 1074 // See if the argument takes a value. If so, then make sure one was 1075 // provided. If not, then make sure none was provided. 1076 if (a.needsValue()) 1077 { 1078 if (argValue == null) 1079 { 1080 if ((i+1) == numArguments) 1081 { 1082 Message message = 1083 ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_SHORT_ID. 1084 get(String.valueOf(argCharacter)); 1085 throw new ArgumentException(message); 1086 } 1087 1088 argValue = rawArguments[++i]; 1089 } 1090 1091 MessageBuilder invalidReason = new MessageBuilder(); 1092 if (! a.valueIsAcceptable(argValue, invalidReason)) 1093 { 1094 Message message = ERR_ARGPARSER_VALUE_UNACCEPTABLE_FOR_SHORT_ID. 1095 get(argValue, String.valueOf(argCharacter), 1096 invalidReason.toString()); 1097 throw new ArgumentException(message); 1098 } 1099 1100 // If the argument already has a value, then make sure it is 1101 // acceptable to have more than one. 1102 if (a.hasValue() && (! a.isMultiValued())) 1103 { 1104 Message message = ERR_ARGPARSER_NOT_MULTIVALUED_FOR_SHORT_ID.get( 1105 String.valueOf(argCharacter)); 1106 throw new ArgumentException(message); 1107 } 1108 1109 a.addValue(argValue); 1110 } 1111 else 1112 { 1113 if (argValue != null) 1114 { 1115 // If we've gotten here, then it means that we're in a scenario like 1116 // "-abc" where "a" is a valid argument that doesn't take a value. 1117 // However, this could still be valid if all remaining characters in 1118 // the value are also valid argument characters that don't take 1119 // values. 1120 int valueLength = argValue.length(); 1121 for (int j=0; j < valueLength; j++) 1122 { 1123 char c = argValue.charAt(j); 1124 Argument b = shortIDMap.get(c); 1125 if (b == null) 1126 { 1127 // There is no such argument registered. 1128 Message message = ERR_ARGPARSER_NO_ARGUMENT_WITH_SHORT_ID.get( 1129 String.valueOf(argCharacter)); 1130 throw new ArgumentException(message); 1131 } 1132 else if (b.needsValue()) 1133 { 1134 // This means we're in a scenario like "-abc" where b is a 1135 // valid argument that takes a value. We don't support that. 1136 Message message = ERR_ARGPARSER_CANT_MIX_ARGS_WITH_VALUES.get( 1137 String.valueOf(argCharacter), argValue, String.valueOf(c)); 1138 throw new ArgumentException(message); 1139 } 1140 else 1141 { 1142 b.setPresent(true); 1143 1144 // If this is the usage argument, then immediately stop and 1145 // print usage information. 1146 if ((usageArgument != null) && 1147 usageArgument.getName().equals(b.getName())) 1148 { 1149 try 1150 { 1151 getUsage(usageOutputStream); 1152 } catch (Exception e) {} 1153 1154 return; 1155 } 1156 } 1157 } 1158 } 1159 } 1160 } 1161 else if (allowsTrailingArguments) 1162 { 1163 // It doesn't start with a dash, so it must be a trailing argument if 1164 // that is acceptable. 1165 inTrailingArgs = true; 1166 trailingArguments.add(arg); 1167 } 1168 else 1169 { 1170 // It doesn't start with a dash and we don't allow trailing arguments, 1171 // so this is illegal. 1172 Message message = ERR_ARGPARSER_DISALLOWED_TRAILING_ARGUMENT.get(arg); 1173 throw new ArgumentException(message); 1174 } 1175 } 1176 1177 1178 // If we allow trailing arguments and there is a minimum number, then make 1179 // sure at least that many were provided. 1180 if (allowsTrailingArguments && (minTrailingArguments > 0)) 1181 { 1182 if (trailingArguments.size() < minTrailingArguments) 1183 { 1184 Message message = 1185 ERR_ARGPARSER_TOO_FEW_TRAILING_ARGUMENTS.get(minTrailingArguments); 1186 throw new ArgumentException(message); 1187 } 1188 } 1189 1190 // If we don't have the argumentProperties, try to load a properties file. 1191 if (argumentProperties == null) 1192 { 1193 argumentProperties = checkExternalProperties(); 1194 } 1195 1196 // Iterate through all of the arguments. For any that were not provided on 1197 // the command line, see if there is an alternate default that can be used. 1198 // For cases where there is not, see that argument is required. 1199 for (Argument a : argumentList) 1200 { 1201 if (! a.isPresent()) 1202 { 1203 // See if there is a value in the properties that can be used 1204 if ((argumentProperties != null) && (a.getPropertyName() != null)) 1205 { 1206 String value = argumentProperties.getProperty(a.getPropertyName() 1207 .toLowerCase()); 1208 MessageBuilder invalidReason = new MessageBuilder(); 1209 if (value != null) 1210 { 1211 Boolean addValue = true; 1212 if (!( a instanceof BooleanArgument)) 1213 { 1214 addValue = a.valueIsAcceptable(value, invalidReason); 1215 } 1216 if (addValue) 1217 { 1218 a.addValue(value); 1219 if (a.needsValue()) 1220 { 1221 a.setPresent(true); 1222 } 1223 a.setValueSetByProperty(true); 1224 } 1225 } 1226 } 1227 } 1228 1229 1230 if ((! a.isPresent()) && a.needsValue()) 1231 { 1232 // See if the argument defines a default. 1233 if (a.getDefaultValue() != null) 1234 { 1235 a.addValue(a.getDefaultValue()); 1236 } 1237 1238 // If there is still no value and the argument is required, then that's 1239 // a problem. 1240 if ((! a.hasValue()) && a.isRequired()) 1241 { 1242 Message message = 1243 ERR_ARGPARSER_NO_VALUE_FOR_REQUIRED_ARG.get(a.getName()); 1244 throw new ArgumentException(message); 1245 } 1246 } 1247 } 1248 } 1249 1250 1251 1252 /** 1253 * Check if we have a properties file. 1254 * 1255 * @return The properties found in the properties file or null. 1256 * @throws ArgumentException 1257 * If a problem was encountered while parsing the provided 1258 * arguments. 1259 */ 1260 protected Properties checkExternalProperties() 1261 throws ArgumentException 1262 { 1263 // We don't look for properties file. 1264 if ((noPropertiesFileArgument != null) 1265 && (noPropertiesFileArgument.isPresent())) 1266 { 1267 return null; 1268 } 1269 1270 // Check if we have a properties file argument 1271 if (filePropertiesPathArgument == null) 1272 { 1273 return null; 1274 } 1275 1276 // check if the properties file argument has been set. If not 1277 // look for default location. 1278 String propertiesFilePath = null; 1279 if (filePropertiesPathArgument.isPresent()) 1280 { 1281 propertiesFilePath = filePropertiesPathArgument.getValue(); 1282 } 1283 else 1284 { 1285 // Check in "user home"/.opends directory 1286 String userDir = System.getProperty("user.home"); 1287 propertiesFilePath = findPropertiesFile(userDir + File.separator 1288 + DEFAULT_OPENDS_CONFIG_DIR); 1289 1290 if (propertiesFilePath == null) 1291 { 1292 // check "Opends instance"/config directory 1293 String instanceDir = DirectoryServer.getServerRoot(); 1294 propertiesFilePath = findPropertiesFile(instanceDir+ File.separator 1295 + "config"); 1296 } 1297 } 1298 1299 // We don't have a properties file location 1300 if (propertiesFilePath == null) 1301 { 1302 return null; 1303 } 1304 1305 // We have a location for the properties file. 1306 Properties argumentProperties = new Properties(); 1307 String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME); 1308 try 1309 { 1310 Properties p = new Properties(); 1311 FileInputStream fis = new FileInputStream(propertiesFilePath); 1312 p.load(fis); 1313 fis.close(); 1314 1315 for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) 1316 { 1317 String currentPropertyName = (String) e.nextElement(); 1318 String propertyName = currentPropertyName; 1319 1320 // Property name form <script name>.<property name> has the 1321 // precedence to <property name> 1322 if (scriptName != null) 1323 { 1324 if (currentPropertyName.startsWith(scriptName)) 1325 { 1326 propertyName = currentPropertyName 1327 .substring(scriptName.length() + 1); 1328 } 1329 else 1330 { 1331 if (p.containsKey(scriptName + "." + currentPropertyName )) 1332 { 1333 continue; 1334 } 1335 } 1336 } 1337 argumentProperties.setProperty(propertyName.toLowerCase(), p 1338 .getProperty(currentPropertyName)); 1339 } 1340 } 1341 catch (Exception e) 1342 { 1343 Message message = ERR_ARGPARSER_CANNOT_READ_PROPERTIES_FILE.get(String 1344 .valueOf(propertiesFilePath), getExceptionMessage(e)); 1345 throw new ArgumentException(message, e); 1346 } 1347 return argumentProperties; 1348 } 1349 1350 1351 /** 1352 * Get the absolute path of the properties file. 1353 * 1354 * @param directory 1355 * The location in which we should look for properties file 1356 * @return The absolute path of the properties file or null 1357 */ 1358 private String findPropertiesFile(String directory) 1359 { 1360 // Look for the tools properties file 1361 File f = new File(directory,DEFAULT_OPENDS_PROPERTIES_FILE_NAME 1362 + DEFAULT_OPENDS_PROPERTIES_FILE_EXTENSION); 1363 if (f.exists() && f.canRead()) 1364 { 1365 return f.getAbsolutePath(); 1366 } 1367 else 1368 { 1369 return null; 1370 } 1371 } 1372 1373 /** 1374 * Appends usage information based on the defined arguments to the 1375 * provided buffer. 1376 * 1377 * @param buffer 1378 * The buffer to which the usage information should be 1379 * appended. 1380 */ 1381 public void getUsage(StringBuilder buffer) 1382 { 1383 usageOrVersionDisplayed = true; 1384 if ((toolDescription != null) && (toolDescription.length() > 0)) 1385 { 1386 buffer.append(wrapText(toolDescription.toString(), MAX_LENGTH - 1)); 1387 buffer.append(EOL); 1388 buffer.append(EOL); 1389 } 1390 1391 String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME); 1392 if ((scriptName == null) || (scriptName.length() == 0)) 1393 { 1394 buffer.append(INFO_ARGPARSER_USAGE_JAVA_CLASSNAME.get(mainClassName)); 1395 } 1396 else 1397 { 1398 buffer.append(INFO_ARGPARSER_USAGE_JAVA_SCRIPTNAME.get(scriptName)); 1399 } 1400 1401 if (allowsTrailingArguments) 1402 { 1403 if (trailingArgsDisplayName == null) 1404 { 1405 buffer.append(" "+INFO_ARGPARSER_USAGE_TRAILINGARGS.get()); 1406 } 1407 else 1408 { 1409 buffer.append(" "); 1410 buffer.append(trailingArgsDisplayName); 1411 } 1412 } 1413 buffer.append(EOL); 1414 buffer.append(INFO_SUBCMDPARSER_WHERE_OPTIONS_INCLUDE.get()); 1415 buffer.append(EOL); 1416 buffer.append(EOL); 1417 1418 Argument helpArgument = null ; 1419 1420 boolean printHeaders = printUsageGroupHeaders(); 1421 for (ArgumentGroup argGroup : argumentGroups) 1422 { 1423 if (argGroup.containsArguments() && printHeaders) 1424 { 1425 // Print the groups description if any 1426 Message groupDesc = argGroup.getDescription(); 1427 if (groupDesc != null && !Message.EMPTY.equals(groupDesc)) { 1428 buffer.append(EOL); 1429 buffer.append(wrapText(groupDesc.toString(), MAX_LENGTH - 1)); 1430 buffer.append(EOL); 1431 buffer.append(EOL); 1432 } 1433 } 1434 1435 for (Argument a : argGroup.getArguments()) 1436 { 1437 // If this argument is hidden, then skip it. 1438 if (a.isHidden()) 1439 { 1440 continue; 1441 } 1442 1443 // Help argument should be printed at the end 1444 if ((usageArgument != null) && 1445 usageArgument.getName().equals(a.getName())) 1446 { 1447 helpArgument = a ; 1448 continue ; 1449 } 1450 printArgumentUsage(a, buffer); 1451 } 1452 } 1453 if (helpArgument != null) 1454 { 1455 printArgumentUsage(helpArgument, buffer); 1456 } 1457 else 1458 { 1459 buffer.append(EOL); 1460 buffer.append("-?"); 1461 buffer.append(EOL); 1462 } 1463 } 1464 1465 1466 1467 /** 1468 * Retrieves a message containing usage information based on the defined 1469 * arguments. 1470 * 1471 * @return A string containing usage information based on the defined 1472 * arguments. 1473 */ 1474 public Message getUsageMessage() 1475 { 1476 StringBuilder buffer = new StringBuilder(); 1477 getUsage(buffer); 1478 1479 // TODO: rework getUsage(OutputStream) to work with messages framework 1480 return Message.raw(buffer.toString()); 1481 } 1482 1483 /** 1484 * Retrieves a string containing usage information based on the defined 1485 * arguments. 1486 * 1487 * @return A string containing usage information based on the defined 1488 * arguments. 1489 */ 1490 public String getUsage() 1491 { 1492 StringBuilder buffer = new StringBuilder(); 1493 getUsage(buffer); 1494 1495 return buffer.toString(); 1496 } 1497 1498 1499 1500 /** 1501 * Writes usage information based on the defined arguments to the provided 1502 * output stream. 1503 * 1504 * @param outputStream The output stream to which the usage information 1505 * should be written. 1506 * 1507 * @throws IOException If a problem occurs while attempting to write the 1508 * usage information to the provided output stream. 1509 */ 1510 public void getUsage(OutputStream outputStream) 1511 throws IOException 1512 { 1513 StringBuilder buffer = new StringBuilder(); 1514 getUsage(buffer); 1515 1516 outputStream.write(getBytes(buffer.toString())); 1517 } 1518 1519 1520 1521 /** 1522 * Indicates whether the version or the usage information has been 1523 * displayed to the end user either by an explicit argument like 1524 * "-H" or "--help", or by a built-in argument like "-?". 1525 * 1526 * @return {@code true} if the usage information has been displayed, 1527 * or {@code false} if not. 1528 */ 1529 public boolean usageOrVersionDisplayed() 1530 { 1531 return usageOrVersionDisplayed; 1532 } 1533 1534 /** 1535 * Appends argument usage information to the provided buffer. 1536 * 1537 * @param a The argument to handle. 1538 * @param buffer 1539 * The buffer to which the usage information should be 1540 * appended. 1541 */ 1542 private void printArgumentUsage(Argument a, StringBuilder buffer) 1543 { 1544 // Write a line with the short and/or long identifiers that may be 1545 // used 1546 // for the argument. 1547 final int indentLength = INDENT.length(); 1548 Character shortID = a.getShortIdentifier(); 1549 String longID = a.getLongIdentifier(); 1550 if (shortID != null) 1551 { 1552 int currentLength = buffer.length(); 1553 1554 if (usageArgument.getName().equals(a.getName())) 1555 { 1556 buffer.append("-?, "); 1557 } 1558 1559 buffer.append("-"); 1560 buffer.append(shortID.charValue()); 1561 1562 if (a.needsValue() && longID == null) 1563 { 1564 buffer.append(" "); 1565 buffer.append(a.getValuePlaceholder()); 1566 } 1567 1568 if (longID != null) 1569 { 1570 StringBuilder newBuffer = new StringBuilder(); 1571 newBuffer.append(", --"); 1572 newBuffer.append(longID); 1573 1574 if (a.needsValue()) 1575 { 1576 newBuffer.append(" "); 1577 newBuffer.append(a.getValuePlaceholder()); 1578 } 1579 1580 int lineLength = (buffer.length() - currentLength) + 1581 newBuffer.length(); 1582 if (lineLength > MAX_LENGTH) 1583 { 1584 buffer.append(EOL); 1585 buffer.append(newBuffer.toString()); 1586 } 1587 else 1588 { 1589 buffer.append(newBuffer.toString()); 1590 } 1591 } 1592 1593 buffer.append(EOL); 1594 } 1595 else 1596 { 1597 if (longID != null) 1598 { 1599 if (usageArgument.getName().equals(a.getName())) 1600 { 1601 buffer.append("-?, "); 1602 } 1603 buffer.append("--"); 1604 buffer.append(longID); 1605 1606 if (a.needsValue()) 1607 { 1608 buffer.append(" "); 1609 buffer.append(a.getValuePlaceholder()); 1610 } 1611 1612 buffer.append(EOL); 1613 } 1614 } 1615 1616 1617 // Write one or more lines with the description of the argument. 1618 // We will 1619 // indent the description five characters and try our best to wrap 1620 // at or 1621 // before column 79 so it will be friendly to 80-column displays. 1622 Message description = a.getDescription(); 1623 int descMaxLength = MAX_LENGTH - indentLength - 1; 1624 if (description.length() <= descMaxLength) 1625 { 1626 buffer.append(INDENT); 1627 buffer.append(description); 1628 buffer.append(EOL); 1629 } 1630 else 1631 { 1632 String s = description.toString(); 1633 while (s.length() > descMaxLength) 1634 { 1635 int spacePos = s.lastIndexOf(' ', descMaxLength); 1636 if (spacePos > 0) 1637 { 1638 buffer.append(INDENT); 1639 buffer.append(s.substring(0, spacePos).trim()); 1640 s = s.substring(spacePos+1).trim(); 1641 buffer.append(EOL); 1642 } 1643 else 1644 { 1645 // There are no spaces in the first 74 columns. See if there 1646 // is one 1647 // after that point. If so, then break there. If not, then 1648 // don't 1649 // break at all. 1650 spacePos = s.indexOf(' '); 1651 if (spacePos > 0) 1652 { 1653 buffer.append(INDENT); 1654 buffer.append(s.substring(0, spacePos).trim()); 1655 s = s.substring(spacePos+1).trim(); 1656 buffer.append(EOL); 1657 } 1658 else 1659 { 1660 buffer.append(INDENT); 1661 buffer.append(s); 1662 s = ""; 1663 buffer.append(EOL); 1664 } 1665 } 1666 } 1667 1668 if (s.length() > 0) 1669 { 1670 buffer.append(INDENT); 1671 buffer.append(s); 1672 buffer.append(EOL); 1673 } 1674 } 1675 } 1676 1677 /** 1678 * Given an argument, returns an appropriate group. Arguments may 1679 * be part of one of the special groups or the default group. 1680 * 1681 * @param argument for which a group is requested 1682 * @return argument group appropriate for <code>argument</code> 1683 */ 1684 protected ArgumentGroup getStandardGroup(Argument argument) { 1685 ArgumentGroup group; 1686 if (isInputOutputArgument(argument)) { 1687 group = ioArgGroup; 1688 } else if (isGeneralArgument(argument)) { 1689 group = generalArgGroup; 1690 } else if (isLdapConnectionArgument(argument)) { 1691 group = ldapArgGroup; 1692 } else { 1693 group = defaultArgGroup; 1694 } 1695 return group; 1696 } 1697 1698 /** 1699 * Indicates whether or not argument group description headers 1700 * should be printed. 1701 * 1702 * @return boolean where true means print the descriptions 1703 */ 1704 protected boolean printUsageGroupHeaders() { 1705 // If there is only a single group then we won't print them. 1706 int groupsContainingArgs = 0; 1707 for (ArgumentGroup argGroup : argumentGroups) 1708 { 1709 if (argGroup.containsNonHiddenArguments()) 1710 { 1711 groupsContainingArgs++; 1712 } 1713 } 1714 return groupsContainingArgs > 1; 1715 } 1716 1717 private void initGroups() { 1718 this.argumentGroups = new TreeSet<ArgumentGroup>(); 1719 this.argumentGroups.add(defaultArgGroup); 1720 this.argumentGroups.add(ldapArgGroup); 1721 this.argumentGroups.add(generalArgGroup); 1722 this.argumentGroups.add(ioArgGroup); 1723 1724 try { 1725 versionArgument = new BooleanArgument( 1726 OPTION_LONG_PRODUCT_VERSION, 1727 OPTION_SHORT_PRODUCT_VERSION, 1728 OPTION_LONG_PRODUCT_VERSION, 1729 INFO_DESCRIPTION_PRODUCT_VERSION.get()); 1730 this.generalArgGroup.addArgument(versionArgument); 1731 } catch (ArgumentException e) { 1732 // ignore 1733 } 1734 } 1735 1736 1737 private boolean isInputOutputArgument(Argument arg) { 1738 boolean io = false; 1739 if (arg != null) { 1740 String longId = arg.getLongIdentifier(); 1741 io = OPTION_LONG_VERBOSE.equals(longId) || 1742 OPTION_LONG_QUIET.equals(longId) || 1743 OPTION_LONG_NO_PROMPT.equals(longId) || 1744 OPTION_LONG_PROP_FILE_PATH.equals(longId) || 1745 OPTION_LONG_NO_PROP_FILE.equals(longId) || 1746 OPTION_LONG_SCRIPT_FRIENDLY.equals(longId) || 1747 OPTION_LONG_DONT_WRAP.equals(longId) || 1748 OPTION_LONG_ENCODING.equals(longId) || 1749 OPTION_DSCFG_LONG_DISPLAY_EQUIVALENT.equals(longId) || 1750 OPTION_LONG_EQUIVALENT_COMMAND_FILE_PATH.equals(longId); 1751 } 1752 return io; 1753 } 1754 1755 private boolean isLdapConnectionArgument(Argument arg) { 1756 boolean ldap = false; 1757 if (arg != null) { 1758 String longId = arg.getLongIdentifier(); 1759 ldap = OPTION_LONG_USE_SSL.equals(longId) || 1760 OPTION_LONG_START_TLS.equals(longId) || 1761 OPTION_LONG_HOST.equals(longId) || 1762 OPTION_LONG_PORT.equals(longId) || 1763 OPTION_LONG_BINDDN.equals(longId) || 1764 OPTION_LONG_BINDPWD.equals(longId) || 1765 OPTION_LONG_BINDPWD_FILE.equals(longId) || 1766 OPTION_LONG_SASLOPTION.equals(longId) || 1767 OPTION_LONG_TRUSTALL.equals(longId) || 1768 OPTION_LONG_TRUSTSTOREPATH.equals(longId) || 1769 OPTION_LONG_TRUSTSTORE_PWD.equals(longId) || 1770 OPTION_LONG_TRUSTSTORE_PWD_FILE.equals(longId) || 1771 OPTION_LONG_KEYSTOREPATH.equals(longId) || 1772 OPTION_LONG_KEYSTORE_PWD.equals(longId) || 1773 OPTION_LONG_KEYSTORE_PWD_FILE.equals(longId) || 1774 OPTION_LONG_CERT_NICKNAME.equals(longId) || 1775 OPTION_LONG_REFERENCED_HOST_NAME.equals(longId) || 1776 OPTION_LONG_ADMIN_UID.equals(longId) || 1777 OPTION_LONG_REPORT_AUTHZ_ID.equals(longId) || 1778 OPTION_LONG_USE_PW_POLICY_CTL.equals(longId) || 1779 OPTION_LONG_USE_SASL_EXTERNAL.equals(longId) || 1780 OPTION_LONG_PROTOCOL_VERSION.equals(longId); 1781 } 1782 return ldap; 1783 } 1784 1785 1786 private boolean isGeneralArgument(Argument arg) { 1787 boolean general = false; 1788 if (arg != null) { 1789 String longId = arg.getLongIdentifier(); 1790 general = OPTION_LONG_HELP.equals(longId) || 1791 OPTION_LONG_PRODUCT_VERSION.equals(longId); 1792 } 1793 return general; 1794 } 1795 1796 /** 1797 * Returns whether the usage argument was provided or not. This method 1798 * should be called after a call to parseArguments. 1799 * @return <CODE>true</CODE> if the usage argument was provided and 1800 * <CODE>false</CODE> otherwise. 1801 */ 1802 public boolean isUsageArgumentPresent() 1803 { 1804 boolean isUsageArgumentPresent = false; 1805 if (usageArgument != null) 1806 { 1807 isUsageArgumentPresent = usageArgument.isPresent(); 1808 } 1809 return isUsageArgumentPresent; 1810 } 1811 1812 /** 1813 * Returns whether the version argument was provided or not. This method 1814 * should be called after a call to parseArguments. 1815 * @return <CODE>true</CODE> if the version argument was provided and 1816 * <CODE>false</CODE> otherwise. 1817 */ 1818 public boolean isVersionArgumentPresent() 1819 { 1820 return versionPresent; 1821 } 1822 } 1823