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