    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    }