10
10
11
11
package org .junit .platform .commons .support .conversion ;
12
12
13
- import static java .util .Arrays .asList ;
14
- import static java .util .Collections .unmodifiableList ;
15
13
import static org .apiguardian .api .API .Status .EXPERIMENTAL ;
16
- import static org .junit .platform .commons .util .ReflectionUtils .getWrapperType ;
17
14
18
- import java .util .List ;
19
- import java .util .Optional ;
15
+ import java .util .ServiceLoader ;
16
+ import java .util .stream .Stream ;
17
+ import java .util .stream .StreamSupport ;
20
18
21
19
import org .apiguardian .api .API ;
22
20
import org .junit .platform .commons .util .ClassLoaderUtils ;
30
28
@ API (status = EXPERIMENTAL , since = "1.11" )
31
29
public final class ConversionSupport {
32
30
33
- private static final List <StringToObjectConverter > stringToObjectConverters = unmodifiableList (asList ( //
34
- new StringToBooleanConverter (), //
35
- new StringToCharacterConverter (), //
36
- new StringToNumberConverter (), //
37
- new StringToClassConverter (), //
38
- new StringToEnumConverter (), //
39
- new StringToJavaTimeConverter (), //
40
- new StringToCommonJavaTypesConverter (), //
41
- new FallbackStringToObjectConverter () //
42
- ));
43
-
44
31
private ConversionSupport () {
45
32
/* no-op */
46
33
}
@@ -49,43 +36,6 @@ private ConversionSupport() {
49
36
* Convert the supplied source {@code String} into an instance of the specified
50
37
* target type.
51
38
*
52
- * <p>If the target type is {@code String}, the source {@code String} will not
53
- * be modified.
54
- *
55
- * <p>Some forms of conversion require a {@link ClassLoader}. If none is
56
- * provided, the {@linkplain ClassLoaderUtils#getDefaultClassLoader() default
57
- * ClassLoader} will be used.
58
- *
59
- * <p>This method is able to convert strings into primitive types and their
60
- * corresponding wrapper types ({@link Boolean}, {@link Character}, {@link Byte},
61
- * {@link Short}, {@link Integer}, {@link Long}, {@link Float}, and
62
- * {@link Double}), enum constants, date and time types from the
63
- * {@code java.time} package, as well as common Java types such as {@link Class},
64
- * {@link java.io.File}, {@link java.nio.file.Path}, {@link java.nio.charset.Charset},
65
- * {@link java.math.BigDecimal}, {@link java.math.BigInteger},
66
- * {@link java.util.Currency}, {@link java.util.Locale}, {@link java.util.UUID},
67
- * {@link java.net.URI}, and {@link java.net.URL}.
68
- *
69
- * <p>If the target type is not covered by any of the above, a convention-based
70
- * conversion strategy will be used to convert the source {@code String} into the
71
- * given target type by invoking a static factory method or factory constructor
72
- * defined in the target type. The search algorithm used in this strategy is
73
- * outlined below.
74
- *
75
- * <h4>Search Algorithm</h4>
76
- *
77
- * <ol>
78
- * <li>Search for a single, non-private static factory method in the target
79
- * type that converts from a String to the target type. Use the factory method
80
- * if present.</li>
81
- * <li>Search for a single, non-private constructor in the target type that
82
- * accepts a String. Use the constructor if present.</li>
83
- * </ol>
84
- *
85
- * <p>If multiple suitable factory methods are discovered they will be ignored.
86
- * If neither a single factory method nor a single constructor is found, the
87
- * convention-based conversion strategy will not apply.
88
- *
89
39
* @param source the source {@code String} to convert; may be {@code null}
90
40
* but only if the target type is a reference type
91
41
* @param targetType the target type the source should be converted into;
@@ -97,48 +47,49 @@ private ConversionSupport() {
97
47
* type is a reference type
98
48
*
99
49
* @since 1.11
50
+ * @see DefaultConversionService
51
+ * @deprecated Use {@link #convert(Object, Class, ClassLoader)} instead.
100
52
*/
101
53
@ SuppressWarnings ("unchecked" )
54
+ @ Deprecated
102
55
public static <T > T convert (String source , Class <T > targetType , ClassLoader classLoader ) {
103
- if (source == null ) {
104
- if (targetType .isPrimitive ()) {
105
- throw new ConversionException (
106
- "Cannot convert null to primitive value of type " + targetType .getTypeName ());
107
- }
108
- return null ;
109
- }
110
-
111
- if (String .class .equals (targetType )) {
112
- return (T ) source ;
113
- }
56
+ return (T ) DefaultConversionService .INSTANCE .convert (source , targetType , getClassLoader (classLoader ));
57
+ }
114
58
115
- Class <?> targetTypeToUse = toWrapperType (targetType );
116
- Optional <StringToObjectConverter > converter = stringToObjectConverters .stream ().filter (
117
- candidate -> candidate .canConvertTo (targetTypeToUse )).findFirst ();
118
- if (converter .isPresent ()) {
119
- try {
120
- ClassLoader classLoaderToUse = classLoader != null ? classLoader
121
- : ClassLoaderUtils .getDefaultClassLoader ();
122
- return (T ) converter .get ().convert (source , targetTypeToUse , classLoaderToUse );
123
- }
124
- catch (Exception ex ) {
125
- if (ex instanceof ConversionException ) {
126
- // simply rethrow it
127
- throw (ConversionException ) ex ;
128
- }
129
- // else
130
- throw new ConversionException (
131
- String .format ("Failed to convert String \" %s\" to type %s" , source , targetType .getTypeName ()), ex );
132
- }
133
- }
59
+ /**
60
+ * Convert the supplied source {@code Object} into an instance of the specified
61
+ * target type.
62
+ *
63
+ * @param source the source {@code Object} to convert; may be {@code null}
64
+ * but only if the target type is a reference type
65
+ * @param targetType the target type the source should be converted into;
66
+ * never {@code null}
67
+ * @param classLoader the {@code ClassLoader} to use; may be {@code null} to
68
+ * use the default {@code ClassLoader}
69
+ * @param <T> the type of the target
70
+ * @return the converted object; may be {@code null} but only if the target
71
+ * type is a reference type
72
+ *
73
+ * @since 1.12
74
+ */
75
+ @ API (status = EXPERIMENTAL , since = "1.12" )
76
+ @ SuppressWarnings ("unchecked" )
77
+ public static <T > T convert (Object source , Class <T > targetType , ClassLoader classLoader ) {
78
+ ClassLoader classLoaderToUse = getClassLoader (classLoader );
79
+ ServiceLoader <ConversionService > serviceLoader = ServiceLoader .load (ConversionService .class , classLoaderToUse );
134
80
135
- throw new ConversionException (
136
- "No built-in converter for source type java.lang.String and target type " + targetType .getTypeName ());
81
+ return (T ) Stream .concat ( //
82
+ StreamSupport .stream (serviceLoader .spliterator (), false ), //
83
+ Stream .of (DefaultConversionService .INSTANCE )) //
84
+ .filter (candidate -> candidate .canConvert (source , targetType , classLoader )) //
85
+ .findFirst () //
86
+ .map (candidate -> candidate .convert (source , targetType , classLoaderToUse )) //
87
+ .orElseThrow (() -> new ConversionException ("No built-in converter for source type "
88
+ + source .getClass ().getTypeName () + " and target type " + targetType .getTypeName ()));
137
89
}
138
90
139
- private static Class <?> toWrapperType (Class <?> targetType ) {
140
- Class <?> wrapperType = getWrapperType (targetType );
141
- return wrapperType != null ? wrapperType : targetType ;
91
+ private static ClassLoader getClassLoader (ClassLoader classLoader ) {
92
+ return classLoader != null ? classLoader : ClassLoaderUtils .getDefaultClassLoader ();
142
93
}
143
94
144
95
}
0 commit comments