Skip to content

Commit 789d5d5

Browse files
committed
Add ConversionContext
1 parent 0825961 commit 789d5d5

18 files changed

+205
-111
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2015-2025 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.platform.commons.support.conversion;
12+
13+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
14+
15+
import org.apiguardian.api.API;
16+
import org.jspecify.annotations.Nullable;
17+
18+
/**
19+
* {@code ConversionContext} encapsulates the <em>context</em> in which the
20+
* current conversion is being executed.
21+
*
22+
* <p>{@link Converter Converters} are provided an instance of
23+
* {@code ConversionContext} to perform their work.
24+
*
25+
* @param sourceType
26+
* @param targetType
27+
* @param classLoader
28+
*
29+
* @since 6.0
30+
* @see Converter
31+
*/
32+
@API(status = EXPERIMENTAL, since = "6.0")
33+
public record ConversionContext(TypeDescriptor sourceType, TypeDescriptor targetType, ClassLoader classLoader) {
34+
35+
/**
36+
*
37+
* @param source
38+
* @param targetType
39+
* @param classLoader
40+
*/
41+
public ConversionContext(@Nullable Object source, TypeDescriptor targetType, ClassLoader classLoader) {
42+
this(TypeDescriptor.forInstance(source), targetType, classLoader);
43+
}
44+
45+
}

junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionSupport.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import org.apiguardian.api.API;
2121
import org.jspecify.annotations.Nullable;
22-
import org.junit.platform.commons.util.ClassLoaderUtils;
2322

2423
/**
2524
* {@code ConversionSupport} provides static utility methods for converting a
@@ -79,20 +78,19 @@ private ConversionSupport() {
7978
@SuppressWarnings({ "unchecked", "rawtypes" })
8079
public static <T> @Nullable T convert(@Nullable Object source, TypeDescriptor targetType,
8180
@Nullable ClassLoader classLoader) {
82-
TypeDescriptor sourceType = TypeDescriptor.forInstance(source);
83-
ClassLoader classLoaderToUse = classLoader != null ? classLoader : ClassLoaderUtils.getDefaultClassLoader();
84-
ServiceLoader<Converter> serviceLoader = ServiceLoader.load(Converter.class, classLoaderToUse);
81+
ConversionContext context = new ConversionContext(source, targetType, classLoader);
82+
ServiceLoader<Converter> serviceLoader = ServiceLoader.load(Converter.class, context.classLoader());
8583

8684
Converter converter = Stream.concat( //
8785
StreamSupport.stream(serviceLoader.spliterator(), false), //
8886
Stream.of(DefaultConverter.INSTANCE)) //
89-
.filter(candidate -> candidate.canConvert(sourceType, targetType)) //
87+
.filter(candidate -> candidate.canConvert(context)) //
9088
.findFirst() //
9189
.orElseThrow(() -> new ConversionException(
9290
"No registered or built-in converter for source '%s' and target type %s".formatted( //
9391
source, targetType.getTypeName())));
9492

95-
return (T) converter.convert(source, sourceType, targetType, classLoaderToUse);
93+
return (T) converter.convert(source, context);
9694
}
9795

9896
}

junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/Converter.java

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
* from a given source type into a given target type and does not need access to
3030
* the {@link ClassLoader} to perform the conversion.
3131
*
32+
* @param <S>
33+
* @param <T>
34+
*
3235
* @since 6.0
3336
* @see ConversionSupport
3437
* @see TypedConverter
@@ -37,34 +40,26 @@
3740
public interface Converter<S, T> {
3841

3942
/**
40-
* Determine if the supplied source type can be converted into an instance
41-
* of the specified target type.
43+
* Determine if the supplied conversion context is supported.
4244
*
43-
* @param sourceType the descriptor of the source type; never {@code null}
44-
* @param targetType the descriptor of the type the source should be converted into;
45-
* never {@code null}
46-
* @return {@code true} if the supplied source type can be converted
45+
* @param context the context for the conversion; never {@code null}
46+
* @return {@code true} if the conversion is supported
4747
*/
48-
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
48+
boolean canConvert(ConversionContext context);
4949

5050
/**
51-
* Convert the supplied source object into an instance of the specified
52-
* target type.
53-
* <p>This method will only be invoked if {@link #canConvert(TypeDescriptor, TypeDescriptor)}
54-
* returned {@code true} for the same type descriptors.
51+
* Convert the supplied source object according to the supplied conversion context.
52+
* <p>This method will only be invoked if {@link #canConvert(ConversionContext)}
53+
* returned {@code true} for the same context.
5554
*
56-
* @param source the source object to convert; may be {@code null} but only
57-
* if the target type is a reference type
58-
* @param sourceType the descriptor of the source type; never {@code null}
59-
* @param targetType the descriptor of the type the source should be converted into;
60-
* never {@code null}
61-
* @param classLoader the {@code ClassLoader} to use; never {@code null}
55+
* @param source the source object to convert; may be {@code null}
56+
* but only if the target type is a reference type
57+
* @param context the context for the conversion; never {@code null}
6258
* @return the converted object; may be {@code null} but only if the target
6359
* type is a reference type
6460
* @throws ConversionException if an error occurs during the conversion
6561
*/
6662
@Nullable
67-
T convert(@Nullable S source, TypeDescriptor sourceType, TypeDescriptor targetType, ClassLoader classLoader)
68-
throws ConversionException;
63+
T convert(@Nullable S source, ConversionContext context) throws ConversionException;
6964

7065
}

junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/DefaultConverter.java

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ public class DefaultConverter implements Converter<String, Object> {
4848

4949
static final DefaultConverter INSTANCE = new DefaultConverter();
5050

51-
private static final List<StringToObjectConverter> stringToObjectConverters = List.of(
52-
new StringToBooleanConverter(), //
51+
private static final List<Converter<String, ?>> stringToObjectConverters = List.of(new StringToBooleanConverter(), //
5352
new StringToCharacterConverter(), //
5453
new StringToNumberConverter(), //
5554
new StringToClassConverter(), //
@@ -64,30 +63,27 @@ private DefaultConverter() {
6463
}
6564

6665
/**
67-
* Determine if the supplied source type can be converted into an instance
68-
* of the specified target type.
66+
* Determine if the supplied conversion context is supported.
67+
* <p>FIXME add more content from {@link Converter#convert} about the conversion algorithm
6968
*
70-
* @param sourceType the descriptor of the source type; never {@code null}
71-
* @param targetType the target type the source should be converted into;
72-
* never {@code null}
73-
* @return {@code true} if the supplied source can be converted
69+
* @param context the context for the conversion; never {@code null}
70+
* @return {@code true} if the conversion is supported
7471
*/
7572
@Override
76-
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
77-
if (sourceType == TypeDescriptor.NONE) {
78-
return !targetType.isPrimitive();
73+
public boolean canConvert(ConversionContext context) {
74+
if (context.sourceType() == TypeDescriptor.NONE) {
75+
return !context.targetType().isPrimitive();
7976
}
8077

81-
if (!(String.class.equals(sourceType.getType()))) {
78+
if (!(String.class.equals(context.sourceType().getType()))) {
8279
return false;
8380
}
8481

85-
if (String.class.equals(targetType.getType())) {
82+
if (String.class.equals(context.targetType().getType())) {
8683
return true;
8784
}
8885

89-
return stringToObjectConverters.stream().anyMatch(
90-
candidate -> candidate.canConvert(targetType.getWrapperType()));
86+
return stringToObjectConverters.stream().anyMatch(candidate -> candidate.canConvert(context));
9187
}
9288

9389
/**
@@ -124,37 +120,32 @@ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType)
124120
* If neither a single factory method nor a single constructor is found, the
125121
* convention-based conversion strategy will not apply.
126122
*
127-
* @param source the source {@link String} to convert; may be {@code null} but only
128-
* if the target type is a reference type
129-
* @param sourceType the descriptor of the source type; never {@code null}
130-
* @param targetType the descriptor of the type the source should be converted into;
131-
* never {@code null}
132-
* @param classLoader the {@code ClassLoader} to use; never {@code null}
123+
* @param source the source {@link String} to convert; may be {@code null}
124+
* but only if the target type is a reference type
125+
* @param context the context for the conversion; never {@code null}
133126
* @return the converted object; may be {@code null} but only if the target
134127
* type is a reference type
135128
* @throws ConversionException if an error occurs during the conversion
136129
*/
137130
@Override
138-
public @Nullable Object convert(@Nullable String source, TypeDescriptor sourceType, TypeDescriptor targetType,
139-
ClassLoader classLoader) throws ConversionException {
131+
public @Nullable Object convert(@Nullable String source, ConversionContext context) throws ConversionException {
140132
if (source == null) {
141-
if (targetType.isPrimitive()) {
133+
if (context.targetType().isPrimitive()) {
142134
throw new ConversionException(
143-
"Cannot convert null to primitive value of type " + targetType.getTypeName());
135+
"Cannot convert null to primitive value of type " + context.targetType().getTypeName());
144136
}
145137
return null;
146138
}
147139

148-
if (String.class.equals(targetType.getType())) {
140+
if (String.class.equals(context.targetType().getType())) {
149141
return source;
150142
}
151143

152-
Class<?> targetTypeToUse = targetType.getWrapperType();
153-
Optional<StringToObjectConverter> converter = stringToObjectConverters.stream().filter(
154-
candidate -> candidate.canConvert(targetTypeToUse)).findFirst();
144+
Optional<Converter<String, ?>> converter = stringToObjectConverters.stream().filter(
145+
candidate -> candidate.canConvert(context)).findFirst();
155146
if (converter.isPresent()) {
156147
try {
157-
return converter.get().convert(source, targetTypeToUse, classLoader);
148+
return converter.get().convert(source, context);
158149
}
159150
catch (Exception ex) {
160151
if (ex instanceof ConversionException) {
@@ -163,12 +154,13 @@ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType)
163154
}
164155
// else
165156
throw new ConversionException(
166-
"Failed to convert String \"%s\" to type %s".formatted(source, targetType.getTypeName()), ex);
157+
"Failed to convert String \"%s\" to type %s".formatted(source, context.targetType().getTypeName()),
158+
ex);
167159
}
168160
}
169161

170-
throw new ConversionException(
171-
"No built-in converter for source type java.lang.String and target type " + targetType.getTypeName());
162+
throw new ConversionException("No built-in converter for source type java.lang.String and target type "
163+
+ context.targetType().getTypeName());
172164
}
173165

174166
}

junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverter.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import org.junit.platform.commons.util.Preconditions;
3131

3232
/**
33-
* {@code FallbackStringToObjectConverter} is a {@link StringToObjectConverter}
33+
* {@code FallbackStringToObjectConverter} is a {@link StringToTargetTypeConverter}
3434
* that provides a fallback conversion strategy for converting from a
3535
* {@link String} to a given target type by invoking a static factory method
3636
* or factory constructor defined in the target type.
@@ -52,7 +52,7 @@
5252
* @since 1.11
5353
* @see ConversionSupport
5454
*/
55-
class FallbackStringToObjectConverter extends StringToObjectConverter {
55+
class FallbackStringToObjectConverter extends StringToTargetTypeConverter<Object> {
5656

5757
/**
5858
* Implementation of the NULL Object Pattern.
@@ -71,13 +71,13 @@ class FallbackStringToObjectConverter extends StringToObjectConverter {
7171
= new ConcurrentHashMap<>(64);
7272

7373
@Override
74-
public boolean canConvert(Class<?> targetType) {
74+
boolean canConvert(Class<?> targetType) {
7575
return findFactoryExecutable(targetType) != NULL_EXECUTABLE;
7676
}
7777

7878
@Override
7979
@Nullable
80-
public Object convert(String source, Class<?> targetType, ClassLoader classLoader) {
80+
Object convert(String source, Class<?> targetType) {
8181
Function<String, @Nullable Object> executable = findFactoryExecutable(targetType);
8282
Preconditions.condition(executable != NULL_EXECUTABLE,
8383
"Illegal state: convert() must not be called if canConvert() returned false");

junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToBooleanConverter.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010

1111
package org.junit.platform.commons.support.conversion;
1212

13+
import org.jspecify.annotations.Nullable;
1314
import org.junit.platform.commons.util.Preconditions;
1415

15-
class StringToBooleanConverter extends StringToObjectConverter {
16+
class StringToBooleanConverter extends StringToWrapperTypeConverter<Boolean> {
1617

1718
@Override
18-
public boolean canConvert(Class<?> targetType) {
19+
boolean canConvert(Class<?> targetType) {
1920
return targetType == Boolean.class;
2021
}
2122

2223
@Override
23-
public Object convert(String source, Class<?> targetType, ClassLoader classLoader) {
24+
@Nullable
25+
Boolean convert(@Nullable String source, Class<?> targetType) throws ConversionException {
2426
boolean isTrue = "true".equalsIgnoreCase(source);
2527
Preconditions.condition(isTrue || "false".equalsIgnoreCase(source),
2628
() -> "String must be 'true' or 'false' (ignoring case): " + source);

junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCharacterConverter.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010

1111
package org.junit.platform.commons.support.conversion;
1212

13+
import org.jspecify.annotations.Nullable;
1314
import org.junit.platform.commons.util.Preconditions;
1415

15-
class StringToCharacterConverter extends StringToObjectConverter {
16+
class StringToCharacterConverter extends StringToWrapperTypeConverter<Character> {
1617

1718
@Override
18-
public boolean canConvert(Class<?> targetType) {
19+
boolean canConvert(Class<?> targetType) {
1920
return targetType == Character.class;
2021
}
2122

2223
@Override
23-
public Object convert(String source, Class<?> targetType, ClassLoader classLoader) {
24+
@Nullable
25+
Character convert(@Nullable String source, Class<?> targetType) throws ConversionException {
2426
Preconditions.condition(source.length() == 1, () -> "String must have length of 1: " + source);
2527
return source.charAt(0);
2628
}

junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToClassConverter.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313
import org.jspecify.annotations.Nullable;
1414
import org.junit.platform.commons.support.ReflectionSupport;
1515

16-
class StringToClassConverter extends StringToObjectConverter {
16+
class StringToClassConverter implements Converter<String, Class<?>> {
1717

1818
@Override
19-
public boolean canConvert(Class<?> targetType) {
20-
return targetType == Class.class;
19+
public boolean canConvert(ConversionContext context) {
20+
return context.targetType().getType() == Class.class;
2121
}
2222

2323
@Override
24-
public @Nullable Object convert(String className, Class<?> targetType, ClassLoader classLoader) {
24+
public @Nullable Class<?> convert(@Nullable String className, ConversionContext context) {
2525
// @formatter:off
26-
return ReflectionSupport.tryToLoadClass(className, classLoader)
26+
return ReflectionSupport.tryToLoadClass(className, context.classLoader())
2727
.getOrThrow(cause -> new ConversionException(
2828
"Failed to convert String \"" + className + "\" to type java.lang.Class", cause));
2929
// @formatter:on

junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCommonJavaTypesConverter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import org.jspecify.annotations.Nullable;
2727
import org.junit.platform.commons.util.Preconditions;
2828

29-
class StringToCommonJavaTypesConverter extends StringToObjectConverter {
29+
class StringToCommonJavaTypesConverter extends StringToTargetTypeConverter<Object> {
3030

3131
@SuppressWarnings("deprecation")
3232
private static final Map<Class<?>, Function<String, ?>> CONVERTERS = Map.of( //
@@ -44,12 +44,12 @@ class StringToCommonJavaTypesConverter extends StringToObjectConverter {
4444
);
4545

4646
@Override
47-
public boolean canConvert(@Nullable Class<?> targetType) {
47+
boolean canConvert(@Nullable Class<?> targetType) {
4848
return CONVERTERS.containsKey(targetType);
4949
}
5050

5151
@Override
52-
public Object convert(@Nullable String source, @Nullable Class<?> targetType, ClassLoader classLoader) {
52+
Object convert(@Nullable String source, @Nullable Class<?> targetType) {
5353
Function<String, ?> converter = Preconditions.notNull(CONVERTERS.get(targetType),
5454
() -> "No registered converter for %s".formatted(targetType != null ? targetType.getName() : null));
5555
return converter.apply(source);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy