Skip to content

Commit 78b86ce

Browse files
committed
Better generics
- The compiler will no longer let you register a converter mismatch - TODO No global map!
1 parent f172b7c commit 78b86ce

4 files changed

Lines changed: 58 additions & 30 deletions

File tree

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public class PatternOptionBuilder {
104104
public static final Class<URL> URL_VALUE = URL.class;
105105

106106
/** The converter to use for Unimplemented data types */
107-
static final Converter<?, UnsupportedOperationException> NOT_IMPLEMENTED = s -> {
107+
static final Converter<?, UnsupportedOperationException> UNSUPPORTED = s -> {
108108
throw new UnsupportedOperationException("Not yet implemented");
109109
};
110110

@@ -221,6 +221,11 @@ public static Options parsePattern(final String pattern) {
221221
* @since 1.7.0
222222
*/
223223
public static void registerTypes() {
224-
TypeHandler.register(PatternOptionBuilder.FILES_VALUE, NOT_IMPLEMENTED);
224+
TypeHandler.register(PatternOptionBuilder.FILES_VALUE, unsupported());
225+
}
226+
227+
@SuppressWarnings("unchecked")
228+
static <T> T unsupported() {
229+
return (T) UNSUPPORTED;
225230
}
226231
}

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,14 @@ public static FileInputStream openFile(final String string) throws ParseExceptio
235235
/**
236236
* 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.
237237
*
238-
* @param clazz the Class to register the Converter and Verifier to.
238+
* @param <T> The Class parameter type.
239+
* @param clazz the Class to register the Converter for.
239240
* @param converter The Converter to associate with Class. May be null.
240241
* @since 1.7.0
241242
*/
242-
public static void register(final Class<?> clazz, final Converter<?, ? extends Throwable> converter) {
243+
public static <T> void register(final Class<T> clazz, final Converter<T, ? extends Throwable> converter) {
243244
if (converter == null) {
244-
converterMap.remove(clazz);
245+
unregister(clazz);
245246
} else {
246247
converterMap.put(clazz, converter);
247248
}
@@ -272,4 +273,14 @@ public static void resetConverters() {
272273
converterMap.put(BigInteger.class, BigInteger::new);
273274
converterMap.put(BigDecimal.class, BigDecimal::new);
274275
}
276+
277+
/**
278+
* 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.
279+
*
280+
* @param clazz the Class to unregister.
281+
* @since 1.7.0
282+
*/
283+
public static void unregister(final Class<?> clazz) {
284+
converterMap.remove(clazz);
285+
}
275286
}

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

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
import java.io.IOException;
3131
import java.io.ObjectInputStream;
3232
import java.io.ObjectOutputStream;
33+
import java.nio.file.InvalidPathException;
34+
import java.nio.file.Path;
35+
import java.nio.file.Paths;
3336

3437
import org.junit.jupiter.api.Test;
3538

@@ -263,29 +266,33 @@ public void testHashCode() {
263266
assertNotEquals(Option.builder("test").build().hashCode(), Option.builder("test").longOpt("long test").build().hashCode());
264267
}
265268

269+
/** Always returns the same Path. */
270+
private static final Converter<Path, InvalidPathException> PATH_CONVERTER = s -> Paths.get("foo");
266271

267272
@Test
268273
public void testSerialization() throws IOException, ClassNotFoundException {
269-
final Option o = Option.builder("o").type(TypeHandlerTest.Instantiable.class).build();
270-
assertEquals(Converter.DEFAULT, o.getConverter());
271-
Option o2 = roundTrip(o);
272-
assertEquals(Converter.DEFAULT, o2.getConverter());
273-
274+
// TODO No global map!
275+
TypeHandler.resetConverters();
276+
final Option option = Option.builder("o").type(TypeHandlerTest.Instantiable.class).build();
277+
assertEquals(Converter.DEFAULT, option.getConverter());
278+
Option roundtrip = roundTrip(option);
279+
assertEquals(Converter.DEFAULT, roundtrip.getConverter());
274280
// verify unregistered class converters and verifiers get reset to default.
275-
o.setConverter(Converter.DATE);
276-
o2 = roundTrip(o);
277-
assertEquals(Converter.DEFAULT, o2.getConverter());
278-
281+
// converters are NOT Serializable, use a serialization proxy if you want that.
282+
option.setConverter(Converter.DATE);
283+
roundtrip = roundTrip(option);
284+
assertEquals(Converter.DEFAULT, roundtrip.getConverter());
279285
// verify registered class converters and verifiers do not get reset to default.
286+
// converters are NOT Serializable, use a serialization proxy if you want that.
280287
try {
281-
TypeHandler.register(TypeHandlerTest.Instantiable.class, Converter.URL);
288+
TypeHandler.register(Path.class, PATH_CONVERTER);
282289
// verify earlier values still set.
283-
assertEquals(Converter.DATE, o.getConverter());
284-
o2 = roundTrip(o);
285-
// verify set to registered value
286-
assertEquals(Converter.URL, o2.getConverter());
290+
assertEquals(Converter.DATE, option.getConverter());
291+
roundtrip = roundTrip(option);
292+
assertEquals(Converter.DEFAULT, roundtrip.getConverter());
287293
} finally {
288-
TypeHandler.register(TypeHandlerTest.Instantiable.class, null);
294+
// TODO No global map!
295+
TypeHandler.resetConverters();
289296
}
290297
}
291298

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2727
import java.math.BigInteger;
2828
import java.net.MalformedURLException;
2929
import java.net.URL;
30+
import java.nio.file.InvalidPathException;
3031
import java.nio.file.Path;
32+
import java.nio.file.Paths;
3133
import java.text.DateFormat;
3234
import java.text.SimpleDateFormat;
3335
import java.util.ArrayList;
@@ -63,6 +65,9 @@ private NotInstantiable() {
6365

6466
}
6567

68+
/** Always returns the same Path. */
69+
private static final Converter<Path, InvalidPathException> PATH_CONVERTER = s -> Paths.get("foo");
70+
6671
private static Stream<Arguments> createValueTestParameters() {
6772
// force the PatternOptionBuilder to load / modify the TypeHandler table.
6873
final Class<?> ignore = PatternOptionBuilder.FILES_VALUE;
@@ -200,27 +205,27 @@ public void testnstantiableEquals() {
200205

201206
@Test
202207
public void testRegister() {
203-
assertEquals(Converter.DEFAULT, TypeHandler.getConverter(NotInstantiable.class));
208+
assertEquals(Converter.PATH, TypeHandler.getConverter(Path.class));
204209
try {
205-
TypeHandler.register(NotInstantiable.class, Converter.DATE);
206-
assertEquals(Converter.DATE, TypeHandler.getConverter(NotInstantiable.class));
210+
TypeHandler.register(Path.class, PATH_CONVERTER);
211+
assertEquals(PATH_CONVERTER, TypeHandler.getConverter(Path.class));
207212
} finally {
208-
TypeHandler.register(NotInstantiable.class, null);
209-
assertEquals(Converter.DEFAULT, TypeHandler.getConverter(NotInstantiable.class));
213+
TypeHandler.unregister(Path.class);
214+
assertEquals(Converter.DEFAULT, TypeHandler.getConverter(Path.class));
210215
}
211216
}
212217

213218
@Test
214219
public void testResetConverters() {
215-
assertEquals(Converter.DEFAULT, TypeHandler.getConverter(NotInstantiable.class));
220+
assertEquals(Converter.PATH, TypeHandler.getConverter(Path.class));
216221
try {
217-
TypeHandler.register(NotInstantiable.class, Converter.DATE);
218-
assertEquals(Converter.DATE, TypeHandler.getConverter(NotInstantiable.class));
222+
TypeHandler.register(Path.class, PATH_CONVERTER);
223+
assertEquals(PATH_CONVERTER, TypeHandler.getConverter(Path.class));
219224
TypeHandler.resetConverters();
220-
assertEquals(Converter.DEFAULT, TypeHandler.getConverter(NotInstantiable.class));
225+
assertEquals(Converter.PATH, TypeHandler.getConverter(Path.class));
221226
assertEquals(Converter.DEFAULT, TypeHandler.getConverter(NotInstantiable.class));
222227
} finally {
223-
TypeHandler.register(NotInstantiable.class, null);
228+
TypeHandler.resetConverters();
224229
}
225230
}
226231
}

0 commit comments

Comments
 (0)