001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.cli;
019    
020    import java.io.PrintWriter;
021    import java.util.ArrayList;
022    import java.util.Collection;
023    import java.util.Collections;
024    import java.util.Comparator;
025    import java.util.Iterator;
026    import java.util.List;
027    
028    /** 
029     * A formatter of help messages for the current command line options
030     *
031     * @author Slawek Zachcial
032     * @author John Keyes (john at integralsource.com)
033     * @version $Revision: 751120 $, $Date: 2009-03-06 14:45:57 -0800 (Fri, 06 Mar 2009) $
034     */
035    public class HelpFormatter
036    {
037        // --------------------------------------------------------------- Constants
038    
039        /** default number of characters per line */
040        public static final int DEFAULT_WIDTH = 74;
041    
042        /** default padding to the left of each line */
043        public static final int DEFAULT_LEFT_PAD = 1;
044    
045        /**
046         * the number of characters of padding to be prefixed
047         * to each description line
048         */
049        public static final int DEFAULT_DESC_PAD = 3;
050    
051        /** the string to display at the beginning of the usage statement */
052        public static final String DEFAULT_SYNTAX_PREFIX = "usage: ";
053    
054        /** default prefix for shortOpts */
055        public static final String DEFAULT_OPT_PREFIX = "-";
056    
057        /** default prefix for long Option */
058        public static final String DEFAULT_LONG_OPT_PREFIX = "--";
059    
060        /** default name for an argument */
061        public static final String DEFAULT_ARG_NAME = "arg";
062    
063        // -------------------------------------------------------------- Attributes
064    
065        /**
066         * number of characters per line
067         *
068         * @deprecated Scope will be made private for next major version
069         * - use get/setWidth methods instead.
070         */
071        public int defaultWidth = DEFAULT_WIDTH;
072    
073        /**
074         * amount of padding to the left of each line
075         *
076         * @deprecated Scope will be made private for next major version
077         * - use get/setLeftPadding methods instead.
078         */
079        public int defaultLeftPad = DEFAULT_LEFT_PAD;
080    
081        /**
082         * the number of characters of padding to be prefixed
083         * to each description line
084         *
085         * @deprecated Scope will be made private for next major version
086         * - use get/setDescPadding methods instead.
087         */
088        public int defaultDescPad = DEFAULT_DESC_PAD;
089    
090        /**
091         * the string to display at the begining of the usage statement
092         *
093         * @deprecated Scope will be made private for next major version
094         * - use get/setSyntaxPrefix methods instead.
095         */
096        public String defaultSyntaxPrefix = DEFAULT_SYNTAX_PREFIX;
097    
098        /**
099         * 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    }