This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push: new 2e82c9f5bd GROOVY-10223: support assignment/cast of `Optional` to array/collection 2e82c9f5bd is described below commit 2e82c9f5bd6201ed4724b26714275f4cef9df6e8 Author: Eric Milles <eric.mil...@thomsonreuters.com> AuthorDate: Fri Jun 24 10:31:23 2022 -0500 GROOVY-10223: support assignment/cast of `Optional` to array/collection --- .../typehandling/DefaultTypeTransformation.java | 31 ++++---- .../DefaultTypeTransformationTest.groovy | 84 ++++++++++++++++++++++ 2 files changed, 101 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java index ac816bdf57..173ad5a2c7 100644 --- a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java +++ b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java @@ -50,6 +50,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Supplier; import java.util.stream.BaseStream; import java.util.stream.DoubleStream; @@ -271,12 +272,6 @@ public class DefaultTypeTransformation { } }; - if (object instanceof BaseStream) { - Collection answer = newCollection.get(); - answer.addAll(asCollection(object)); - return answer; - } - if (object.getClass().isArray()) { Collection answer = newCollection.get(); // we cannot just wrap in a List as we support primitive type arrays @@ -287,6 +282,12 @@ public class DefaultTypeTransformation { return answer; } + if (object instanceof BaseStream || object instanceof Optional) { + Collection answer = newCollection.get(); + answer.addAll(asCollection(object)); + return answer; + } + return continueCastOnNumber(object, type); } @@ -484,22 +485,24 @@ public class DefaultTypeTransformation { return arrayAsCollection(value); } else if (value instanceof BaseStream) { return StreamGroovyMethods.toList((BaseStream) value); - } else if (value instanceof MethodClosure) { - MethodClosure method = (MethodClosure) value; - IteratorClosureAdapter adapter = new IteratorClosureAdapter(method.getDelegate()); - method.call(adapter); - return adapter.asList(); } else if (value instanceof String || value instanceof GString) { return StringGroovyMethods.toList((CharSequence) value); + } else if (value instanceof Optional) { // GROOVY-10223 + return ((Optional<?>) value).map(Collections::singleton).orElseGet(Collections::emptySet); + } else if (value instanceof Class && ((Class) value).isEnum()) { + Object[] values = (Object[]) InvokerHelper.invokeMethod(value, "values", EMPTY_OBJECT_ARRAY); + return Arrays.asList(values); } else if (value instanceof File) { try { return ResourceGroovyMethods.readLines((File) value); } catch (IOException e) { throw new GroovyRuntimeException("Error reading file: " + value, e); } - } else if (value instanceof Class && ((Class) value).isEnum()) { - Object[] values = (Object[]) InvokerHelper.invokeMethod(value, "values", EMPTY_OBJECT_ARRAY); - return Arrays.asList(values); + } else if (value instanceof MethodClosure) { + MethodClosure method = (MethodClosure) value; + IteratorClosureAdapter<?> adapter = new IteratorClosureAdapter<>(method.getDelegate()); + method.call(adapter); + return adapter.asList(); } else { // let's assume it's a collection of 1 return Collections.singletonList(value); diff --git a/src/test/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformationTest.groovy b/src/test/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformationTest.groovy index 5054fb57a3..fc03274ae2 100644 --- a/src/test/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformationTest.groovy +++ b/src/test/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformationTest.groovy @@ -20,6 +20,7 @@ package org.codehaus.groovy.runtime.typehandling import org.junit.Test +import static groovy.test.GroovyAssert.assertScript import static groovy.test.GroovyAssert.shouldFail final class DefaultTypeTransformationTest { @@ -118,6 +119,89 @@ final class DefaultTypeTransformationTest { assert result[1] == 1 } + @Test // GROOVY-10223 + void testCastToType5() { + def err = shouldFail GroovyCastException, { + DefaultTypeTransformation.castToType(Optional.of('123'), Number[]) + } + assert err =~ /Cannot cast object '123' with class 'java.lang.String' to class 'java.lang.Number'/ + + def nothing = Optional.empty(), something = Optional.of(12345.000) + def result + + result = DefaultTypeTransformation.castToType(something, Number[]) + assert result instanceof Number[] + assert result.length == 1 + assert result[0] == 12345 + result = DefaultTypeTransformation.castToType(nothing, Number[]) + assert result instanceof Number[] + assert result.length == 0 + + result = DefaultTypeTransformation.castToType(something, int[]) + assert result instanceof int[] + assert result.length == 1 + assert result[0] == 12345 + result = DefaultTypeTransformation.castToType(nothing, int[]) + assert result instanceof int[] + assert result.length == 0 + + result = DefaultTypeTransformation.castToType(something, List) + assert result instanceof List + assert result.size() == 1 + assert result[0] == 12345 + result = DefaultTypeTransformation.castToType(nothing, List) + assert result instanceof List + assert result.isEmpty() + + result = DefaultTypeTransformation.castToType(something, Set) + assert result instanceof Set + assert result.size() == 1 + assert result[0] == 12345 + result = DefaultTypeTransformation.castToType(nothing, Set) + assert result instanceof Set + assert result.isEmpty() + } + + @Test // GROOVY-10223: interesting emergent properties + void testAsCollection() { + assertScript ''' + def nothing = Optional.empty(), something = Optional.of('foo') + + // array assignment + Object[] array = nothing + assert array.length == 0 + array = something + assert array.length == 1 + assert array[0] == 'foo' + + // iterator() support + def iterator = nothing.iterator() + assert !iterator.hasNext() + iterator = something.iterator() + assert iterator.hasNext() + assert iterator.next() == 'foo' + assert !iterator.hasNext() + + // for-each supported via iterator() + int values = 0 + for (value in nothing) { + values += 1 + } + assert values == 0 + for (value in something) { + assert value == 'foo' + values += 1 + } + assert values == 1 + ''' + + shouldFail ''' // requires support in ScriptBytecodeAdapter#despreadList + def nothing = Optional.empty() + def list = [*nothing] + assert list.isEmpty() + ''' + } + @Test void testCompareTo() { // objects