1616package org .springframework .data .cassandra .core .convert ;
1717
1818import java .util .ArrayList ;
19+ import java .util .Arrays ;
20+ import java .util .Collection ;
1921import java .util .Collections ;
2022import java .util .Date ;
2123import java .util .List ;
24+ import java .util .function .Consumer ;
2225import java .util .function .Predicate ;
2326
2427import org .springframework .core .convert .converter .Converter ;
28+ import org .springframework .core .convert .converter .ConverterFactory ;
29+ import org .springframework .core .convert .converter .GenericConverter ;
2530import org .springframework .core .convert .converter .GenericConverter .ConvertiblePair ;
31+ import org .springframework .data .cassandra .core .mapping .CassandraPersistentProperty ;
2632import org .springframework .data .cassandra .core .mapping .CassandraSimpleTypeHolder ;
33+ import org .springframework .data .convert .ConverterBuilder ;
2734import org .springframework .data .convert .Jsr310Converters ;
35+ import org .springframework .data .convert .PropertyValueConversions ;
36+ import org .springframework .data .convert .PropertyValueConverter ;
37+ import org .springframework .data .convert .PropertyValueConverterFactory ;
38+ import org .springframework .data .convert .PropertyValueConverterRegistrar ;
39+ import org .springframework .data .convert .SimplePropertyValueConversions ;
2840import org .springframework .data .mapping .model .SimpleTypeHolder ;
41+ import org .springframework .util .Assert ;
2942
3043/**
3144 * Value object to capture custom conversion. {@link CassandraCustomConversions} also act as factory for
@@ -59,7 +72,33 @@ public class CassandraCustomConversions extends org.springframework.data.convert
5972 * @param converters must not be {@literal null}.
6073 */
6174 public CassandraCustomConversions (List <?> converters ) {
62- super (new CassandraConverterConfiguration (STORE_CONVERSIONS , converters ));
75+ super (new CassandraConverterConfiguration (converters ));
76+ }
77+
78+ /**
79+ * Create a new {@link CassandraCustomConversions} given {@link CassandraConverterConfigurationAdapter}.
80+ *
81+ * @param conversionConfiguration must not be {@literal null}.
82+ * @since 4.2
83+ */
84+ protected CassandraCustomConversions (CassandraConverterConfigurationAdapter conversionConfiguration ) {
85+ super (conversionConfiguration .createConverterConfiguration ());
86+ }
87+
88+ /**
89+ * Functional style {@link org.springframework.data.convert.CustomConversions} creation giving users a convenient way
90+ * of configuring store specific capabilities by providing deferred hooks to what will be configured when creating the
91+ * {@link org.springframework.data.convert.CustomConversions#CustomConversions(ConverterConfiguration) instance}.
92+ *
93+ * @param configurer must not be {@literal null}.
94+ * @since 4.2
95+ */
96+ public static CassandraCustomConversions create (Consumer <CassandraConverterConfigurationAdapter > configurer ) {
97+
98+ CassandraConverterConfigurationAdapter adapter = new CassandraConverterConfigurationAdapter ();
99+ configurer .accept (adapter );
100+
101+ return new CassandraCustomConversions (adapter );
63102 }
64103
65104 /**
@@ -68,14 +107,185 @@ public CassandraCustomConversions(List<?> converters) {
68107 */
69108 static class CassandraConverterConfiguration extends ConverterConfiguration {
70109
71- CassandraConverterConfiguration (StoreConversions storeConversions , List <?> userConverters ) {
72- super (storeConversions , userConverters , getConverterFilter ());
110+ CassandraConverterConfiguration (List <?> converters ) {
111+ super (STORE_CONVERSIONS , converters , getConverterFilter ());
112+
113+ }
114+
115+ CassandraConverterConfiguration (List <?> userConverters , PropertyValueConversions propertyValueConversions ) {
116+ super (STORE_CONVERSIONS , userConverters , getConverterFilter (), propertyValueConversions );
73117 }
74118
75119 static Predicate <ConvertiblePair > getConverterFilter () {
76120
77121 return convertiblePair -> !(Jsr310Converters .supports (convertiblePair .getSourceType ())
78- && Date .class .isAssignableFrom (convertiblePair .getTargetType ()));
122+ && Date .class .isAssignableFrom (convertiblePair .getTargetType ()));
123+ }
124+ }
125+
126+ /**
127+ * {@link CassandraConverterConfigurationAdapter} encapsulates creation of
128+ * {@link org.springframework.data.convert.CustomConversions.ConverterConfiguration} with Cassandra specifics.
129+ *
130+ * @author Mark Paluch
131+ * @since 4.2
132+ */
133+ public static class CassandraConverterConfigurationAdapter {
134+
135+ private final List <Object > customConverters = new ArrayList <>();
136+
137+ private final PropertyValueConversions internalValueConversion = PropertyValueConversions .simple (it -> {});
138+ private PropertyValueConversions propertyValueConversions = internalValueConversion ;
139+
140+ /**
141+ * Create a {@link CassandraConverterConfigurationAdapter} using the provided {@code converters} and our own codecs
142+ * for JSR-310 types.
143+ *
144+ * @param converters must not be {@literal null}.
145+ * @return
146+ */
147+ public static CassandraConverterConfigurationAdapter from (List <?> converters ) {
148+
149+ Assert .notNull (converters , "Converters must not be null" );
150+
151+ CassandraConverterConfigurationAdapter adapter = new CassandraConverterConfigurationAdapter ();
152+ adapter .registerConverters (converters );
153+
154+ return adapter ;
155+ }
156+
157+ /**
158+ * Add a custom {@link Converter} implementation.
159+ *
160+ * @param converter must not be {@literal null}.
161+ * @return this.
162+ */
163+ public CassandraConverterConfigurationAdapter registerConverter (Converter <?, ?> converter ) {
164+
165+ Assert .notNull (converter , "Converter must not be null" );
166+
167+ customConverters .add (converter );
168+ return this ;
169+ }
170+
171+ /**
172+ * Add a custom {@link ConverterFactory} implementation.
173+ *
174+ * @param converterFactory must not be {@literal null}.
175+ * @return this.
176+ */
177+ public CassandraConverterConfigurationAdapter registerConverterFactory (ConverterFactory <?, ?> converterFactory ) {
178+
179+ Assert .notNull (converterFactory , "ConverterFactory must not be null" );
180+
181+ customConverters .add (converterFactory );
182+ return this ;
183+ }
184+
185+ /**
186+ * Add {@link Converter converters}, {@link ConverterFactory factories}, {@link ConverterBuilder.ConverterAware
187+ * converter-aware objects}, and {@link GenericConverter generic converters}.
188+ *
189+ * @param converters must not be {@literal null} nor contain {@literal null} values.
190+ * @return this.
191+ */
192+ public CassandraConverterConfigurationAdapter registerConverters (Object ... converters ) {
193+ return registerConverters (Arrays .asList (converters ));
194+ }
195+
196+ /**
197+ * Add {@link Converter converters}, {@link ConverterFactory factories}, {@link ConverterBuilder.ConverterAware
198+ * converter-aware objects}, and {@link GenericConverter generic converters}.
199+ *
200+ * @param converters must not be {@literal null} nor contain {@literal null} values.
201+ * @return this.
202+ */
203+ public CassandraConverterConfigurationAdapter registerConverters (Collection <?> converters ) {
204+
205+ Assert .notNull (converters , "Converters must not be null" );
206+ Assert .noNullElements (converters , "Converters must not be null nor contain null values" );
207+
208+ customConverters .addAll (converters );
209+ return this ;
210+ }
211+
212+ /**
213+ * Add a custom/default {@link PropertyValueConverterFactory} implementation used to serve
214+ * {@link PropertyValueConverter}.
215+ *
216+ * @param converterFactory must not be {@literal null}.
217+ * @return this.
218+ */
219+ public CassandraConverterConfigurationAdapter registerPropertyValueConverterFactory (
220+ PropertyValueConverterFactory converterFactory ) {
221+
222+ Assert .state (valueConversions () instanceof SimplePropertyValueConversions ,
223+ "Configured PropertyValueConversions does not allow setting custom ConverterRegistry" );
224+
225+ ((SimplePropertyValueConversions ) valueConversions ()).setConverterFactory (converterFactory );
226+ return this ;
227+ }
228+
229+ /**
230+ * Gateway to register property specific converters.
231+ *
232+ * @param configurationAdapter must not be {@literal null}.
233+ * @return this.
234+ */
235+ @ SuppressWarnings ({ "rawtypes" , "unchecked" })
236+ public CassandraConverterConfigurationAdapter configurePropertyConversions (
237+ Consumer <PropertyValueConverterRegistrar <CassandraPersistentProperty >> configurationAdapter ) {
238+
239+ Assert .state (valueConversions () instanceof SimplePropertyValueConversions ,
240+ "Configured PropertyValueConversions does not allow setting custom ConverterRegistry" );
241+
242+ PropertyValueConverterRegistrar propertyValueConverterRegistrar = new PropertyValueConverterRegistrar ();
243+ configurationAdapter .accept (propertyValueConverterRegistrar );
244+
245+ ((SimplePropertyValueConversions ) valueConversions ())
246+ .setValueConverterRegistry (propertyValueConverterRegistrar .buildRegistry ());
247+ return this ;
248+ }
249+
250+ /**
251+ * Optionally set the {@link PropertyValueConversions} to be applied during mapping.
252+ * <p>
253+ * Use this method if {@link #configurePropertyConversions(Consumer)} and
254+ * {@link #registerPropertyValueConverterFactory(PropertyValueConverterFactory)} are not sufficient.
255+ *
256+ * @param valueConversions must not be {@literal null}.
257+ * @return this.
258+ */
259+ public CassandraConverterConfigurationAdapter withPropertyValueConversions (
260+ PropertyValueConversions valueConversions ) {
261+
262+ Assert .notNull (valueConversions , "PropertyValueConversions must not be null" );
263+
264+ this .propertyValueConversions = valueConversions ;
265+ return this ;
266+ }
267+
268+ PropertyValueConversions valueConversions () {
269+
270+ if (this .propertyValueConversions == null ) {
271+ this .propertyValueConversions = internalValueConversion ;
272+ }
273+
274+ return this .propertyValueConversions ;
275+ }
276+
277+ CassandraConverterConfiguration createConverterConfiguration () {
278+
279+ if (hasDefaultPropertyValueConversions ()
280+ && propertyValueConversions instanceof SimplePropertyValueConversions svc ) {
281+ svc .init ();
282+ }
283+
284+ return new CassandraConverterConfiguration (this .customConverters , this .propertyValueConversions );
285+ }
286+
287+ private boolean hasDefaultPropertyValueConversions () {
288+ return propertyValueConversions == internalValueConversion ;
79289 }
80290 }
81291}
0 commit comments