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
018package org.apache.commons.cli;
019
020import java.io.BufferedReader;
021import java.io.IOException;
022import java.io.PrintWriter;
023import java.io.Serializable;
024import java.io.StringReader;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.Comparator;
030import java.util.Iterator;
031import java.util.List;
032
033/**
034 * A formatter of help messages for command line options.
035 *
036 * <p>
037 * Example:
038 * </p>
039 *
040 * <pre>
041 * Options options = new Options();
042 * options.addOption(OptionBuilder.withLongOpt("file").withDescription("The file to be processed").hasArg().withArgName("FILE").isRequired().create('f'));
043 * options.addOption(OptionBuilder.withLongOpt("version").withDescription("Print the version of the application").create('v'));
044 * options.addOption(OptionBuilder.withLongOpt("help").create('h'));
045 *
046 * String header = "Do something useful with an input file\n\n";
047 * String footer = "\nPlease report issues at http://example.com/issues";
048 *
049 * HelpFormatter formatter = new HelpFormatter();
050 * formatter.printHelp("myapp", header, options, footer, true);
051 * </pre>
052 *
053 * This produces the following output:
054 *
055 * <pre>
056 * usage: myapp -f &lt;FILE&gt; [-h] [-v]
057 * Do something useful with an input file
058 *
059 *  -f,--file &lt;FILE&gt;   The file to be processed
060 *  -h,--help
061 *  -v,--version       Print the version of the application
062 *
063 * Please report issues at http://example.com/issues
064 * </pre>
065 */
066public class HelpFormatter {
067
068    /**
069     * This class implements the {@code Comparator} interface for comparing Options.
070     */
071    private static class OptionComparator implements Comparator<Option>, Serializable {
072        /** The serial version UID. */
073        private static final long serialVersionUID = 5305467873966684014L;
074
075        /**
076         * Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument
077         * is less than, equal to, or greater than the second.
078         *
079         * @param opt1 The first Option to be compared.
080         * @param opt2 The second Option to be compared.
081         * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than
082         *         the second.
083         */
084        @Override
085        public int compare(final Option opt1, final Option opt2) {
086            return opt1.getKey().compareToIgnoreCase(opt2.getKey());
087        }
088    }
089
090    /** Default number of characters per line */
091    public static final int DEFAULT_WIDTH = 74;
092
093    /** Default padding to the left of each line */
094    public static final int DEFAULT_LEFT_PAD = 1;
095
096    /** number of space characters to be prefixed to each description line */
097    public static final int DEFAULT_DESC_PAD = 3;
098
099    /** The string to display at the beginning of the usage statement */
100    public static final String DEFAULT_SYNTAX_PREFIX = "usage: ";
101
102    /** Default prefix for shortOpts */
103    public static final String DEFAULT_OPT_PREFIX = "-";
104
105    /** Default prefix for long Option */
106    public static final String DEFAULT_LONG_OPT_PREFIX = "--";
107
108    /**
109     * default separator displayed between a long Option and its value
110     *
111     * @since 1.3
112     **/
113    public static final String DEFAULT_LONG_OPT_SEPARATOR = " ";
114
115    /** Default name for an argument */
116    public static final String DEFAULT_ARG_NAME = "arg";
117
118    /**
119     * number of characters per line
120     *
121     * @deprecated Scope will be made private for next major version - use get/setWidth methods instead.
122     */
123    @Deprecated
124    public int defaultWidth = DEFAULT_WIDTH;
125
126    /**
127     * amount of padding to the left of each line
128     *
129     * @deprecated Scope will be made private for next major version - use get/setLeftPadding methods instead.
130     */
131    @Deprecated
132    public int defaultLeftPad = DEFAULT_LEFT_PAD;
133
134    /**
135     * the number of characters of padding to be prefixed to each description line
136     *
137     * @deprecated Scope will be made private for next major version - use get/setDescPadding methods instead.
138     */
139    @Deprecated
140    public int defaultDescPad = DEFAULT_DESC_PAD;
141
142    /**
143     * the string to display at the beginning of the usage statement
144     *
145     * @deprecated Scope will be made private for next major version - use get/setSyntaxPrefix methods instead.
146     */
147    @Deprecated
148    public String defaultSyntaxPrefix = DEFAULT_SYNTAX_PREFIX;
149
150    /**
151     * the new line string
152     *
153     * @deprecated Scope will be made private for next major version - use get/setNewLine methods instead.
154     */
155    @Deprecated
156    public String defaultNewLine = System.getProperty("line.separator");
157
158    /**
159     * the shortOpt prefix
160     *
161     * @deprecated Scope will be made private for next major version - use get/setOptPrefix methods instead.
162     */
163    @Deprecated
164    public String defaultOptPrefix = DEFAULT_OPT_PREFIX;
165
166    /**
167     * the long Opt prefix
168     *
169     * @deprecated Scope will be made private for next major version - use get/setLongOptPrefix methods instead.
170     */
171    @Deprecated
172    public String defaultLongOptPrefix = DEFAULT_LONG_OPT_PREFIX;
173
174    /**
175     * the name of the argument
176     *
177     * @deprecated Scope will be made private for next major version - use get/setArgName methods instead.
178     */
179    @Deprecated
180    public String defaultArgName = DEFAULT_ARG_NAME;
181
182    /**
183     * Comparator used to sort the options when they output in help text
184     *
185     * Defaults to case-insensitive alphabetical sorting by option key
186     */
187    protected Comparator<Option> optionComparator = new OptionComparator();
188
189    /** The separator displayed between the long option and its value. */
190    private String longOptSeparator = DEFAULT_LONG_OPT_SEPARATOR;
191
192    /**
193     * Appends the usage clause for an Option to a StringBuffer.
194     *
195     * @param buff the StringBuffer to append to
196     * @param option the Option to append
197     * @param required whether the Option is required or not
198     */
199    private void appendOption(final StringBuffer buff, final Option option, final boolean required) {
200        if (!required) {
201            buff.append("[");
202        }
203
204        if (option.getOpt() != null) {
205            buff.append("-").append(option.getOpt());
206        } else {
207            buff.append("--").append(option.getLongOpt());
208        }
209
210        // if the Option has a value and a non blank argname
211        if (option.hasArg() && (option.getArgName() == null || !option.getArgName().isEmpty())) {
212            buff.append(option.getOpt() == null ? longOptSeparator : " ");
213            buff.append("<").append(option.getArgName() != null ? option.getArgName() : getArgName()).append(">");
214        }
215
216        // if the Option is not a required option
217        if (!required) {
218            buff.append("]");
219        }
220    }
221
222    /**
223     * Appends the usage clause for an OptionGroup to a StringBuffer. The clause is wrapped in square brackets if the group
224     * is required. The display of the options is handled by appendOption
225     *
226     * @param buff the StringBuffer to append to
227     * @param group the group to append
228     * @see #appendOption(StringBuffer,Option,boolean)
229     */
230    private void appendOptionGroup(final StringBuffer buff, final OptionGroup group) {
231        if (!group.isRequired()) {
232            buff.append("[");
233        }
234
235        final List<Option> optList = new ArrayList<>(group.getOptions());
236        if (getOptionComparator() != null) {
237            Collections.sort(optList, getOptionComparator());
238        }
239        // for each option in the OptionGroup
240        for (final Iterator<Option> it = optList.iterator(); it.hasNext();) {
241            // whether the option is required or not is handled at group level
242            appendOption(buff, it.next(), true);
243
244            if (it.hasNext()) {
245                buff.append(" | ");
246            }
247        }
248
249        if (!group.isRequired()) {
250            buff.append("]");
251        }
252    }
253
254    /**
255     * Return a String of padding of length {@code len}.
256     *
257     * @param len The length of the String of padding to create.
258     *
259     * @return The String of padding
260     */
261    protected String createPadding(final int len) {
262        final char[] padding = new char[len];
263        Arrays.fill(padding, ' ');
264
265        return new String(padding);
266    }
267
268    /**
269     * Finds the next text wrap position after {@code startPos} for the text in {@code text} with the column width
270     * {@code width}. The wrap point is the last position before startPos+width having a whitespace character (space,
271     * \n, \r). If there is no whitespace character before startPos+width, it will return startPos+width.
272     *
273     * @param text The text being searched for the wrap position
274     * @param width width of the wrapped text
275     * @param startPos position from which to start the lookup whitespace character
276     * @return position on which the text must be wrapped or -1 if the wrap position is at the end of the text
277     */
278    protected int findWrapPos(final String text, final int width, final int startPos) {
279        // the line ends before the max wrap pos or a new line char found
280        int pos = text.indexOf('\n', startPos);
281        if (pos != -1 && pos <= width) {
282            return pos + 1;
283        }
284
285        pos = text.indexOf('\t', startPos);
286        if (pos != -1 && pos <= width) {
287            return pos + 1;
288        }
289
290        if (startPos + width >= text.length()) {
291            return -1;
292        }
293
294        // look for the last whitespace character before startPos+width
295        for (pos = startPos + width; pos >= startPos; --pos) {
296            final char c = text.charAt(pos);
297            if (c == ' ' || c == '\n' || c == '\r') {
298                break;
299            }
300        }
301
302        // if we found it - just return
303        if (pos > startPos) {
304            return pos;
305        }
306
307        // if we didn't find one, simply chop at startPos+width
308        pos = startPos + width;
309
310        return pos == text.length() ? -1 : pos;
311    }
312
313    /**
314     * Gets the 'argName'.
315     *
316     * @return the 'argName'
317     */
318    public String getArgName() {
319        return defaultArgName;
320    }
321
322    /**
323     * Gets the 'descPadding'.
324     *
325     * @return the 'descPadding'
326     */
327    public int getDescPadding() {
328        return defaultDescPad;
329    }
330
331    /**
332     * Gets the 'leftPadding'.
333     *
334     * @return the 'leftPadding'
335     */
336    public int getLeftPadding() {
337        return defaultLeftPad;
338    }
339
340    /**
341     * Gets the 'longOptPrefix'.
342     *
343     * @return the 'longOptPrefix'
344     */
345    public String getLongOptPrefix() {
346        return defaultLongOptPrefix;
347    }
348
349    /**
350     * Gets the separator displayed between a long option and its value.
351     *
352     * @return the separator
353     * @since 1.3
354     */
355    public String getLongOptSeparator() {
356        return longOptSeparator;
357    }
358
359    /**
360     * Gets the 'newLine'.
361     *
362     * @return the 'newLine'
363     */
364    public String getNewLine() {
365        return defaultNewLine;
366    }
367
368    /**
369     * Comparator used to sort the options when they output in help text. Defaults to case-insensitive alphabetical sorting
370     * by option key.
371     *
372     * @return the {@link Comparator} currently in use to sort the options
373     * @since 1.2
374     */
375    public Comparator<Option> getOptionComparator() {
376        return optionComparator;
377    }
378
379    /**
380     * Gets the 'optPrefix'.
381     *
382     * @return the 'optPrefix'
383     */
384    public String getOptPrefix() {
385        return defaultOptPrefix;
386    }
387
388    /**
389     * Gets the 'syntaxPrefix'.
390     *
391     * @return the 'syntaxPrefix'
392     */
393    public String getSyntaxPrefix() {
394        return defaultSyntaxPrefix;
395    }
396
397    /**
398     * Gets the 'width'.
399     *
400     * @return the 'width'
401     */
402    public int getWidth() {
403        return defaultWidth;
404    }
405
406    /**
407     * Print the help for {@code options} with the specified command line syntax. This method prints help information
408     * to System.out.
409     *
410     * @param width the number of characters to be displayed on each line
411     * @param cmdLineSyntax the syntax for this application
412     * @param header the banner to display at the beginning of the help
413     * @param options the Options instance
414     * @param footer the banner to display at the end of the help
415     */
416    public void printHelp(final int width, final String cmdLineSyntax, final String header, final Options options, final String footer) {
417        printHelp(width, cmdLineSyntax, header, options, footer, false);
418    }
419
420    /**
421     * Print the help for {@code options} with the specified command line syntax. This method prints help information
422     * to System.out.
423     *
424     * @param width the number of characters to be displayed on each line
425     * @param cmdLineSyntax the syntax for this application
426     * @param header the banner to display at the beginning of the help
427     * @param options the Options instance
428     * @param footer the banner to display at the end of the help
429     * @param autoUsage whether to print an automatically generated usage statement
430     */
431    public void printHelp(final int width, final String cmdLineSyntax, final String header, final Options options, final String footer,
432        final boolean autoUsage) {
433        final PrintWriter pw = new PrintWriter(System.out);
434
435        printHelp(pw, width, cmdLineSyntax, header, options, getLeftPadding(), getDescPadding(), footer, autoUsage);
436        pw.flush();
437    }
438
439    /**
440     * Print the help for {@code options} with the specified command line syntax.
441     *
442     * @param pw the writer to which the help will be written
443     * @param width the number of characters to be displayed on each line
444     * @param cmdLineSyntax the syntax for this application
445     * @param header the banner to display at the beginning of the help
446     * @param options the Options instance
447     * @param leftPad the number of characters of padding to be prefixed to each line
448     * @param descPad the number of characters of padding to be prefixed to each description line
449     * @param footer the banner to display at the end of the help
450     *
451     * @throws IllegalStateException if there is no room to print a line
452     */
453    public void printHelp(final PrintWriter pw, final int width, final String cmdLineSyntax, final String header, final Options options, final int leftPad,
454        final int descPad, final String footer) {
455        printHelp(pw, width, cmdLineSyntax, header, options, leftPad, descPad, footer, false);
456    }
457
458    /**
459     * Print the help for {@code options} with the specified command line syntax.
460     *
461     * @param pw the writer to which the help will be written
462     * @param width the number of characters to be displayed on each line
463     * @param cmdLineSyntax the syntax for this application
464     * @param header the banner to display at the beginning of the help
465     * @param options the Options instance
466     * @param leftPad the number of characters of padding to be prefixed to each line
467     * @param descPad the number of characters of padding to be prefixed to each description line
468     * @param footer the banner to display at the end of the help
469     * @param autoUsage whether to print an automatically generated usage statement
470     *
471     * @throws IllegalStateException if there is no room to print a line
472     */
473    public void printHelp(final PrintWriter pw, final int width, final String cmdLineSyntax, final String header, final Options options, final int leftPad,
474        final int descPad, final String footer, final boolean autoUsage) {
475        if (cmdLineSyntax == null || cmdLineSyntax.isEmpty()) {
476            throw new IllegalArgumentException("cmdLineSyntax not provided");
477        }
478
479        if (autoUsage) {
480            printUsage(pw, width, cmdLineSyntax, options);
481        } else {
482            printUsage(pw, width, cmdLineSyntax);
483        }
484
485        if (header != null && !header.isEmpty()) {
486            printWrapped(pw, width, header);
487        }
488
489        printOptions(pw, width, options, leftPad, descPad);
490
491        if (footer != null && !footer.isEmpty()) {
492            printWrapped(pw, width, footer);
493        }
494    }
495
496    /**
497     * Print the help for {@code options} with the specified command line syntax. This method prints help information
498     * to System.out.
499     *
500     * @param cmdLineSyntax the syntax for this application
501     * @param options the Options instance
502     */
503    public void printHelp(final String cmdLineSyntax, final Options options) {
504        printHelp(getWidth(), cmdLineSyntax, null, options, null, false);
505    }
506
507    /**
508     * Print the help for {@code options} with the specified command line syntax. This method prints help information
509     * to System.out.
510     *
511     * @param cmdLineSyntax the syntax for this application
512     * @param options the Options instance
513     * @param autoUsage whether to print an automatically generated usage statement
514     */
515    public void printHelp(final String cmdLineSyntax, final Options options, final boolean autoUsage) {
516        printHelp(getWidth(), cmdLineSyntax, null, options, null, autoUsage);
517    }
518
519    /**
520     * Print the help for {@code options} with the specified command line syntax. This method prints help information
521     * to System.out.
522     *
523     * @param cmdLineSyntax the syntax for this application
524     * @param header the banner to display at the beginning of the help
525     * @param options the Options instance
526     * @param footer the banner to display at the end of the help
527     */
528    public void printHelp(final String cmdLineSyntax, final String header, final Options options, final String footer) {
529        printHelp(cmdLineSyntax, header, options, footer, false);
530    }
531
532    /**
533     * Print the help for {@code options} with the specified command line syntax. This method prints help information
534     * to System.out.
535     *
536     * @param cmdLineSyntax the syntax for this application
537     * @param header the banner to display at the beginning of the help
538     * @param options the Options instance
539     * @param footer the banner to display at the end of the help
540     * @param autoUsage whether to print an automatically generated usage statement
541     */
542    public void printHelp(final String cmdLineSyntax, final String header, final Options options, final String footer, final boolean autoUsage) {
543        printHelp(getWidth(), cmdLineSyntax, header, options, footer, autoUsage);
544    }
545
546    /**
547     * Print the help for the specified Options to the specified writer, using the specified width, left padding and
548     * description padding.
549     *
550     * @param pw The printWriter to write the help to
551     * @param width The number of characters to display per line
552     * @param options The command line Options
553     * @param leftPad the number of characters of padding to be prefixed to each line
554     * @param descPad the number of characters of padding to be prefixed to each description line
555     */
556    public void printOptions(final PrintWriter pw, final int width, final Options options, final int leftPad, final int descPad) {
557        final StringBuffer sb = new StringBuffer();
558
559        renderOptions(sb, width, options, leftPad, descPad);
560        pw.println(sb.toString());
561    }
562
563    /**
564     * Print the cmdLineSyntax to the specified writer, using the specified width.
565     *
566     * @param pw The printWriter to write the help to
567     * @param width The number of characters per line for the usage statement.
568     * @param cmdLineSyntax The usage statement.
569     */
570    public void printUsage(final PrintWriter pw, final int width, final String cmdLineSyntax) {
571        final int argPos = cmdLineSyntax.indexOf(' ') + 1;
572
573        printWrapped(pw, width, getSyntaxPrefix().length() + argPos, getSyntaxPrefix() + cmdLineSyntax);
574    }
575
576    /**
577     * Prints the usage statement for the specified application.
578     *
579     * @param pw The PrintWriter to print the usage statement
580     * @param width The number of characters to display per line
581     * @param app The application name
582     * @param options The command line Options
583     */
584    public void printUsage(final PrintWriter pw, final int width, final String app, final Options options) {
585        // initialize the string buffer
586        final StringBuffer buff = new StringBuffer(getSyntaxPrefix()).append(app).append(" ");
587
588        // create a list for processed option groups
589        final Collection<OptionGroup> processedGroups = new ArrayList<>();
590
591        final List<Option> optList = new ArrayList<>(options.getOptions());
592        if (getOptionComparator() != null) {
593            Collections.sort(optList, getOptionComparator());
594        }
595        // iterate over the options
596        for (final Iterator<Option> it = optList.iterator(); it.hasNext();) {
597            // get the next Option
598            final Option option = it.next();
599
600            // check if the option is part of an OptionGroup
601            final OptionGroup group = options.getOptionGroup(option);
602
603            // if the option is part of a group
604            if (group != null) {
605                // and if the group has not already been processed
606                if (!processedGroups.contains(group)) {
607                    // add the group to the processed list
608                    processedGroups.add(group);
609
610                    // add the usage clause
611                    appendOptionGroup(buff, group);
612                }
613
614                // otherwise the option was displayed in the group
615                // previously so ignore it.
616            }
617
618            // if the Option is not part of an OptionGroup
619            else {
620                appendOption(buff, option, option.isRequired());
621            }
622
623            if (it.hasNext()) {
624                buff.append(" ");
625            }
626        }
627
628        // call printWrapped
629        printWrapped(pw, width, buff.toString().indexOf(' ') + 1, buff.toString());
630    }
631
632    /**
633     * Print the specified text to the specified PrintWriter.
634     *
635     * @param pw The printWriter to write the help to
636     * @param width The number of characters to display per line
637     * @param nextLineTabStop The position on the next line for the first tab.
638     * @param text The text to be written to the PrintWriter
639     */
640    public void printWrapped(final PrintWriter pw, final int width, final int nextLineTabStop, final String text) {
641        final StringBuffer sb = new StringBuffer(text.length());
642
643        renderWrappedTextBlock(sb, width, nextLineTabStop, text);
644        pw.println(sb.toString());
645    }
646
647    /**
648     * Print the specified text to the specified PrintWriter.
649     *
650     * @param pw The printWriter to write the help to
651     * @param width The number of characters to display per line
652     * @param text The text to be written to the PrintWriter
653     */
654    public void printWrapped(final PrintWriter pw, final int width, final String text) {
655        printWrapped(pw, width, 0, text);
656    }
657
658    /**
659     * Render the specified Options and return the rendered Options in a StringBuffer.
660     *
661     * @param sb The StringBuffer to place the rendered Options into.
662     * @param width The number of characters to display per line
663     * @param options The command line Options
664     * @param leftPad the number of characters of padding to be prefixed to each line
665     * @param descPad the number of characters of padding to be prefixed to each description line
666     *
667     * @return the StringBuffer with the rendered Options contents.
668     */
669    protected StringBuffer renderOptions(final StringBuffer sb, final int width, final Options options, final int leftPad, final int descPad) {
670        final String lpad = createPadding(leftPad);
671        final String dpad = createPadding(descPad);
672
673        // first create list containing only <lpad>-a,--aaa where
674        // -a is opt and --aaa is long opt; in parallel look for
675        // the longest opt string this list will be then used to
676        // sort options ascending
677        int max = 0;
678        final List<StringBuffer> prefixList = new ArrayList<>();
679
680        final List<Option> optList = options.helpOptions();
681
682        if (getOptionComparator() != null) {
683            Collections.sort(optList, getOptionComparator());
684        }
685
686        for (final Option option : optList) {
687            final StringBuffer optBuf = new StringBuffer();
688
689            if (option.getOpt() == null) {
690                optBuf.append(lpad).append("   ").append(getLongOptPrefix()).append(option.getLongOpt());
691            } else {
692                optBuf.append(lpad).append(getOptPrefix()).append(option.getOpt());
693
694                if (option.hasLongOpt()) {
695                    optBuf.append(',').append(getLongOptPrefix()).append(option.getLongOpt());
696                }
697            }
698
699            if (option.hasArg()) {
700                final String argName = option.getArgName();
701                if (argName != null && argName.isEmpty()) {
702                    // if the option has a blank argname
703                    optBuf.append(' ');
704                } else {
705                    optBuf.append(option.hasLongOpt() ? longOptSeparator : " ");
706                    optBuf.append("<").append(argName != null ? option.getArgName() : getArgName()).append(">");
707                }
708            }
709
710            prefixList.add(optBuf);
711            max = optBuf.length() > max ? optBuf.length() : max;
712        }
713
714        int x = 0;
715
716        for (final Iterator<Option> it = optList.iterator(); it.hasNext();) {
717            final Option option = it.next();
718            final StringBuilder optBuf = new StringBuilder(prefixList.get(x++).toString());
719
720            if (optBuf.length() < max) {
721                optBuf.append(createPadding(max - optBuf.length()));
722            }
723
724            optBuf.append(dpad);
725
726            final int nextLineTabStop = max + descPad;
727
728            if (option.getDescription() != null) {
729                optBuf.append(option.getDescription());
730            }
731
732            renderWrappedText(sb, width, nextLineTabStop, optBuf.toString());
733
734            if (it.hasNext()) {
735                sb.append(getNewLine());
736            }
737        }
738
739        return sb;
740    }
741
742    /**
743     * Render the specified text and return the rendered Options in a StringBuffer.
744     *
745     * @param sb The StringBuffer to place the rendered text into.
746     * @param width The number of characters to display per line
747     * @param nextLineTabStop The position on the next line for the first tab.
748     * @param text The text to be rendered.
749     *
750     * @return the StringBuffer with the rendered Options contents.
751     */
752    protected StringBuffer renderWrappedText(final StringBuffer sb, final int width, int nextLineTabStop, String text) {
753        int pos = findWrapPos(text, width, 0);
754
755        if (pos == -1) {
756            sb.append(rtrim(text));
757
758            return sb;
759        }
760        sb.append(rtrim(text.substring(0, pos))).append(getNewLine());
761
762        if (nextLineTabStop >= width) {
763            // stops infinite loop happening
764            nextLineTabStop = 1;
765        }
766
767        // all following lines must be padded with nextLineTabStop space characters
768        final String padding = createPadding(nextLineTabStop);
769
770        while (true) {
771            text = padding + text.substring(pos).trim();
772            pos = findWrapPos(text, width, 0);
773
774            if (pos == -1) {
775                sb.append(text);
776
777                return sb;
778            }
779
780            if (text.length() > width && pos == nextLineTabStop - 1) {
781                pos = width;
782            }
783
784            sb.append(rtrim(text.substring(0, pos))).append(getNewLine());
785        }
786    }
787
788    /**
789     * Render the specified text width a maximum width. This method differs from renderWrappedText by not removing leading
790     * spaces after a new line.
791     *
792     * @param sb The StringBuffer to place the rendered text into.
793     * @param width The number of characters to display per line
794     * @param nextLineTabStop The position on the next line for the first tab.
795     * @param text The text to be rendered.
796     */
797    private Appendable renderWrappedTextBlock(final StringBuffer sb, final int width, final int nextLineTabStop, final String text) {
798        try {
799            final BufferedReader in = new BufferedReader(new StringReader(text));
800            String line;
801            boolean firstLine = true;
802            while ((line = in.readLine()) != null) {
803                if (!firstLine) {
804                    sb.append(getNewLine());
805                } else {
806                    firstLine = false;
807                }
808                renderWrappedText(sb, width, nextLineTabStop, line);
809            }
810        } catch (final IOException e) { // NOPMD
811            // cannot happen
812        }
813
814        return sb;
815    }
816
817    /**
818     * Remove the trailing whitespace from the specified String.
819     *
820     * @param s The String to remove the trailing padding from.
821     *
822     * @return The String of without the trailing padding
823     */
824    protected String rtrim(final String s) {
825        if (s == null || s.isEmpty()) {
826            return s;
827        }
828
829        int pos = s.length();
830
831        while (pos > 0 && Character.isWhitespace(s.charAt(pos - 1))) {
832            --pos;
833        }
834
835        return s.substring(0, pos);
836    }
837
838    /**
839     * Sets the 'argName'.
840     *
841     * @param name the new value of 'argName'
842     */
843    public void setArgName(final String name) {
844        this.defaultArgName = name;
845    }
846
847    /**
848     * Sets the 'descPadding'.
849     *
850     * @param padding the new value of 'descPadding'
851     */
852    public void setDescPadding(final int padding) {
853        this.defaultDescPad = padding;
854    }
855
856    /**
857     * Sets the 'leftPadding'.
858     *
859     * @param padding the new value of 'leftPadding'
860     */
861    public void setLeftPadding(final int padding) {
862        this.defaultLeftPad = padding;
863    }
864
865    /**
866     * Sets the 'longOptPrefix'.
867     *
868     * @param prefix the new value of 'longOptPrefix'
869     */
870    public void setLongOptPrefix(final String prefix) {
871        this.defaultLongOptPrefix = prefix;
872    }
873
874    /**
875     * Set the separator displayed between a long option and its value. Ensure that the separator specified is supported by
876     * the parser used, typically ' ' or '='.
877     *
878     * @param longOptSeparator the separator, typically ' ' or '='.
879     * @since 1.3
880     */
881    public void setLongOptSeparator(final String longOptSeparator) {
882        this.longOptSeparator = longOptSeparator;
883    }
884
885    /**
886     * Sets the 'newLine'.
887     *
888     * @param newline the new value of 'newLine'
889     */
890    public void setNewLine(final String newline) {
891        this.defaultNewLine = newline;
892    }
893
894    /**
895     * Set the comparator used to sort the options when they output in help text. Passing in a null comparator will keep the
896     * options in the order they were declared.
897     *
898     * @param comparator the {@link Comparator} to use for sorting the options
899     * @since 1.2
900     */
901    public void setOptionComparator(final Comparator<Option> comparator) {
902        this.optionComparator = comparator;
903    }
904
905    /**
906     * Sets the 'optPrefix'.
907     *
908     * @param prefix the new value of 'optPrefix'
909     */
910    public void setOptPrefix(final String prefix) {
911        this.defaultOptPrefix = prefix;
912    }
913
914    /**
915     * Sets the 'syntaxPrefix'.
916     *
917     * @param prefix the new value of 'syntaxPrefix'
918     */
919    public void setSyntaxPrefix(final String prefix) {
920        this.defaultSyntaxPrefix = prefix;
921    }
922
923    /**
924     * Sets the 'width'.
925     *
926     * @param width the new value of 'width'
927     */
928    public void setWidth(final int width) {
929        this.defaultWidth = width;
930    }
931
932}