Skip to content

Commit e729227

Browse files
odrotbohmmp911de
authored andcommitted
Fluent registration API for PropertyValueConverters.
Introduce a builder API to register PropertyValueConverters using simple (Bi)Functions. Additional methods on ValueConversionContext to enable advanced use cases in converter implementation. Tweak generics of VCC to be able to expose store-specific PersistentProperty implementations via the context. See #1484 Original pull request: #2566.
1 parent caf49ad commit e729227

12 files changed

+344
-58
lines changed

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

+2-1
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.convert.PropertyValueConverter.ValueConversionContext;
3637
import org.springframework.data.mapping.PersistentProperty;
3738
import org.springframework.data.mapping.model.SimpleTypeHolder;
3839
import org.springframework.data.util.Predicates;
@@ -199,7 +200,7 @@ public boolean hasPropertyValueConverter(PersistentProperty<?> property) {
199200
* @since ?
200201
*/
201202
@Nullable
202-
public <A, B, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<A, B, C> getPropertyValueConverter(
203+
public <A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getPropertyValueConverter(
203204
PersistentProperty<?> property) {
204205
return propertyValueConversions != null ? propertyValueConversions.getValueConverter(property) : null;
205206
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@
4242
*
4343
* @return the configured {@link PropertyValueConverter}. {@link ObjectToObjectPropertyValueConverter} by default.
4444
*/
45-
Class<? extends PropertyValueConverter<?, ?, ? extends PropertyValueConverter.ValueConversionContext>> value() default ObjectToObjectPropertyValueConverter.class;
45+
Class<? extends PropertyValueConverter> value() default ObjectToObjectPropertyValueConverter.class;
4646

4747
}

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.convert;
1717

18+
import org.springframework.data.convert.PropertyValueConverter.ValueConversionContext;
1819
import org.springframework.data.mapping.PersistentProperty;
1920
import org.springframework.lang.Nullable;
2021

@@ -23,7 +24,7 @@
2324
* applied to a specific property. Other than {@link org.springframework.core.convert.converter.Converter converters}
2425
* registered in {@link CustomConversions} the property based variants accept and allow returning {@literal null} values
2526
* and provide access to a store specific {@link PropertyValueConverter.ValueConversionContext conversion context}.
26-
*
27+
*
2728
* @author Christoph Strobl
2829
* @since ?
2930
* @currentBook The Desert Prince - Peter V. Brett
@@ -49,6 +50,6 @@ default boolean hasValueConverter(PersistentProperty<?> property) {
4950
* @return the suitable {@link PropertyValueConverter} or {@literal null} if none available.
5051
*/
5152
@Nullable
52-
<A, B, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<A, B, C> getValueConverter(
53+
<A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getValueConverter(
5354
PersistentProperty<?> property);
5455
}

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

+84-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
*/
1616
package org.springframework.data.convert;
1717

18+
import org.springframework.data.convert.PropertyValueConverter.ValueConversionContext;
1819
import org.springframework.data.mapping.PersistentProperty;
20+
import org.springframework.data.util.ClassTypeInformation;
21+
import org.springframework.data.util.TypeInformation;
1922
import org.springframework.lang.Nullable;
2023

2124
/**
@@ -30,7 +33,7 @@
3033
* @param <B> store native type
3134
* @since 2.7
3235
*/
33-
public interface PropertyValueConverter<A, B, C extends PropertyValueConverter.ValueConversionContext> {
36+
public interface PropertyValueConverter<A, B, C extends ValueConversionContext<? extends PersistentProperty<?>>> {
3437

3538
/**
3639
* Convert the given store specific value into it's domain value representation. Typically a {@literal read}
@@ -56,18 +59,95 @@ public interface PropertyValueConverter<A, B, C extends PropertyValueConverter.V
5659

5760
/**
5861
* @author Christoph Strobl
62+
* @author Oliver Drotbohm
5963
*/
60-
interface ValueConversionContext {
64+
interface ValueConversionContext<P extends PersistentProperty<P>> {
6165

62-
PersistentProperty<?> getProperty();
66+
/**
67+
* Return the {@link PersistentProperty} to be handled.
68+
*
69+
* @return will never be {@literal null}.
70+
*/
71+
P getProperty();
72+
73+
/**
74+
* Write to whatever type is considered best for the given source.
75+
*
76+
* @param value
77+
* @return
78+
*/
79+
@Nullable
80+
default Object write(@Nullable Object value) {
81+
return null;
82+
}
83+
84+
/**
85+
* Write as the given type.
86+
*
87+
* @param value can be {@literal null}.
88+
* @param target must not be {@literal null}.
89+
* @return can be {@literal null}.
90+
*/
91+
@Nullable
92+
default <T> T write(@Nullable Object value, Class<T> target) {
93+
return write(value, ClassTypeInformation.from(target));
94+
}
95+
96+
/**
97+
* Write as the given type.
98+
*
99+
* @param value can be {@literal null}.
100+
* @param target must not be {@literal null}.
101+
* @return can be {@literal null}.
102+
*/
103+
@Nullable
104+
default <T> T write(@Nullable Object value, TypeInformation<T> target) {
105+
return null;
106+
}
107+
108+
/**
109+
* Reads the value into the type of the current property.
110+
*
111+
* @param value can be {@literal null}.
112+
* @return can be {@literal null}.
113+
*/
114+
@Nullable
115+
default Object read(@Nullable Object value) {
116+
return read(value, getProperty().getTypeInformation());
117+
}
118+
119+
/**
120+
* Reads the value as the given type.
121+
*
122+
* @param value can be {@literal null}.
123+
* @param target must not be {@literal null}.
124+
* @return can be {@literal null}.
125+
*/
126+
@Nullable
127+
default <T> T read(@Nullable Object value, Class<T> target) {
128+
return null;
129+
}
130+
131+
/**
132+
* Reads the value as the given type.
133+
*
134+
* @param value can be {@literal null}.
135+
* @param target must not be {@literal null}.
136+
* @return can be {@literal null}.
137+
*/
138+
@Nullable
139+
default <T> T read(@Nullable Object value, TypeInformation<T> target) {
140+
return null;
141+
}
63142
}
64143

65144
/**
66145
* NoOp {@link PropertyValueConverter} implementation.
67146
*
68147
* @author Christoph Strobl
69148
*/
70-
enum ObjectToObjectPropertyValueConverter implements PropertyValueConverter<Object, Object, ValueConversionContext> {
149+
@SuppressWarnings({ "rawtypes", "null" })
150+
enum ObjectToObjectPropertyValueConverter implements PropertyValueConverter {
71151

72152
INSTANCE;
73153

@@ -81,5 +161,4 @@ public Object domainToNative(Object value, ValueConversionContext context) {
81161
return value;
82162
}
83163
}
84-
85164
}

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

+24-23
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.beans.factory.BeanFactory;
2727
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2828
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
29+
import org.springframework.data.convert.PropertyValueConverter.ValueConversionContext;
2930
import org.springframework.data.mapping.PersistentProperty;
3031
import org.springframework.lang.Nullable;
3132
import org.springframework.util.Assert;
@@ -59,14 +60,14 @@ static class CompositePropertyValueConverterFactory implements PropertyValueConv
5960

6061
@Nullable
6162
@Override
62-
public <A, B, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<A, B, C> getConverter(
63+
public <A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getConverter(
6364
PersistentProperty<?> property) {
6465
return delegates.stream().map(it -> (PropertyValueConverter<A, B, C>) it.getConverter(property))
6566
.filter(Objects::nonNull).findFirst().orElse(null);
6667
}
6768

6869
@Override
69-
public <S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<S, T, C> getConverter(
70+
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
7071
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
7172
return delegates.stream().filter(it -> it.getConverter(converterType) != null).findFirst()
7273
.map(it -> it.getConverter(converterType)).orElse(null);
@@ -82,7 +83,7 @@ public <S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyV
8283
static class SimplePropertyConverterFactory implements PropertyValueConverterFactory {
8384

8485
@Override
85-
public <S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<S, T, C> getConverter(
86+
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
8687
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
8788

8889
Assert.notNull(converterType, "ConverterType must not be null!");
@@ -110,7 +111,7 @@ public BeanFactoryAwarePropertyValueConverterFactory(BeanFactory beanFactory) {
110111
}
111112

112113
@Override
113-
public <S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<S, T, C> getConverter(
114+
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
114115
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
115116

116117
Assert.state(beanFactory != null, "BeanFactory must not be null. Did you forget to set it!");
@@ -145,14 +146,14 @@ public ConfiguredInstanceServingValueConverterFactory(PropertyValueConverterRegi
145146

146147
@Nullable
147148
@Override
148-
public <A, B, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<A, B, C> getConverter(
149+
public <A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getConverter(
149150
PersistentProperty<?> property) {
150151
return (PropertyValueConverter<A, B, C>) conversionsRegistrar.getConverter(property.getOwner().getType(),
151152
property.getName());
152153
}
153154

154155
@Override
155-
public <S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<S, T, C> getConverter(
156+
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
156157
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
157158
return null;
158159
}
@@ -170,55 +171,55 @@ static class CachingPropertyValueConverterFactory implements PropertyValueConver
170171
private final Cache cache = new Cache();
171172

172173
public CachingPropertyValueConverterFactory(PropertyValueConverterFactory delegate) {
173-
174+
174175
Assert.notNull(delegate, "Delegate must not be null!");
175176
this.delegate = delegate;
176177
}
177178

178179
@Nullable
179180
@Override
180-
public <S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<S, T, C> getConverter(
181+
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
181182
PersistentProperty<?> property) {
182183

183184
PropertyValueConverter converter = cache.get(property);
184-
if (converter != null) {
185-
return converter;
186-
}
187-
return cache.cache(property, delegate.getConverter(property));
185+
186+
return converter != null
187+
? converter
188+
: cache.cache(property, delegate.getConverter(property));
188189
}
189190

190191
@Override
191-
public <S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<S, T, C> getConverter(
192+
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
192193
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
193194

194195
PropertyValueConverter converter = cache.get(converterType);
195-
if (converter != null) {
196-
return converter;
197-
}
198-
return cache.cache(converterType, delegate.getConverter(converterType));
196+
197+
return converter != null
198+
? converter
199+
: cache.cache(converterType, delegate.getConverter(converterType));
199200
}
200201

201202
static class Cache {
202203

203-
Map<PersistentProperty<?>, PropertyValueConverter<?, ?, ? extends PropertyValueConverter.ValueConversionContext>> perPropertyCache = new HashMap<>();
204-
Map<Class<?>, PropertyValueConverter<?, ?, ? extends PropertyValueConverter.ValueConversionContext>> typeCache = new HashMap<>();
204+
Map<PersistentProperty<?>, PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>> perPropertyCache = new HashMap<>();
205+
Map<Class<?>, PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>> typeCache = new HashMap<>();
205206

206-
PropertyValueConverter<?, ?, ? extends PropertyValueConverter.ValueConversionContext> get(PersistentProperty<?> property) {
207+
PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>> get(PersistentProperty<?> property) {
207208
return perPropertyCache.get(property);
208209
}
209210

210-
PropertyValueConverter<?, ?, ? extends PropertyValueConverter.ValueConversionContext> get(Class<?> type) {
211+
PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>> get(Class<?> type) {
211212
return typeCache.get(type);
212213
}
213214

214-
<S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<S, T, C> cache(PersistentProperty<?> property,
215+
<S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> cache(PersistentProperty<?> property,
215216
PropertyValueConverter<S, T, C> converter) {
216217
perPropertyCache.putIfAbsent(property, converter);
217218
cache(property.getValueConverterType(), converter);
218219
return converter;
219220
}
220221

221-
<S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<S, T, C> cache(Class<?> type,
222+
<S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> cache(Class<?> type,
222223
PropertyValueConverter<S, T, C> converter) {
223224
typeCache.putIfAbsent(type, converter);
224225
return converter;

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.List;
2020

2121
import org.springframework.beans.factory.BeanFactory;
22+
import org.springframework.data.convert.PropertyValueConverter.ValueConversionContext;
2223
import org.springframework.data.convert.PropertyValueConverterFactories.BeanFactoryAwarePropertyValueConverterFactory;
2324
import org.springframework.data.convert.PropertyValueConverterFactories.CachingPropertyValueConverterFactory;
2425
import org.springframework.data.convert.PropertyValueConverterFactories.CompositePropertyValueConverterFactory;
@@ -49,7 +50,8 @@ public interface PropertyValueConverterFactory {
4950
* @return can be {@literal null}.
5051
*/
5152
@Nullable
52-
default <A, B, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<A, B, C> getConverter(
53+
@SuppressWarnings("unchecked")
54+
default <A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getConverter(
5355
PersistentProperty<?> property) {
5456

5557
if (!property.hasValueConverter()) {
@@ -59,7 +61,7 @@ default <A, B, C extends PropertyValueConverter.ValueConversionContext> Property
5961
}
6062

6163
@Nullable
62-
<S, T, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<S, T, C> getConverter(
64+
<S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
6365
Class<? extends PropertyValueConverter<S, T, C>> converterType);
6466

6567
/**

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.concurrent.atomic.AtomicBoolean;
2121

2222
import org.springframework.beans.factory.InitializingBean;
23+
import org.springframework.data.convert.PropertyValueConverter.ValueConversionContext;
2324
import org.springframework.data.mapping.PersistentProperty;
2425
import org.springframework.lang.Nullable;
2526

@@ -47,7 +48,7 @@ public void setConverterCacheEnabled(boolean converterCacheEnabled) {
4748
}
4849

4950
@Override
50-
public <A, B, C extends PropertyValueConverter.ValueConversionContext> PropertyValueConverter<A, B, C> getValueConverter(
51+
public <A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getValueConverter(
5152
PersistentProperty<?> property) {
5253

5354
if (!initialized.get()) {
@@ -68,7 +69,7 @@ public void init() {
6869
factoryList.add(PropertyValueConverterFactory.simple());
6970
}
7071

71-
if (converterRegistrar != null && !converterRegistrar.isEmpty()) {
72+
if ((converterRegistrar != null) && !converterRegistrar.isEmpty()) {
7273
factoryList.add(PropertyValueConverterFactory.configuredInstance(converterRegistrar));
7374
}
7475

src/main/java/org/springframework/data/mapping/PersistentProperty.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -427,9 +427,12 @@ default <T> PersistentPropertyAccessor<T> getAccessorForOwner(T owner) {
427427
}
428428

429429
@Nullable
430-
default Class<? extends PropertyValueConverter<?,?, ? extends ValueConversionContext>> getValueConverterType() {
430+
default Class<? extends PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>> getValueConverterType() {
431+
431432
PropertyConverter annotation = findAnnotation(PropertyConverter.class);
432-
return annotation == null ? null : annotation.value();
433+
434+
return annotation == null ? null
435+
: (Class<? extends PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>>) annotation.value();
433436
}
434437

435438
default boolean hasValueConverter() {

src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,12 @@ public TypeInformation<?> getAssociationTargetTypeInformation() {
287287

288288
@Nullable
289289
@Override
290-
public Class<? extends PropertyValueConverter<?, ?, ? extends ValueConversionContext>> getValueConverterType() {
290+
@SuppressWarnings("unchecked")
291+
public Class<? extends PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>> getValueConverterType() {
291292

292293
return doFindAnnotation(PropertyConverter.class) //
293294
.map(PropertyConverter::value) //
295+
.map(Class.class::cast) //
294296
.orElse(null);
295297
}
296298

0 commit comments

Comments
 (0)