Browse Source

Improves the CustomConversions API.

* Annotates the API with Spring's @NonNull and @Nullable annotations.
* Re-instates the hasValueConverter(:PersistentProperty) method.
* Removes explamation mark in Exception messages.
* Uses Java 17 language features to simplify code.
* Edits Javadoc.

Closes #2617.
pull/2627/head
John Blum 4 years ago
parent
commit
87c5e866ca
  1. 250
      src/main/java/org/springframework/data/convert/CustomConversions.java
  2. 53
      src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java

250
src/main/java/org/springframework/data/convert/CustomConversions.java

@ -43,10 +43,12 @@ import org.springframework.core.convert.converter.GenericConverter; @@ -43,10 +43,12 @@ import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.ConverterBuilder.ConverterAware;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.Predicates;
import org.springframework.data.util.Streamable;
import org.springframework.data.util.VavrCollectionConverters;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@ -67,13 +69,13 @@ import org.springframework.util.ObjectUtils; @@ -67,13 +69,13 @@ import org.springframework.util.ObjectUtils;
public class CustomConversions {
private static final Log logger = LogFactory.getLog(CustomConversions.class);
private static final String READ_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as reading converter although it doesn't convert from a store-supported type! You might want to check your annotation setup at the converter implementation.";
private static final String WRITE_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as writing converter although it doesn't convert to a store-supported type! You might want to check your annotation setup at the converter implementation.";
private static final String NOT_A_CONVERTER = "Converter %s is neither a Spring Converter, GenericConverter or ConverterFactory!";
private static final String READ_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as reading converter although it doesn't convert from a store-supported type; You might want to check your annotation setup at the converter implementation.";
private static final String WRITE_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as writing converter although it doesn't convert to a store-supported type; You might want to check your annotation setup at the converter implementation.";
private static final String NOT_A_CONVERTER = "Converter %s is neither a Spring Converter, GenericConverter or ConverterFactory";
private static final String CONVERTER_FILTER = "converter from %s to %s as %s converter.";
private static final String ADD_CONVERTER = "Adding %s" + CONVERTER_FILTER;
private static final String SKIP_CONVERTER = "Skipping " + CONVERTER_FILTER
+ " %s is not a store supported simple type!";
+ " %s is not a store supported simple type";
private static final List<Object> DEFAULT_CONVERTERS;
static {
@ -106,23 +108,24 @@ public class CustomConversions { @@ -106,23 +108,24 @@ public class CustomConversions {
private final Function<ConvertiblePair, Class<?>> getRawWriteTarget = convertiblePair -> getCustomTarget(
convertiblePair.getSourceType(), null, writingPairs);
private @Nullable PropertyValueConversions propertyValueConversions;
@Nullable
private final PropertyValueConversions propertyValueConversions;
/**
* @param converterConfiguration the {@link ConverterConfiguration} to apply.
* @since 2.3
*/
public CustomConversions(ConverterConfiguration converterConfiguration) {
public CustomConversions(@NonNull ConverterConfiguration converterConfiguration) {
this.converterConfiguration = converterConfiguration;
List<Object> registeredConverters = collectPotentialConverterRegistrations(
converterConfiguration.getStoreConversions(), converterConfiguration.getUserConverters()).stream() //
.filter(this::isSupportedConverter) //
.filter(this::shouldRegister) //
.map(ConverterRegistrationIntent::getConverterRegistration) //
.map(this::register) //
.distinct() //
converterConfiguration.getStoreConversions(), converterConfiguration.getUserConverters()).stream()
.filter(this::isSupportedConverter)
.filter(this::shouldRegister)
.map(ConverterRegistrationIntent::getConverterRegistration)
.map(this::register)
.distinct()
.collect(Collectors.toList());
Collections.reverse(registeredConverters);
@ -142,30 +145,60 @@ public class CustomConversions { @@ -142,30 +145,60 @@ public class CustomConversions {
* @param storeConversions must not be {@literal null}.
* @param converters must not be {@literal null}.
*/
public CustomConversions(StoreConversions storeConversions, Collection<?> converters) {
public CustomConversions(@NonNull StoreConversions storeConversions, @NonNull Collection<?> converters) {
this(new ConverterConfiguration(storeConversions, new ArrayList<>(converters)));
}
/**
* Returns the underlying {@link SimpleTypeHolder}.
*
* @return
* @return the underlying {@link SimpleTypeHolder}.
* @see SimpleTypeHolder
*/
public SimpleTypeHolder getSimpleTypeHolder() {
public @NonNull SimpleTypeHolder getSimpleTypeHolder() {
return simpleTypeHolder;
}
/**
* Determines whether the given, required {@link PersistentProperty property} has a value-specific converter
* registered. Returns {@literal false} if no {@link PropertyValueConversions} have been configured for the
* underlying store.
* <p>
* This method protects against {@literal null} when not {@link PropertyValueConversions} have been configured for
* the underlying data store, and is a shortcut for:
*
* <code>
* customConversions.getPropertyValueConversions().hasValueConverter(property);
* </code>
*
* @param property {@link PersistentProperty} to evaluate; must not be {@literal null}.
* @return a boolean value indicating whether {@link PropertyValueConverter} has been configured and registered
* for the {@link PersistentProperty property}.
* @see PropertyValueConversions#hasValueConverter(PersistentProperty)
* @see #getPropertyValueConversions()
* @see PersistentProperty
*/
public boolean hasValueConverter(@NonNull PersistentProperty<?> property) {
PropertyValueConversions propertyValueConversions = getPropertyValueConversions();
return propertyValueConversions != null && propertyValueConversions.hasValueConverter(property);
}
/**
* Returns whether the given type is considered to be simple. That means it's either a general simple type or we have
* a writing {@link Converter} registered for a particular type.
*
* @see SimpleTypeHolder#isSimpleType(Class)
* @param type
* @return
* @param type {@link Class} to evaluate as a simple type, such as a primitive type.
* @return a boolean value indicating whether the given, required {@link Class type} is simple.
*/
public boolean isSimpleType(Class<?> type) {
// TODO: Technically, an 'isXyz(..)' method (returning a boolean to answer a user's question should not throw an Exception).
// Rather, a null Class type argument should simply return false to indicate it is clearly not a "simple type".
// How much data store specific code relies on the existing behavior?
public boolean isSimpleType(@NonNull Class<?> type) {
Assert.notNull(type, "Type must not be null!");
Assert.notNull(type, "Type must not be null");
return simpleTypeHolder.isSimpleType(type);
}
@ -173,16 +206,25 @@ public class CustomConversions { @@ -173,16 +206,25 @@ public class CustomConversions {
/**
* Populates the given {@link GenericConversionService} with the converters registered.
*
* @param conversionService
* @param conversionService {@link ConverterRegistry} to populate; must not be {@literal null}.
* @see ConverterRegistry
*/
public void registerConvertersIn(ConverterRegistry conversionService) {
public void registerConvertersIn(@NonNull ConverterRegistry conversionService) {
Assert.notNull(conversionService, "ConversionService must not be null!");
Assert.notNull(conversionService, "ConversionService must not be null");
converters.forEach(it -> registerConverterIn(it, conversionService));
VavrCollectionConverters.getConvertersToRegister().forEach(it -> registerConverterIn(it, conversionService));
}
/**
* Gets a reference to the configured {@link PropertyValueConversions} if property value conversions
* are supported by the underlying data store.
*
* @return a reference to the configured {@link PropertyValueConversions}; may be {@literal null}
* if the underlying data store does not support property value conversions.
* @see PropertyValueConversions
*/
@Nullable
public PropertyValueConversions getPropertyValueConversions() {
return propertyValueConversions;
@ -191,32 +233,33 @@ public class CustomConversions { @@ -191,32 +233,33 @@ public class CustomConversions {
/**
* Get all converters and add origin information
*
* @param storeConversions
* @param converters
* @return
* @param storeConversions collection of store-base conversions; must not be {@literal null}.
* @param converters collections of custom, user-based converters; must not be {@literal null}.
* @return a {@link List} of intended {@link ConverterRegistration ConverterRegistrations}.
* @see ConverterRegistration
* @since 2.3
*/
private List<ConverterRegistrationIntent> collectPotentialConverterRegistrations(StoreConversions storeConversions,
Collection<?> converters) {
private List<ConverterRegistrationIntent> collectPotentialConverterRegistrations(@
NonNull StoreConversions storeConversions, @NonNull Collection<?> converters) {
List<ConverterRegistrationIntent> converterRegistrations = new ArrayList<>();
converters.stream() //
.map(storeConversions::getRegistrationsFor) //
.flatMap(Streamable::stream) //
.map(ConverterRegistrationIntent::userConverters) //
converters.stream()
.map(storeConversions::getRegistrationsFor)
.flatMap(Streamable::stream)
.map(ConverterRegistrationIntent::userConverters)
.forEach(converterRegistrations::add);
storeConversions.getStoreConverters().stream() //
.map(storeConversions::getRegistrationsFor) //
.flatMap(Streamable::stream) //
.map(ConverterRegistrationIntent::storeConverters) //
storeConversions.getStoreConverters().stream()
.map(storeConversions::getRegistrationsFor)
.flatMap(Streamable::stream)
.map(ConverterRegistrationIntent::storeConverters)
.forEach(converterRegistrations::add);
DEFAULT_CONVERTERS.stream() //
.map(storeConversions::getRegistrationsFor) //
.flatMap(Streamable::stream) //
.map(ConverterRegistrationIntent::defaultConverters) //
DEFAULT_CONVERTERS.stream()
.map(storeConversions::getRegistrationsFor)
.flatMap(Streamable::stream)
.map(ConverterRegistrationIntent::defaultConverters)
.forEach(converterRegistrations::add);
return converterRegistrations;
@ -230,38 +273,29 @@ public class CustomConversions { @@ -230,38 +273,29 @@ public class CustomConversions {
*/
private void registerConverterIn(Object candidate, ConverterRegistry conversionService) {
if (candidate instanceof Converter) {
conversionService.addConverter(Converter.class.cast(candidate));
return;
}
if (candidate instanceof ConverterFactory) {
conversionService.addConverterFactory(ConverterFactory.class.cast(candidate));
return;
if (candidate instanceof Converter converter) {
conversionService.addConverter(converter);
} else if (candidate instanceof ConverterFactory converterFactory) {
conversionService.addConverterFactory(converterFactory);
} else if (candidate instanceof GenericConverter genericConverter) {
conversionService.addConverter(genericConverter);
} else if (candidate instanceof ConverterAware converterAware) {
converterAware.getConverters().forEach(it -> registerConverterIn(it, conversionService));
} else {
throw new IllegalArgumentException(String.format(NOT_A_CONVERTER, candidate));
}
if (candidate instanceof GenericConverter) {
conversionService.addConverter(GenericConverter.class.cast(candidate));
return;
}
if (candidate instanceof ConverterAware) {
ConverterAware.class.cast(candidate).getConverters().forEach(it -> registerConverterIn(it, conversionService));
return;
}
throw new IllegalArgumentException(String.format(NOT_A_CONVERTER, candidate));
}
/**
* Registers the given {@link ConvertiblePair} as reading or writing pair depending on the type sides being basic
* types.
*
* @param converterRegistration
* @param converterRegistration {@link ConverterRegistration} to register; must not be {@literal null}.
* @see ConverterRegistration
*/
private Object register(ConverterRegistration converterRegistration) {
private Object register(@NonNull ConverterRegistration converterRegistration) {
Assert.notNull(converterRegistration, "Converter registration must not be null!");
Assert.notNull(converterRegistration, "Converter registration must not be null");
ConvertiblePair pair = converterRegistration.getConvertiblePair();
@ -288,15 +322,15 @@ public class CustomConversions { @@ -288,15 +322,15 @@ public class CustomConversions {
}
/**
* Validate a given {@link ConverterRegistration} in a specific setup. <br />
* Validate a given {@link ConverterRegistration} in a specific setup.<br/>
* Non {@link ReadingConverter reading} and user defined {@link Converter converters} are only considered supported if
* the {@link ConverterRegistrationIntent#isSimpleTargetType() target type} is considered to be a store simple type.
*
* @param registrationIntent
* @param registrationIntent {@link ConverterRegistrationIntent} to validate; must not be {@literal null}.
* @return {@literal true} if supported.
* @since 2.3
*/
private boolean isSupportedConverter(ConverterRegistrationIntent registrationIntent) {
private boolean isSupportedConverter(@NonNull ConverterRegistrationIntent registrationIntent) {
boolean register = registrationIntent.isUserConverter() || registrationIntent.isStoreConverter()
|| (registrationIntent.isReading() && registrationIntent.isSimpleSourceType())
@ -323,7 +357,7 @@ public class CustomConversions { @@ -323,7 +357,7 @@ public class CustomConversions {
* @return {@literal false} if the given {@link ConverterRegistration} shall be skipped.
* @since 2.3
*/
private boolean shouldRegister(ConverterRegistrationIntent intent) {
private boolean shouldRegister(@NonNull ConverterRegistrationIntent intent) {
return !intent.isDefaultConverter()
|| converterConfiguration.shouldRegister(intent.getConverterRegistration().getConvertiblePair());
}
@ -333,11 +367,12 @@ public class CustomConversions { @@ -333,11 +367,12 @@ public class CustomConversions {
* type into a store native one.
*
* @param sourceType must not be {@literal null}
* @return
* @return the target type to convert to in case we have a custom conversion registered to convert the given source
* type into a store native one.
*/
public Optional<Class<?>> getCustomWriteTarget(Class<?> sourceType) {
public Optional<Class<?>> getCustomWriteTarget(@NonNull Class<?> sourceType) {
Assert.notNull(sourceType, "Source type must not be null!");
Assert.notNull(sourceType, "Source type must not be null");
Class<?> target = customWriteTargetTypes.computeIfAbsent(sourceType, getRawWriteTarget);
@ -351,12 +386,12 @@ public class CustomConversions { @@ -351,12 +386,12 @@ public class CustomConversions {
*
* @param sourceType must not be {@literal null}
* @param requestedTargetType must not be {@literal null}.
* @return
* @return the target type we can read an inject of the given source type to.
*/
public Optional<Class<?>> getCustomWriteTarget(Class<?> sourceType, Class<?> requestedTargetType) {
public Optional<Class<?>> getCustomWriteTarget(@NonNull Class<?> sourceType, @NonNull Class<?> requestedTargetType) {
Assert.notNull(sourceType, "Source type must not be null!");
Assert.notNull(requestedTargetType, "Target type must not be null!");
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(requestedTargetType, "Target type must not be null");
Class<?> target = customWriteTargetTypes.computeIfAbsent(sourceType, requestedTargetType, getWriteTarget);
@ -368,11 +403,11 @@ public class CustomConversions { @@ -368,11 +403,11 @@ public class CustomConversions {
* type might be a subclass of the given expected type though.
*
* @param sourceType must not be {@literal null}
* @return
* @return whether we have a custom conversion registered to read {@code sourceType} into a native type.
*/
public boolean hasCustomWriteTarget(Class<?> sourceType) {
public boolean hasCustomWriteTarget(@NonNull Class<?> sourceType) {
Assert.notNull(sourceType, "Source type must not be null!");
Assert.notNull(sourceType, "Source type must not be null");
return getCustomWriteTarget(sourceType).isPresent();
}
@ -383,12 +418,13 @@ public class CustomConversions { @@ -383,12 +418,13 @@ public class CustomConversions {
*
* @param sourceType must not be {@literal null}.
* @param targetType must not be {@literal null}.
* @return
* @return whether we have a custom conversion registered to read an object of the given source type into an object
* of the given native target type.
*/
public boolean hasCustomWriteTarget(Class<?> sourceType, Class<?> targetType) {
public boolean hasCustomWriteTarget(@NonNull Class<?> sourceType, @NonNull Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null!");
Assert.notNull(targetType, "Target type must not be null!");
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
return getCustomWriteTarget(sourceType, targetType).isPresent();
}
@ -398,12 +434,12 @@ public class CustomConversions { @@ -398,12 +434,12 @@ public class CustomConversions {
*
* @param sourceType must not be {@literal null}
* @param targetType must not be {@literal null}
* @return
* @return whether we have a custom conversion registered to read the given source into the given target type.
*/
public boolean hasCustomReadTarget(Class<?> sourceType, Class<?> targetType) {
public boolean hasCustomReadTarget(@NonNull Class<?> sourceType, @NonNull Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null!");
Assert.notNull(targetType, "Target type must not be null!");
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
return getCustomReadTarget(sourceType, targetType) != null;
}
@ -414,24 +450,24 @@ public class CustomConversions { @@ -414,24 +450,24 @@ public class CustomConversions {
*
* @param sourceType must not be {@literal null}.
* @param targetType must not be {@literal null}.
* @return
* @return the actual target type for the given {@code sourceType} and {@code targetType}.
*/
@Nullable
private Class<?> getCustomReadTarget(Class<?> sourceType, Class<?> targetType) {
private Class<?> getCustomReadTarget(@NonNull Class<?> sourceType, @NonNull Class<?> targetType) {
return customReadTargetTypes.computeIfAbsent(sourceType, targetType, getReadTarget);
}
/**
* Inspects the given {@link ConvertiblePair ConvertiblePairs} for ones that have a source compatible type as source.
* Additionally checks assignability of the target type if one is given.
* Additionally, checks assignability of the target type if one is given.
*
* @param sourceType must not be {@literal null}.
* @param targetType can be {@literal null}.
* @param pairs must not be {@literal null}.
* @return
* @return the base {@link Class type} for the (requested) {@link Class target type} if present.
*/
@Nullable
private Class<?> getCustomTarget(Class<?> sourceType, @Nullable Class<?> targetType,
private Class<?> getCustomTarget(@NonNull Class<?> sourceType, @Nullable Class<?> targetType,
Collection<ConvertiblePair> pairs) {
if (targetType != null && pairs.contains(new ConvertiblePair(sourceType, targetType))) {
@ -456,11 +492,13 @@ public class CustomConversions { @@ -456,11 +492,13 @@ public class CustomConversions {
return null;
}
private static boolean hasAssignableSourceType(ConvertiblePair pair, Class<?> sourceType) {
private static boolean hasAssignableSourceType(@NonNull ConvertiblePair pair, @NonNull Class<?> sourceType) {
return pair.getSourceType().isAssignableFrom(sourceType);
}
private static boolean requestedTargetTypeIsAssignable(@Nullable Class<?> requestedTargetType, Class<?> targetType) {
private static boolean requestedTargetTypeIsAssignable(@Nullable Class<?> requestedTargetType,
@NonNull Class<?> targetType) {
return requestedTargetType == null || targetType.isAssignableFrom(requestedTargetType);
}
@ -736,8 +774,8 @@ public class CustomConversions { @@ -736,8 +774,8 @@ public class CustomConversions {
*/
public static StoreConversions of(SimpleTypeHolder storeTypeHolder, Object... converters) {
Assert.notNull(storeTypeHolder, "SimpleTypeHolder must not be null!");
Assert.notNull(converters, "Converters must not be null!");
Assert.notNull(storeTypeHolder, "SimpleTypeHolder must not be null");
Assert.notNull(converters, "Converters must not be null");
return new StoreConversions(storeTypeHolder, Arrays.asList(converters));
}
@ -752,8 +790,8 @@ public class CustomConversions { @@ -752,8 +790,8 @@ public class CustomConversions {
*/
public static StoreConversions of(SimpleTypeHolder storeTypeHolder, Collection<?> converters) {
Assert.notNull(storeTypeHolder, "SimpleTypeHolder must not be null!");
Assert.notNull(converters, "Converters must not be null!");
Assert.notNull(storeTypeHolder, "SimpleTypeHolder must not be null");
Assert.notNull(converters, "Converters must not be null");
return new StoreConversions(storeTypeHolder, converters);
}
@ -766,23 +804,23 @@ public class CustomConversions { @@ -766,23 +804,23 @@ public class CustomConversions {
*/
public Streamable<ConverterRegistration> getRegistrationsFor(Object converter) {
Assert.notNull(converter, "Converter must not be null!");
Assert.notNull(converter, "Converter must not be null");
Class<?> type = converter.getClass();
boolean isWriting = isAnnotatedWith(type, WritingConverter.class);
boolean isReading = isAnnotatedWith(type, ReadingConverter.class);
if (converter instanceof ConverterAware) {
if (converter instanceof ConverterAware converterAware) {
return Streamable.of(() -> ConverterAware.class.cast(converter).getConverters().stream()//
return Streamable.of(() -> converterAware.getConverters().stream()
.flatMap(it -> getRegistrationsFor(it).stream()));
} else if (converter instanceof GenericConverter) {
} else if (converter instanceof GenericConverter genericConverter) {
Set<ConvertiblePair> convertibleTypes = GenericConverter.class.cast(converter).getConvertibleTypes();
Set<ConvertiblePair> convertibleTypes = genericConverter.getConvertibleTypes();
return convertibleTypes == null //
? Streamable.empty() //
return convertibleTypes == null
? Streamable.empty()
: Streamable.of(convertibleTypes).map(it -> register(converter, it, isReading, isWriting));
} else if (converter instanceof ConverterFactory) {
@ -794,7 +832,7 @@ public class CustomConversions { @@ -794,7 +832,7 @@ public class CustomConversions {
return getRegistrationFor(converter, Converter.class, isReading, isWriting);
} else {
throw new IllegalArgumentException(String.format("Unsupported converter type %s!", converter));
throw new IllegalArgumentException(String.format("Unsupported converter type %s", converter));
}
}
@ -809,7 +847,7 @@ public class CustomConversions { @@ -809,7 +847,7 @@ public class CustomConversions {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converterType, type);
if (arguments == null) {
throw new IllegalStateException(String.format("Couldn't resolve type arguments for %s!", converterType));
throw new IllegalStateException(String.format("Couldn't resolve type arguments for %s", converterType));
}
return Streamable.of(register(converter, arguments[0], arguments[1], isReading, isWriting));
@ -922,8 +960,8 @@ public class CustomConversions { @@ -922,8 +960,8 @@ public class CustomConversions {
* @param propertyValueConversions can be {@literal null}.
* @since 2.7
*/
public ConverterConfiguration(StoreConversions storeConversions, List<?> userConverters,
Predicate<ConvertiblePair> converterRegistrationFilter,
public ConverterConfiguration(@NonNull StoreConversions storeConversions, @NonNull List<?> userConverters,
@NonNull Predicate<ConvertiblePair> converterRegistrationFilter,
@Nullable PropertyValueConversions propertyValueConversions) {
this.storeConversions = storeConversions;

53
src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java

@ -42,6 +42,7 @@ import org.springframework.data.convert.CustomConversions.ConverterConfiguration @@ -42,6 +42,7 @@ import org.springframework.data.convert.CustomConversions.ConverterConfiguration
import org.springframework.data.convert.CustomConversions.StoreConversions;
import org.springframework.data.convert.Jsr310Converters.LocalDateTimeToDateConverter;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.model.SimpleTypeHolder;
/**
@ -59,7 +60,7 @@ class CustomConversionsUnitTests { @@ -59,7 +60,7 @@ class CustomConversionsUnitTests {
@Override
public boolean isSimpleType(Class<?> type) {
return type.getName().startsWith("java.time") ? false : super.isSimpleType(type);
return !type.getName().startsWith("java.time") && super.isSimpleType(type);
}
};
@ -103,7 +104,7 @@ class CustomConversionsUnitTests { @@ -103,7 +104,7 @@ class CustomConversionsUnitTests {
GenericConversionService conversionService = new DefaultConversionService();
var conversions = new CustomConversions(StoreConversions.NONE,
Arrays.asList(StringToFormatConverter.INSTANCE));
Collections.singletonList(StringToFormatConverter.INSTANCE));
conversions.registerConvertersIn(conversionService);
assertThat(conversionService.canConvert(String.class, Format.class)).isTrue();
@ -113,7 +114,7 @@ class CustomConversionsUnitTests { @@ -113,7 +114,7 @@ class CustomConversionsUnitTests {
void doesNotConsiderTypeSimpleIfOnlyReadConverterIsRegistered() {
var conversions = new CustomConversions(StoreConversions.NONE,
Arrays.asList(StringToFormatConverter.INSTANCE));
Collections.singletonList(StringToFormatConverter.INSTANCE));
assertThat(conversions.isSimpleType(Format.class)).isFalse();
}
@ -121,7 +122,7 @@ class CustomConversionsUnitTests { @@ -121,7 +122,7 @@ class CustomConversionsUnitTests {
void discoversConvertersForSubtypesOfMongoTypes() {
var conversions = new CustomConversions(StoreConversions.NONE,
Arrays.asList(StringToIntegerConverter.INSTANCE));
Collections.singletonList(StringToIntegerConverter.INSTANCE));
assertThat(conversions.hasCustomReadTarget(String.class, Integer.class)).isTrue();
assertThat(conversions.hasCustomWriteTarget(String.class, Integer.class)).isTrue();
}
@ -130,7 +131,7 @@ class CustomConversionsUnitTests { @@ -130,7 +131,7 @@ class CustomConversionsUnitTests {
void shouldSelectPropertCustomWriteTargetForCglibProxiedType() {
var conversions = new CustomConversions(StoreConversions.NONE,
Arrays.asList(FormatToStringConverter.INSTANCE));
Collections.singletonList(FormatToStringConverter.INSTANCE));
assertThat(conversions.getCustomWriteTarget(createProxyTypeFor(Format.class))).hasValue(String.class);
}
@ -138,7 +139,7 @@ class CustomConversionsUnitTests { @@ -138,7 +139,7 @@ class CustomConversionsUnitTests {
void shouldSelectPropertCustomReadTargetForCglibProxiedType() {
var conversions = new CustomConversions(StoreConversions.NONE,
Arrays.asList(CustomTypeToStringConverter.INSTANCE));
Collections.singletonList(CustomTypeToStringConverter.INSTANCE));
assertThat(conversions.hasCustomReadTarget(createProxyTypeFor(CustomType.class), String.class)).isTrue();
}
@ -202,7 +203,7 @@ class CustomConversionsUnitTests { @@ -202,7 +203,7 @@ class CustomConversionsUnitTests {
Collections.emptyList());
conversions.registerConvertersIn(registry);
assertThat(conversions.isSimpleType(Point.class));
assertThat(conversions.isSimpleType(Point.class)).isTrue(); // Point is a custom simple type
verify(registry).addConverter(any(PointToMapConverter.class));
}
@ -279,6 +280,44 @@ class CustomConversionsUnitTests { @@ -279,6 +280,44 @@ class CustomConversionsUnitTests {
new ConverterConfiguration(StoreConversions.NONE, Collections.emptyList(), (it) -> true, null));
}
@Test
void hasValueConverterReturnsFalseWhenNoPropertyValueConversionsAreConfigured() {
ConverterConfiguration configuration = new ConverterConfiguration(StoreConversions.NONE,
Collections.emptyList(), it -> true, null);
CustomConversions conversions = new CustomConversions(configuration);
PersistentProperty<?> mockProperty = mock(PersistentProperty.class);
assertThat(conversions.getPropertyValueConversions()).isNull();
assertThat(conversions.hasValueConverter(mockProperty)).isFalse();
verifyNoInteractions(mockProperty);
}
@Test
public void hasValueConverterReturnsTrueWhenConverterRegisteredForProperty() {
PersistentProperty<?> mockProperty = mock(PersistentProperty.class);
PropertyValueConversions mockPropertyValueConversions = mock(PropertyValueConversions.class);
doReturn(true).when(mockPropertyValueConversions).hasValueConverter(eq(mockProperty));
ConverterConfiguration configuration = new ConverterConfiguration(StoreConversions.NONE,
Collections.emptyList(), it -> true, mockPropertyValueConversions);
CustomConversions conversions = new CustomConversions(configuration);
assertThat(conversions.getPropertyValueConversions()).isSameAs(mockPropertyValueConversions);
assertThat(conversions.hasValueConverter(mockProperty)).isTrue();
verify(mockPropertyValueConversions, times(1)).hasValueConverter(eq(mockProperty));
verifyNoMoreInteractions(mockPropertyValueConversions);
verifyNoInteractions(mockProperty);
}
private static Class<?> createProxyTypeFor(Class<?> type) {
var factory = new ProxyFactory();

Loading…
Cancel
Save