Add converter support for Stream

Add StreamConverter to provide full support for converting
java.util.stream.Stream instances to and from collections or arrays.

Also attempt to convert the element type if necessary.

StreamConverter is registered by default in the DefaultConversionService
as long as Java8 is available.

Issue: SPR-12175
This commit is contained in:
Stephane Nicoll 2015-02-22 10:29:43 +01:00
parent c3408d869c
commit 018adb04f2
5 changed files with 410 additions and 33 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -23,9 +23,11 @@ import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.lang.UsesJava8;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@ -38,6 +40,7 @@ import org.springframework.util.ObjectUtils;
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @author Stephane Nicoll
* @since 3.0
*/
@SuppressWarnings("serial")
@ -45,6 +48,9 @@ public class TypeDescriptor implements Serializable {
static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
private static final boolean streamAvailable = ClassUtils.isPresent(
"java.util.stream.Stream", TypeDescriptor.class.getClassLoader());
private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<Class<?>, TypeDescriptor>(18);
private static final Class<?>[] CACHED_COMMON_TYPES = {
@ -316,6 +322,7 @@ public class TypeDescriptor implements Serializable {
/**
* If this type is an array, returns the array's component type.
* If this type is a {@code Stream}, returns the stream's component type.
* If this type is a {@link Collection} and it is parameterized, returns the Collection's element type.
* If the Collection is not parameterized, returns {@code null} indicating the element type is not declared.
* @return the array component type or Collection element type, or {@code null} if this type is a
@ -326,6 +333,9 @@ public class TypeDescriptor implements Serializable {
if (this.resolvableType.isArray()) {
return new TypeDescriptor(this.resolvableType.getComponentType(), null, this.annotations);
}
if (streamAvailable && StreamHelper.isStream(this.type)) {
return StreamHelper.getStreamElementType(this);
}
return getRelatedIfResolvable(this, this.resolvableType.asCollection().getGeneric());
}
@ -681,4 +691,19 @@ public class TypeDescriptor implements Serializable {
return new TypeDescriptor(type, null, source.annotations);
}
/**
* Inner class to avoid a hard dependency on Java 8.
*/
@UsesJava8
private static class StreamHelper {
private static boolean isStream(Class<?> type) {
return Stream.class.isAssignableFrom(type);
}
private static TypeDescriptor getStreamElementType(TypeDescriptor source) {
return getRelatedIfResolvable(source, source.resolvableType.as(Stream.class).getGeneric());
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 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.
@ -33,6 +33,7 @@ import org.springframework.util.ClassUtils;
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 3.1
*/
public class DefaultConversionService extends GenericConversionService {
@ -45,6 +46,11 @@ public class DefaultConversionService extends GenericConversionService {
private static final boolean jsr310Available =
ClassUtils.isPresent("java.time.ZoneId", DefaultConversionService.class.getClassLoader());
/** Java 8's java.util.stream.Stream class available? */
private static final boolean streamAvailable = ClassUtils.isPresent(
"java.util.stream.Stream", DefaultConversionService.class.getClassLoader());
/**
* Create a new {@code DefaultConversionService} with the set of
@ -132,6 +138,10 @@ public class DefaultConversionService extends GenericConversionService {
converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
if (streamAvailable) {
converterRegistry.addConverter(new StreamConverter(conversionService));
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2002-2015 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.core.convert.support;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.lang.UsesJava8;
/**
* Convert a {@link Stream} to an from a collection or array, converting the
* element type if necessary.
*
* @author Stephane Nicoll
* @since 4.2
*/
@UsesJava8
public class StreamConverter implements ConditionalGenericConverter {
private static final TypeDescriptor STREAM_TYPE = TypeDescriptor.valueOf(Stream.class);
private static final Set<ConvertiblePair> CONVERTIBLE_TYPES = createConvertibleTypes();
private final ConversionService conversionService;
public StreamConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return CONVERTIBLE_TYPES;
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (sourceType.isAssignableTo(STREAM_TYPE)) {
return matchesFromStream(sourceType.getElementTypeDescriptor(), targetType);
}
if (targetType.isAssignableTo(STREAM_TYPE)) {
return matchesToStream(targetType.getElementTypeDescriptor(), sourceType);
}
return false;
}
/**
* Validate that a {@link Collection} of the elements held within the stream can be
* converted to the specified {@code targetType}.
* @param elementType the type of the stream elements
* @param targetType the type to convert to
*/
public boolean matchesFromStream(TypeDescriptor elementType, TypeDescriptor targetType) {
TypeDescriptor collectionOfElement = TypeDescriptor.collection(Collection.class, elementType);
return this.conversionService.canConvert(collectionOfElement, targetType);
}
/**
* Validate that the specified {@code sourceType} can be converted to a {@link Collection} of
* type type of the stream elements
* @param elementType the type of the stream elements
* @param sourceType the type to convert from
*/
public boolean matchesToStream(TypeDescriptor elementType, TypeDescriptor sourceType) {
TypeDescriptor collectionOfElement = TypeDescriptor.collection(Collection.class, elementType);
return this.conversionService.canConvert(sourceType, collectionOfElement);
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (sourceType.isAssignableTo(STREAM_TYPE)) {
return convertFromStream((Stream<?>) source, sourceType, targetType);
}
if (targetType.isAssignableTo(STREAM_TYPE)) {
return convertToStream(source, sourceType, targetType);
}
// Should not happen
throw new IllegalStateException("Unexpected source/target types");
}
private Object convertFromStream(Stream<?> source, TypeDescriptor streamType, TypeDescriptor targetType) {
List<Object> content = source.collect(Collectors.toList());
TypeDescriptor listType = TypeDescriptor.collection(List.class, streamType.getElementTypeDescriptor());
return this.conversionService.convert(content, listType, targetType);
}
private Object convertToStream(Object source, TypeDescriptor sourceType, TypeDescriptor streamType) {
TypeDescriptor targetCollection =
TypeDescriptor.collection(List.class, streamType.getElementTypeDescriptor());
List<?> target = (List<?>) this.conversionService.convert(source, sourceType, targetCollection);
return target.stream();
}
private static Set<ConvertiblePair> createConvertibleTypes() {
Set<ConvertiblePair> convertiblePairs = new HashSet<ConvertiblePair>();
convertiblePairs.add(new ConvertiblePair(Stream.class, Collection.class));
convertiblePairs.add(new ConvertiblePair(Stream.class, Object[].class));
convertiblePairs.add(new ConvertiblePair(Collection.class, Stream.class));
convertiblePairs.add(new ConvertiblePair(Object[].class, Stream.class));
return convertiblePairs;
}
}

View File

@ -36,6 +36,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.Test;
@ -53,6 +54,7 @@ import static org.junit.Assert.*;
/**
* @author Keith Donald
* @author Juergen Hoeller
* @author Stephane Nicoll
*/
public class DefaultConversionTests {
@ -81,24 +83,24 @@ public class DefaultConversionTests {
@Test
public void testStringToBooleanTrue() {
assertEquals(Boolean.valueOf(true), conversionService.convert("true", Boolean.class));
assertEquals(Boolean.valueOf(true), conversionService.convert("on", Boolean.class));
assertEquals(Boolean.valueOf(true), conversionService.convert("yes", Boolean.class));
assertEquals(Boolean.valueOf(true), conversionService.convert("1", Boolean.class));
assertEquals(Boolean.valueOf(true), conversionService.convert("TRUE", Boolean.class));
assertEquals(Boolean.valueOf(true), conversionService.convert("ON", Boolean.class));
assertEquals(Boolean.valueOf(true), conversionService.convert("YES", Boolean.class));
assertEquals(true, conversionService.convert("true", Boolean.class));
assertEquals(true, conversionService.convert("on", Boolean.class));
assertEquals(true, conversionService.convert("yes", Boolean.class));
assertEquals(true, conversionService.convert("1", Boolean.class));
assertEquals(true, conversionService.convert("TRUE", Boolean.class));
assertEquals(true, conversionService.convert("ON", Boolean.class));
assertEquals(true, conversionService.convert("YES", Boolean.class));
}
@Test
public void testStringToBooleanFalse() {
assertEquals(Boolean.valueOf(false), conversionService.convert("false", Boolean.class));
assertEquals(Boolean.valueOf(false), conversionService.convert("off", Boolean.class));
assertEquals(Boolean.valueOf(false), conversionService.convert("no", Boolean.class));
assertEquals(Boolean.valueOf(false), conversionService.convert("0", Boolean.class));
assertEquals(Boolean.valueOf(false), conversionService.convert("FALSE", Boolean.class));
assertEquals(Boolean.valueOf(false), conversionService.convert("OFF", Boolean.class));
assertEquals(Boolean.valueOf(false), conversionService.convert("NO", Boolean.class));
assertEquals(false, conversionService.convert("false", Boolean.class));
assertEquals(false, conversionService.convert("off", Boolean.class));
assertEquals(false, conversionService.convert("no", Boolean.class));
assertEquals(false, conversionService.convert("0", Boolean.class));
assertEquals(false, conversionService.convert("FALSE", Boolean.class));
assertEquals(false, conversionService.convert("OFF", Boolean.class));
assertEquals(false, conversionService.convert("NO", Boolean.class));
}
@Test
@ -123,7 +125,7 @@ public class DefaultConversionTests {
@Test
public void testByteToString() {
assertEquals("65", conversionService.convert(new String("A").getBytes()[0], String.class));
assertEquals("65", conversionService.convert("A".getBytes()[0], String.class));
}
@Test
@ -227,11 +229,11 @@ public class DefaultConversionTests {
assertEquals("BAR", conversionService.convert(Foo.BAR, String.class));
}
public static enum Foo {
public enum Foo {
BAR, BAZ
}
public static enum SubFoo {
public enum SubFoo {
BAR {
@Override
@ -262,12 +264,12 @@ public class DefaultConversionTests {
@Test
public void testNumberToNumber() {
assertEquals(Long.valueOf(1), conversionService.convert(Integer.valueOf(1), Long.class));
assertEquals(Long.valueOf(1), conversionService.convert(1, Long.class));
}
@Test(expected=ConversionFailedException.class)
public void testNumberToNumberNotSupportedNumber() {
conversionService.convert(Integer.valueOf(1), CustomNumber.class);
conversionService.convert(1, CustomNumber.class);
}
@SuppressWarnings("serial")
@ -297,7 +299,7 @@ public class DefaultConversionTests {
@Test
public void testNumberToCharacter() {
assertEquals(Character.valueOf('A'), conversionService.convert(Integer.valueOf(65), Character.class));
assertEquals(Character.valueOf('A'), conversionService.convert(65, Character.class));
}
@Test
@ -319,6 +321,7 @@ public class DefaultConversionTests {
@Test
public void convertArrayToCollectionGenericTypeConversion() throws Exception {
@SuppressWarnings("unchecked")
List<Integer> result = (List<Integer>) conversionService.convert(new String[] { "1", "2", "3" }, TypeDescriptor
.valueOf(String[].class), new TypeDescriptor(getClass().getDeclaredField("genericList")));
assertEquals(new Integer("1"), result.get(0));
@ -326,11 +329,26 @@ public class DefaultConversionTests {
assertEquals(new Integer("3"), result.get(2));
}
public Stream<Integer> genericStream;
@Test
public void convertArrayToStream() throws Exception {
String[] source = {"1", "3", "4"};
@SuppressWarnings("unchecked")
Stream<Integer> result = (Stream<Integer>) this.conversionService.convert(source,
TypeDescriptor.valueOf(String[].class),
new TypeDescriptor(getClass().getDeclaredField("genericStream")));
assertEquals(8, result.mapToInt((x) -> x).sum());
}
@Test
public void testSpr7766() throws Exception {
ConverterRegistry registry = (conversionService);
registry.addConverter(new ColorConverter());
List<Color> colors = (List<Color>) conversionService.convert(new String[] { "ffffff", "#000000" }, TypeDescriptor.valueOf(String[].class), new TypeDescriptor(new MethodParameter(getClass().getMethod("handlerMethod", List.class), 0)));
@SuppressWarnings("unchecked")
List<Color> colors = (List<Color>) conversionService.convert(new String[] { "ffffff", "#000000" },
TypeDescriptor.valueOf(String[].class),
new TypeDescriptor(new MethodParameter(getClass().getMethod("handlerMethod", List.class), 0)));
assertEquals(2, colors.size());
assertEquals(Color.WHITE, colors.get(0));
assertEquals(Color.BLACK, colors.get(1));
@ -474,14 +492,14 @@ public class DefaultConversionTests {
@Test
public void convertCollectionToString() {
List<String> list = Arrays.asList(new String[] { "foo", "bar" });
List<String> list = Arrays.asList("foo", "bar");
String result = conversionService.convert(list, String.class);
assertEquals("foo,bar", result);
}
@Test
public void convertCollectionToStringWithElementConversion() throws Exception {
List<Integer> list = Arrays.asList(new Integer[] { 3, 5 });
List<Integer> list = Arrays.asList(3, 5);
String result = (String) conversionService.convert(list,
new TypeDescriptor(getClass().getField("genericList")), TypeDescriptor.valueOf(String.class));
assertEquals("3,5", result);
@ -501,9 +519,9 @@ public class DefaultConversionTests {
List result = (List) conversionService.convert("1,2,3", TypeDescriptor.valueOf(String.class),
new TypeDescriptor(getClass().getField("genericList")));
assertEquals(3, result.size());
assertEquals(new Integer(1), result.get(0));
assertEquals(new Integer(2), result.get(1));
assertEquals(new Integer(3), result.get(2));
assertEquals(1, result.get(0));
assertEquals(2, result.get(1));
assertEquals(3, result.get(2));
}
@Test
@ -551,6 +569,7 @@ public class DefaultConversionTests {
@Test
public void convertObjectToCollection() {
@SuppressWarnings("unchecked")
List<String> result = (List<String>) conversionService.convert(3L, List.class);
assertEquals(1, result.size());
assertEquals(3L, result.get(0));
@ -558,6 +577,7 @@ public class DefaultConversionTests {
@Test
public void convertObjectToCollectionWithElementConversion() throws Exception {
@SuppressWarnings("unchecked")
List<Integer> result = (List<Integer>) conversionService.convert(3L, TypeDescriptor.valueOf(Long.class),
new TypeDescriptor(getClass().getField("genericList")));
assertEquals(1, result.size());
@ -594,6 +614,7 @@ public class DefaultConversionTests {
foo.add("1");
foo.add("2");
foo.add("3");
@SuppressWarnings("unchecked")
List<Integer> bar = (List<Integer>) conversionService.convert(foo, TypeDescriptor.forObject(foo),
new TypeDescriptor(getClass().getField("genericList")));
assertEquals(new Integer(1), bar.get(0));
@ -603,6 +624,7 @@ public class DefaultConversionTests {
@Test
public void convertCollectionToCollectionNull() throws Exception {
@SuppressWarnings("unchecked")
List<Integer> bar = (List<Integer>) conversionService.convert(null,
TypeDescriptor.valueOf(LinkedHashSet.class), new TypeDescriptor(getClass().getField("genericList")));
assertNull(bar);
@ -621,6 +643,7 @@ public class DefaultConversionTests {
assertEquals("3", bar.get(2));
}
@SuppressWarnings("unchecked")
@Test
public void convertCollectionToCollectionSpecialCaseSourceImpl() throws Exception {
Map map = new LinkedHashMap();
@ -641,7 +664,9 @@ public class DefaultConversionTests {
List<String> strings = new ArrayList<String>();
strings.add("3");
strings.add("9");
List<Integer> integers = (List<Integer>) conversionService.convert(strings, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)));
@SuppressWarnings("unchecked")
List<Integer> integers = (List<Integer>) conversionService.convert(strings,
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)));
assertEquals(new Integer(3), integers.get(0));
assertEquals(new Integer(9), integers.get(1));
}
@ -653,7 +678,8 @@ public class DefaultConversionTests {
Map<String, String> foo = new HashMap<String, String>();
foo.put("1", "BAR");
foo.put("2", "BAZ");
Map<String, FooEnum> map = (Map<String, FooEnum>) conversionService.convert(foo,
@SuppressWarnings("unchecked")
Map<Integer, FooEnum> map = (Map<Integer, FooEnum>) conversionService.convert(foo,
TypeDescriptor.forObject(foo), new TypeDescriptor(getClass().getField("genericMap")));
assertEquals(FooEnum.BAR, map.get(1));
assertEquals(FooEnum.BAZ, map.get(2));
@ -664,7 +690,9 @@ public class DefaultConversionTests {
Map<String, String> strings = new HashMap<String, String>();
strings.put("3", "9");
strings.put("6", "31");
Map<Integer, Integer> integers = (Map<Integer, Integer>) conversionService.convert(strings, TypeDescriptor.map(Map.class, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(Integer.class)));
@SuppressWarnings("unchecked")
Map<Integer, Integer> integers = (Map<Integer, Integer>) conversionService.convert(strings,
TypeDescriptor.map(Map.class, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(Integer.class)));
assertEquals(new Integer(9), integers.get(3));
assertEquals(new Integer(31), integers.get(6));
}
@ -747,7 +775,8 @@ public class DefaultConversionTests {
@Test
public void convertObjectToObjectFinderMethodWithNull() {
TestEntity e = (TestEntity) conversionService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(TestEntity.class));
TestEntity e = (TestEntity) conversionService.convert(null,
TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(TestEntity.class));
assertNull(e);
}
@ -803,7 +832,8 @@ public class DefaultConversionTests {
@Test
public void convertObjectToOptionalNull() {
assertSame(Optional.empty(), conversionService.convert(null, TypeDescriptor.valueOf(Object.class), TypeDescriptor.valueOf(Optional.class)));
assertSame(Optional.empty(), conversionService.convert(null, TypeDescriptor.valueOf(Object.class),
TypeDescriptor.valueOf(Optional.class)));
assertSame(Optional.empty(), conversionService.convert(null, Optional.class));
}

View File

@ -0,0 +1,188 @@
/*
* Copyright 2002-2015 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.core.convert.support;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
public class StreamConverterTest {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private GenericConversionService conversionService;
private StreamConverter streamConverter;
@Before
public void setup() {
this.conversionService = new GenericConversionService();
this.streamConverter = new StreamConverter(this.conversionService);
this.conversionService.addConverter(new CollectionToCollectionConverter(this.conversionService));
this.conversionService.addConverter(new ArrayToCollectionConverter(this.conversionService));
this.conversionService.addConverter(new CollectionToArrayConverter(this.conversionService));
this.conversionService.addConverter(this.streamConverter);
}
@Test
public void convertFromStreamToList() throws NoSuchFieldException {
this.conversionService.addConverter(Number.class, String.class, new ObjectToStringConverter());
Stream<Integer> stream = Arrays.asList(1, 2, 3).stream();
TypeDescriptor listOfStrings = new TypeDescriptor(Types.class.getField("listOfStrings")); ;
Object result = this.conversionService.convert(stream, listOfStrings);
assertNotNull("converted object must not be null", result);
assertTrue("Converted object must be a list", result instanceof List);
@SuppressWarnings("unchecked")
List<String> content = (List<String>) result;
assertEquals("1", content.get(0));
assertEquals("2", content.get(1));
assertEquals("3", content.get(2));
assertEquals("Wrong number of elements", 3, content.size());
}
@Test
public void convertFromStreamToArray() throws NoSuchFieldException {
this.conversionService.addConverterFactory(new NumberToNumberConverterFactory());
Stream<Integer> stream = Arrays.asList(1, 2, 3).stream();
TypeDescriptor arrayOfLongs = new TypeDescriptor(Types.class.getField("arrayOfLongs")); ;
Object result = this.conversionService.convert(stream, arrayOfLongs);
assertNotNull("converted object must not be null", result);
assertTrue("Converted object must be an array", result.getClass().isArray());
Long[] content = (Long[]) result;
assertEquals(Long.valueOf(1L), content[0]);
assertEquals(Long.valueOf(2L), content[1]);
assertEquals(Long.valueOf(3L), content[2]);
assertEquals("Wrong number of elements", 3, content.length);
}
@Test
public void convertFromStreamToRawList() throws NoSuchFieldException {
Stream<Integer> stream = Arrays.asList(1, 2, 3).stream();
TypeDescriptor listOfStrings = new TypeDescriptor(Types.class.getField("rawList")); ;
Object result = this.conversionService.convert(stream, listOfStrings);
assertNotNull("converted object must not be null", result);
assertTrue("Converted object must be a list", result instanceof List);
@SuppressWarnings("unchecked")
List<Object> content = (List<Object>) result;
assertEquals(1, content.get(0));
assertEquals(2, content.get(1));
assertEquals(3, content.get(2));
assertEquals("Wrong number of elements", 3, content.size());
}
@Test
public void convertFromStreamToArrayNoConverter() throws NoSuchFieldException {
Stream<Integer> stream = Arrays.asList(1, 2, 3).stream();
TypeDescriptor arrayOfLongs = new TypeDescriptor(Types.class.getField("arrayOfLongs")); ;
thrown.expect(ConversionFailedException.class);
thrown.expectCause(is(instanceOf(ConverterNotFoundException.class)));
this.conversionService.convert(stream, arrayOfLongs);
}
@Test
public void convertFromListToStream() throws NoSuchFieldException {
this.conversionService.addConverterFactory(new StringToNumberConverterFactory());
List<String> stream = Arrays.asList("1", "2", "3");
TypeDescriptor streamOfInteger = new TypeDescriptor(Types.class.getField("streamOfIntegers")); ;
Object result = this.conversionService.convert(stream, streamOfInteger);
assertNotNull("converted object must not be null", result);
assertTrue("Converted object must be a stream", result instanceof Stream);
@SuppressWarnings("unchecked")
Stream<Integer> content = (Stream<Integer>) result;
assertEquals(6, content.mapToInt((x) -> x).sum());
}
@Test
public void convertFromArrayToStream() throws NoSuchFieldException {
Integer[] stream = new Integer[] {1, 0, 1};
this.conversionService.addConverter(new Converter<Integer, Boolean>() {
@Override
public Boolean convert(Integer source) {
return source == 1;
}
});
TypeDescriptor streamOfBoolean = new TypeDescriptor(Types.class.getField("streamOfBooleans")); ;
Object result = this.conversionService.convert(stream, streamOfBoolean);
assertNotNull("converted object must not be null", result);
assertTrue("Converted object must be a stream", result instanceof Stream);
@SuppressWarnings("unchecked")
Stream<Boolean> content = (Stream<Boolean>) result;
assertEquals(2, content.filter(x -> x).count());
}
@Test
public void convertFromListToRawStream() throws NoSuchFieldException {
List<String> stream = Arrays.asList("1", "2", "3");
TypeDescriptor streamOfInteger = new TypeDescriptor(Types.class.getField("rawStream")); ;
Object result = this.conversionService.convert(stream, streamOfInteger);
assertNotNull("converted object must not be null", result);
assertTrue("Converted object must be a stream", result instanceof Stream);
@SuppressWarnings("unchecked")
Stream<Object> content = (Stream<Object>) result;
StringBuilder sb = new StringBuilder();
content.forEach(sb::append);
assertEquals("123", sb.toString());
}
@Test
public void doesNotMatchIfNoStream() throws NoSuchFieldException {
assertFalse("Should not match non stream type", this.streamConverter.matches(
new TypeDescriptor(Types.class.getField("listOfStrings")),
new TypeDescriptor(Types.class.getField("arrayOfLongs"))));
}
@Test
public void shouldFailToConvertIfNoStream() throws NoSuchFieldException {
thrown.expect(IllegalStateException.class);
this.streamConverter.convert(new Object(), new TypeDescriptor(Types.class.getField("listOfStrings")),
new TypeDescriptor(Types.class.getField("arrayOfLongs")));
}
@SuppressWarnings("unused")
static class Types {
public List<String> listOfStrings;
public Long[] arrayOfLongs;
public Stream<Integer> streamOfIntegers;
public Stream<Boolean> streamOfBooleans;
public Stream rawStream;
public List rawList;
}
}