/*
* Copyright 2017 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
*
* http://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 lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Wither;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.data.convert.ConverterBuilder.ConverterAware;
import org.springframework.data.convert.ConverterBuilder.ReadingConverterBuilder;
import org.springframework.data.convert.ConverterBuilder.WritingConverterBuilder;
import org.springframework.data.util.Optionals;
/**
* Builder to easily set up (bi-directional) {@link Converter} instances for Spring Data type mapping using Lambdas. Use
* factory methods on {@link ConverterBuilder} to create instances of this class.
*
* @author Oliver Gierke
* @since 2.0
* @see ConverterBuilder#writing(Class, Class, Function)
* @see ConverterBuilder#reading(Class, Class, Function)
* @soundtrack John Mayer - Still Feel Like Your Man (The Search for Everything)
*/
@Wither(AccessLevel.PACKAGE)
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
class DefaultConverterBuilder<S, T>
implements ConverterAware, ReadingConverterBuilder<T, S>, WritingConverterBuilder<S, T> {
private final @NonNull ConvertiblePair convertiblePair;
private final @NonNull Optional<Function<? super S, ? extends T>> writing;
private final @NonNull Optional<Function<? super T, ? extends S>> reading;
/*
* (non-Javadoc)
* @see org.springframework.data.convert.WritingConverterBuilder#andReading(java.util.function.Function)
*/
@Override
public ConverterAware andReading(Function<? super T, ? extends S> function) {
return withReading(Optional.of(function));
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.ReadingConverterBuilder#andWriting(java.util.function.Function)
*/
@Override
public ConverterAware andWriting(Function<? super S, ? extends T> function) {
return withWriting(Optional.of(function));
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.ReadingConverterBuilder#getRequiredReadingConverter()
*/
@Override
public GenericConverter getReadingConverter() {
return getOptionalReadingConverter()
.orElseThrow(() -> new IllegalStateException("No reading converter specified!"));
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.WritingConverterBuilder#getRequiredWritingConverter()
*/
@Override
public GenericConverter getWritingConverter() {
return getOptionalWritingConverter()
.orElseThrow(() -> new IllegalStateException("No writing converter specified!"));
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.ConverterBuilder#getConverters()
*/
@Override
public Set<GenericConverter> getConverters() {
return Optionals//
.toStream(getOptionalReadingConverter(), getOptionalWritingConverter())//
.collect(Collectors.toSet());
}
private Optional<GenericConverter> getOptionalReadingConverter() {
return reading.map(it -> new ConfigurableGenericConverter.Reading<>(convertiblePair, it));
}
private Optional<GenericConverter> getOptionalWritingConverter() {
return writing.map(it -> new ConfigurableGenericConverter.Writing<>(invertedPair(), it));
}
private ConvertiblePair invertedPair() {
return new ConvertiblePair(convertiblePair.getTargetType(), convertiblePair.getSourceType());
}
@RequiredArgsConstructor
@EqualsAndHashCode
private static class ConfigurableGenericConverter<S, T> implements GenericConverter {
private final ConvertiblePair convertiblePair;
private final Function<? super S, ? extends T> function;
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.GenericConverter#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
*/
@Override
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return function.apply((S) source);
}
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.GenericConverter#getConvertibleTypes()
*/
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(convertiblePair);
}
@WritingConverter
private static class Writing<S, T> extends ConfigurableGenericConverter<S, T> {
Writing(ConvertiblePair convertiblePair, Function<? super S, ? extends T> function) {
super(convertiblePair, function);
}
}
@ReadingConverter
private static class Reading<S, T> extends ConfigurableGenericConverter<S, T> {
Reading(ConvertiblePair convertiblePair, Function<? super S, ? extends T> function) {
super(convertiblePair, function);
}
}
}
}