Skip to content

Commit caf49ad

Browse files
christophstroblmp911de
authored andcommitted
Add support for property-specific converters.
Closes #1484 Original pull request: #2566.
1 parent 9a3a38d commit caf49ad

15 files changed

+1220
-8
lines changed

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

+51
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
3434
import org.springframework.core.convert.support.GenericConversionService;
3535
import org.springframework.data.convert.ConverterBuilder.ConverterAware;
36+
import org.springframework.data.mapping.PersistentProperty;
3637
import org.springframework.data.mapping.model.SimpleTypeHolder;
3738
import org.springframework.data.util.Predicates;
3839
import org.springframework.data.util.Streamable;
@@ -96,6 +97,8 @@ public class CustomConversions {
9697
private final Function<ConvertiblePair, Class<?>> getRawWriteTarget = convertiblePair -> getCustomTarget(
9798
convertiblePair.getSourceType(), null, writingPairs);
9899

100+
private PropertyValueConversions propertyValueConversions;
101+
99102
/**
100103
* @param converterConfiguration the {@link ConverterConfiguration} to apply.
101104
* @since 2.3
@@ -118,6 +121,7 @@ public CustomConversions(ConverterConfiguration converterConfiguration) {
118121
this.converters = Collections.unmodifiableList(registeredConverters);
119122
this.simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes,
120123
converterConfiguration.getStoreConversions().getStoreTypeHolder());
124+
this.propertyValueConversions = converterConfiguration.getPropertyValueConversions();
121125
}
122126

123127
/**
@@ -170,6 +174,36 @@ public void registerConvertersIn(ConverterRegistry conversionService) {
170174
VavrCollectionConverters.getConvertersToRegister().forEach(it -> registerConverterIn(it, conversionService));
171175
}
172176

177+
/**
178+
* Delegate check if a {@link PropertyValueConverter} for the given {@literal property} is present via
179+
* {@link PropertyValueConversions}.
180+
*
181+
* @param property must not be {@literal null}.
182+
* @return {@literal true} if a specific {@link PropertyValueConverter} is available.
183+
* @see PropertyValueConversions#hasValueConverter(PersistentProperty)
184+
* @since ?
185+
*/
186+
public boolean hasPropertyValueConverter(PersistentProperty<?> property) {
187+
return propertyValueConversions != null ? propertyValueConversions.hasValueConverter(property) : false;
188+
}
189+
190+
/**
191+
* Delegate to obtain the {@link PropertyValueConverter} for the given {@literal property} from
192+
* {@link PropertyValueConversions}.
193+
*
194+
* @param property must not be {@literal null}. param <A> domain specific type
195+
* @param <B> store native type
196+
* @param <C> conversion context type
197+
* @return the suitable {@link PropertyValueConverter} or {@literal null} if none available.
198+
* @see PropertyValueConversions#getValueConverter(PersistentProperty)
199+
* @since ?
200+
*/
201+
@Nullable
202+
public <A, B, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<A, B, C> getPropertyValueConverter(
203+
PersistentProperty<?> property) {
204+
return propertyValueConversions != null ? propertyValueConversions.getValueConverter(property) : null;
205+
}
206+
173207
/**
174208
* Get all converters and add origin information
175209
*
@@ -862,6 +896,7 @@ protected static class ConverterConfiguration {
862896
private final StoreConversions storeConversions;
863897
private final List<?> userConverters;
864898
private final Predicate<ConvertiblePair> converterRegistrationFilter;
899+
private final PropertyValueConversions propertyValueConversions;
865900

866901
/**
867902
* Create a new ConverterConfiguration holding the given {@link StoreConversions} and user defined converters.
@@ -887,9 +922,16 @@ public ConverterConfiguration(StoreConversions storeConversions, List<?> userCon
887922
public ConverterConfiguration(StoreConversions storeConversions, List<?> userConverters,
888923
Predicate<ConvertiblePair> converterRegistrationFilter) {
889924

925+
this(storeConversions, userConverters, converterRegistrationFilter, new SimplePropertyValueConversions());
926+
}
927+
928+
public ConverterConfiguration(StoreConversions storeConversions, List<?> userConverters,
929+
Predicate<ConvertiblePair> converterRegistrationFilter, @Nullable PropertyValueConversions propertyValueConversions) {
930+
890931
this.storeConversions = storeConversions;
891932
this.userConverters = new ArrayList<>(userConverters);
892933
this.converterRegistrationFilter = converterRegistrationFilter;
934+
this.propertyValueConversions = propertyValueConversions;
893935
}
894936

895937
/**
@@ -912,5 +954,14 @@ List<?> getUserConverters() {
912954
boolean shouldRegister(ConvertiblePair candidate) {
913955
return this.converterRegistrationFilter.test(candidate);
914956
}
957+
958+
/**
959+
* @return the configured {@link PropertyValueConversions} if set, {@literal null} otherwise.
960+
* @since ?
961+
*/
962+
@Nullable
963+
public PropertyValueConversions getPropertyValueConversions() {
964+
return this.propertyValueConversions;
965+
}
915966
}
916967
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.convert;
17+
18+
import static java.lang.annotation.ElementType.*;
19+
20+
import java.lang.annotation.Documented;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import org.springframework.data.convert.PropertyValueConverter.ObjectToObjectPropertyValueConverter;
26+
27+
/**
28+
* Annotation to define usage of a {@link PropertyValueConverter} to read/write the property.
29+
* <p>
30+
* Consult the store specific documentation for details and support notes.
31+
*
32+
* @author Christoph Strobl
33+
* @since ?
34+
*/
35+
@Target(FIELD)
36+
@Documented
37+
@Retention(RetentionPolicy.RUNTIME)
38+
public @interface PropertyConverter {
39+
40+
/**
41+
* The {@link PropertyValueConverter} type handling the value conversion of the annotated property.
42+
*
43+
* @return the configured {@link PropertyValueConverter}. {@link ObjectToObjectPropertyValueConverter} by default.
44+
*/
45+
Class<? extends PropertyValueConverter<?, ?, ? extends PropertyValueConverter.ValueConversionContext>> value() default ObjectToObjectPropertyValueConverter.class;
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.convert;
17+
18+
import org.springframework.data.mapping.PersistentProperty;
19+
import org.springframework.lang.Nullable;
20+
21+
/**
22+
* {@link PropertyValueConversions} provides access to {@link PropertyValueConverter converters} that may only be
23+
* applied to a specific property. Other than {@link org.springframework.core.convert.converter.Converter converters}
24+
* registered in {@link CustomConversions} the property based variants accept and allow returning {@literal null} values
25+
* and provide access to a store specific {@link PropertyValueConverter.ValueConversionContext conversion context}.
26+
*
27+
* @author Christoph Strobl
28+
* @since ?
29+
* @currentBook The Desert Prince - Peter V. Brett
30+
*/
31+
public interface PropertyValueConversions {
32+
33+
/**
34+
* Check if a {@link PropertyValueConverter} is present for the given {@literal property}.
35+
*
36+
* @param property must not be {@literal null}.
37+
* @return {@literal true} if a specific {@link PropertyValueConverter} is available.
38+
*/
39+
default boolean hasValueConverter(PersistentProperty<?> property) {
40+
return getValueConverter(property) != null;
41+
}
42+
43+
/**
44+
* Get the {@link PropertyValueConverter} for the given {@literal property} if present.
45+
*
46+
* @param property must not be {@literal null}. param <A> domain specific type
47+
* @param <B> store native type
48+
* @param <C> conversion context type
49+
* @return the suitable {@link PropertyValueConverter} or {@literal null} if none available.
50+
*/
51+
@Nullable
52+
<A, B, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<A, B, C> getValueConverter(
53+
PersistentProperty<?> property);
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.convert;
17+
18+
import org.springframework.data.mapping.PersistentProperty;
19+
import org.springframework.lang.Nullable;
20+
21+
/**
22+
* {@link PropertyValueConverter} provides a symmetric way of converting certain properties from domain to store
23+
* specific values.
24+
* <p>
25+
* A {@link PropertyValueConverter} is, other than a {@link ReadingConverter} or {@link WritingConverter}, only applied
26+
* to special annotated fields which allows a fine grained conversion of certain values within a specific context.
27+
*
28+
* @author Christoph Strobl
29+
* @param <A> domain specific type
30+
* @param <B> store native type
31+
* @since 2.7
32+
*/
33+
public interface PropertyValueConverter<A, B, C extends PropertyValueConverter.ValueConversionContext> {
34+
35+
/**
36+
* Convert the given store specific value into it's domain value representation. Typically a {@literal read}
37+
* operation.
38+
*
39+
* @param nativeValue can be {@literal null}.
40+
* @param context never {@literal null}.
41+
* @return the converted value. Can be {@literal null}.
42+
*/
43+
@Nullable
44+
A /*read*/ nativeToDomain(@Nullable B nativeValue, C context);
45+
46+
/**
47+
* Convert the given domain specific value into it's native store representation. Typically a {@literal write}
48+
* operation.
49+
*
50+
* @param domainValue can be {@literal null}.
51+
* @param context never {@literal null}.
52+
* @return the converted value. Can be {@literal null}.
53+
*/
54+
@Nullable
55+
B /*write*/ domainToNative(@Nullable A domainValue, C context);
56+
57+
/**
58+
* @author Christoph Strobl
59+
*/
60+
interface ValueConversionContext {
61+
62+
PersistentProperty<?> getProperty();
63+
}
64+
65+
/**
66+
* NoOp {@link PropertyValueConverter} implementation.
67+
*
68+
* @author Christoph Strobl
69+
*/
70+
enum ObjectToObjectPropertyValueConverter implements PropertyValueConverter<Object, Object, ValueConversionContext> {
71+
72+
INSTANCE;
73+
74+
@Override
75+
public Object nativeToDomain(Object value, ValueConversionContext context) {
76+
return value;
77+
}
78+
79+
@Override
80+
public Object domainToNative(Object value, ValueConversionContext context) {
81+
return value;
82+
}
83+
}
84+
85+
}

0 commit comments

Comments
 (0)