Skip to content

Commit db34345

Browse files
committed
GROOVY-10378: coerce iterable to array or collection
3_0_X backport
1 parent 653627c commit db34345

File tree

2 files changed

+93
-22
lines changed

2 files changed

+93
-22
lines changed

Diff for: src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java

+27-21
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,7 @@
2323
import groovy.lang.GroovyRuntimeException;
2424
import org.codehaus.groovy.reflection.ReflectionCache;
2525
import org.codehaus.groovy.reflection.stdclasses.CachedSAMClass;
26-
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
27-
import org.codehaus.groovy.runtime.InvokerHelper;
28-
import org.codehaus.groovy.runtime.InvokerInvocationException;
29-
import org.codehaus.groovy.runtime.IteratorClosureAdapter;
30-
import org.codehaus.groovy.runtime.MethodClosure;
31-
import org.codehaus.groovy.runtime.NullObject;
32-
import org.codehaus.groovy.runtime.ResourceGroovyMethods;
33-
import org.codehaus.groovy.runtime.StringGroovyMethods;
26+
import org.codehaus.groovy.runtime.*;
3427

3528
import java.io.File;
3629
import java.io.IOException;
@@ -49,6 +42,7 @@
4942
import java.util.List;
5043
import java.util.Map;
5144
import java.util.Objects;
45+
import java.util.function.Supplier;
5246

5347
/**
5448
* Class providing various type conversions, coercions and boxing/unboxing operations.
@@ -248,20 +242,22 @@ private static Object continueCastOnCollection(Object object, Class type) {
248242
return new LinkedHashSet((Collection) object);
249243
}
250244

251-
if (object.getClass().isArray()) {
252-
Collection answer;
245+
Supplier<Collection> newCollection = () -> {
253246
if (type.isAssignableFrom(ArrayList.class) && Modifier.isAbstract(type.getModifiers())) {
254-
answer = new ArrayList();
247+
return new ArrayList();
255248
} else if (type.isAssignableFrom(LinkedHashSet.class) && Modifier.isAbstract(type.getModifiers())) {
256-
answer = new LinkedHashSet();
249+
return new LinkedHashSet();
257250
} else {
258251
try {
259-
answer = (Collection) type.getDeclaredConstructor().newInstance();
252+
return (Collection) type.getDeclaredConstructor().newInstance();
260253
} catch (Exception e) {
261254
throw new GroovyCastException("Could not instantiate instance of: " + type.getName() + ". Reason: " + e);
262255
}
263256
}
257+
};
264258

259+
if (object.getClass().isArray()) {
260+
Collection answer = newCollection.get();
265261
// we cannot just wrap in a List as we support primitive type arrays
266262
int length = Array.getLength(object);
267263
for (int i = 0; i < length; i += 1) {
@@ -270,6 +266,14 @@ private static Object continueCastOnCollection(Object object, Class type) {
270266
return answer;
271267
}
272268

269+
if (object instanceof Iterable // GROOVY-11378
270+
&& !(object instanceof Collection)) { // GROOVY-7867
271+
Collection answer = newCollection.get();
272+
Iterator iterator = ((Iterable) object).iterator();
273+
while (iterator.hasNext()) answer.add(iterator.next());
274+
return answer;
275+
}
276+
273277
return continueCastOnNumber(object, type);
274278
}
275279

@@ -442,22 +446,24 @@ public static Collection asCollection(Object value) {
442446
return map.entrySet();
443447
} else if (value.getClass().isArray()) {
444448
return arrayAsCollection(value);
445-
} else if (value instanceof MethodClosure) {
446-
MethodClosure method = (MethodClosure) value;
447-
IteratorClosureAdapter adapter = new IteratorClosureAdapter(method.getDelegate());
448-
method.call(adapter);
449-
return adapter.asList();
450449
} else if (value instanceof String || value instanceof GString) {
451450
return StringGroovyMethods.toList((CharSequence) value);
451+
} else if (value instanceof Iterable) { // GROOVY-10378
452+
return DefaultGroovyMethods.toList((Iterable<?>) value);
453+
} else if (value instanceof Class && ((Class) value).isEnum()) {
454+
Object[] values = (Object[]) InvokerHelper.invokeMethod(value, "values", EMPTY_OBJECT_ARRAY);
455+
return Arrays.asList(values);
452456
} else if (value instanceof File) {
453457
try {
454458
return ResourceGroovyMethods.readLines((File) value);
455459
} catch (IOException e) {
456460
throw new GroovyRuntimeException("Error reading file: " + value, e);
457461
}
458-
} else if (value instanceof Class && ((Class) value).isEnum()) {
459-
Object[] values = (Object[]) InvokerHelper.invokeMethod(value, "values", EMPTY_OBJECT_ARRAY);
460-
return Arrays.asList(values);
462+
} else if (value instanceof MethodClosure) {
463+
MethodClosure method = (MethodClosure) value;
464+
IteratorClosureAdapter<?> adapter = new IteratorClosureAdapter<>(method.getDelegate());
465+
method.call(adapter);
466+
return adapter.asList();
461467
} else {
462468
// let's assume it's a collection of 1
463469
return Collections.singletonList(value);

Diff for: src/test/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformationTest.groovy

+66-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import static groovy.test.GroovyAssert.shouldFail
2525
final class DefaultTypeTransformationTest {
2626

2727
@Test
28-
void testCastToType() {
28+
void testCastToType1() {
2929
def input = null, result
3030

3131
result = DefaultTypeTransformation.castToType(input, int)
@@ -53,6 +53,71 @@ final class DefaultTypeTransformationTest {
5353
assert result === input
5454
}
5555

56+
@Test
57+
void testCastToType2() {
58+
def input = new int[] {0,1}, result
59+
60+
result = DefaultTypeTransformation.castToType(input, Number[])
61+
assert result instanceof Number[]
62+
assert result[0] == 0
63+
assert result[1] == 1
64+
65+
result = DefaultTypeTransformation.castToType(input, List)
66+
assert result instanceof List
67+
assert result[0] == 0
68+
assert result[1] == 1
69+
70+
result = DefaultTypeTransformation.castToType(input, Set)
71+
assert result instanceof Set
72+
assert result[0] == 0
73+
assert result[1] == 1
74+
}
75+
76+
@Test
77+
void testCastToType3() {
78+
def input = Arrays.asList(0,1), result
79+
80+
result = DefaultTypeTransformation.castToType(input, Number[])
81+
assert result instanceof Number[]
82+
assert result[0] == 0
83+
assert result[1] == 1
84+
85+
result = DefaultTypeTransformation.castToType(input, List)
86+
assert result === input
87+
assert result[0] == 0
88+
assert result[1] == 1
89+
90+
result = DefaultTypeTransformation.castToType(input, Set)
91+
assert result instanceof Set
92+
assert result[0] == 0
93+
assert result[1] == 1
94+
}
95+
96+
@Test // GROOVY-11378
97+
void testCastToType4() {
98+
def input = new org.codehaus.groovy.util.ArrayIterable<Integer>(0,1), result
99+
100+
result = DefaultTypeTransformation.castToType(input, Number[])
101+
assert result instanceof Number[]
102+
assert result[0] == 0
103+
assert result[1] == 1
104+
105+
result = DefaultTypeTransformation.castToType(input, int[])
106+
assert result instanceof int[]
107+
assert result[0] == 0
108+
assert result[1] == 1
109+
110+
result = DefaultTypeTransformation.castToType(input, List)
111+
assert result instanceof List
112+
assert result[0] == 0
113+
assert result[1] == 1
114+
115+
result = DefaultTypeTransformation.castToType(input, Set)
116+
assert result instanceof Set
117+
assert result[0] == 0
118+
assert result[1] == 1
119+
}
120+
56121
@Test
57122
void testCompareTo() {
58123
// objects

0 commit comments

Comments
 (0)