Skip to content

Commit

Permalink
GROOVY-10378: coerce iterable to array or collection
Browse files Browse the repository at this point in the history
3_0_X backport
  • Loading branch information
eric-milles committed May 16, 2024
1 parent 653627c commit db34345
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,7 @@
import groovy.lang.GroovyRuntimeException;
import org.codehaus.groovy.reflection.ReflectionCache;
import org.codehaus.groovy.reflection.stdclasses.CachedSAMClass;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.InvokerInvocationException;
import org.codehaus.groovy.runtime.IteratorClosureAdapter;
import org.codehaus.groovy.runtime.MethodClosure;
import org.codehaus.groovy.runtime.NullObject;
import org.codehaus.groovy.runtime.ResourceGroovyMethods;
import org.codehaus.groovy.runtime.StringGroovyMethods;
import org.codehaus.groovy.runtime.*;

import java.io.File;
import java.io.IOException;
Expand All @@ -49,6 +42,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

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

if (object.getClass().isArray()) {
Collection answer;
Supplier<Collection> newCollection = () -> {
if (type.isAssignableFrom(ArrayList.class) && Modifier.isAbstract(type.getModifiers())) {
answer = new ArrayList();
return new ArrayList();
} else if (type.isAssignableFrom(LinkedHashSet.class) && Modifier.isAbstract(type.getModifiers())) {
answer = new LinkedHashSet();
return new LinkedHashSet();
} else {
try {
answer = (Collection) type.getDeclaredConstructor().newInstance();
return (Collection) type.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new GroovyCastException("Could not instantiate instance of: " + type.getName() + ". Reason: " + e);
}
}
};

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

if (object instanceof Iterable // GROOVY-11378
&& !(object instanceof Collection)) { // GROOVY-7867
Collection answer = newCollection.get();
Iterator iterator = ((Iterable) object).iterator();
while (iterator.hasNext()) answer.add(iterator.next());
return answer;
}

return continueCastOnNumber(object, type);
}

Expand Down Expand Up @@ -442,22 +446,24 @@ public static Collection asCollection(Object value) {
return map.entrySet();
} else if (value.getClass().isArray()) {
return arrayAsCollection(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 Iterable) { // GROOVY-10378
return DefaultGroovyMethods.toList((Iterable<?>) value);
} 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import static groovy.test.GroovyAssert.shouldFail
final class DefaultTypeTransformationTest {

@Test
void testCastToType() {
void testCastToType1() {
def input = null, result

result = DefaultTypeTransformation.castToType(input, int)
Expand Down Expand Up @@ -53,6 +53,71 @@ final class DefaultTypeTransformationTest {
assert result === input
}

@Test
void testCastToType2() {
def input = new int[] {0,1}, result

result = DefaultTypeTransformation.castToType(input, Number[])
assert result instanceof Number[]
assert result[0] == 0
assert result[1] == 1

result = DefaultTypeTransformation.castToType(input, List)
assert result instanceof List
assert result[0] == 0
assert result[1] == 1

result = DefaultTypeTransformation.castToType(input, Set)
assert result instanceof Set
assert result[0] == 0
assert result[1] == 1
}

@Test
void testCastToType3() {
def input = Arrays.asList(0,1), result

result = DefaultTypeTransformation.castToType(input, Number[])
assert result instanceof Number[]
assert result[0] == 0
assert result[1] == 1

result = DefaultTypeTransformation.castToType(input, List)
assert result === input
assert result[0] == 0
assert result[1] == 1

result = DefaultTypeTransformation.castToType(input, Set)
assert result instanceof Set
assert result[0] == 0
assert result[1] == 1
}

@Test // GROOVY-11378
void testCastToType4() {
def input = new org.codehaus.groovy.util.ArrayIterable<Integer>(0,1), result

result = DefaultTypeTransformation.castToType(input, Number[])
assert result instanceof Number[]
assert result[0] == 0
assert result[1] == 1

result = DefaultTypeTransformation.castToType(input, int[])
assert result instanceof int[]
assert result[0] == 0
assert result[1] == 1

result = DefaultTypeTransformation.castToType(input, List)
assert result instanceof List
assert result[0] == 0
assert result[1] == 1

result = DefaultTypeTransformation.castToType(input, Set)
assert result instanceof Set
assert result[0] == 0
assert result[1] == 1
}

@Test
void testCompareTo() {
// objects
Expand Down

0 comments on commit db34345

Please sign in to comment.