From e22e6f920911960e866328764093d31fb6b262bf Mon Sep 17 00:00:00 2001 From: Juliette de Rancourt Date: Tue, 17 Oct 2023 15:21:52 +0200 Subject: [PATCH 1/5] Introduce `StringConversionSupport` in `junit-platform-commons` Resolves #3449 --- .../release-notes-5.11.0-M1.adoc | 3 +- .../converter/DefaultArgumentConverter.java | 64 +------ .../DefaultArgumentConverterTests.java | 4 + .../commons/support/ConversionException.java | 37 ++++ .../FallbackStringToObjectConverter.java | 173 ++++++++++++++++++ .../support/StringConversionSupport.java | 106 +++++++++++ .../support/StringToBooleanConverter.java | 30 +++ .../support/StringToCharacterConverter.java | 28 +++ .../support/StringToClassConverter.java | 36 ++++ .../StringToCommonJavaTypesConverter.java | 70 +++++++ .../support/StringToEnumConverter.java | 26 +++ .../support/StringToJavaTimeConverter.java | 65 +++++++ .../support/StringToNumberConverter.java | 50 +++++ .../support/StringToObjectConverter.java | 46 +++++ 14 files changed, 682 insertions(+), 56 deletions(-) create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/ConversionException.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/FallbackStringToObjectConverter.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToBooleanConverter.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCharacterConverter.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToClassConverter.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCommonJavaTypesConverter.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToEnumConverter.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToJavaTimeConverter.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToNumberConverter.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToObjectConverter.java diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-M1.adoc index f428b0e5a636..69659b2106a2 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-M1.adoc @@ -23,7 +23,8 @@ JUnit repository on GitHub. ==== New Features and Improvements -* ❓ +* New `StringConversionSupport` in `junit-platform-commons` to expose + internal conversion logic used by Jupiter's `DefaultArgumentConverter` [[release-notes-5.11.0-M1-junit-jupiter]] diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java index fe697c5696e3..c152b558b0bb 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java @@ -10,10 +10,7 @@ package org.junit.jupiter.params.converter; -import static java.util.Arrays.asList; -import static java.util.Collections.unmodifiableList; import static org.apiguardian.api.API.Status.INTERNAL; -import static org.junit.platform.commons.util.ReflectionUtils.getWrapperType; import java.io.File; import java.math.BigDecimal; @@ -21,15 +18,14 @@ import java.net.URI; import java.net.URL; import java.util.Currency; -import java.util.List; import java.util.Locale; -import java.util.Optional; import java.util.UUID; import org.apiguardian.api.API; import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.platform.commons.support.ConversionException; +import org.junit.platform.commons.support.StringConversionSupport; import org.junit.platform.commons.util.ClassLoaderUtils; -import org.junit.platform.commons.util.ReflectionUtils; /** * {@code DefaultArgumentConverter} is the default implementation of the @@ -47,23 +43,13 @@ * * @since 5.0 * @see org.junit.jupiter.params.converter.ArgumentConverter + * @see org.junit.platform.commons.support.StringConversionSupport */ @API(status = INTERNAL, since = "5.0") public class DefaultArgumentConverter implements ArgumentConverter { public static final DefaultArgumentConverter INSTANCE = new DefaultArgumentConverter(); - private static final List stringToObjectConverters = unmodifiableList(asList( // - new StringToBooleanConverter(), // - new StringToCharacterConverter(), // - new StringToNumberConverter(), // - new StringToClassConverter(), // - new StringToEnumConverter(), // - new StringToJavaTimeConverter(), // - new StringToCommonJavaTypesConverter(), // - new FallbackStringToObjectConverter() // - )); - private DefaultArgumentConverter() { // nothing to initialize } @@ -75,47 +61,15 @@ public final Object convert(Object source, ParameterContext context) { } public final Object convert(Object source, Class targetType, ParameterContext context) { - if (source == null) { - if (targetType.isPrimitive()) { - throw new ArgumentConversionException( - "Cannot convert null to primitive value of type " + targetType.getTypeName()); - } - return null; - } + Class declaringClass = context.getDeclaringExecutable().getDeclaringClass(); + ClassLoader classLoader = ClassLoaderUtils.getClassLoader(declaringClass); - if (ReflectionUtils.isAssignableTo(source, targetType)) { - return source; + try { + return StringConversionSupport.convert(source, targetType, classLoader); } - - if (source instanceof String) { - Class targetTypeToUse = toWrapperType(targetType); - Optional converter = stringToObjectConverters.stream().filter( - candidate -> candidate.canConvert(targetTypeToUse)).findFirst(); - if (converter.isPresent()) { - Class declaringClass = context.getDeclaringExecutable().getDeclaringClass(); - ClassLoader classLoader = ClassLoaderUtils.getClassLoader(declaringClass); - try { - return converter.get().convert((String) source, targetTypeToUse, classLoader); - } - catch (Exception ex) { - if (ex instanceof ArgumentConversionException) { - // simply rethrow it - throw (ArgumentConversionException) ex; - } - // else - throw new ArgumentConversionException( - "Failed to convert String \"" + source + "\" to type " + targetType.getTypeName(), ex); - } - } + catch (ConversionException e) { + throw new ArgumentConversionException(e.getMessage(), e); } - throw new ArgumentConversionException( - String.format("No built-in converter for source type %s and target type %s", - source.getClass().getTypeName(), targetType.getTypeName())); - } - - private static Class toWrapperType(Class targetType) { - Class wrapperType = getWrapperType(targetType); - return wrapperType != null ? wrapperType : targetType; } } diff --git a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java b/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java index f801058f4e59..371c0382b461 100644 --- a/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java +++ b/junit-jupiter-params/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java @@ -157,24 +157,28 @@ void throwsExceptionOnInvalidStringForPrimitiveTypes() { .isThrownBy(() -> convert("ab", char.class)) // .withMessage("Failed to convert String \"ab\" to type char") // .havingCause() // + .havingCause() // .withMessage("String must have length of 1: ab"); assertThatExceptionOfType(ArgumentConversionException.class) // .isThrownBy(() -> convert("tru", boolean.class)) // .withMessage("Failed to convert String \"tru\" to type boolean") // .havingCause() // + .havingCause() // .withMessage("String must be 'true' or 'false' (ignoring case): tru"); assertThatExceptionOfType(ArgumentConversionException.class) // .isThrownBy(() -> convert("null", boolean.class)) // .withMessage("Failed to convert String \"null\" to type boolean") // .havingCause() // + .havingCause() // .withMessage("String must be 'true' or 'false' (ignoring case): null"); assertThatExceptionOfType(ArgumentConversionException.class) // .isThrownBy(() -> convert("NULL", boolean.class)) // .withMessage("Failed to convert String \"NULL\" to type boolean") // .havingCause() // + .havingCause() // .withMessage("String must be 'true' or 'false' (ignoring case): NULL"); } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ConversionException.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ConversionException.java new file mode 100644 index 000000000000..14d914eb4856 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ConversionException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import org.apiguardian.api.API; +import org.junit.platform.commons.JUnitException; + +/** + * {@code ConversionException} is an exception that can occur when an + * object is converted to another object. + * + * @since 1.11 + */ +@API(status = EXPERIMENTAL, since = "1.11") +public class ConversionException extends JUnitException { + + private static final long serialVersionUID = 1L; + + public ConversionException(String message) { + super(message); + } + + public ConversionException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/FallbackStringToObjectConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/FallbackStringToObjectConverter.java new file mode 100644 index 000000000000..e2ade2548b37 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/FallbackStringToObjectConverter.java @@ -0,0 +1,173 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP; +import static org.junit.platform.commons.util.ReflectionUtils.findConstructors; +import static org.junit.platform.commons.util.ReflectionUtils.findMethods; +import static org.junit.platform.commons.util.ReflectionUtils.invokeMethod; +import static org.junit.platform.commons.util.ReflectionUtils.isNotPrivate; +import static org.junit.platform.commons.util.ReflectionUtils.isNotStatic; +import static org.junit.platform.commons.util.ReflectionUtils.newInstance; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.junit.platform.commons.util.Preconditions; + +/** + * {@code FallbackStringToObjectConverter} is a {@link StringToObjectConverter} + * that provides a fallback conversion strategy for converting from a + * {@link String} to a given target type by invoking a static factory method + * or factory constructor defined in the target type. + * + *

Search Algorithm

+ * + *
    + *
  1. Search for a single, non-private static factory method in the target + * type that converts from a String to the target type. Use the factory method + * if present.
  2. + *
  3. Search for a single, non-private constructor in the target type that + * accepts a String. Use the constructor if present.
  4. + *
+ * + *

If multiple suitable factory methods are discovered they will be ignored. + * If neither a single factory method nor a single constructor is found, this + * converter acts as a no-op. + * + * @since 1.11 + * @see StringConversionSupport + */ +class FallbackStringToObjectConverter implements StringToObjectConverter { + + /** + * Implementation of the NULL Object Pattern. + */ + private static final Function NULL_EXECUTABLE = source -> source; + + /** + * Cache for factory methods and factory constructors. + * + *

Searches that do not find a factory method or constructor are tracked + * by the presence of a {@link #NULL_EXECUTABLE} object stored in the map. + * This prevents the framework from repeatedly searching for things which + * are already known not to exist. + */ + private static final ConcurrentHashMap, Function> factoryExecutableCache // + = new ConcurrentHashMap<>(64); + + @Override + public boolean canConvert(Class targetType) { + return findFactoryExecutable(targetType) != NULL_EXECUTABLE; + } + + @Override + public Object convert(String source, Class targetType) throws Exception { + Function executable = findFactoryExecutable(targetType); + Preconditions.condition(executable != NULL_EXECUTABLE, + "Illegal state: convert() must not be called if canConvert() returned false"); + + return executable.apply(source); + } + + private static Function findFactoryExecutable(Class targetType) { + return factoryExecutableCache.computeIfAbsent(targetType, type -> { + Method factoryMethod = findFactoryMethod(type); + if (factoryMethod != null) { + return source -> invokeMethod(factoryMethod, null, source); + } + Constructor constructor = findFactoryConstructor(type); + if (constructor != null) { + return source -> newInstance(constructor, source); + } + return NULL_EXECUTABLE; + }); + } + + private static Method findFactoryMethod(Class targetType) { + List factoryMethods = findMethods(targetType, new IsFactoryMethod(targetType), BOTTOM_UP); + if (factoryMethods.size() == 1) { + return factoryMethods.get(0); + } + return null; + } + + private static Constructor findFactoryConstructor(Class targetType) { + List> constructors = findConstructors(targetType, new IsFactoryConstructor(targetType)); + if (constructors.size() == 1) { + return constructors.get(0); + } + return null; + } + + /** + * {@link Predicate} that determines if the {@link Method} supplied to + * {@link #test(Method)} is a non-private static factory method for the + * supplied {@link #targetType}. + */ + static class IsFactoryMethod implements Predicate { + + private final Class targetType; + + IsFactoryMethod(Class targetType) { + this.targetType = targetType; + } + + @Override + public boolean test(Method method) { + // Please do not collapse the following into a single statement. + if (!method.getReturnType().equals(this.targetType)) { + return false; + } + if (isNotStatic(method)) { + return false; + } + return isNotPrivateAndAcceptsSingleStringArgument(method); + } + + } + + /** + * {@link Predicate} that determines if the {@link Constructor} supplied to + * {@link #test(Constructor)} is a non-private factory constructor for the + * supplied {@link #targetType}. + */ + static class IsFactoryConstructor implements Predicate> { + + private final Class targetType; + + IsFactoryConstructor(Class targetType) { + this.targetType = targetType; + } + + @Override + public boolean test(Constructor constructor) { + // Please do not collapse the following into a single statement. + if (!constructor.getDeclaringClass().equals(this.targetType)) { + return false; + } + return isNotPrivateAndAcceptsSingleStringArgument(constructor); + } + + } + + private static boolean isNotPrivateAndAcceptsSingleStringArgument(Executable executable) { + return isNotPrivate(executable) // + && (executable.getParameterCount() == 1) // + && (executable.getParameterTypes()[0] == String.class); + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java new file mode 100644 index 000000000000..8727216b923b --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java @@ -0,0 +1,106 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.junit.platform.commons.util.ReflectionUtils.getWrapperType; + +import java.io.File; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.net.URL; +import java.util.Currency; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.UUID; + +import org.apiguardian.api.API; +import org.junit.platform.commons.util.ClassLoaderUtils; +import org.junit.platform.commons.util.ReflectionUtils; + +/** + * The {@code DefaultArgumentConverter} is able to convert from strings to a + * number of primitive types and their corresponding wrapper types (Byte, Short, + * Integer, Long, Float, and Double), date and time types from the + * {@code java.time} package, and some additional common Java types such as + * {@link File}, {@link BigDecimal}, {@link BigInteger}, {@link Currency}, + * {@link Locale}, {@link URI}, {@link URL}, {@link UUID}, etc. + * + *

If the source and target types are identical the source object will not + * be modified. + */ +@API(status = EXPERIMENTAL, since = "1.11") +public final class StringConversionSupport { + + private static final List stringToObjectConverters = unmodifiableList(asList( // + new StringToBooleanConverter(), // + new StringToCharacterConverter(), // + new StringToNumberConverter(), // + new StringToClassConverter(), // + new StringToEnumConverter(), // + new StringToJavaTimeConverter(), // + new StringToCommonJavaTypesConverter(), // + new FallbackStringToObjectConverter() // + )); + + private StringConversionSupport() { + /* no-op */ + } + + @SuppressWarnings("unchecked") + public static T convert(Object source, Class targetType, ClassLoader classLoader) { + if (source == null) { + if (targetType.isPrimitive()) { + throw new ConversionException( + "Cannot convert null to primitive value of type " + targetType.getTypeName()); + } + return null; + } + + if (ReflectionUtils.isAssignableTo(source, targetType)) { + return (T) source; + } + + if (source instanceof String) { + Class targetTypeToUse = toWrapperType(targetType); + Optional converter = stringToObjectConverters.stream().filter( + candidate -> candidate.canConvert(targetTypeToUse)).findFirst(); + if (converter.isPresent()) { + try { + ClassLoader classLoaderToUse = Optional.ofNullable(classLoader) // + .orElseGet(ClassLoaderUtils::getDefaultClassLoader); + return (T) converter.get().convert((String) source, targetTypeToUse, classLoaderToUse); + } + catch (Exception ex) { + if (ex instanceof ConversionException) { + // simply rethrow it + throw (ConversionException) ex; + } + // else + throw new ConversionException( + "Failed to convert String \"" + source + "\" to type " + targetType.getTypeName(), ex); + } + } + } + throw new ConversionException(String.format("No built-in converter for source type %s and target type %s", + source.getClass().getTypeName(), targetType.getTypeName())); + } + + private static Class toWrapperType(Class targetType) { + Class wrapperType = getWrapperType(targetType); + return wrapperType != null ? wrapperType : targetType; + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToBooleanConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToBooleanConverter.java new file mode 100644 index 000000000000..83d1aef7bf0c --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToBooleanConverter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import org.junit.platform.commons.util.Preconditions; + +class StringToBooleanConverter implements StringToObjectConverter { + + @Override + public boolean canConvert(Class targetType) { + return targetType == Boolean.class; + } + + @Override + public Object convert(String source, Class targetType) { + boolean isTrue = "true".equalsIgnoreCase(source); + Preconditions.condition(isTrue || "false".equalsIgnoreCase(source), + () -> "String must be 'true' or 'false' (ignoring case): " + source); + return isTrue; + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCharacterConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCharacterConverter.java new file mode 100644 index 000000000000..54d73c64b909 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCharacterConverter.java @@ -0,0 +1,28 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import org.junit.platform.commons.util.Preconditions; + +class StringToCharacterConverter implements StringToObjectConverter { + + @Override + public boolean canConvert(Class targetType) { + return targetType == Character.class; + } + + @Override + public Object convert(String source, Class targetType) { + Preconditions.condition(source.length() == 1, () -> "String must have length of 1: " + source); + return source.charAt(0); + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToClassConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToClassConverter.java new file mode 100644 index 000000000000..673afd1ff97f --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToClassConverter.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import org.junit.platform.commons.util.ReflectionUtils; + +class StringToClassConverter implements StringToObjectConverter { + + @Override + public boolean canConvert(Class targetType) { + return targetType == Class.class; + } + + @Override + public Object convert(String source, Class targetType) throws Exception { + throw new UnsupportedOperationException("Invoke convert(String, Class, ClassLoader) instead"); + } + + @Override + public Object convert(String className, Class targetType, ClassLoader classLoader) throws Exception { + // @formatter:off + return ReflectionUtils.tryToLoadClass(className, classLoader) + .getOrThrow(cause -> new ConversionException( + "Failed to convert String \"" + className + "\" to type java.lang.Class", cause)); + // @formatter:on + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCommonJavaTypesConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCommonJavaTypesConverter.java new file mode 100644 index 000000000000..3e807def3351 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCommonJavaTypesConverter.java @@ -0,0 +1,70 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import static java.util.Collections.unmodifiableMap; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Currency; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; + +class StringToCommonJavaTypesConverter implements StringToObjectConverter { + + private static final Map, Function> CONVERTERS; + + static { + Map, Function> converters = new HashMap<>(); + + // java.io and java.nio + converters.put(File.class, File::new); + converters.put(Charset.class, Charset::forName); + converters.put(Path.class, Paths::get); + // java.net + converters.put(URI.class, URI::create); + converters.put(URL.class, StringToCommonJavaTypesConverter::toURL); + // java.util + converters.put(Currency.class, Currency::getInstance); + converters.put(Locale.class, Locale::new); + converters.put(UUID.class, UUID::fromString); + + CONVERTERS = unmodifiableMap(converters); + } + + @Override + public boolean canConvert(Class targetType) { + return CONVERTERS.containsKey(targetType); + } + + @Override + public Object convert(String source, Class targetType) throws Exception { + return CONVERTERS.get(targetType).apply(source); + } + + private static URL toURL(String url) { + try { + return URI.create(url).toURL(); + } + catch (MalformedURLException ex) { + throw new ConversionException("Failed to convert String \"" + url + "\" to type java.net.URL", ex); + } + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToEnumConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToEnumConverter.java new file mode 100644 index 000000000000..ccc4b1b655c4 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToEnumConverter.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +class StringToEnumConverter implements StringToObjectConverter { + + @Override + public boolean canConvert(Class targetType) { + return targetType.isEnum(); + } + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object convert(String source, Class targetType) throws Exception { + return Enum.valueOf(targetType, source); + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToJavaTimeConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToJavaTimeConverter.java new file mode 100644 index 000000000000..4acfcdda5952 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToJavaTimeConverter.java @@ -0,0 +1,65 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import static java.util.Collections.unmodifiableMap; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.MonthDay; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.Period; +import java.time.Year; +import java.time.YearMonth; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +class StringToJavaTimeConverter implements StringToObjectConverter { + + private static final Map, Function> CONVERTERS; + static { + Map, Function> converters = new HashMap<>(); + converters.put(Duration.class, Duration::parse); + converters.put(Instant.class, Instant::parse); + converters.put(LocalDate.class, LocalDate::parse); + converters.put(LocalDateTime.class, LocalDateTime::parse); + converters.put(LocalTime.class, LocalTime::parse); + converters.put(MonthDay.class, MonthDay::parse); + converters.put(OffsetDateTime.class, OffsetDateTime::parse); + converters.put(OffsetTime.class, OffsetTime::parse); + converters.put(Period.class, Period::parse); + converters.put(Year.class, Year::parse); + converters.put(YearMonth.class, YearMonth::parse); + converters.put(ZonedDateTime.class, ZonedDateTime::parse); + converters.put(ZoneId.class, ZoneId::of); + converters.put(ZoneOffset.class, ZoneOffset::of); + CONVERTERS = unmodifiableMap(converters); + } + + @Override + public boolean canConvert(Class targetType) { + return CONVERTERS.containsKey(targetType); + } + + @Override + public Object convert(String source, Class targetType) throws Exception { + return CONVERTERS.get(targetType).apply(source); + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToNumberConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToNumberConverter.java new file mode 100644 index 000000000000..e42617d0d529 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToNumberConverter.java @@ -0,0 +1,50 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +import static java.util.Collections.unmodifiableMap; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +class StringToNumberConverter implements StringToObjectConverter { + + private static final Map, Function> CONVERTERS; + static { + Map, Function> converters = new HashMap<>(); + converters.put(Byte.class, Byte::decode); + converters.put(Short.class, Short::decode); + converters.put(Integer.class, Integer::decode); + converters.put(Long.class, Long::decode); + converters.put(Float.class, Float::valueOf); + converters.put(Double.class, Double::valueOf); + // Technically, BigInteger and BigDecimal constructors are covered by + // FallbackStringToObjectConverter, but we have explicit conversion + // configured for them anyway. + converters.put(BigInteger.class, BigInteger::new); + converters.put(BigDecimal.class, BigDecimal::new); + CONVERTERS = unmodifiableMap(converters); + } + + @Override + public boolean canConvert(Class targetType) { + return CONVERTERS.containsKey(targetType); + } + + @Override + public Object convert(String source, Class targetType) { + return CONVERTERS.get(targetType).apply(source.replace("_", "")); + } + +} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToObjectConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToObjectConverter.java new file mode 100644 index 000000000000..9a9dc8a47a09 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToObjectConverter.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.support; + +/** + * Internal API for converting arguments of type {@link String} to a specified + * target type. + */ +interface StringToObjectConverter { + + /** + * Determine if this converter can convert from a {@link String} to the + * supplied target type (which is guaranteed to be a wrapper type for + * primitives — for example, {@link Integer} instead of {@code int}). + */ + boolean canConvert(Class targetType); + + /** + * Convert the supplied {@link String} to the supplied target type (which is + * guaranteed to be a wrapper type for primitives — for example, + * {@link Integer} instead of {@code int}). + */ + Object convert(String source, Class targetType) throws Exception; + + /** + * Convert the supplied {@link String} to the supplied target type (which is + * guaranteed to be a wrapper type for primitives — for example, + * {@link Integer} instead of {@code int}). + * + *

The default implementation simply delegates to {@link #convert(String, Class)}. + * Can be overridden by concrete implementations of this interface that need + * access to the supplied {@link ClassLoader}. + */ + default Object convert(String source, Class targetType, ClassLoader classLoader) throws Exception { + return convert(source, targetType); + } + +} From 1e04b62faefdd5d5c02b5143ed3d39805af7303f Mon Sep 17 00:00:00 2001 From: Juliette de Rancourt Date: Wed, 8 Nov 2023 11:12:09 +0100 Subject: [PATCH 2/5] Naming & javadoc --- .../jupiter/params/converter/DefaultArgumentConverter.java | 4 ++-- .../platform/commons/support/StringConversionSupport.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java index c152b558b0bb..c7b7a62bcbdc 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java @@ -67,8 +67,8 @@ public final Object convert(Object source, Class targetType, ParameterContext try { return StringConversionSupport.convert(source, targetType, classLoader); } - catch (ConversionException e) { - throw new ArgumentConversionException(e.getMessage(), e); + catch (ConversionException ex) { + throw new ArgumentConversionException(ex.getMessage(), ex); } } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java index 8727216b923b..fa341dae6ce9 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java @@ -31,14 +31,14 @@ import org.junit.platform.commons.util.ReflectionUtils; /** - * The {@code DefaultArgumentConverter} is able to convert from strings to a - * number of primitive types and their corresponding wrapper types (Byte, Short, + * {@code StringConversionSupport} is able to convert from strings to a number + * of primitive types and their corresponding wrapper types (Byte, Short, * Integer, Long, Float, and Double), date and time types from the * {@code java.time} package, and some additional common Java types such as * {@link File}, {@link BigDecimal}, {@link BigInteger}, {@link Currency}, * {@link Locale}, {@link URI}, {@link URL}, {@link UUID}, etc. * - *

If the source and target types are identical the source object will not + *

If the target type is {@code String} the source {@code String} will not * be modified. */ @API(status = EXPERIMENTAL, since = "1.11") From e2d664ce57d6c40d06416638da7dad5c18d89ae3 Mon Sep 17 00:00:00 2001 From: Juliette de Rancourt Date: Wed, 8 Nov 2023 11:40:34 +0100 Subject: [PATCH 3/5] Change source type to String --- .../converter/DefaultArgumentConverter.java | 30 ++++++++++--- .../support/StringConversionSupport.java | 44 +++++++++---------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java index c7b7a62bcbdc..585a160afbe9 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java @@ -26,6 +26,7 @@ import org.junit.platform.commons.support.ConversionException; import org.junit.platform.commons.support.StringConversionSupport; import org.junit.platform.commons.util.ClassLoaderUtils; +import org.junit.platform.commons.util.ReflectionUtils; /** * {@code DefaultArgumentConverter} is the default implementation of the @@ -61,15 +62,32 @@ public final Object convert(Object source, ParameterContext context) { } public final Object convert(Object source, Class targetType, ParameterContext context) { - Class declaringClass = context.getDeclaringExecutable().getDeclaringClass(); - ClassLoader classLoader = ClassLoaderUtils.getClassLoader(declaringClass); + if (source == null) { + if (targetType.isPrimitive()) { + throw new ArgumentConversionException( + "Cannot convert null to primitive value of type " + targetType.getTypeName()); + } + return null; + } - try { - return StringConversionSupport.convert(source, targetType, classLoader); + if (ReflectionUtils.isAssignableTo(source, targetType)) { + return source; } - catch (ConversionException ex) { - throw new ArgumentConversionException(ex.getMessage(), ex); + + if (source instanceof String) { + Class declaringClass = context.getDeclaringExecutable().getDeclaringClass(); + ClassLoader classLoader = ClassLoaderUtils.getClassLoader(declaringClass); + try { + return StringConversionSupport.convert((String) source, targetType, classLoader); + } + catch (ConversionException ex) { + throw new ArgumentConversionException(ex.getMessage(), ex); + } } + + throw new ArgumentConversionException( + String.format("No built-in converter for source type %s and target type %s", + source.getClass().getTypeName(), targetType.getTypeName())); } } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java index fa341dae6ce9..56f660a7bf9a 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java @@ -28,7 +28,6 @@ import org.apiguardian.api.API; import org.junit.platform.commons.util.ClassLoaderUtils; -import org.junit.platform.commons.util.ReflectionUtils; /** * {@code StringConversionSupport} is able to convert from strings to a number @@ -60,7 +59,7 @@ private StringConversionSupport() { } @SuppressWarnings("unchecked") - public static T convert(Object source, Class targetType, ClassLoader classLoader) { + public static T convert(String source, Class targetType, ClassLoader classLoader) { if (source == null) { if (targetType.isPrimitive()) { throw new ConversionException( @@ -69,33 +68,32 @@ public static T convert(Object source, Class targetType, ClassLoader clas return null; } - if (ReflectionUtils.isAssignableTo(source, targetType)) { + if (String.class.equals(targetType)) { return (T) source; } - if (source instanceof String) { - Class targetTypeToUse = toWrapperType(targetType); - Optional converter = stringToObjectConverters.stream().filter( - candidate -> candidate.canConvert(targetTypeToUse)).findFirst(); - if (converter.isPresent()) { - try { - ClassLoader classLoaderToUse = Optional.ofNullable(classLoader) // - .orElseGet(ClassLoaderUtils::getDefaultClassLoader); - return (T) converter.get().convert((String) source, targetTypeToUse, classLoaderToUse); - } - catch (Exception ex) { - if (ex instanceof ConversionException) { - // simply rethrow it - throw (ConversionException) ex; - } - // else - throw new ConversionException( - "Failed to convert String \"" + source + "\" to type " + targetType.getTypeName(), ex); + Class targetTypeToUse = toWrapperType(targetType); + Optional converter = stringToObjectConverters.stream().filter( + candidate -> candidate.canConvert(targetTypeToUse)).findFirst(); + if (converter.isPresent()) { + try { + ClassLoader classLoaderToUse = Optional.ofNullable(classLoader) // + .orElseGet(ClassLoaderUtils::getDefaultClassLoader); + return (T) converter.get().convert(source, targetTypeToUse, classLoaderToUse); + } + catch (Exception ex) { + if (ex instanceof ConversionException) { + // simply rethrow it + throw (ConversionException) ex; } + // else + throw new ConversionException( + String.format("Failed to convert String \"%s\" to type %s", source, targetType.getTypeName()), ex); } } - throw new ConversionException(String.format("No built-in converter for source type %s and target type %s", - source.getClass().getTypeName(), targetType.getTypeName())); + + throw new ConversionException( + "No built-in converter for source type java.lang.String and target type " + targetType.getTypeName()); } private static Class toWrapperType(Class targetType) { From 8303181ee55e8dcdb2cc35e6b9a8ebae7f5ef5ee Mon Sep 17 00:00:00 2001 From: Juliette de Rancourt Date: Wed, 8 Nov 2023 22:42:26 +0100 Subject: [PATCH 4/5] Add javadoc --- .../support/StringConversionSupport.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java index 56f660a7bf9a..9f7050b10dbd 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java @@ -39,6 +39,8 @@ * *

If the target type is {@code String} the source {@code String} will not * be modified. + * + * @since 1.11 */ @API(status = EXPERIMENTAL, since = "1.11") public final class StringConversionSupport { @@ -58,6 +60,23 @@ private StringConversionSupport() { /* no-op */ } + /** + * Convert a {@code String} into an object of the supplied type. + * + *

Some underlying converters can require a {@code ClassLoader}. + * If none is provided, the default one given by + * {@link ClassLoaderUtils#getDefaultClassLoader()} will be used. + * + * @param source the source {@code String} to convert; may be {@code null} + * @param targetType the target type the source should be converted into; + * never {@code null} + * @param classLoader the {@code ClassLoader} to use; may be {@code null} + * @param the type of the target + * @return the converted object; may be {@code null} but only if the target + * type is a reference type + * + * @since 1.11 + */ @SuppressWarnings("unchecked") public static T convert(String source, Class targetType, ClassLoader classLoader) { if (source == null) { @@ -77,8 +96,8 @@ public static T convert(String source, Class targetType, ClassLoader clas candidate -> candidate.canConvert(targetTypeToUse)).findFirst(); if (converter.isPresent()) { try { - ClassLoader classLoaderToUse = Optional.ofNullable(classLoader) // - .orElseGet(ClassLoaderUtils::getDefaultClassLoader); + ClassLoader classLoaderToUse = classLoader != null ? classLoader + : ClassLoaderUtils.getDefaultClassLoader(); return (T) converter.get().convert(source, targetTypeToUse, classLoaderToUse); } catch (Exception ex) { From 3c47bed9047b82bc97ac8a6b85f5db8193266907 Mon Sep 17 00:00:00 2001 From: Juliette de Rancourt Date: Fri, 17 Nov 2023 14:54:35 +0100 Subject: [PATCH 5/5] Add conversion subpackage --- .../jupiter/params/converter/DefaultArgumentConverter.java | 6 +++--- .../support/{ => conversion}/ConversionException.java | 2 +- .../{ => conversion}/FallbackStringToObjectConverter.java | 2 +- .../support/{ => conversion}/StringConversionSupport.java | 2 +- .../support/{ => conversion}/StringToBooleanConverter.java | 2 +- .../{ => conversion}/StringToCharacterConverter.java | 2 +- .../support/{ => conversion}/StringToClassConverter.java | 2 +- .../{ => conversion}/StringToCommonJavaTypesConverter.java | 2 +- .../support/{ => conversion}/StringToEnumConverter.java | 2 +- .../support/{ => conversion}/StringToJavaTimeConverter.java | 2 +- .../support/{ => conversion}/StringToNumberConverter.java | 2 +- .../support/{ => conversion}/StringToObjectConverter.java | 2 +- .../platform/commons/support/conversion/package-info.java | 5 +++++ .../src/module/org.junit.platform.commons/module-info.java | 1 + .../jar-describe-module/junit-platform-commons.expected.txt | 1 + 15 files changed, 21 insertions(+), 14 deletions(-) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/ConversionException.java (94%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/FallbackStringToObjectConverter.java (99%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/StringConversionSupport.java (98%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/StringToBooleanConverter.java (93%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/StringToCharacterConverter.java (93%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/StringToClassConverter.java (95%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/StringToCommonJavaTypesConverter.java (97%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/StringToEnumConverter.java (92%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/StringToJavaTimeConverter.java (97%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/StringToNumberConverter.java (96%) rename junit-platform-commons/src/main/java/org/junit/platform/commons/support/{ => conversion}/StringToObjectConverter.java (96%) create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/package-info.java diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java index 585a160afbe9..adbb606da691 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java @@ -23,8 +23,8 @@ import org.apiguardian.api.API; import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.platform.commons.support.ConversionException; -import org.junit.platform.commons.support.StringConversionSupport; +import org.junit.platform.commons.support.conversion.ConversionException; +import org.junit.platform.commons.support.conversion.StringConversionSupport; import org.junit.platform.commons.util.ClassLoaderUtils; import org.junit.platform.commons.util.ReflectionUtils; @@ -44,7 +44,7 @@ * * @since 5.0 * @see org.junit.jupiter.params.converter.ArgumentConverter - * @see org.junit.platform.commons.support.StringConversionSupport + * @see org.junit.platform.commons.support.conversion.StringConversionSupport */ @API(status = INTERNAL, since = "5.0") public class DefaultArgumentConverter implements ArgumentConverter { diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ConversionException.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionException.java similarity index 94% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/ConversionException.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionException.java index 14d914eb4856..439334635776 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ConversionException.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionException.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; import static org.apiguardian.api.API.Status.EXPERIMENTAL; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/FallbackStringToObjectConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverter.java similarity index 99% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/FallbackStringToObjectConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverter.java index e2ade2548b37..06dc29153e31 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/FallbackStringToObjectConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverter.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP; import static org.junit.platform.commons.util.ReflectionUtils.findConstructors; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringConversionSupport.java similarity index 98% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringConversionSupport.java index 9f7050b10dbd..2ac35dc67461 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringConversionSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringConversionSupport.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToBooleanConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToBooleanConverter.java similarity index 93% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToBooleanConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToBooleanConverter.java index 83d1aef7bf0c..2bde9ac323c2 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToBooleanConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToBooleanConverter.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; import org.junit.platform.commons.util.Preconditions; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCharacterConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCharacterConverter.java similarity index 93% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCharacterConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCharacterConverter.java index 54d73c64b909..925acdfe62d5 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCharacterConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCharacterConverter.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; import org.junit.platform.commons.util.Preconditions; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToClassConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToClassConverter.java similarity index 95% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToClassConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToClassConverter.java index 673afd1ff97f..df2b0164ad5c 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToClassConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToClassConverter.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; import org.junit.platform.commons.util.ReflectionUtils; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCommonJavaTypesConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCommonJavaTypesConverter.java similarity index 97% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCommonJavaTypesConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCommonJavaTypesConverter.java index 3e807def3351..2988714318e3 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToCommonJavaTypesConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCommonJavaTypesConverter.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; import static java.util.Collections.unmodifiableMap; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToEnumConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToEnumConverter.java similarity index 92% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToEnumConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToEnumConverter.java index ccc4b1b655c4..48c07fa59eb7 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToEnumConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToEnumConverter.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; class StringToEnumConverter implements StringToObjectConverter { diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToJavaTimeConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToJavaTimeConverter.java similarity index 97% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToJavaTimeConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToJavaTimeConverter.java index 4acfcdda5952..6ecbf84b25e5 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToJavaTimeConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToJavaTimeConverter.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; import static java.util.Collections.unmodifiableMap; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToNumberConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToNumberConverter.java similarity index 96% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToNumberConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToNumberConverter.java index e42617d0d529..b8cd6e7d3e4f 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToNumberConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToNumberConverter.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; import static java.util.Collections.unmodifiableMap; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToObjectConverter.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToObjectConverter.java similarity index 96% rename from junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToObjectConverter.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToObjectConverter.java index 9a9dc8a47a09..243bfeec4afc 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/StringToObjectConverter.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToObjectConverter.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.commons.support; +package org.junit.platform.commons.support.conversion; /** * Internal API for converting arguments of type {@link String} to a specified diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/package-info.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/package-info.java new file mode 100644 index 000000000000..e51977179941 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/package-info.java @@ -0,0 +1,5 @@ +/** + * Maintained conversion APIs provided by the JUnit Platform. + */ + +package org.junit.platform.commons.support.conversion; diff --git a/junit-platform-commons/src/module/org.junit.platform.commons/module-info.java b/junit-platform-commons/src/module/org.junit.platform.commons/module-info.java index f33ffd314feb..774684198f9f 100644 --- a/junit-platform-commons/src/module/org.junit.platform.commons/module-info.java +++ b/junit-platform-commons/src/module/org.junit.platform.commons/module-info.java @@ -36,6 +36,7 @@ org.junit.platform.testkit, org.junit.vintage.engine; exports org.junit.platform.commons.support; + exports org.junit.platform.commons.support.conversion; exports org.junit.platform.commons.util to org.junit.jupiter.api, org.junit.jupiter.engine, diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt index 5c0c9b44e4ef..cb3eb72ae947 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt @@ -3,6 +3,7 @@ exports org.junit.platform.commons exports org.junit.platform.commons.annotation exports org.junit.platform.commons.function exports org.junit.platform.commons.support +exports org.junit.platform.commons.support.conversion requires java.base mandated requires java.logging requires java.management