Skip to content

Commit 5ce9669

Browse files
committed
Additional refinements.
Resolves #2577 Closes #2592
1 parent cad739c commit 5ce9669

9 files changed

+271
-145
lines changed

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

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,50 @@
1616
package org.springframework.data.convert;
1717

1818
import org.springframework.data.mapping.PersistentProperty;
19+
import org.springframework.lang.NonNull;
1920
import org.springframework.lang.Nullable;
2021
import org.springframework.util.Assert;
2122

2223
/**
23-
* Conversion service based on {@link CustomConversions} to convert domain and store values using
24+
* Conversion service based on {@link CustomConversions} used to convert domain and store values using
2425
* {@link PropertyValueConverter property-specific converters}.
2526
*
2627
* @author Mark Paluch
28+
* @see PropertyValueConverter
2729
* @since 2.7
2830
*/
2931
public class PropertyValueConversionService {
3032

3133
private final PropertyValueConversions conversions;
3234

33-
public PropertyValueConversionService(CustomConversions conversions) {
35+
/**
36+
* Constructs a new instance of the {@link PropertyValueConversionService} initialized with the given,
37+
* required {@link CustomConversions} for resolving the {@link PropertyValueConversions} used to
38+
* convert {@link PersistentProperty} values during data access operations.
39+
*
40+
* @param conversions {@link CustomConversions} used to handle domain and store type conversions;
41+
* must not be {@literal null}.
42+
* @throws IllegalArgumentException if {@link CustomConversions} is {@literal null}.
43+
* @see CustomConversions
44+
*/
45+
public PropertyValueConversionService(@NonNull CustomConversions conversions) {
3446

3547
Assert.notNull(conversions, "CustomConversions must not be null");
3648

37-
PropertyValueConversions pvc = conversions.getPropertyValueConversions();
38-
this.conversions = pvc == null ? NoOpPropertyValueConversions.INSTANCE : pvc;
49+
PropertyValueConversions valueConversions = conversions.getPropertyValueConversions();
50+
51+
this.conversions = valueConversions != null ? valueConversions : NoOpPropertyValueConversions.INSTANCE;
3952
}
4053

4154
/**
42-
* Return {@literal true} there is a converter registered for {@link PersistentProperty}.
55+
* Return {@literal true} if a {@link PropertyValueConverter} is registered for the {@link PersistentProperty}.
4356
* <p>
4457
* If this method returns {@literal true}, it means {@link #read(Object, PersistentProperty, ValueConversionContext)}
45-
* and {@link #write(Object, PersistentProperty, ValueConversionContext)} are capable to invoke conversion.
58+
* and {@link #write(Object, PersistentProperty, ValueConversionContext)} are capable of handling conversions.
4659
*
47-
* @param property the underlying property.
48-
* @return {@literal true} there is a converter registered for {@link PersistentProperty}.
60+
* @param property {@link PersistentProperty property} to evaluate for registration.
61+
* @return {@literal true} if a {@link PropertyValueConverter} is registered for the {@link PersistentProperty}.
62+
* @see PersistentProperty
4963
*/
5064
public boolean hasConverter(PersistentProperty<?> property) {
5165
return conversions.hasValueConverter(property);
@@ -68,11 +82,8 @@ public <P extends PersistentProperty<P>, VCC extends ValueConversionContext<P>>
6882
PropertyValueConverter<Object, Object, ValueConversionContext<P>> converter = conversions
6983
.getValueConverter(property);
7084

71-
if (value == null) {
72-
return converter.readNull(context);
73-
}
74-
75-
return converter.read(value, context);
85+
return value != null ? converter.read(value, context)
86+
: converter.readNull(context);
7687
}
7788

7889
/**
@@ -92,11 +103,8 @@ public <P extends PersistentProperty<P>, VCC extends ValueConversionContext<P>>
92103
PropertyValueConverter<Object, Object, ValueConversionContext<P>> converter = conversions
93104
.getValueConverter(property);
94105

95-
if (value == null) {
96-
return converter.writeNull(context);
97-
}
98-
99-
return converter.write(value, context);
106+
return value != null ? converter.write(value, context)
107+
: converter.writeNull(context);
100108
}
101109

102110
enum NoOpPropertyValueConversions implements PropertyValueConversions {
@@ -111,7 +119,7 @@ public boolean hasValueConverter(PersistentProperty<?> property) {
111119
@Override
112120
public <DV, SV, P extends PersistentProperty<P>, VCC extends ValueConversionContext<P>> PropertyValueConverter<DV, SV, VCC> getValueConverter(
113121
P property) {
114-
throw new UnsupportedOperationException();
122+
throw new UnsupportedOperationException("No PropertyValueConversions was configured");
115123
}
116124
}
117125
}

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

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121

2222
/**
2323
* {@link PropertyValueConversions} provides access to {@link PropertyValueConverter converters} that may only be
24-
* applied to a specific property. Other than {@link org.springframework.core.convert.converter.Converter converters}
25-
* registered in {@link CustomConversions}, the property based variants accept and allow returning {@literal null}
26-
* values and provide access to a store specific {@link ValueConversionContext conversion context}.
24+
* applied to a specific {@link PersistentProperty property}. Other than
25+
* {@link org.springframework.core.convert.converter.Converter converters} registered in {@link CustomConversions},
26+
* the {@link PersistentProperty property} based variants accept and allow returning {@literal null} values
27+
* and provide access to a store-specific {@link ValueConversionContext conversion context}.
2728
*
2829
* @author Christoph Strobl
2930
* @since 2.7
@@ -32,30 +33,41 @@
3233
public interface PropertyValueConversions {
3334

3435
/**
35-
* Check if a {@link PropertyValueConverter} is present for the given {@literal property}.
36+
* Check if a {@link PropertyValueConverter converter} is registered for
37+
* the given {@link PersistentProperty property}.
3638
*
37-
* @param property must not be {@literal null}.
38-
* @return {@literal true} if a specific {@link PropertyValueConverter} is available.
39+
* @param property {@link PersistentProperty} to evaluate; must not be {@literal null}.
40+
* @return {@literal true} if a specific {@link PropertyValueConverter converter} is registered for
41+
* the given {@link PersistentProperty}.
42+
* @see PersistentProperty
3943
*/
4044
boolean hasValueConverter(PersistentProperty<?> property);
4145

4246
/**
43-
* Get the {@link PropertyValueConverter} for the given {@literal property}.
47+
* Get the {@link PropertyValueConverter converter} registered for the given {@link PersistentProperty property}.
4448
*
45-
* @param property must not be {@literal null}.
49+
* @param property {@link PersistentProperty} used to look up the registered {@link PropertyValueConverter};
50+
* must not be {@literal null}.
4651
* @param <DV> domain-specific type
4752
* @param <SV> store-native type
4853
* @param <P> conversion context type
49-
* @return the suitable {@link PropertyValueConverter}.
50-
* @throws IllegalArgumentException if there is no converter available for {@code property}.
54+
* @return the {@link PropertyValueConverter} registered for the given {@link PersistentProperty};
55+
* never {@literal null}.
56+
* @throws IllegalArgumentException if no {@link PropertyValueConverter} was registered for
57+
* the given {@link PersistentProperty}.
5158
* @see #hasValueConverter(PersistentProperty)
59+
* @see PropertyValueConverter
60+
* @see PersistentProperty
5261
*/
5362
<DV, SV, P extends PersistentProperty<P>, VCC extends ValueConversionContext<P>> PropertyValueConverter<DV, SV, VCC> getValueConverter(
5463
P property);
5564

5665
/**
57-
* Helper that allows to create {@link PropertyValueConversions} instance with the configured
58-
* {@link PropertyValueConverter converters} provided via the given callback.
66+
* Helper method used to create a {@link PropertyValueConversions} instance with the configured
67+
* {@link PropertyValueConverter converters} provided by the {@link Consumer callback}.
68+
*
69+
* @see PropertyValueConverterRegistrar
70+
* @see PropertyValueConversions
5971
*/
6072
@SuppressWarnings({ "rawtypes", "unchecked" })
6173
static <P extends PersistentProperty<P>> PropertyValueConversions simple(

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

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.function.BiFunction;
1919

2020
import org.springframework.data.mapping.PersistentProperty;
21+
import org.springframework.lang.NonNull;
2122
import org.springframework.lang.Nullable;
2223

2324
/**
@@ -36,6 +37,8 @@
3637
* @param <DV> domain-specific type.
3738
* @param <SV> store-native type.
3839
* @param <C> the store specific {@link ValueConversionContext conversion context}.
40+
* @see org.springframework.data.convert.ValueConversionContext
41+
* @see org.springframework.data.mapping.PersistentProperty
3942
* @since 2.7
4043
*/
4144
public interface PropertyValueConverter<DV, SV, C extends ValueConversionContext<? extends PersistentProperty<?>>> {
@@ -44,18 +47,20 @@ public interface PropertyValueConverter<DV, SV, C extends ValueConversionContext
4447
* Convert the given store specific value into it's domain value representation. Typically, a {@literal read}
4548
* operation.
4649
*
47-
* @param value the value to read.
48-
* @param context never {@literal null}.
50+
* @param value value to read.
51+
* @param context {@link ValueConversionContext} containing store-specific metadata
52+
* used in the value conversion; never {@literal null}.
4953
* @return the converted value. Can be {@literal null}.
5054
*/
5155
@Nullable
5256
DV read(SV value, C context);
5357

5458
/**
55-
* Convert the given {@code null} value from the store into it's domain value representation. Typically, a
59+
* Convert the given {@code null} value from the store into its domain value representation. Typically, a
5660
* {@literal read} operation. Returns {@code null} by default.
5761
*
58-
* @param context never {@literal null}.
62+
* @param context {@link ValueConversionContext} containing store-specific metadata
63+
* used in the value conversion; never {@literal null}.
5964
* @return the converted value. Can be {@literal null}.
6065
*/
6166
@Nullable
@@ -67,8 +72,9 @@ default DV readNull(C context) {
6772
* Convert the given domain-specific value into it's native store representation. Typically, a {@literal write}
6873
* operation.
6974
*
70-
* @param value the value to write.
71-
* @param context never {@literal null}.
75+
* @param value value to write; can be {@literal null}.
76+
* @param context {@link ValueConversionContext} containing store-specific metadata
77+
* used in the value conversion; never {@literal null}.
7278
* @return the converted value. Can be {@literal null}.
7379
*/
7480
@Nullable
@@ -78,7 +84,8 @@ default DV readNull(C context) {
7884
* Convert the given {@code null} value from the domain model into it's native store representation. Typically, a
7985
* {@literal write} operation. Returns {@code null} by default.
8086
*
81-
* @param context never {@literal null}.
87+
* @param context {@link ValueConversionContext} containing store-specific metadata
88+
* used in the value conversion; never {@literal null}.
8289
* @return the converted value. Can be {@literal null}.
8390
*/
8491
@Nullable
@@ -120,32 +127,32 @@ class FunctionPropertyValueConverter<DV, SV, P extends PersistentProperty<P>>
120127
private final BiFunction<DV, ValueConversionContext<P>, SV> writer;
121128
private final BiFunction<SV, ValueConversionContext<P>, DV> reader;
122129

123-
public FunctionPropertyValueConverter(BiFunction<DV, ValueConversionContext<P>, SV> writer,
124-
BiFunction<SV, ValueConversionContext<P>, DV> reader) {
130+
public FunctionPropertyValueConverter(@NonNull BiFunction<DV, ValueConversionContext<P>, SV> writer,
131+
@NonNull BiFunction<SV, ValueConversionContext<P>, DV> reader) {
125132

126133
this.writer = writer;
127134
this.reader = reader;
128135
}
129136

130137
@Nullable
131138
@Override
132-
public SV write(DV value, ValueConversionContext<P> context) {
139+
public SV write(@Nullable DV value, @NonNull ValueConversionContext<P> context) {
133140
return writer.apply(value, context);
134141
}
135142

136143
@Override
137-
public SV writeNull(ValueConversionContext<P> context) {
144+
public SV writeNull(@NonNull ValueConversionContext<P> context) {
138145
return writer.apply(null, context);
139146
}
140147

141148
@Nullable
142149
@Override
143-
public DV read(@Nullable SV value, ValueConversionContext<P> context) {
150+
public DV read(@Nullable SV value, @NonNull ValueConversionContext<P> context) {
144151
return reader.apply(value, context);
145152
}
146153

147154
@Override
148-
public DV readNull(ValueConversionContext<P> context) {
155+
public DV readNull(@NonNull ValueConversionContext<P> context) {
149156
return reader.apply(null, context);
150157
}
151158
}

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,26 @@ final class PropertyValueConverterFactories {
5050
*/
5151
static class ChainedPropertyValueConverterFactory implements PropertyValueConverterFactory {
5252

53-
private List<PropertyValueConverterFactory> delegates;
53+
private final List<PropertyValueConverterFactory> delegates;
5454

5555
ChainedPropertyValueConverterFactory(List<PropertyValueConverterFactory> delegates) {
5656
this.delegates = Collections.unmodifiableList(delegates);
5757
}
5858

5959
@Nullable
6060
@Override
61+
@SuppressWarnings("unchecked")
6162
public <A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getConverter(
6263
PersistentProperty<?> property) {
64+
6365
return delegates.stream().map(it -> (PropertyValueConverter<A, B, C>) it.getConverter(property))
6466
.filter(Objects::nonNull).findFirst().orElse(null);
6567
}
6668

6769
@Override
6870
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
6971
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
72+
7073
return delegates.stream().filter(it -> it.getConverter(converterType) != null).findFirst()
7174
.map(it -> it.getConverter(converterType)).orElse(null);
7275
}
@@ -82,6 +85,7 @@ public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T,
8285
static class SimplePropertyConverterFactory implements PropertyValueConverterFactory {
8386

8487
@Override
88+
@SuppressWarnings({ "rawtypes", "unchecked" })
8589
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
8690
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
8791

@@ -90,6 +94,7 @@ public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T,
9094
if (converterType.isEnum()) {
9195
return (PropertyValueConverter<S, T, C>) EnumSet.allOf((Class) converterType).iterator().next();
9296
}
97+
9398
return BeanUtils.instantiateClass(converterType);
9499
}
95100
}
@@ -110,10 +115,11 @@ static class BeanFactoryAwarePropertyValueConverterFactory implements PropertyVa
110115
}
111116

112117
@Override
118+
@SuppressWarnings("unchecked")
113119
public <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
114120
Class<? extends PropertyValueConverter<DV, SV, C>> converterType) {
115121

116-
Assert.notNull(converterType, "ConverterType must not be null!");
122+
Assert.notNull(converterType, "ConverterType must not be null");
117123

118124
PropertyValueConverter<DV, SV, C> converter = beanFactory.getBeanProvider(converterType).getIfAvailable();
119125

@@ -139,21 +145,25 @@ static class ConfiguredInstanceServingValueConverterFactory implements PropertyV
139145

140146
ConfiguredInstanceServingValueConverterFactory(ValueConverterRegistry<?> converterRegistry) {
141147

142-
Assert.notNull(converterRegistry, "ConversionsRegistrar must not be null!");
148+
Assert.notNull(converterRegistry, "ConversionsRegistrar must not be null");
149+
143150
this.converterRegistry = converterRegistry;
144151
}
145152

146153
@Nullable
147154
@Override
155+
@SuppressWarnings("unchecked")
148156
public <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
149157
PersistentProperty<?> property) {
158+
150159
return (PropertyValueConverter<DV, SV, C>) converterRegistry.getConverter(property.getOwner().getType(),
151160
property.getName());
152161
}
153162

154163
@Override
155164
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
156165
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
166+
157167
return null;
158168
}
159169
}
@@ -172,12 +182,14 @@ static class CachingPropertyValueConverterFactory implements PropertyValueConver
172182

173183
CachingPropertyValueConverterFactory(PropertyValueConverterFactory delegate) {
174184

175-
Assert.notNull(delegate, "Delegate must not be null!");
185+
Assert.notNull(delegate, "Delegate must not be null");
186+
176187
this.delegate = delegate;
177188
}
178189

179190
@Nullable
180191
@Override
192+
@SuppressWarnings({ "rawtypes", "unchecked" })
181193
public <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
182194
PersistentProperty<?> property) {
183195

@@ -188,6 +200,7 @@ public <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV,
188200
}
189201

190202
@Override
203+
@SuppressWarnings({ "rawtypes", "unchecked" })
191204
public <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
192205
Class<? extends PropertyValueConverter<DV, SV, C>> converterType) {
193206

@@ -218,15 +231,19 @@ <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> cach
218231
AnnotatedPropertyValueConverterAccessor accessor = new AnnotatedPropertyValueConverterAccessor(property);
219232
Class<? extends PropertyValueConverter<?, ?, ? extends ValueConversionContext<? extends PersistentProperty<?>>>> valueConverterType = accessor
220233
.getValueConverterType();
234+
221235
if (valueConverterType != null) {
222236
cache(valueConverterType, converter);
223237
}
238+
224239
return converter;
225240
}
226241

227242
<S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> cache(Class<?> type,
228243
@Nullable PropertyValueConverter<S, T, C> converter) {
244+
229245
typeCache.putIfAbsent(type, Optional.ofNullable(converter));
246+
230247
return converter;
231248
}
232249
}

0 commit comments

Comments
 (0)