Coverage Report - joptsimple.BuiltinHelpFormatter
 
Classes in this File Line Coverage Branch Coverage Complexity
BuiltinHelpFormatter
99%
109/110
96%
56/58
2.138
BuiltinHelpFormatter$1
100%
2/2
N/A
2.138
 
 1  
 /*
 2  
  The MIT License
 3  
 
 4  
  Copyright (c) 2004-2015 Paul R. Holser, Jr.
 5  
 
 6  
  Permission is hereby granted, free of charge, to any person obtaining
 7  
  a copy of this software and associated documentation files (the
 8  
  "Software"), to deal in the Software without restriction, including
 9  
  without limitation the rights to use, copy, modify, merge, publish,
 10  
  distribute, sublicense, and/or sell copies of the Software, and to
 11  
  permit persons to whom the Software is furnished to do so, subject to
 12  
  the following conditions:
 13  
 
 14  
  The above copyright notice and this permission notice shall be
 15  
  included in all copies or substantial portions of the Software.
 16  
 
 17  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 18  
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 19  
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 20  
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 21  
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 22  
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 23  
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 24  
 */
 25  
 
 26  
 package joptsimple;
 27  
 
 28  
 import java.util.*;
 29  
 
 30  
 import joptsimple.internal.Messages;
 31  
 import joptsimple.internal.Rows;
 32  
 import joptsimple.internal.Strings;
 33  
 
 34  
 import static joptsimple.ParserRules.*;
 35  
 import static joptsimple.internal.Classes.*;
 36  
 import static joptsimple.internal.Strings.*;
 37  
 
 38  
 /**
 39  
  * <p>A help formatter that allows configuration of overall row width and column separator width.</p>
 40  
  *
 41  
  * <p>The formatter produces output in two sections: one for the options, and one for non-option arguments.</p>
 42  
  *
 43  
  * <p>The options section has two columns: the left column for the options, and the right column for their
 44  
  * descriptions. The formatter will allow as much space as possible for the descriptions, by minimizing the option
 45  
  * column's width, no greater than slightly less than half the overall desired width.</p>
 46  
  *
 47  
  * <p>The non-option arguments section is one column, occupying as much width as it can.</p>
 48  
  *
 49  
  * <p>Subclasses are free to override bits of this implementation as they see fit. Inspect the code
 50  
  * carefully to understand the flow of control that this implementation guarantees.</p>
 51  
  *
 52  
  * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
 53  
  */
 54  
 public class BuiltinHelpFormatter implements HelpFormatter {
 55  
     private final Rows nonOptionRows;
 56  
     private final Rows optionRows;
 57  
 
 58  
     /**
 59  
      * Makes a formatter with a pre-configured overall row width and column separator width.
 60  
      */
 61  
     BuiltinHelpFormatter() {
 62  381
         this( 80, 2 );
 63  381
     }
 64  
 
 65  
     /**
 66  
      * Makes a formatter with a given overall row width and column separator width.
 67  
      *
 68  
      * @param desiredOverallWidth how many characters wide to make the overall help display
 69  
      * @param desiredColumnSeparatorWidth how many characters wide to make the separation between option column and
 70  
      * description column
 71  
      */
 72  408
     public BuiltinHelpFormatter( int desiredOverallWidth, int desiredColumnSeparatorWidth ) {
 73  408
         nonOptionRows = new Rows( desiredOverallWidth * 2, 0 );
 74  408
         optionRows = new Rows( desiredOverallWidth, desiredColumnSeparatorWidth );
 75  408
     }
 76  
 
 77  
     /**
 78  
      * {@inheritDoc}
 79  
      *
 80  
      * <p>This implementation:</p>
 81  
      * <ul>
 82  
      *     <li>Sorts the given descriptors by their first elements of {@link OptionDescriptor#options()}</li>
 83  
      *     <li>Passes the resulting sorted set to {@link #addRows(java.util.Collection)}</li>
 84  
      *     <li>Returns the result of {@link #formattedHelpOutput()}</li>
 85  
      * </ul>
 86  
      */
 87  
     public String format( Map<String, ? extends OptionDescriptor> options ) {
 88  64
         Comparator<OptionDescriptor> comparator =
 89  573
             new Comparator<OptionDescriptor>() {
 90  
                 public int compare( OptionDescriptor first, OptionDescriptor second ) {
 91  509
                     return first.options().iterator().next().compareTo( second.options().iterator().next() );
 92  
                 }
 93  
             };
 94  
 
 95  64
         Set<OptionDescriptor> sorted = new TreeSet<OptionDescriptor>( comparator );
 96  64
         sorted.addAll( options.values() );
 97  
 
 98  64
         addRows( sorted );
 99  
 
 100  64
         return formattedHelpOutput();
 101  
     }
 102  
 
 103  
     /**
 104  
      * Adds a row of option help output in the left column, with empty space in the right column.
 105  
      *
 106  
      * @param single text to put in the left column
 107  
      */
 108  
     protected void addOptionRow( String single ) {
 109  10
         addOptionRow( single, "" );
 110  10
     }
 111  
 
 112  
     /**
 113  
      * Adds a row of option help output in the left and right columns.
 114  
      *
 115  
      * @param left text to put in the left column
 116  
      * @param right text to put in the right column
 117  
      */
 118  
     protected void addOptionRow( String left, String right ) {
 119  220
         optionRows.add(left, right);
 120  220
     }
 121  
 
 122  
     /**
 123  
      * Adds a single row of non-option argument help.
 124  
      *
 125  
      * @param single single row of non-option argument help text
 126  
      */
 127  
     protected void addNonOptionRow( String single ) {
 128  12
         nonOptionRows.add( single, "" );
 129  12
     }
 130  
 
 131  
     /**
 132  
      * Resizes the columns of all the rows to be no wider than the widest element in that column.
 133  
      */
 134  
     protected void fitRowsToWidth() {
 135  64
         nonOptionRows.fitToWidth();
 136  64
         optionRows.fitToWidth();
 137  64
     }
 138  
 
 139  
     /**
 140  
      * Produces non-option argument help.
 141  
      *
 142  
      * @return non-option argument help
 143  
      */
 144  
     protected String nonOptionOutput() {
 145  64
         return nonOptionRows.render();
 146  
     }
 147  
 
 148  
     /**
 149  
      * Produces help for options and their descriptions.
 150  
      *
 151  
      * @return option help
 152  
      */
 153  
     protected String optionOutput() {
 154  64
         return optionRows.render();
 155  
     }
 156  
 
 157  
     /**
 158  
      * <p>Produces help output for an entire set of options and non-option arguments.</p>
 159  
      *
 160  
      * <p>This implementation concatenates:</p>
 161  
      * <ul>
 162  
      *     <li>the result of {@link #nonOptionOutput()}</li>
 163  
      *     <li>if there is non-option output, a line separator</li>
 164  
      *     <li>the result of {@link #optionOutput()}</li>
 165  
      * </ul>
 166  
      *
 167  
      * @return help output for entire set of options and non-option arguments
 168  
      */
 169  
     protected String formattedHelpOutput() {
 170  64
         StringBuilder formatted = new StringBuilder();
 171  64
         String nonOptionDisplay = nonOptionOutput();
 172  64
         if ( !Strings.isNullOrEmpty( nonOptionDisplay ) )
 173  6
             formatted.append( nonOptionDisplay ).append( LINE_SEPARATOR );
 174  64
         formatted.append( optionOutput() );
 175  
 
 176  64
         return formatted.toString();
 177  
     }
 178  
 
 179  
     /**
 180  
      * <p>Adds rows of help output for the given options.</p>
 181  
      *
 182  
      * <p>This implementation:</p>
 183  
      * <ul>
 184  
      *     <li>Calls {@link #addNonOptionsDescription(java.util.Collection)} with the options as the argument</li>
 185  
      *     <ul>
 186  
      *         <li>If there are no options, calls {@link #addOptionRow(String)} with an argument that indicates
 187  
      *         that no options are specified.</li>
 188  
      *         <li>Otherwise, calls {@link #addHeaders(java.util.Collection)} with the options as the argument,
 189  
      *         followed by {@link #addOptions(java.util.Collection)} with the options as the argument.</li>
 190  
      *     </ul>
 191  
      *     <li>Calls {@link #fitRowsToWidth()}.</li>
 192  
      * </ul>
 193  
      *
 194  
      * @param options descriptors for the configured options of a parser
 195  
      */
 196  
     protected void addRows( Collection<? extends OptionDescriptor> options ) {
 197  64
         addNonOptionsDescription( options );
 198  
 
 199  64
         if ( options.isEmpty() )
 200  10
             addOptionRow( message( "no.options.specified" ) );
 201  
         else {
 202  54
             addHeaders( options );
 203  54
             addOptions( options );
 204  
         }
 205  
 
 206  64
         fitRowsToWidth();
 207  64
     }
 208  
 
 209  
     /**
 210  
      * <p>Adds non-option arguments descriptions to the help output.</p>
 211  
      *
 212  
      * <p>This implementation:</p>
 213  
      * <ul>
 214  
      *     <li>{@linkplain #findAndRemoveNonOptionsSpec(java.util.Collection) Finds and removes the non-option
 215  
      *     arguments descriptor}</li>
 216  
      *     <li>{@linkplain #shouldShowNonOptionArgumentDisplay(OptionDescriptor) Decides whether there is
 217  
      *     anything to show for non-option arguments}</li>
 218  
      *     <li>If there is, {@linkplain #addNonOptionRow(String) adds a header row} and
 219  
      *     {@linkplain #addNonOptionRow(String) adds a}
 220  
      *     {@linkplain #createNonOptionArgumentsDisplay(OptionDescriptor) non-option arguments description} </li>
 221  
      * </ul>
 222  
      *
 223  
      * @param options descriptors for the configured options of a parser
 224  
      */
 225  
     protected void addNonOptionsDescription( Collection<? extends OptionDescriptor> options ) {
 226  64
         OptionDescriptor nonOptions = findAndRemoveNonOptionsSpec( options );
 227  64
         if ( shouldShowNonOptionArgumentDisplay(nonOptions) ) {
 228  6
             addNonOptionRow( message( "non.option.arguments.header" ) );
 229  6
             addNonOptionRow( createNonOptionArgumentsDisplay( nonOptions ) );
 230  
         }
 231  64
     }
 232  
 
 233  
     /**
 234  
      * <p>Decides whether or not to show a non-option arguments help.</p>
 235  
      *
 236  
      * <p>This implementation responds with {@code true} if the non-option descriptor has a non-{@code null},
 237  
      * non-empty value for any of {@link OptionDescriptor#description()},
 238  
      * {@link OptionDescriptor#argumentTypeIndicator()}, or {@link OptionDescriptor#argumentDescription()}.</p>
 239  
      *
 240  
      * @param nonOptionDescriptor non-option argument descriptor
 241  
      * @return {@code true} if non-options argument help should be shown
 242  
      */
 243  
     protected boolean shouldShowNonOptionArgumentDisplay( OptionDescriptor nonOptionDescriptor ) {
 244  64
         return !Strings.isNullOrEmpty( nonOptionDescriptor.description() )
 245  
             || !Strings.isNullOrEmpty( nonOptionDescriptor.argumentTypeIndicator() )
 246  
             || !Strings.isNullOrEmpty( nonOptionDescriptor.argumentDescription() );
 247  
     }
 248  
 
 249  
     /**
 250  
      * <p>Creates a non-options argument help string.</p>
 251  
      *
 252  
      * <p>This implementation creates an empty string buffer and calls
 253  
      * {@link #maybeAppendOptionInfo(StringBuilder, OptionDescriptor)}
 254  
      * and {@link #maybeAppendNonOptionsDescription(StringBuilder, OptionDescriptor)}, passing them the
 255  
      * buffer and the non-option arguments descriptor.</p>
 256  
      *
 257  
      * @param nonOptionDescriptor non-option argument descriptor
 258  
      * @return help string for non-options
 259  
      */
 260  
     protected String createNonOptionArgumentsDisplay( OptionDescriptor nonOptionDescriptor ) {
 261  6
         StringBuilder buffer = new StringBuilder();
 262  6
         maybeAppendOptionInfo( buffer, nonOptionDescriptor );
 263  6
         maybeAppendNonOptionsDescription( buffer, nonOptionDescriptor );
 264  
 
 265  6
         return buffer.toString();
 266  
     }
 267  
 
 268  
     /**
 269  
      * <p>Appends help for the given non-option arguments descriptor to the given buffer.</p>
 270  
      *
 271  
      * <p>This implementation appends {@code " -- "} if the buffer has text in it and the non-option arguments
 272  
      * descriptor has a {@link OptionDescriptor#description()}; followed by the
 273  
      * {@link OptionDescriptor#description()}.</p>
 274  
      *
 275  
      * @param buffer string buffer
 276  
      * @param nonOptions non-option arguments descriptor
 277  
      */
 278  
     protected void maybeAppendNonOptionsDescription( StringBuilder buffer, OptionDescriptor nonOptions ) {
 279  6
         buffer.append( buffer.length() > 0 && !Strings.isNullOrEmpty( nonOptions.description() ) ? " -- " : "" )
 280  
             .append( nonOptions.description() );
 281  6
     }
 282  
 
 283  
     /**
 284  
      * Finds the non-option arguments descriptor in the given collection, removes it, and returns it.
 285  
      *
 286  
      * @param options descriptors for the configured options of a parser
 287  
      * @return the non-option arguments descriptor
 288  
      */
 289  
     protected OptionDescriptor findAndRemoveNonOptionsSpec( Collection<? extends OptionDescriptor> options ) {
 290  64
         for ( Iterator<? extends OptionDescriptor> it = options.iterator(); it.hasNext(); ) {
 291  82
             OptionDescriptor next = it.next();
 292  82
             if ( next.representsNonOptions() ) {
 293  64
                 it.remove();
 294  64
                 return next;
 295  
             }
 296  18
         }
 297  
 
 298  0
         throw new AssertionError( "no non-options argument spec" );
 299  
     }
 300  
 
 301  
     /**
 302  
      * <p>Adds help row headers for option help columns.</p>
 303  
      *
 304  
      * <p>This implementation uses the headers {@code "Option"} and {@code "Description"}. If the options contain
 305  
      * a "required" option, the {@code "Option"} header looks like {@code "Option (* = required)}. Both headers
 306  
      * are "underlined" using {@code "-"}.</p>
 307  
      *
 308  
      * @param options descriptors for the configured options of a parser
 309  
      */
 310  
     protected void addHeaders( Collection<? extends OptionDescriptor> options ) {
 311  54
         if ( hasRequiredOption( options ) ) {
 312  2
             addOptionRow( message( "option.header.with.required.indicator" ), message( "description.header" ) );
 313  2
             addOptionRow( message( "option.divider.with.required.indicator" ), message( "description.divider" ) );
 314  
         } else {
 315  52
             addOptionRow( message( "option.header" ), message( "description.header" ) );
 316  52
             addOptionRow( message( "option.divider" ), message( "description.divider" ) );
 317  
         }
 318  54
     }
 319  
 
 320  
     /**
 321  
      * Tells whether the given option descriptors contain a "required" option.
 322  
      *
 323  
      * @param options descriptors for the configured options of a parser
 324  
      * @return {@code true} if at least one of the options is "required"
 325  
      */
 326  
     protected final boolean hasRequiredOption( Collection<? extends OptionDescriptor> options ) {
 327  54
         for ( OptionDescriptor each : options ) {
 328  102
             if ( each.isRequired() )
 329  2
                 return true;
 330  100
         }
 331  
 
 332  52
         return false;
 333  
     }
 334  
 
 335  
     /**
 336  
      * <p>Adds help rows for the given options.</p>
 337  
      *
 338  
      * <p>This implementation loops over the given options, and for each, calls {@link #addOptionRow(String, String)}
 339  
      * using the results of {@link #createOptionDisplay(OptionDescriptor)} and
 340  
      * {@link #createDescriptionDisplay(OptionDescriptor)}, respectively, as arguments.</p>
 341  
      *
 342  
      * @param options descriptors for the configured options of a parser
 343  
      */
 344  
     protected void addOptions( Collection<? extends OptionDescriptor> options ) {
 345  54
         for ( OptionDescriptor each : options ) {
 346  102
             if ( !each.representsNonOptions() )
 347  102
                 addOptionRow( createOptionDisplay( each ), createDescriptionDisplay( each ) );
 348  102
         }
 349  54
     }
 350  
 
 351  
     /**
 352  
      * <p>Creates a string for how the given option descriptor is to be represented in help.</p>
 353  
      *
 354  
      * <p>This implementation gives a string consisting of the concatenation of:</p>
 355  
      * <ul>
 356  
      *     <li>{@code "* "} for "required" options, otherwise {@code ""}</li>
 357  
      *     <li>For each of the {@link OptionDescriptor#options()} of the descriptor, separated by {@code ", "}:
 358  
      *         <ul>
 359  
      *             <li>{@link #optionLeader(String)} of the option</li>
 360  
      *             <li>the option</li>
 361  
      *         </ul>
 362  
      *     </li>
 363  
      *     <li>the result of {@link #maybeAppendOptionInfo(StringBuilder, OptionDescriptor)}</li>
 364  
      * </ul>
 365  
      *
 366  
      * @param descriptor a descriptor for a configured option of a parser
 367  
      * @return help string
 368  
      */
 369  
     protected String createOptionDisplay( OptionDescriptor descriptor ) {
 370  102
         StringBuilder buffer = new StringBuilder( descriptor.isRequired() ? "* " : "" );
 371  
 
 372  102
         for ( Iterator<String> i = descriptor.options().iterator(); i.hasNext(); ) {
 373  160
             String option = i.next();
 374  160
             buffer.append( optionLeader( option ) );
 375  160
             buffer.append( option );
 376  
 
 377  160
             if ( i.hasNext() )
 378  58
                 buffer.append( ", " );
 379  160
         }
 380  
 
 381  102
         maybeAppendOptionInfo( buffer, descriptor );
 382  
 
 383  102
         return buffer.toString();
 384  
     }
 385  
 
 386  
     /**
 387  
      * <p>Gives a string that represents the given option's "option leader" in help.</p>
 388  
      *
 389  
      * <p>This implementation answers with {@code "--"} for options of length greater than one; otherwise answers
 390  
      * with {@code "-"}.</p>
 391  
      *
 392  
      * @param option a string option
 393  
      * @return an "option leader" string
 394  
      */
 395  
     protected String optionLeader( String option ) {
 396  160
         return option.length() > 1 ? DOUBLE_HYPHEN : HYPHEN;
 397  
     }
 398  
 
 399  
     /**
 400  
      * <p>Appends additional info about the given option to the given buffer.</p>
 401  
      *
 402  
      * <p>This implementation:</p>
 403  
      * <ul>
 404  
      *     <li>calls {@link #extractTypeIndicator(OptionDescriptor)} for the descriptor</li>
 405  
      *     <li>calls {@link joptsimple.OptionDescriptor#argumentDescription()} for the descriptor</li>
 406  
      *     <li>if either of the above is present, calls
 407  
      *     {@link #appendOptionHelp(StringBuilder, String, String, boolean)}</li>
 408  
      * </ul>
 409  
      *
 410  
      * @param buffer string buffer
 411  
      * @param descriptor a descriptor for a configured option of a parser
 412  
      */
 413  
     protected void maybeAppendOptionInfo( StringBuilder buffer, OptionDescriptor descriptor ) {
 414  108
         String indicator = extractTypeIndicator( descriptor );
 415  108
         String description = descriptor.argumentDescription();
 416  108
         if ( indicator != null || !isNullOrEmpty( description ) )
 417  36
             appendOptionHelp( buffer, indicator, description, descriptor.requiresArgument() );
 418  108
     }
 419  
 
 420  
     /**
 421  
      * <p>Gives an indicator of the type of arguments of the option described by the given descriptor,
 422  
      * for use in help.</p>
 423  
      *
 424  
      * <p>This implementation asks for the {@link OptionDescriptor#argumentTypeIndicator()} of the given
 425  
      * descriptor, and if it is present and not {@code "java.lang.String"}, parses it as a fully qualified
 426  
      * class name and returns the base name of that class; otherwise returns {@code null}.</p>
 427  
      *
 428  
      * @param descriptor a descriptor for a configured option of a parser
 429  
      * @return type indicator text
 430  
      */
 431  
     protected String extractTypeIndicator( OptionDescriptor descriptor ) {
 432  108
         String indicator = descriptor.argumentTypeIndicator();
 433  
 
 434  108
         if ( !isNullOrEmpty( indicator ) && !String.class.getName().equals( indicator ) )
 435  29
             return shortNameOf( indicator );
 436  
 
 437  79
         return null;
 438  
     }
 439  
 
 440  
     /**
 441  
      * <p>Appends info about an option's argument to the given buffer.</p>
 442  
      *
 443  
      * <p>This implementation calls {@link #appendTypeIndicator(StringBuilder, String, String, char, char)} with
 444  
      * the surrounding characters {@code '<'} and {@code '>'} for options with {@code required} arguments, and
 445  
      * with the surrounding characters {@code '['} and {@code ']'} for options with optional arguments.</p>
 446  
      *
 447  
      * @param buffer string buffer
 448  
      * @param typeIndicator type indicator
 449  
      * @param description type description
 450  
      * @param required indicator of "required"-ness of the argument of the option
 451  
      */
 452  
     protected void appendOptionHelp( StringBuilder buffer, String typeIndicator, String description,
 453  
                                      boolean required ) {
 454  36
         if ( required )
 455  15
             appendTypeIndicator( buffer, typeIndicator, description, '<', '>' );
 456  
         else
 457  21
             appendTypeIndicator( buffer, typeIndicator, description, '[', ']' );
 458  36
     }
 459  
 
 460  
     /**
 461  
      * <p>Appends a type indicator for an option's argument to the given buffer.</p>
 462  
      *
 463  
      * <p>This implementation appends, in order:</p>
 464  
      * <ul>
 465  
      *     <li>{@code ' '}</li>
 466  
      *     <li>{@code start}</li>
 467  
      *     <li>the type indicator, if not {@code null}</li>
 468  
      *     <li>if the description is present, then {@code ": "} plus the description if the type indicator is
 469  
      *     present; otherwise the description only</li>
 470  
      *     <li>{@code end}</li>
 471  
      * </ul>
 472  
      *
 473  
      * @param buffer string buffer
 474  
      * @param typeIndicator type indicator
 475  
      * @param description type description
 476  
      * @param start starting character
 477  
      * @param end ending character
 478  
      */
 479  
     protected void appendTypeIndicator( StringBuilder buffer, String typeIndicator, String description,
 480  
                                         char start, char end ) {
 481  36
         buffer.append( ' ' ).append( start );
 482  36
         if ( typeIndicator != null )
 483  29
             buffer.append( typeIndicator );
 484  
 
 485  36
         if ( !Strings.isNullOrEmpty( description ) ) {
 486  23
             if ( typeIndicator != null )
 487  16
                 buffer.append( ": " );
 488  
 
 489  23
             buffer.append( description );
 490  
         }
 491  
 
 492  36
         buffer.append( end );
 493  36
     }
 494  
 
 495  
     /**
 496  
      * <p>Gives a string representing a description of the option with the given descriptor.</p>
 497  
      *
 498  
      * <p>This implementation:</p>
 499  
      * <ul>
 500  
      *     <li>Asks for the descriptor's {@link OptionDescriptor#defaultValues()}</li>
 501  
      *     <li>If they're not present, answers the descriptor's {@link OptionDescriptor#description()}.</li>
 502  
      *     <li>If they are present, concatenates and returns:
 503  
      *         <ul>
 504  
      *             <li>the descriptor's {@link OptionDescriptor#description()}</li>
 505  
      *             <li>{@code ' '}</li>
 506  
      *             <li>{@code "default: "} plus the result of {@link #createDefaultValuesDisplay(java.util.List)},
 507  
      *             surrounded by parentheses</li>
 508  
      *         </ul>
 509  
      *     </li>
 510  
      * </ul>
 511  
      *
 512  
      * @param descriptor a descriptor for a configured option of a parser
 513  
      * @return display text for the option's description
 514  
      */
 515  
     protected String createDescriptionDisplay( OptionDescriptor descriptor ) {
 516  102
         List<?> defaultValues = descriptor.defaultValues();
 517  102
         if ( defaultValues.isEmpty() )
 518  94
             return descriptor.description();
 519  
 
 520  8
         String defaultValuesDisplay = createDefaultValuesDisplay( defaultValues );
 521  8
         return ( descriptor.description()
 522  
             + ' '
 523  
             + surround( message( "default.value.header" ) + ' ' + defaultValuesDisplay, '(', ')' )
 524  
         ).trim();
 525  
     }
 526  
 
 527  
     /**
 528  
      * <p>Gives a display string for the default values of an option's argument.</p>
 529  
      *
 530  
      * <p>This implementation gives the {@link Object#toString()} of the first value if there is only one value,
 531  
      * otherwise gives the {@link Object#toString()} of the whole list.</p>
 532  
      *
 533  
      * @param defaultValues some default values for a given option's argument
 534  
      * @return a display string for those default values
 535  
      */
 536  
     protected String createDefaultValuesDisplay( List<?> defaultValues ) {
 537  8
         return defaultValues.size() == 1 ? defaultValues.get( 0 ).toString() : defaultValues.toString();
 538  
     }
 539  
 
 540  
     /**
 541  
      * <p>Looks up and gives a resource bundle message.</p>
 542  
      *
 543  
      * <p>This implementation looks in the bundle {@code "joptsimple.HelpFormatterMessages"} in the default
 544  
      * locale, using a key that is the concatenation of this class's fully qualified name, {@code '.'},
 545  
      * and the given key suffix, formats the corresponding value using the given arguments, and returns
 546  
      * the result.</p>
 547  
      *
 548  
      * @param keySuffix suffix to use when looking up the bundle message
 549  
      * @param args arguments to fill in the message template with
 550  
      * @return a formatted localized message
 551  
      */
 552  
     protected String message( String keySuffix, Object... args ) {
 553  240
         return Messages.message(
 554  
             Locale.getDefault(),
 555  
             "joptsimple.HelpFormatterMessages",
 556  
             BuiltinHelpFormatter.class,
 557  
             keySuffix,
 558  
             args );
 559  
     }
 560  
 }