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