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