Skip to content

Commit c6605dc

Browse files
committed
Don't use a global variable
Simplify new APIs
1 parent 631c536 commit c6605dc

7 files changed

Lines changed: 143 additions & 198 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ public int getArgs() {
582582
* @since 1.7.0
583583
*/
584584
public Converter<?, ?> getConverter() {
585-
return converter == null ? TypeHandler.getConverter(type) : converter;
585+
return converter == null ? TypeHandler.getDefault().getConverter(type) : converter;
586586
}
587587

588588
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public static Option create(final String opt) throws IllegalArgumentException {
109109
option.setOptionalArg(optionalArg);
110110
option.setArgs(argCount);
111111
option.setType(type);
112-
option.setConverter(TypeHandler.getConverter(type));
112+
option.setConverter(TypeHandler.getDefault().getConverter(type));
113113
option.setValueSeparator(valueSeparator);
114114
option.setArgName(argName);
115115
} finally {

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

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2121
import java.io.FileInputStream;
2222
import java.net.URL;
2323
import java.util.Date;
24+
import java.util.Map;
2425

2526
/**
2627
* Allows Options to be created from a single String. The pattern contains various single character flags and via an
@@ -104,14 +105,10 @@ public class PatternOptionBuilder {
104105
public static final Class<URL> URL_VALUE = URL.class;
105106

106107
/** The converter to use for Unimplemented data types */
107-
static final Converter<?, UnsupportedOperationException> UNSUPPORTED = s -> {
108+
private static final Converter<?, UnsupportedOperationException> UNSUPPORTED = s -> {
108109
throw new UnsupportedOperationException("Not yet implemented");
109110
};
110111

111-
static {
112-
registerTypes();
113-
}
114-
115112
/**
116113
* Retrieve the class that {@code ch} represents.
117114
*
@@ -202,7 +199,10 @@ public static Options parsePattern(final String pattern) {
202199
required = true;
203200
} else {
204201
type = getValueType(ch);
205-
converter = TypeHandler.getConverter(getValueType(ch));
202+
final Map<Class<?>, Converter<?, ? extends Throwable>> map = TypeHandler.createDefaultMap();
203+
// Backward compatibility (probably).
204+
map.put(FILES_VALUE, unsupported());
205+
converter = new TypeHandler(map).getConverter(getValueType(ch));
206206
}
207207
}
208208

@@ -216,15 +216,6 @@ public static Options parsePattern(final String pattern) {
216216
return options;
217217
}
218218

219-
/**
220-
* Registers custom {@code Converter}s with the {@code TypeHandler}.
221-
*
222-
* @since 1.7.0
223-
*/
224-
public static void registerTypes() {
225-
TypeHandler.register(PatternOptionBuilder.FILES_VALUE, unsupported());
226-
}
227-
228219
@SuppressWarnings("unchecked")
229220
static <T> T unsupported() {
230221
return (T) UNSUPPORTED;

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

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -38,25 +38,13 @@ Licensed to the Apache Software Foundation (ASF) under one or more
3838
*/
3939
public class TypeHandler {
4040

41-
/** Value of hex conversion of strings */
42-
private static final int HEX_RADIX = 16;
43-
4441
/**
45-
* Map of Class to Converter.
46-
* <p>
47-
* The Class type parameter matches the Converter's first generic type.
48-
* </p>
42+
* The default TypeHandler.
4943
*/
50-
private static Map<Class<?>, Converter<?, ? extends Throwable>> converterMap = createDefaultMap();
44+
private static final TypeHandler DEFAULT = new TypeHandler();
5145

52-
/**
53-
* Unregisters all Converters.
54-
*
55-
* @since 1.7.0
56-
*/
57-
public static void clear() {
58-
converterMap.clear();
59-
}
46+
/** Value of hex conversion of strings */
47+
private static final int HEX_RADIX = 16;
6048

6149
/**
6250
* Returns the class whose name is {@code className}.
@@ -82,7 +70,13 @@ public static Date createDate(final String string) {
8270
return createValueUnchecked(string, Date.class);
8371
}
8472

85-
private static Map<Class<?>, Converter<?, ? extends Throwable>> createDefaultMap() {
73+
/**
74+
* Creates a default converter map.
75+
*
76+
* @return a default converter map.
77+
* @since 1.7.0
78+
*/
79+
public static Map<Class<?>, Converter<?, ? extends Throwable>> createDefaultMap() {
8680
return putDefaultMap(new HashMap<>());
8781
}
8882

@@ -162,7 +156,7 @@ public static URL createURL(final String string) throws ParseException {
162156
*/
163157
public static <T> T createValue(final String string, final Class<T> clazz) throws ParseException {
164158
try {
165-
return getConverter(clazz).apply(string);
159+
return getDefault().getConverter(clazz).apply(string);
166160
} catch (final Throwable e) {
167161
throw ParseException.wrap(e);
168162
}
@@ -200,16 +194,13 @@ private static <T> T createValueUnchecked(final String string, final Class<T> cl
200194
}
201195

202196
/**
203-
* Gets the registered converter for the the Class, or {@link Converter#DEFAULT} if absent.
197+
* Gets the default TypeHandler.
204198
*
205-
* @param <T> The Class parameter type.
206-
* @param clazz The Class to get the Converter for.
207-
* @return the registered converter if any, {@link Converter#DEFAULT} otherwise.
199+
* @return the default TypeHandler.
208200
* @since 1.7.0
209201
*/
210-
@SuppressWarnings("unchecked") // returned value will have type T because it is fixed by clazz
211-
public static <T> Converter<T, ?> getConverter(final Class<T> clazz) {
212-
return (Converter<T, ?>) converterMap.getOrDefault(clazz, Converter.DEFAULT);
202+
public static TypeHandler getDefault() {
203+
return DEFAULT;
213204
}
214205

215206
/**
@@ -247,38 +238,41 @@ public static FileInputStream openFile(final String string) throws ParseExceptio
247238
}
248239

249240
/**
250-
* Registers a Converter for a Class. If {@code converter} is null registration is cleared for {@code clazz}, and no converter will be used in processing.
251-
*
252-
* @param <T> The Class parameter type.
253-
* @param clazz the Class to register the Converter for.
254-
* @param converter The Converter to associate with Class. May be null.
255-
* @since 1.7.0
241+
* Map of Class to Converter.
242+
* <p>
243+
* The Class type parameter matches the Converter's first generic type.
244+
* </p>
256245
*/
257-
public static <T> void register(final Class<T> clazz, final Converter<T, ? extends Throwable> converter) {
258-
if (converter == null) {
259-
unregister(clazz);
260-
} else {
261-
converterMap.put(clazz, converter);
262-
}
246+
private final Map<Class<?>, Converter<?, ? extends Throwable>> converterMap;
247+
248+
/**
249+
* Constructs a default initialized instance.
250+
*/
251+
public TypeHandler() {
252+
this(createDefaultMap());
263253
}
264254

265255
/**
266-
* Resets the registered Converters to the default state.
256+
* Constructs a default initialized instance.
267257
*
258+
* @param converterMap The converter map.
268259
* @since 1.7.0
269260
*/
270-
public static void resetConverters() {
271-
converterMap.clear();
272-
putDefaultMap(converterMap);
261+
public TypeHandler(final Map<Class<?>, Converter<?, ? extends Throwable>> converterMap) {
262+
this.converterMap = converterMap;
273263
}
274264

275265
/**
276-
* Unregisters a Converter for a Class. If {@code converter} is null registration is cleared for {@code clazz}, and no converter will be used in processing.
266+
* Gets the registered converter for the the Class, or {@link Converter#DEFAULT} if absent.
277267
*
278-
* @param clazz the Class to unregister.
268+
* @param <T> The Class parameter type.
269+
* @param clazz The Class to get the Converter for.
270+
* @return the registered converter if any, {@link Converter#DEFAULT} otherwise.
279271
* @since 1.7.0
280272
*/
281-
public static void unregister(final Class<?> clazz) {
282-
converterMap.remove(clazz);
273+
@SuppressWarnings("unchecked") // returned value will have type T because it is fixed by clazz
274+
public <T> Converter<T, ?> getConverter(final Class<T> clazz) {
275+
return (Converter<T, ?>) converterMap.getOrDefault(clazz, Converter.DEFAULT);
283276
}
277+
284278
}

src/test/java/org/apache/commons/cli/OptionTest.java

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ public boolean addValue(final String value) {
6868
}
6969
}
7070

71+
/** Always returns the same Path. */
72+
private static final Converter<Path, InvalidPathException> PATH_CONVERTER = s -> Paths.get("foo");
73+
7174
private static void checkOption(final Option option, final String opt, final String description, final String longOpt, final int numArgs,
7275
final String argName, final boolean required, final boolean optionalArg, final char valueSeparator, final Class<?> cls, final String deprecatedDesc,
7376
final Boolean deprecatedForRemoval, final String deprecatedSince) {
@@ -266,13 +269,8 @@ public void testHashCode() {
266269
assertNotEquals(Option.builder("test").build().hashCode(), Option.builder("test").longOpt("long test").build().hashCode());
267270
}
268271

269-
/** Always returns the same Path. */
270-
private static final Converter<Path, InvalidPathException> PATH_CONVERTER = s -> Paths.get("foo");
271-
272272
@Test
273273
public void testSerialization() throws IOException, ClassNotFoundException {
274-
// TODO No global map!
275-
TypeHandler.resetConverters();
276274
final Option option = Option.builder("o").type(TypeHandlerTest.Instantiable.class).build();
277275
assertEquals(Converter.DEFAULT, option.getConverter());
278276
Option roundtrip = roundTrip(option);
@@ -284,16 +282,10 @@ public void testSerialization() throws IOException, ClassNotFoundException {
284282
assertEquals(Converter.DEFAULT, roundtrip.getConverter());
285283
// verify registered class converters and verifiers do not get reset to default.
286284
// converters are NOT Serializable, use a serialization proxy if you want that.
287-
try {
288-
TypeHandler.register(Path.class, PATH_CONVERTER);
289-
// verify earlier values still set.
290-
assertEquals(Converter.DATE, option.getConverter());
291-
roundtrip = roundTrip(option);
292-
assertEquals(Converter.DEFAULT, roundtrip.getConverter());
293-
} finally {
294-
// TODO No global map!
295-
TypeHandler.resetConverters();
296-
}
285+
// verify earlier values still set.
286+
assertEquals(Converter.DATE, option.getConverter());
287+
roundtrip = roundTrip(option);
288+
assertEquals(Converter.DEFAULT, roundtrip.getConverter());
297289
}
298290

299291
@Test

src/test/java/org/apache/commons/cli/PatternOptionBuilderTest.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2121
import static org.junit.jupiter.api.Assertions.assertFalse;
2222
import static org.junit.jupiter.api.Assertions.assertNotNull;
2323
import static org.junit.jupiter.api.Assertions.assertNull;
24+
import static org.junit.jupiter.api.Assertions.assertThrows;
2425
import static org.junit.jupiter.api.Assertions.assertTrue;
2526
import static org.junit.jupiter.api.Assertions.fail;
2627

@@ -33,18 +34,13 @@ Licensed to the Apache Software Foundation (ASF) under one or more
3334
import java.util.Date;
3435
import java.util.Vector;
3536

36-
import org.junit.jupiter.api.BeforeAll;
3737
import org.junit.jupiter.api.Test;
3838

3939
/**
4040
* Test case for the PatternOptionBuilder class.
4141
*/
4242
@SuppressWarnings("deprecation") // tests some deprecated classes
4343
public class PatternOptionBuilderTest {
44-
@BeforeAll
45-
public static void setup() {
46-
PatternOptionBuilder.registerTypes();
47-
}
4844

4945
@Test
5046
public void testClassPattern() throws Exception {
@@ -164,12 +160,7 @@ public void testSimplePattern() throws Exception {
164160
assertEquals(new URL("https://commons.apache.org"), line.getOptionObject('t'), "url flag t");
165161

166162
// FILES NOT SUPPORTED YET
167-
try {
168-
assertEquals(new File[0], line.getOptionObject('m'), "files flag m");
169-
fail("Multiple files are not supported yet, should have failed");
170-
} catch (final UnsupportedOperationException uoe) {
171-
// expected
172-
}
163+
assertThrows(UnsupportedOperationException.class, () -> line.getOptionObject('m'));
173164

174165
assertEquals(expectedDate, line.getOptionObject('z'), "date flag z");
175166

0 commit comments

Comments
 (0)