Skip to content

Commit 00fb0a1

Browse files
committed
2 parents 26acfdc + 9256a4d commit 00fb0a1

15 files changed

Lines changed: 837 additions & 182 deletions

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@
192192
<artifactId>junit-vintage-engine</artifactId>
193193
<scope>test</scope>
194194
</dependency>
195+
<dependency>
196+
<groupId>org.junit.jupiter</groupId>
197+
<artifactId>junit-jupiter-params</artifactId>
198+
<scope>test</scope>
199+
</dependency>
195200
</dependencies>
196201

197202
<properties>
@@ -246,6 +251,7 @@
246251
<configuration>
247252
<excludeFilterFile>${basedir}/src/conf/spotbugs-exclude-filter.xml</excludeFilterFile>
248253
</configuration>
254+
<version>4.8.2.0</version>
249255
</plugin>
250256
<plugin>
251257
<groupId>org.apache.maven.plugins</groupId>

src/main/java/org/apache/commons/cli/CommandLine.java

Lines changed: 99 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2525
import java.util.LinkedList;
2626
import java.util.List;
2727
import java.util.Properties;
28+
import java.util.function.Supplier;
2829

2930
/**
3031
* Represents list of arguments parsed against a {@link Options} descriptor.
@@ -254,6 +255,18 @@ public String getOptionValue(final char opt) {
254255
* @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
255256
*/
256257
public String getOptionValue(final char opt, final String defaultValue) {
258+
return getOptionValue(String.valueOf(opt), () -> defaultValue);
259+
}
260+
261+
/**
262+
* Gets the argument, if any, of an option.
263+
*
264+
* @param opt character name of the option
265+
* @param defaultValue is a supplier for the default value to be returned if the option is not specified.
266+
* @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
267+
* @since 1.7.0
268+
*/
269+
public String getOptionValue(final char opt, final Supplier<String> defaultValue) {
257270
return getOptionValue(String.valueOf(opt), defaultValue);
258271
}
259272

@@ -281,10 +294,22 @@ public String getOptionValue(final Option option) {
281294
* @since 1.5.0
282295
*/
283296
public String getOptionValue(final Option option, final String defaultValue) {
284-
final String answer = getOptionValue(option);
285-
return answer != null ? answer : defaultValue;
297+
return getOptionValue(option, () -> defaultValue);
286298
}
287299

300+
/**
301+
* Gets the first argument, if any, of an option.
302+
*
303+
* @param option name of the option.
304+
* @param defaultValue is a supplier for the default value to be returned if the option is not specified.
305+
* @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
306+
* @since 1.7.0
307+
*/
308+
public String getOptionValue(final Option option, final Supplier<String> defaultValue) {
309+
final String answer = getOptionValue(option);
310+
return answer != null ? answer : defaultValue.get();
311+
}
312+
288313
/**
289314
* Gets the first argument, if any, of this option.
290315
*
@@ -303,9 +328,22 @@ public String getOptionValue(final String opt) {
303328
* @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
304329
*/
305330
public String getOptionValue(final String opt, final String defaultValue) {
331+
return getOptionValue(resolveOption(opt), () -> defaultValue);
332+
}
333+
334+
/**
335+
* Gets the first argument, if any, of an option.
336+
*
337+
* @param opt name of the option.
338+
* @param defaultValue is a supplier for the default value to be returned if the option is not specified.
339+
* @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
340+
* @since 1.7.0
341+
*/
342+
public String getOptionValue(final String opt, final Supplier<String> defaultValue) {
306343
return getOptionValue(resolveOption(opt), defaultValue);
307344
}
308345

346+
309347
/**
310348
* Gets the array of values, if any, of an option.
311349
*
@@ -349,46 +387,97 @@ public String[] getOptionValues(final String opt) {
349387
* Gets a version of this {@code Option} converted to a particular type.
350388
*
351389
* @param opt the name of the option.
390+
* @param <T> The return type for the method.
352391
* @return the value parsed into a particular object.
353392
* @throws ParseException if there are problems turning the option value into the desired type
354393
* @see PatternOptionBuilder
355394
* @since 1.5.0
356395
*/
357-
public Object getParsedOptionValue(final char opt) throws ParseException {
396+
public <T> T getParsedOptionValue(final char opt) throws ParseException {
358397
return getParsedOptionValue(String.valueOf(opt));
359398
}
360399

361400
/**
362401
* Gets a version of this {@code Option} converted to a particular type.
363402
*
364403
* @param option the name of the option.
404+
* @param <T> The return type for the method.
365405
* @return the value parsed into a particular object.
366406
* @throws ParseException if there are problems turning the option value into the desired type
367407
* @see PatternOptionBuilder
368408
* @since 1.5.0
369409
*/
370-
public Object getParsedOptionValue(final Option option) throws ParseException {
410+
public <T> T getParsedOptionValue(final Option option) throws ParseException {
411+
return getParsedOptionValue(option, null);
412+
}
413+
414+
/**
415+
* Gets a version of this {@code Option} converted to a particular type.
416+
*
417+
* @param opt the name of the option.
418+
* @param <T> The return type for the method.
419+
* @return the value parsed into a particular object.
420+
* @throws ParseException if there are problems turning the option value into the desired type
421+
* @see PatternOptionBuilder
422+
* @since 1.2
423+
*/
424+
public <T> T getParsedOptionValue(final String opt) throws ParseException {
425+
return getParsedOptionValue(resolveOption(opt));
426+
}
427+
428+
/**
429+
* Gets a version of this {@code Option} converted to a particular type.
430+
*
431+
* @param opt the name of the option.
432+
* @param defaultValue the default value to return if opt is not set.
433+
* @param <T> The return type for the method.
434+
* @return the value parsed into a particular object.
435+
* @throws ParseException if there are problems turning the option value into the desired type
436+
* @see PatternOptionBuilder
437+
* @since 1.7.0
438+
*/
439+
public <T> T getParsedOptionValue(final char opt, final T defaultValue) throws ParseException {
440+
return getParsedOptionValue(String.valueOf(opt), defaultValue);
441+
}
442+
443+
/**
444+
* Gets a version of this {@code Option} converted to a particular type.
445+
*
446+
* @param option the name of the option.
447+
* @param defaultValue the default value to return if opt is not set.
448+
* @param <T> The return type for the method.
449+
* @return the value parsed into a particular object.
450+
* @throws ParseException if there are problems turning the option value into the desired type
451+
* @see PatternOptionBuilder
452+
* @since 1.7.0
453+
*/
454+
@SuppressWarnings("unchecked")
455+
public <T> T getParsedOptionValue(final Option option, final T defaultValue) throws ParseException {
371456
if (option == null) {
372457
return null;
373458
}
374459
final String res = getOptionValue(option);
375-
if (res == null) {
376-
return null;
460+
461+
try {
462+
return res == null ? defaultValue : (T) option.getConverter().apply(res);
463+
} catch (Exception e) {
464+
throw ParseException.wrap(e);
377465
}
378-
return TypeHandler.createValue(res, option.getType());
379466
}
380467

381468
/**
382469
* Gets a version of this {@code Option} converted to a particular type.
383470
*
384471
* @param opt the name of the option.
472+
* @param defaultValue the default value to return if opt is not set.
473+
* @param <T> The return type for the method.
385474
* @return the value parsed into a particular object.
386475
* @throws ParseException if there are problems turning the option value into the desired type
387476
* @see PatternOptionBuilder
388-
* @since 1.2
477+
* @since 1.7.0
389478
*/
390-
public Object getParsedOptionValue(final String opt) throws ParseException {
391-
return getParsedOptionValue(resolveOption(opt));
479+
public <T> T getParsedOptionValue(final String opt, final T defaultValue) throws ParseException {
480+
return getParsedOptionValue(resolveOption(opt), defaultValue);
392481
}
393482

394483
/**
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
package org.apache.commons.cli;
18+
19+
import java.io.File;
20+
import java.net.URL;
21+
import java.nio.file.Path;
22+
import java.text.SimpleDateFormat;
23+
import java.util.Date;
24+
25+
/**
26+
* The definition of the functional interface to call when doing a conversion.
27+
* Like {@code Function<String,T>} but can throw an Exception.
28+
*
29+
* @param <T> The return type for the function.
30+
* @since 1.7.0
31+
*/
32+
@FunctionalInterface
33+
public interface Converter<T> {
34+
35+
/** The default converter. Does nothing. */
36+
Converter<?> DEFAULT = s -> s;
37+
38+
/** Class name converter. Calls {@code Class.forName}. */
39+
Converter<Class<?>> CLASS = s -> Class.forName(s);
40+
41+
/** File name converter. Calls @{code new File(s)} */
42+
Converter<File> FILE = s -> new File(s);
43+
44+
/** Path converter. Calls @{code new Path(s)} */
45+
Converter<Path> PATH = s -> new File(s).toPath();
46+
47+
/**
48+
* Number converter. Converts to a Double if a decimal point ('.') is in the
49+
* string or a Long otherwise.
50+
*/
51+
Converter<Number> NUMBER = s -> {
52+
if (s.indexOf('.') != -1) {
53+
return Double.valueOf(s);
54+
}
55+
return Long.valueOf(s);
56+
};
57+
58+
/**
59+
* Converts a class name to an instance of the class. Uses the Class converter
60+
* to find the class and then call the default constructor.
61+
* @see #CLASS
62+
*/
63+
Converter<Object> OBJECT = s -> CLASS.apply(s).getConstructor().newInstance();
64+
65+
/** Creates a URL. Calls {@code new URL(s)}. */
66+
Converter<URL> URL = s -> new URL(s);
67+
68+
/** Converts to a date using the format string Form "EEE MMM dd HH:mm:ss zzz yyyy". */
69+
Converter<Date> DATE = s -> new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy").parse(s);
70+
71+
/**
72+
* Applies the conversion function to the String argument.
73+
* @param str the String to convert
74+
* @return the Object from the conversion.
75+
* @throws Exception on error.
76+
*/
77+
T apply(String str) throws Exception;
78+
}

src/main/java/org/apache/commons/cli/Option.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ public static final class Builder {
8282

8383
/** The character that is the value separator */
8484
private char valueSeparator;
85+
86+
/** The converter to convert to type **/
87+
private Converter<?> converter;
8588

8689
/**
8790
* Constructs a new {@code Builder} with the minimum required parameters for an {@code Option} instance.
@@ -270,6 +273,19 @@ public Builder valueSeparator(final char valueSeparator) {
270273
this.valueSeparator = valueSeparator;
271274
return this;
272275
}
276+
277+
/**
278+
* Sets the converter for the option.
279+
* <p>Note: see {@link TypeHandler} for serialization discussion.</p>
280+
* @param converter the Converter to use.
281+
* @return this builder, to allow method chaining.
282+
* @since 1.7.0
283+
*/
284+
public Builder converter(final Converter<?> converter) {
285+
this.converter = converter;
286+
return this;
287+
}
288+
273289
}
274290

275291
/** Specifies the number of argument values has not been specified */
@@ -335,6 +351,9 @@ public static Builder builder(final String option) {
335351

336352
/** The character that is the value separator. */
337353
private char valuesep;
354+
355+
/** The explicit converter for this option. May be null */
356+
private transient Converter<?> converter;
338357

339358
/**
340359
* Private constructor used by the nested Builder class.
@@ -351,6 +370,7 @@ private Option(final Builder builder) {
351370
this.required = builder.required;
352371
this.type = builder.type;
353372
this.valuesep = builder.valueSeparator;
373+
this.converter = builder.converter;
354374
}
355375

356376
/**
@@ -423,7 +443,6 @@ private void add(final String value) {
423443
if (!acceptsArg()) {
424444
throw new IllegalArgumentException("Cannot add value, list full.");
425445
}
426-
427446
// store value
428447
values.add(value);
429448
}
@@ -695,6 +714,8 @@ private boolean hasNoValues() {
695714
}
696715

697716
/**
717+
* Returns whether this Option can have an optional argument.
718+
*
698719
* @return whether this Option can have an optional argument
699720
*/
700721
public boolean hasOptionalArg() {
@@ -865,6 +886,24 @@ public void setType(final Object type) {
865886
public void setValueSeparator(final char sep) {
866887
this.valuesep = sep;
867888
}
889+
890+
/**
891+
* Gets the value to type converter.
892+
* @return the value to type converter
893+
* @since 1.7.0
894+
*/
895+
public Converter<?> getConverter() {
896+
return converter == null ? TypeHandler.getConverter(type) : converter;
897+
}
898+
899+
/**
900+
* Sets the value to type converter.
901+
* @param converter The converter to convert the string value to the type.
902+
* @since 1.7.0
903+
*/
904+
public void setConverter(final Converter<?> converter) {
905+
this.converter = converter;
906+
}
868907

869908
/**
870909
* Dump state, suitable for debugging.

src/main/java/org/apache/commons/cli/OptionBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
3131
@Deprecated
3232
public final class OptionBuilder {
3333

34+
3435
/** Long option */
3536
private static String longOption;
3637

@@ -108,6 +109,7 @@ public static Option create(final String opt) throws IllegalArgumentException {
108109
option.setOptionalArg(optionalArg);
109110
option.setArgs(argCount);
110111
option.setType(type);
112+
option.setConverter(TypeHandler.getConverter(type));
111113
option.setValueSeparator(valueSeparator);
112114
option.setArgName(argName);
113115
} finally {

0 commit comments

Comments
 (0)