1010
1111package  org .junit .platform .commons .support .conversion ;
1212
13- import  static  java .util .Arrays .asList ;
14- import  static  java .util .Collections .unmodifiableList ;
1513import  static  org .apiguardian .api .API .Status .EXPERIMENTAL ;
16- import  static  org .junit .platform .commons .util .ReflectionUtils .getWrapperType ;
1714
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 ;
2018
2119import  org .apiguardian .api .API ;
2220import  org .junit .platform .commons .util .ClassLoaderUtils ;
3028@ API (status  = EXPERIMENTAL , since  = "1.11" )
3129public  final  class  ConversionSupport  {
3230
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- 
4431	private  ConversionSupport () {
4532		/* no-op */ 
4633	}
@@ -49,43 +36,6 @@ private ConversionSupport() {
4936	 * Convert the supplied source {@code String} into an instance of the specified 
5037	 * target type. 
5138	 * 
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- 	 * 
8939	 * @param source the source {@code String} to convert; may be {@code null} 
9040	 * but only if the target type is a reference type 
9141	 * @param targetType the target type the source should be converted into; 
@@ -97,48 +47,48 @@ private ConversionSupport() {
9747	 * type is a reference type 
9848	 * 
9949	 * @since 1.11 
50+ 	 * @see DefaultConversionService 
51+ 	 * @deprecated Use {@link #convert(Object, Class, ClassLoader)} instead. 
10052	 */ 
10153	@ SuppressWarnings ("unchecked" )
54+ 	@ Deprecated 
10255	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+ 	}
11458
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+ 	 *  
61+ 	 * 
62+ 	 * @param source the source {@code Object} to convert; may be {@code null} 
63+ 	 * but only if the target type is a reference type 
64+ 	 * @param targetType the target type the source should be converted into; 
65+ 	 * never {@code null} 
66+ 	 * @param classLoader the {@code ClassLoader} to use; may be {@code null} to 
67+ 	 * use the default {@code ClassLoader} 
68+ 	 * @param <T> the type of the target 
69+ 	 * @return the converted object; may be {@code null} but only if the target 
70+ 	 * type is a reference type 
71+ 	 * 
72+ 	 * @since 1.12 
73+ 	 */ 
74+ 	@ API (status  = EXPERIMENTAL , since  = "1.12" )
75+ 	@ SuppressWarnings ("unchecked" )
76+ 	public  static  <T > T  convert (Object  source , Class <T > targetType , ClassLoader  classLoader ) {
77+ 		ClassLoader  classLoaderToUse  = getClassLoader (classLoader );
78+ 		ServiceLoader <ConversionService > serviceLoader  = ServiceLoader .load (ConversionService .class , classLoaderToUse );
13479
135- 		throw  new  ConversionException (
136- 			"No built-in converter for source type java.lang.String and target type "  + targetType .getTypeName ());
80+ 		return  (T ) Stream .concat ( // 
81+ 			StreamSupport .stream (serviceLoader .spliterator (), false ), // 
82+ 			Stream .of (DefaultConversionService .INSTANCE )) // 
83+ 				.filter (candidate  -> candidate .canConvert (source , targetType , classLoader )) // 
84+ 				.findFirst () // 
85+ 				.map (candidate  -> candidate .convert (source , targetType , classLoaderToUse )) // 
86+ 				.orElseThrow (() -> new  ConversionException ("No built-in converter for source type " 
87+ 						+ source .getClass ().getTypeName () + " and target type "  + targetType .getTypeName ()));
13788	}
13889
139- 	private  static  Class <?> toWrapperType (Class <?> targetType ) {
140- 		Class <?> wrapperType  = getWrapperType (targetType );
141- 		return  wrapperType  != null  ? wrapperType  : targetType ;
90+ 	private  static  ClassLoader  getClassLoader (ClassLoader  classLoader ) {
91+ 		return  classLoader  != null  ? classLoader  : ClassLoaderUtils .getDefaultClassLoader ();
14292	}
14393
14494}
0 commit comments