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