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:17 UTC

[groovy] branch GROOVY-10223 created (now 8caf89bd1e)

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a change to branch GROOVY-10223
in repository https://gitbox.apache.org/repos/asf/groovy.git


      at 8caf89bd1e GROOVY-10223: support assignment/cast of `Optional` to array/collection

This branch includes the following new commits:

     new 8caf89bd1e GROOVY-10223: support assignment/cast of `Optional` to array/collection

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[groovy] 01/01: GROOVY-10223: support assignment/cast of `Optional` to array/collection

Posted by em...@apache.org.
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