You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/06/24 15:32:18 UTC
[groovy] 01/01: GROOVY-10223: support assignment/cast of `Optional` to array/collection
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY-10223
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 8caf89bd1e8ee695f509fb6df65c278e3469addd
Author: Eric Milles <er...@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