DATACMNS-1034 - Introduced API to easily register converters using Java 8 lambdas.

Introduced ConverterBuilder which exposes API to register Spring GenericConverters for the use with a store module's CustomConversions. This allows simple registration of such converters using Java 8 lambdas.

ConverterAware converters = ConverterBuilder.reading(String.class, Long.class,
  it -> Long.valueOf(it)).andWriting(it -> Object::toString);

The setup can also be done from the reading side which would just need the method invocations inverted. The resulting ConverterAware will expose the registered converters so that they can be easily handed to a CustomConversions instance. Partial creation of either reading or writing converters is possible, too with the returned instance then only exposing one of the two converters.

CustomConversions now considers ConverterAware and treats them appropriately for ConvertiblePair registration as well as during the registration in the ConversionService. Tweaked parameter types in CustomConversions to rather accept a Collection<?> for the converters (previously List<?>). Also, registerConverterIn(…) now takes a ConverterRegistry over a GenericConversionService. Polished Javadoc and non-null assertions.

Original pull request: #209.
This commit is contained in:
Oliver Gierke
2017-04-20 10:09:10 +02:00
parent 3fb7c870fb
commit a190c082f7
5 changed files with 479 additions and 25 deletions

View File

@@ -0,0 +1,89 @@
/*
* 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 static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.mockito.internal.util.Supplier;
import org.springframework.core.convert.TypeDescriptor;
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;
/**
* Unit tests for {@link DefaultConverterBuilder}.
*
* @author Oliver Gierke
* @since 2.0
* @soundtrack John Mayer - In the Blood (The Search for Everything)
*/
public class ConverterBuilderUnitTests {
@Test // DATACMNS-1034
public void setsUpBidirectionalConvertersFromReading() {
ConverterAware builder = ConverterBuilder.reading(String.class, Long.class, it -> Long.valueOf(it))
.andWriting(Object::toString);
assertConverter(builder.getReadingConverter(), "1", 1L);
assertConverter(builder.getWritingConverter(), 1L, "1");
}
@Test // DATACMNS-1034
public void setsUpBidirectionalConvertersFromWriting() {
ConverterAware builder = ConverterBuilder.writing(Long.class, String.class, Object::toString)
.andReading(it -> Long.valueOf(it));
assertConverter(builder.getReadingConverter(), "1", 1L);
assertConverter(builder.getWritingConverter(), 1L, "1");
}
@Test // DATACMNS-1034
public void setsUpReadingConverter() {
ReadingConverterBuilder<String, Long> builder = ConverterBuilder.reading(String.class, Long.class,
string -> Long.valueOf(string));
assertConverter(builder.getReadingConverter(), "1", 1L);
assertOnlyConverter(builder, builder::getReadingConverter);
}
@Test // DATACMNS-1034
public void setsUpWritingConverter() {
WritingConverterBuilder<Long, String> builder = ConverterBuilder.writing(Long.class, String.class,
Object::toString);
assertConverter(builder.getWritingConverter(), 1L, "1");
assertOnlyConverter(builder, builder::getWritingConverter);
}
private static void assertConverter(GenericConverter converter, Object source, Object target) {
assertThat(converter.getConvertibleTypes())
.containsExactly(new ConvertiblePair(source.getClass(), target.getClass()));
assertThat(converter.convert(source, TypeDescriptor.forObject(source), TypeDescriptor.forObject(target)))
.isEqualTo(target);
}
private static void assertOnlyConverter(ConverterBuilder builder, Supplier<GenericConverter> supplier) {
assertThat(builder.getConverters()).containsExactly(supplier.get());
}
}

View File

@@ -32,8 +32,10 @@ import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.ConverterBuilder.ConverterAware;
import org.springframework.data.convert.CustomConversions.StoreConversions;
import org.threeten.bp.LocalDateTime;
@@ -158,6 +160,24 @@ public class CustomConversionsUnitTests {
assertThat(customConversions.getCustomWriteTarget(String.class, SimpleDateFormat.class)).isPresent();
}
@Test // DATACMNS-1034
public void registersConverterFromConverterAware() {
ConverterAware converters = ConverterBuilder.reading(Left.class, Right.class, left -> new Right())
.andWriting(right -> new Left());
CustomConversions conversions = new CustomConversions(StoreConversions.NONE, Collections.singletonList(converters));
assertThat(conversions.hasCustomWriteTarget(Right.class)).isTrue();
assertThat(conversions.hasCustomReadTarget(Left.class, Right.class)).isTrue();
ConfigurableConversionService conversionService = new GenericConversionService();
conversions.registerConvertersIn(conversionService);
assertThat(conversionService.canConvert(Left.class, Right.class)).isTrue();
assertThat(conversionService.canConvert(Right.class, Left.class)).isTrue();
}
private static Class<?> createProxyTypeFor(Class<?> type) {
ProxyFactory factory = new ProxyFactory();
@@ -274,4 +294,8 @@ public class CustomConversionsUnitTests {
}
}
}
static class Left {}
static class Right {}
}