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