View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.cli;
19  
20  import java.io.BufferedReader;
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.io.Serializable;
24  import java.io.StringReader;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.Comparator;
30  import java.util.Iterator;
31  import java.util.List;
32  
33  /**
34   * A formatter of help messages for command line options.
35   *
36   * <p>Example:</p>
37   * 
38   * <pre>
39   * Options options = new Options();
40   * options.addOption(OptionBuilder.withLongOpt("file")
41   *                                .withDescription("The file to be processed")
42   *                                .hasArg()
43   *                                .withArgName("FILE")
44   *                                .isRequired()
45   *                                .create('f'));
46   * options.addOption(OptionBuilder.withLongOpt("version")
47   *                                .withDescription("Print the version of the application")
48   *                                .create('v'));
49   * options.addOption(OptionBuilder.withLongOpt("help").create('h'));
50   * 
51   * String header = "Do something useful with an input file\n\n";
52   * String footer = "\nPlease report issues at http://example.com/issues";
53   * 
54   * HelpFormatter formatter = new HelpFormatter();
55   * formatter.printHelp("myapp", header, options, footer, true);
56   * </pre>
57   * 
58   * This produces the following output:
59   * 
60   * <pre>
61   * usage: myapp -f &lt;FILE&gt; [-h] [-v]
62   * Do something useful with an input file
63   * 
64   *  -f,--file &lt;FILE&gt;   The file to be processed
65   *  -h,--help
66   *  -v,--version       Print the version of the application
67   * 
68   * Please report issues at http://example.com/issues
69   * </pre>
70   */
71  public class HelpFormatter
72  {
73      // --------------------------------------------------------------- Constants
74  
75      /** default number of characters per line */
76      public static final int DEFAULT_WIDTH = 74;
77  
78      /** default padding to the left of each line */
79      public static final int DEFAULT_LEFT_PAD = 1;
80  
81      /** number of space characters to be prefixed to each description line */
82      public static final int DEFAULT_DESC_PAD = 3;
83  
84      /** the string to display at the beginning of the usage statement */
85      public static final String DEFAULT_SYNTAX_PREFIX = "usage: ";
86  
87      /** default prefix for shortOpts */
88      public static final String DEFAULT_OPT_PREFIX = "-";
89  
90      /** default prefix for long Option */
91      public static final String DEFAULT_LONG_OPT_PREFIX = "--";
92  
93      /** 
94       * default separator displayed between a long Option and its value
95       * 
96       * @since 1.3
97       **/
98      public static final String DEFAULT_LONG_OPT_SEPARATOR = " ";
99  
100     /** default name for an argument */
101     public static final String DEFAULT_ARG_NAME = "arg";
102 
103     // -------------------------------------------------------------- Attributes
104 
105     /**
106      * number of characters per line
107      *
108      * @deprecated Scope will be made private for next major version
109      * - use get/setWidth methods instead.
110      */
111     @Deprecated
112     public int defaultWidth = DEFAULT_WIDTH;
113 
114     /**
115      * amount of padding to the left of each line
116      *
117      * @deprecated Scope will be made private for next major version
118      * - use get/setLeftPadding methods instead.
119      */
120     @Deprecated
121     public int defaultLeftPad = DEFAULT_LEFT_PAD;
122 
123     /**
124      * the number of characters of padding to be prefixed
125      * to each description line
126      *
127      * @deprecated Scope will be made private for next major version
128      * - use get/setDescPadding methods instead.
129      */
130     @Deprecated
131     public int defaultDescPad = DEFAULT_DESC_PAD;
132 
133     /**
134      * the string to display at the beginning of the usage statement
135      *
136      * @deprecated Scope will be made private for next major version
137      * - use get/setSyntaxPrefix methods instead.
138      */
139     @Deprecated
140     public String defaultSyntaxPrefix = DEFAULT_SYNTAX_PREFIX;
141 
142     /**
143      * the new line string
144      *
145      * @deprecated Scope will be made private for next major version
146      * - use get/setNewLine methods instead.
147      */
148     @Deprecated
149     public String defaultNewLine = System.getProperty("line.separator");
150 
151     /**
152      * the shortOpt prefix
153      *
154      * @deprecated Scope will be made private for next major version
155      * - use get/setOptPrefix methods instead.
156      */
157     @Deprecated
158     public String defaultOptPrefix = DEFAULT_OPT_PREFIX;
159 
160     /**
161      * the long Opt prefix
162      *
163      * @deprecated Scope will be made private for next major version
164      * - use get/setLongOptPrefix methods instead.
165      */
166     @Deprecated
167     public String defaultLongOptPrefix = DEFAULT_LONG_OPT_PREFIX;
168 
169     /**
170      * the name of the argument
171      *
172      * @deprecated Scope will be made private for next major version
173      * - use get/setArgName methods instead.
174      */
175     @Deprecated
176     public String defaultArgName = DEFAULT_ARG_NAME;
177 
178     /**
179      * Comparator used to sort the options when they output in help text
180      * 
181      * Defaults to case-insensitive alphabetical sorting by option key
182      */
183     protected Comparator<Option> optionComparator = new OptionComparator();
184 
185     /** The separator displayed between the long option and its value. */
186     private String longOptSeparator = DEFAULT_LONG_OPT_SEPARATOR;
187 
188     /**
189      * Sets the 'width'.
190      *
191      * @param width the new value of 'width'
192      */
193     public void setWidth(final int width)
194     {
195         this.defaultWidth = width;
196     }
197 
198     /**
199      * Returns the 'width'.
200      *
201      * @return the 'width'
202      */
203     public int getWidth()
204     {
205         return defaultWidth;
206     }
207 
208     /**
209      * Sets the 'leftPadding'.
210      *
211      * @param padding the new value of 'leftPadding'
212      */
213     public void setLeftPadding(final int padding)
214     {
215         this.defaultLeftPad = padding;
216     }
217 
218     /**
219      * Returns the 'leftPadding'.
220      *
221      * @return the 'leftPadding'
222      */
223     public int getLeftPadding()
224     {
225         return defaultLeftPad;
226     }
227 
228     /**
229      * Sets the 'descPadding'.
230      *
231      * @param padding the new value of 'descPadding'
232      */
233     public void setDescPadding(final int padding)
234     {
235         this.defaultDescPad = padding;
236     }
237 
238     /**
239      * Returns the 'descPadding'.
240      *
241      * @return the 'descPadding'
242      */
243     public int getDescPadding()
244     {
245         return defaultDescPad;
246     }
247 
248     /**
249      * Sets the 'syntaxPrefix'.
250      *
251      * @param prefix the new value of 'syntaxPrefix'
252      */
253     public void setSyntaxPrefix(final String prefix)
254     {
255         this.defaultSyntaxPrefix = prefix;
256     }
257 
258     /**
259      * Returns the 'syntaxPrefix'.
260      *
261      * @return the 'syntaxPrefix'
262      */
263     public String getSyntaxPrefix()
264     {
265         return defaultSyntaxPrefix;
266     }
267 
268     /**
269      * Sets the 'newLine'.
270      *
271      * @param newline the new value of 'newLine'
272      */
273     public void setNewLine(final String newline)
274     {
275         this.defaultNewLine = newline;
276     }
277 
278     /**
279      * Returns the 'newLine'.
280      *
281      * @return the 'newLine'
282      */
283     public String getNewLine()
284     {
285         return defaultNewLine;
286     }
287 
288     /**
289      * Sets the 'optPrefix'.
290      *
291      * @param prefix the new value of 'optPrefix'
292      */
293     public void setOptPrefix(final String prefix)
294     {
295         this.defaultOptPrefix = prefix;
296     }
297 
298     /**
299      * Returns the 'optPrefix'.
300      *
301      * @return the 'optPrefix'
302      */
303     public String getOptPrefix()
304     {
305         return defaultOptPrefix;
306     }
307 
308     /**
309      * Sets the 'longOptPrefix'.
310      *
311      * @param prefix the new value of 'longOptPrefix'
312      */
313     public void setLongOptPrefix(final String prefix)
314     {
315         this.defaultLongOptPrefix = prefix;
316     }
317 
318     /**
319      * Returns the 'longOptPrefix'.
320      *
321      * @return the 'longOptPrefix'
322      */
323     public String getLongOptPrefix()
324     {
325         return defaultLongOptPrefix;
326     }
327 
328     /**
329      * Set the separator displayed between a long option and its value.
330      * Ensure that the separator specified is supported by the parser used,
331      * typically ' ' or '='.
332      * 
333      * @param longOptSeparator the separator, typically ' ' or '='.
334      * @since 1.3
335      */
336     public void setLongOptSeparator(final String longOptSeparator)
337     {
338         this.longOptSeparator = longOptSeparator;
339     }
340 
341     /**
342      * Returns the separator displayed between a long option and its value.
343      * 
344      * @return the separator
345      * @since 1.3
346      */
347     public String getLongOptSeparator()
348     {
349         return longOptSeparator;
350     }
351 
352     /**
353      * Sets the 'argName'.
354      *
355      * @param name the new value of 'argName'
356      */
357     public void setArgName(final String name)
358     {
359         this.defaultArgName = name;
360     }
361 
362     /**
363      * Returns the 'argName'.
364      *
365      * @return the 'argName'
366      */
367     public String getArgName()
368     {
369         return defaultArgName;
370     }
371 
372     /**
373      * Comparator used to sort the options when they output in help text.
374      * Defaults to case-insensitive alphabetical sorting by option key.
375      *
376      * @return the {@link Comparator} currently in use to sort the options
377      * @since 1.2
378      */
379     public Comparator<Option> getOptionComparator()
380     {
381         return optionComparator;
382     }
383 
384     /**
385      * Set the comparator used to sort the options when they output in help text.
386      * Passing in a null comparator will keep the options in the order they were declared.
387      *
388      * @param comparator the {@link Comparator} to use for sorting the options
389      * @since 1.2
390      */
391     public void setOptionComparator(final Comparator<Option> comparator)
392     {
393         this.optionComparator = comparator;
394     }
395 
396     /**
397      * Print the help for <code>options</code> with the specified
398      * command line syntax.  This method prints help information to
399      * System.out.
400      *
401      * @param cmdLineSyntax the syntax for this application
402      * @param options the Options instance
403      */
404     public void printHelp(final String cmdLineSyntax, final Options options)
405     {
406         printHelp(getWidth(), cmdLineSyntax, null, options, null, false);
407     }
408 
409     /**
410      * Print the help for <code>options</code> with the specified
411      * command line syntax.  This method prints help information to 
412      * System.out.
413      *
414      * @param cmdLineSyntax the syntax for this application
415      * @param options the Options instance
416      * @param autoUsage whether to print an automatically generated
417      * usage statement
418      */
419     public void printHelp(final String cmdLineSyntax, final Options options, final boolean autoUsage)
420     {
421         printHelp(getWidth(), cmdLineSyntax, null, options, null, autoUsage);
422     }
423 
424     /**
425      * Print the help for <code>options</code> with the specified
426      * command line syntax.  This method prints help information to
427      * System.out.
428      *
429      * @param cmdLineSyntax the syntax for this application
430      * @param header the banner to display at the beginning of the help
431      * @param options the Options instance
432      * @param footer the banner to display at the end of the help
433      */
434     public void printHelp(final String cmdLineSyntax, final String header, final Options options, final String footer)
435     {
436         printHelp(cmdLineSyntax, header, options, footer, false);
437     }
438 
439     /**
440      * Print the help for <code>options</code> with the specified
441      * command line syntax.  This method prints help information to 
442      * System.out.
443      *
444      * @param cmdLineSyntax the syntax for this application
445      * @param header the banner to display at the beginning of the help
446      * @param options the Options instance
447      * @param footer the banner to display at the end of the help
448      * @param autoUsage whether to print an automatically generated
449      * usage statement
450      */
451     public void printHelp(final String cmdLineSyntax, final String header, final Options options, final String footer, final boolean autoUsage)
452     {
453         printHelp(getWidth(), cmdLineSyntax, header, options, footer, autoUsage);
454     }
455 
456     /**
457      * Print the help for <code>options</code> with the specified
458      * command line syntax.  This method prints help information to
459      * System.out.
460      *
461      * @param width the number of characters to be displayed on each line
462      * @param cmdLineSyntax the syntax for this application
463      * @param header the banner to display at the beginning of the help
464      * @param options the Options instance
465      * @param footer the banner to display at the end of the help
466      */
467     public void printHelp(final int width, final String cmdLineSyntax, final String header, final Options options, final String footer)
468     {
469         printHelp(width, cmdLineSyntax, header, options, footer, false);
470     }
471 
472     /**
473      * Print the help for <code>options</code> with the specified
474      * command line syntax.  This method prints help information to
475      * System.out.
476      *
477      * @param width the number of characters to be displayed on each line
478      * @param cmdLineSyntax the syntax for this application
479      * @param header the banner to display at the beginning of the help
480      * @param options the Options instance
481      * @param footer the banner to display at the end of the help
482      * @param autoUsage whether to print an automatically generated 
483      * usage statement
484      */
485     public void printHelp(final int width, final String cmdLineSyntax, final String header,
486                           final Options options, final String footer, final boolean autoUsage)
487     {
488         final PrintWriter pw = new PrintWriter(System.out);
489 
490         printHelp(pw, width, cmdLineSyntax, header, options, getLeftPadding(), getDescPadding(), footer, autoUsage);
491         pw.flush();
492     }
493 
494     /**
495      * Print the help for <code>options</code> with the specified
496      * command line syntax.
497      *
498      * @param pw the writer to which the help will be written
499      * @param width the number of characters to be displayed on each line
500      * @param cmdLineSyntax the syntax for this application
501      * @param header the banner to display at the beginning of the help
502      * @param options the Options instance
503      * @param leftPad the number of characters of padding to be prefixed
504      * to each line
505      * @param descPad the number of characters of padding to be prefixed
506      * to each description line
507      * @param footer the banner to display at the end of the help
508      *
509      * @throws IllegalStateException if there is no room to print a line
510      */
511     public void printHelp(final PrintWriter pw, final int width, final String cmdLineSyntax, 
512                           final String header, final Options options, final int leftPad, 
513                           final int descPad, final String footer)
514     {
515         printHelp(pw, width, cmdLineSyntax, header, options, leftPad, descPad, footer, false);
516     }
517 
518 
519     /**
520      * Print the help for <code>options</code> with the specified
521      * command line syntax.
522      *
523      * @param pw the writer to which the help will be written
524      * @param width the number of characters to be displayed on each line
525      * @param cmdLineSyntax the syntax for this application
526      * @param header the banner to display at the beginning of the help
527      * @param options the Options instance
528      * @param leftPad the number of characters of padding to be prefixed
529      * to each line
530      * @param descPad the number of characters of padding to be prefixed
531      * to each description line
532      * @param footer the banner to display at the end of the help
533      * @param autoUsage whether to print an automatically generated
534      * usage statement
535      *
536      * @throws IllegalStateException if there is no room to print a line
537      */
538     public void printHelp(final PrintWriter pw, final int width, final String cmdLineSyntax,
539                           final String header, final Options options, final int leftPad,
540                           final int descPad, final String footer, final boolean autoUsage)
541     {
542         if (cmdLineSyntax == null || cmdLineSyntax.length() == 0)
543         {
544             throw new IllegalArgumentException("cmdLineSyntax not provided");
545         }
546 
547         if (autoUsage)
548         {
549             printUsage(pw, width, cmdLineSyntax, options);
550         }
551         else
552         {
553             printUsage(pw, width, cmdLineSyntax);
554         }
555 
556         if (header != null && header.trim().length() > 0)
557         {
558             printWrapped(pw, width, header);
559         }
560 
561         printOptions(pw, width, options, leftPad, descPad);
562 
563         if (footer != null && footer.trim().length() > 0)
564         {
565             printWrapped(pw, width, footer);
566         }
567     }
568 
569     /**
570      * Prints the usage statement for the specified application.
571      *
572      * @param pw The PrintWriter to print the usage statement 
573      * @param width The number of characters to display per line
574      * @param app The application name
575      * @param options The command line Options
576      */
577     public void printUsage(final PrintWriter pw, final int width, final String app, final Options options)
578     {
579         // initialise the string buffer
580         final StringBuffer buff = new StringBuffer(getSyntaxPrefix()).append(app).append(" ");
581 
582         // create a list for processed option groups
583         final Collection<OptionGroup> processedGroups = new ArrayList<OptionGroup>();
584 
585         final List<Option> optList = new ArrayList<Option>(options.getOptions());
586         if (getOptionComparator() != null)
587         {
588             Collections.sort(optList, getOptionComparator());
589         }
590         // iterate over the options
591         for (final Iterator<Option> it = optList.iterator(); it.hasNext();)
592         {
593             // get the next Option
594             final Option option = it.next();
595 
596             // check if the option is part of an OptionGroup
597             final OptionGroup group = options.getOptionGroup(option);
598 
599             // if the option is part of a group 
600             if (group != null)
601             {
602                 // and if the group has not already been processed
603                 if (!processedGroups.contains(group))
604                 {
605                     // add the group to the processed list
606                     processedGroups.add(group);
607 
608 
609                     // add the usage clause
610                     appendOptionGroup(buff, group);
611                 }
612 
613                 // otherwise the option was displayed in the group
614                 // previously so ignore it.
615             }
616 
617             // if the Option is not part of an OptionGroup
618             else
619             {
620                 appendOption(buff, option, option.isRequired());
621             }
622 
623             if (it.hasNext())
624             {
625                 buff.append(" ");
626             }
627         }
628 
629 
630         // call printWrapped
631         printWrapped(pw, width, buff.toString().indexOf(' ') + 1, buff.toString());
632     }
633 
634     /**
635      * Appends the usage clause for an OptionGroup to a StringBuffer.  
636      * The clause is wrapped in square brackets if the group is required.
637      * The display of the options is handled by appendOption
638      * @param buff the StringBuffer to append to
639      * @param group the group to append
640      * @see #appendOption(StringBuffer,Option,boolean)
641      */
642     private void appendOptionGroup(final StringBuffer buff, final OptionGroup group)
643     {
644         if (!group.isRequired())
645         {
646             buff.append("[");
647         }
648 
649         final List<Option> optList = new ArrayList<Option>(group.getOptions());
650         if (getOptionComparator() != null)
651         {
652             Collections.sort(optList, getOptionComparator());
653         }
654         // for each option in the OptionGroup
655         for (final Iterator<Option> it = optList.iterator(); it.hasNext();)
656         {
657             // whether the option is required or not is handled at group level
658             appendOption(buff, it.next(), true);
659 
660             if (it.hasNext())
661             {
662                 buff.append(" | ");
663             }
664         }
665 
666         if (!group.isRequired())
667         {
668             buff.append("]");
669         }
670     }
671 
672     /**
673      * Appends the usage clause for an Option to a StringBuffer.  
674      *
675      * @param buff the StringBuffer to append to
676      * @param option the Option to append
677      * @param required whether the Option is required or not
678      */
679     private void appendOption(final StringBuffer buff, final Option option, final boolean required)
680     {
681         if (!required)
682         {
683             buff.append("[");
684         }
685 
686         if (option.getOpt() != null)
687         {
688             buff.append("-").append(option.getOpt());
689         }
690         else
691         {
692             buff.append("--").append(option.getLongOpt());
693         }
694         
695         // if the Option has a value and a non blank argname
696         if (option.hasArg() && (option.getArgName() == null || option.getArgName().length() != 0))
697         {
698             buff.append(option.getOpt() == null ? longOptSeparator : " ");
699             buff.append("<").append(option.getArgName() != null ? option.getArgName() : getArgName()).append(">");
700         }
701         
702         // if the Option is not a required option
703         if (!required)
704         {
705             buff.append("]");
706         }
707     }
708 
709     /**
710      * Print the cmdLineSyntax to the specified writer, using the
711      * specified width.
712      *
713      * @param pw The printWriter to write the help to
714      * @param width The number of characters per line for the usage statement.
715      * @param cmdLineSyntax The usage statement.
716      */
717     public void printUsage(final PrintWriter pw, final int width, final String cmdLineSyntax)
718     {
719         final int argPos = cmdLineSyntax.indexOf(' ') + 1;
720 
721         printWrapped(pw, width, getSyntaxPrefix().length() + argPos, getSyntaxPrefix() + cmdLineSyntax);
722     }
723 
724     /**
725      * Print the help for the specified Options to the specified writer, 
726      * using the specified width, left padding and description padding.
727      *
728      * @param pw The printWriter to write the help to
729      * @param width The number of characters to display per line
730      * @param options The command line Options
731      * @param leftPad the number of characters of padding to be prefixed
732      * to each line
733      * @param descPad the number of characters of padding to be prefixed
734      * to each description line
735      */
736     public void printOptions(final PrintWriter pw, final int width, final Options options, 
737                              final int leftPad, final int descPad)
738     {
739         final StringBuffer sb = new StringBuffer();
740 
741         renderOptions(sb, width, options, leftPad, descPad);
742         pw.println(sb.toString());
743     }
744 
745     /**
746      * Print the specified text to the specified PrintWriter.
747      *
748      * @param pw The printWriter to write the help to
749      * @param width The number of characters to display per line
750      * @param text The text to be written to the PrintWriter
751      */
752     public void printWrapped(final PrintWriter pw, final int width, final String text)
753     {
754         printWrapped(pw, width, 0, text);
755     }
756 
757     /**
758      * Print the specified text to the specified PrintWriter.
759      *
760      * @param pw The printWriter to write the help to
761      * @param width The number of characters to display per line
762      * @param nextLineTabStop The position on the next line for the first tab.
763      * @param text The text to be written to the PrintWriter
764      */
765     public void printWrapped(final PrintWriter pw, final int width, final int nextLineTabStop, final String text)
766     {
767         final StringBuffer sb = new StringBuffer(text.length());
768 
769         renderWrappedTextBlock(sb, width, nextLineTabStop, text);
770         pw.println(sb.toString());
771     }
772 
773     // --------------------------------------------------------------- Protected
774 
775     /**
776      * Render the specified Options and return the rendered Options
777      * in a StringBuffer.
778      *
779      * @param sb The StringBuffer to place the rendered Options into.
780      * @param width The number of characters to display per line
781      * @param options The command line Options
782      * @param leftPad the number of characters of padding to be prefixed
783      * to each line
784      * @param descPad the number of characters of padding to be prefixed
785      * to each description line
786      *
787      * @return the StringBuffer with the rendered Options contents.
788      */
789     protected StringBuffer renderOptions(final StringBuffer sb, final int width, final Options options, final int leftPad, final int descPad)
790     {
791         final String lpad = createPadding(leftPad);
792         final String dpad = createPadding(descPad);
793 
794         // first create list containing only <lpad>-a,--aaa where 
795         // -a is opt and --aaa is long opt; in parallel look for 
796         // the longest opt string this list will be then used to 
797         // sort options ascending
798         int max = 0;
799         final List<StringBuffer> prefixList = new ArrayList<StringBuffer>();
800 
801         final List<Option> optList = options.helpOptions();
802 
803         if (getOptionComparator() != null)
804         {
805             Collections.sort(optList, getOptionComparator());
806         }
807 
808         for (final Option option : optList)
809         {
810             final StringBuffer optBuf = new StringBuffer();
811 
812             if (option.getOpt() == null)
813             {
814                 optBuf.append(lpad).append("   ").append(getLongOptPrefix()).append(option.getLongOpt());
815             }
816             else
817             {
818                 optBuf.append(lpad).append(getOptPrefix()).append(option.getOpt());
819 
820                 if (option.hasLongOpt())
821                 {
822                     optBuf.append(',').append(getLongOptPrefix()).append(option.getLongOpt());
823                 }
824             }
825 
826             if (option.hasArg())
827             {
828                 final String argName = option.getArgName();
829                 if (argName != null && argName.length() == 0)
830                 {
831                     // if the option has a blank argname
832                     optBuf.append(' ');
833                 }
834                 else
835                 {
836                     optBuf.append(option.hasLongOpt() ? longOptSeparator : " ");
837                     optBuf.append("<").append(argName != null ? option.getArgName() : getArgName()).append(">");
838                 }
839             }
840 
841             prefixList.add(optBuf);
842             max = optBuf.length() > max ? optBuf.length() : max;
843         }
844 
845         int x = 0;
846 
847         for (final Iterator<Option> it = optList.iterator(); it.hasNext();)
848         {
849             final Option option = it.next();
850             final StringBuilder optBuf = new StringBuilder(prefixList.get(x++).toString());
851 
852             if (optBuf.length() < max)
853             {
854                 optBuf.append(createPadding(max - optBuf.length()));
855             }
856 
857             optBuf.append(dpad);
858 
859             final int nextLineTabStop = max + descPad;
860 
861             if (option.getDescription() != null)
862             {
863                 optBuf.append(option.getDescription());
864             }
865 
866             renderWrappedText(sb, width, nextLineTabStop, optBuf.toString());
867 
868             if (it.hasNext())
869             {
870                 sb.append(getNewLine());
871             }
872         }
873 
874         return sb;
875     }
876 
877     /**
878      * Render the specified text and return the rendered Options
879      * in a StringBuffer.
880      *
881      * @param sb The StringBuffer to place the rendered text into.
882      * @param width The number of characters to display per line
883      * @param nextLineTabStop The position on the next line for the first tab.
884      * @param text The text to be rendered.
885      *
886      * @return the StringBuffer with the rendered Options contents.
887      */
888     protected StringBuffer renderWrappedText(final StringBuffer sb, final int width, 
889                                              int nextLineTabStop, String text)
890     {
891         int pos = findWrapPos(text, width, 0);
892 
893         if (pos == -1)
894         {
895             sb.append(rtrim(text));
896 
897             return sb;
898         }
899         sb.append(rtrim(text.substring(0, pos))).append(getNewLine());
900 
901         if (nextLineTabStop >= width)
902         {
903             // stops infinite loop happening
904             nextLineTabStop = 1;
905         }
906 
907         // all following lines must be padded with nextLineTabStop space characters
908         final String padding = createPadding(nextLineTabStop);
909 
910         while (true)
911         {
912             text = padding + text.substring(pos).trim();
913             pos = findWrapPos(text, width, 0);
914 
915             if (pos == -1)
916             {
917                 sb.append(text);
918 
919                 return sb;
920             }
921 
922             if (text.length() > width && pos == nextLineTabStop - 1)
923             {
924                 pos = width;
925             }
926 
927             sb.append(rtrim(text.substring(0, pos))).append(getNewLine());
928         }
929     }
930 
931     /**
932      * Render the specified text width a maximum width. This method differs
933      * from renderWrappedText by not removing leading spaces after a new line.
934      *
935      * @param sb The StringBuffer to place the rendered text into.
936      * @param width The number of characters to display per line
937      * @param nextLineTabStop The position on the next line for the first tab.
938      * @param text The text to be rendered.
939      */
940     private Appendable renderWrappedTextBlock(final StringBuffer sb, final int width, final int nextLineTabStop, final String text)
941     {
942         try
943         {
944             final BufferedReader in = new BufferedReader(new StringReader(text));
945             String line;
946             boolean firstLine = true;
947             while ((line = in.readLine()) != null)
948             {
949                 if (!firstLine)
950                 {
951                     sb.append(getNewLine());
952                 }
953                 else
954                 {
955                     firstLine = false;
956                 }
957                 renderWrappedText(sb, width, nextLineTabStop, line);
958             }
959         }
960         catch (final IOException e) //NOPMD
961         {
962             // cannot happen
963         }
964 
965         return sb;
966     }
967 
968     /**
969      * Finds the next text wrap position after <code>startPos</code> for the
970      * text in <code>text</code> with the column width <code>width</code>.
971      * The wrap point is the last position before startPos+width having a 
972      * whitespace character (space, \n, \r). If there is no whitespace character
973      * before startPos+width, it will return startPos+width.
974      *
975      * @param text The text being searched for the wrap position
976      * @param width width of the wrapped text
977      * @param startPos position from which to start the lookup whitespace
978      * character
979      * @return position on which the text must be wrapped or -1 if the wrap
980      * position is at the end of the text
981      */
982     protected int findWrapPos(final String text, final int width, final int startPos)
983     {
984         // the line ends before the max wrap pos or a new line char found
985         int pos = text.indexOf('\n', startPos);
986         if (pos != -1 && pos <= width)
987         {
988             return pos + 1;
989         }
990 
991         pos = text.indexOf('\t', startPos);
992         if (pos != -1 && pos <= width)
993         {
994             return pos + 1;
995         }
996 
997         if (startPos + width >= text.length())
998         {
999             return -1;
1000         }
1001 
1002         // look for the last whitespace character before startPos+width
1003         for (pos = startPos + width; pos >= startPos; --pos)
1004         {
1005             final char c = text.charAt(pos);
1006             if (c == ' ' || c == '\n' || c == '\r')
1007             {
1008                 break;
1009             }
1010         }
1011 
1012         // if we found it - just return
1013         if (pos > startPos)
1014         {
1015             return pos;
1016         }
1017 
1018         // if we didn't find one, simply chop at startPos+width
1019         pos = startPos + width;
1020 
1021         return pos == text.length() ? -1 : pos;
1022     }
1023 
1024     /**
1025      * Return a String of padding of length <code>len</code>.
1026      *
1027      * @param len The length of the String of padding to create.
1028      *
1029      * @return The String of padding
1030      */
1031     protected String createPadding(final int len)
1032     {
1033         final char[] padding = new char[len];
1034         Arrays.fill(padding, ' ');
1035 
1036         return new String(padding);
1037     }
1038 
1039     /**
1040      * Remove the trailing whitespace from the specified String.
1041      *
1042      * @param s The String to remove the trailing padding from.
1043      *
1044      * @return The String of without the trailing padding
1045      */
1046     protected String rtrim(final String s)
1047     {
1048         if (s == null || s.length() == 0)
1049         {
1050             return s;
1051         }
1052 
1053         int pos = s.length();
1054 
1055         while (pos > 0 && Character.isWhitespace(s.charAt(pos - 1)))
1056         {
1057             --pos;
1058         }
1059 
1060         return s.substring(0, pos);
1061     }
1062 
1063     // ------------------------------------------------------ Package protected
1064     // ---------------------------------------------------------------- Private
1065     // ---------------------------------------------------------- Inner classes
1066     /**
1067      * This class implements the <code>Comparator</code> interface
1068      * for comparing Options.
1069      */
1070     private static class OptionComparator implements Comparator<Option>, Serializable
1071     {
1072         /** The serial version UID. */
1073         private static final long serialVersionUID = 5305467873966684014L;
1074 
1075         /**
1076          * Compares its two arguments for order. Returns a negative
1077          * integer, zero, or a positive integer as the first argument
1078          * is less than, equal to, or greater than the second.
1079          *
1080          * @param opt1 The first Option to be compared.
1081          * @param opt2 The second Option to be compared.
1082          * @return a negative integer, zero, or a positive integer as
1083          *         the first argument is less than, equal to, or greater than the
1084          *         second.
1085          */
1086         public int compare(final Option opt1, final Option opt2)
1087         {
1088             return opt1.getKey().compareToIgnoreCase(opt2.getKey());
1089         }
1090     }
1091 
1092 }