diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 7345b526f6..f7176c7f1a 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -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, TypeDescriptor> commonTypesCache = new HashMap, 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()); + } + } + } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java index 2b4007bf19..6946e14b69 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java @@ -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)); + } } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/StreamConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/StreamConverter.java new file mode 100644 index 0000000000..0d51c2af18 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/convert/support/StreamConverter.java @@ -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 CONVERTIBLE_TYPES = createConvertibleTypes(); + + private final ConversionService conversionService; + + public StreamConverter(ConversionService conversionService) { + this.conversionService = conversionService; + } + + @Override + public Set 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 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 createConvertibleTypes() { + Set convertiblePairs = new HashSet(); + 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; + } + +} diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java index d932aa2eba..43b94f324e 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java @@ -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 result = (List) 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 genericStream; + + @Test + public void convertArrayToStream() throws Exception { + String[] source = {"1", "3", "4"}; + @SuppressWarnings("unchecked") + Stream result = (Stream) 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 colors = (List) conversionService.convert(new String[] { "ffffff", "#000000" }, TypeDescriptor.valueOf(String[].class), new TypeDescriptor(new MethodParameter(getClass().getMethod("handlerMethod", List.class), 0))); + @SuppressWarnings("unchecked") + List colors = (List) 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 list = Arrays.asList(new String[] { "foo", "bar" }); + List list = Arrays.asList("foo", "bar"); String result = conversionService.convert(list, String.class); assertEquals("foo,bar", result); } @Test public void convertCollectionToStringWithElementConversion() throws Exception { - List list = Arrays.asList(new Integer[] { 3, 5 }); + List 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 result = (List) 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 result = (List) 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 bar = (List) 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 bar = (List) 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 strings = new ArrayList(); strings.add("3"); strings.add("9"); - List integers = (List) conversionService.convert(strings, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class))); + @SuppressWarnings("unchecked") + List integers = (List) 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 foo = new HashMap(); foo.put("1", "BAR"); foo.put("2", "BAZ"); - Map map = (Map) conversionService.convert(foo, + @SuppressWarnings("unchecked") + Map map = (Map) 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 strings = new HashMap(); strings.put("3", "9"); strings.put("6", "31"); - Map integers = (Map) conversionService.convert(strings, TypeDescriptor.map(Map.class, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(Integer.class))); + @SuppressWarnings("unchecked") + Map integers = (Map) 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)); } diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/StreamConverterTest.java b/spring-core/src/test/java/org/springframework/core/convert/support/StreamConverterTest.java new file mode 100644 index 0000000000..d0f80ba521 --- /dev/null +++ b/spring-core/src/test/java/org/springframework/core/convert/support/StreamConverterTest.java @@ -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 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 content = (List) 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 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 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 content = (List) 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 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 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 content = (Stream) 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() { + @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 content = (Stream) result; + assertEquals(2, content.filter(x -> x).count()); + } + + @Test + public void convertFromListToRawStream() throws NoSuchFieldException { + List 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 content = (Stream) 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 listOfStrings; + + public Long[] arrayOfLongs; + + public Stream streamOfIntegers; + + public Stream streamOfBooleans; + + public Stream rawStream; + + public List rawList; + } + +} \ No newline at end of file