org.springframework.data.convert.PropertyValueConverterRegistrar Maven / Gradle / Ivy
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import org.springframework.data.convert.PropertyValueConverter.FunctionPropertyValueConverter;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.MethodInvocationRecorder;
import org.springframework.util.Assert;
/**
* Configuration class used to register a {@link PropertyValueConverter} with
* a {@link SimplePropertyValueConverterRegistry} that can be used in {@link PropertyValueConversions}.
*
* It is possible to register type safe converters via {@link #registerConverter(Class, Function)}
*
*
* registrar.registerConverter(Person.class, Person::getName) //
* .writing(StringConverter::encrypt) //
* .reading(StringConverter::decrypt);
*
*
* @author Christoph Strobl
* @author Oliver Drotbohm
* @since 2.7
*/
public class PropertyValueConverterRegistrar> {
private final SimplePropertyValueConverterRegistry
registry = new SimplePropertyValueConverterRegistry<>();
/**
* Starts a converter registration by pointing to a property of a domain type.
*
* @param the domain type
* @param the property type
* @param type the domain type to obtain the property from
* @param property a {@link Function} to describe the property to be referenced.
* Usually a method handle to a getter.
* @return will never be {@literal null}.
*/
public WritingConverterRegistrationBuilder registerConverter(Class type, Function property) {
String propertyName = MethodInvocationRecorder.forProxyOf(type).record(property).getPropertyPath()
.orElseThrow(() -> new IllegalArgumentException("Cannot obtain property name"));
return new WritingConverterRegistrationBuilder<>(type, propertyName, this);
}
/**
* Starts a converter registration by pointing to a property of a domain type.
*
* @param the domain type
* @param the property type
* @param type the domain type to obtain the property from
* @param propertyName a {@link Function} to describe the property to be referenced.
* Usually a method handle to a getter.
* @return will never be {@literal null}.
*/
public WritingConverterRegistrationBuilder registerConverter(Class type, String propertyName,
@SuppressWarnings("unused") Class propertyType) {
return new WritingConverterRegistrationBuilder<>(type, propertyName, this);
}
/**
* Register the given {@link PropertyValueConverter converter} for the given type and property identified by
* its name.
*
* @param type the domain type to obtain the property from
* @param path the property name.
* @param converter the {@link PropertyValueConverter converter} to apply.
* @return this.
*/
public PropertyValueConverterRegistrar registerConverter(Class> type, String path,
PropertyValueConverter, ?, ? extends ValueConversionContext>> converter) {
registry.registerConverter(type, path,
(PropertyValueConverter, ?, ? extends ValueConversionContext
>) converter);
return this;
}
/**
* Register collected {@link PropertyValueConverter converters} within the given
* {@link ValueConverterRegistry registry}.
*
* @param target {@link ValueConverterRegistry} from which to register {@link PropertyValueConverter converters};
* must not be {@literal null}.
* @throws IllegalArgumentException if the {@link ValueConverterRegistry} is {@literal null}.
* @see ValueConverterRegistry
*/
public void registerConvertersIn(ValueConverterRegistry
target) {
Assert.notNull(target, "Target registry must not be null");
registry.getConverterRegistrationMap().forEach((key, value) ->
target.registerConverter(key.type, key.path, value));
}
/**
* Obtain the {@link SimplePropertyValueConverterRegistry}.
*
* @return new instance of {@link SimplePropertyValueConverterRegistry}.
*/
public ValueConverterRegistry
buildRegistry() {
return new SimplePropertyValueConverterRegistry<>(registry);
}
/**
* Helper class used to build up a fluent registration API starting with writing.
*
* @author Oliver Drotbohm
*/
public static class WritingConverterRegistrationBuilder> {
private final Consumer>> registration;
private final PropertyValueConverterRegistrar config;
WritingConverterRegistrationBuilder(Class type, String property,
PropertyValueConverterRegistrar config) {
this.config = config;
this.registration = converter -> config.registerConverter(type, property, converter);
}
public ReadingConverterRegistrationBuilder writingAsIs() {
return writing((source, context) -> source);
}
public ReadingConverterRegistrationBuilder writing(Function writer) {
return writing((source, context) -> writer.apply(source));
}
/**
* Describes how to convert the domain property value into the database native property.
*
* @param the type to be written to the database
* @param writer the property conversion to extract a value written to the database
* @return will never be {@literal null}.
*/
public ReadingConverterRegistrationBuilder writing(
BiFunction, R> writer) {
return new ReadingConverterRegistrationBuilder<>(this, writer);
}
}
/**
* Helper class used to build a fluent API to register how to read a database value into a domain object property.
*
* @author Oliver Drotbohm
*/
public static class ReadingConverterRegistrationBuilder> {
private final WritingConverterRegistrationBuilder origin;
private final BiFunction, R> writer;
ReadingConverterRegistrationBuilder(WritingConverterRegistrationBuilder origin,
BiFunction, R> writer) {
this.origin = origin;
this.writer = writer;
}
@SuppressWarnings("unchecked")
public PropertyValueConverterRegistrar readingAsIs() {
return reading((source, context) -> (S) source);
}
public PropertyValueConverterRegistrar
reading(Function reader) {
return reading((source, context) -> reader.apply(source));
}
/**
* Describes how to read a database value into a domain object's property value.
*
* @param reader must not be {@literal null}.
* @return the confiured {@link PropertyValueConverterRegistrar}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public PropertyValueConverterRegistrar reading(BiFunction, S> reader) {
origin.registration.accept(new FunctionPropertyValueConverter(writer, reader));
return origin.config;
}
}
}