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 }