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