Repository: brooklyn-server Updated Branches: refs/heads/master e69da19f9 -> 6c7751d74
Fix type-coercion to/from arrays Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/7fbd734a Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/7fbd734a Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/7fbd734a Branch: refs/heads/master Commit: 7fbd734a98d4af9823888baa763ffd611c89d33c Parents: 75e8cc8 Author: Aled Sage <[email protected]> Authored: Thu May 25 12:55:50 2017 +0100 Committer: Aled Sage <[email protected]> Committed: Thu May 25 12:55:50 2017 +0100 ---------------------------------------------------------------------- utils/common/pom.xml | 5 ++ .../brooklyn/util/javalang/Reflections.java | 11 +++++ .../coerce/CommonAdaptorTryCoercions.java | 40 +++++++++++++++ .../javalang/coerce/TypeCoercerExtensible.java | 17 ++++++- .../brooklyn/util/javalang/ReflectionsTest.java | 6 +++ .../util/javalang/coerce/TypeCoercionsTest.java | 52 ++++++++++++++++++++ 6 files changed, 130 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7fbd734a/utils/common/pom.xml ---------------------------------------------------------------------- diff --git a/utils/common/pom.xml b/utils/common/pom.xml index c35ba0f..e1d7475 100644 --- a/utils/common/pom.xml +++ b/utils/common/pom.xml @@ -74,6 +74,11 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <scope>test</scope> http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7fbd734a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java index 6929dd7..bedcc54 100644 --- a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java +++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java @@ -32,6 +32,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -1099,4 +1100,14 @@ public class Reflections { return hasSpecialSerializationMethods(type.getSuperclass()); } + public static List<?> arrayToList(Object input) { + // We can't just use Arrays.asList(), because that would return a list containing the single + // value "input". + List<Object> result = new ArrayList<>(); + int length = Array.getLength(input); + for (int i = 0; i < length; i++) { + result.add(Array.get(input, i)); + } + return result; + } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7fbd734a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTryCoercions.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTryCoercions.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTryCoercions.java index b0a494a..890b8b7 100644 --- a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTryCoercions.java +++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTryCoercions.java @@ -18,6 +18,7 @@ */ package org.apache.brooklyn.util.javalang.coerce; +import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; @@ -25,8 +26,10 @@ import java.util.List; import org.apache.brooklyn.util.exceptions.CompoundRuntimeException; import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.javalang.JavaClassNames; +import org.apache.brooklyn.util.javalang.Reflections; import org.apache.brooklyn.util.text.Strings; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.reflect.TypeToken; @@ -45,6 +48,7 @@ public class CommonAdaptorTryCoercions { public CommonAdaptorTryCoercions registerAllAdapters() { registerAdapter(new TryCoercerWithFromMethod()); registerAdapter(new TryCoercerToEnum()); + registerAdapter(new TryCoercerToArray(coercer)); registerAdapter(new TryCoercerForPrimitivesAndStrings()); return this; } @@ -101,6 +105,42 @@ public class CommonAdaptorTryCoercions { } } + protected static class TryCoercerToArray implements TryCoercer { + private final TypeCoercerExtensible coercer; + + public TryCoercerToArray(TypeCoercerExtensible coercer) { + this.coercer = coercer; + } + + @Override + @SuppressWarnings("unchecked") + public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> targetType) { + if (!targetType.isArray()) return null; + + TypeToken<?> targetComponentType = targetType.getComponentType(); + Iterable<?> castInput; + if (input.getClass().isArray()) { + castInput = Reflections.arrayToList(input); + } else if (Iterable.class.isAssignableFrom(input.getClass())) { + castInput = (Iterable<?>) input; + } else { + return null; + } + + Object result = Array.newInstance(targetComponentType.getRawType(), Iterables.size(castInput)); + int index = 0; + for (Object member : castInput) { + Maybe<?> coercedMember = coercer.tryCoerce(member, targetComponentType); + if (coercedMember == null || coercedMember.isAbsent()) { + RuntimeException cause = Maybe.Absent.getException(coercedMember); + return Maybe.absent("Array member at index "+index+" cannot be coerced to "+targetComponentType, cause); + } + Array.set(result, index++, coercedMember.get()); + } + return (Maybe<T>) Maybe.of(result); + } + } + protected static class TryCoercerForPrimitivesAndStrings implements TryCoercer { @Override public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> targetType) { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7fbd734a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java index 7aeb3fb..fdf1940 100644 --- a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java +++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java @@ -29,6 +29,7 @@ import java.util.Set; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.javalang.Boxing; +import org.apache.brooklyn.util.javalang.Reflections; import org.apache.brooklyn.util.time.Duration; import org.apache.brooklyn.util.time.Time; import org.slf4j.Logger; @@ -124,7 +125,16 @@ public class TypeCoercerExtensible implements TypeCoercer { result = tryCoerceIterable(value, targetTypeToken, targetType); if (result != null && result.isAbsent() && targetType.isInstance(value)) { - log.warn("Failed to coerce collection from " + value.getClass().getName() + " to " + targetTypeToken + log.warn("Failed to coerce iterable from " + value.getClass().getName() + " to " + targetTypeToken + + "; returning uncoerced result to preserve (deprecated) backwards compatibility", + Maybe.getException(result)); + } + + } else if (value.getClass().isArray() && Iterable.class.isAssignableFrom(targetType)) { + result = tryCoerceArray(value, targetTypeToken, targetType); + + if (result != null && result.isAbsent() && targetType.isInstance(value)) { + log.warn("Failed to coerce array from " + value.getClass().getName() + " to " + targetTypeToken + "; returning uncoerced result to preserve (deprecated) backwards compatibility", Maybe.getException(result)); } @@ -234,6 +244,11 @@ public class TypeCoercerExtensible implements TypeCoercer { return Maybe.of((T) Maps.newLinkedHashMap(coerced)); } + protected <T> Maybe<T> tryCoerceArray(Object value, TypeToken<T> targetTypeToken, Class<? super T> targetType) { + List<?> listValue = Reflections.arrayToList(value); + return tryCoerceIterable(listValue, targetTypeToken, targetType); + } + /** tries to coerce a list; * returns null if it just doesn't apply, a {@link Maybe.Present} if it succeeded, * or {@link Maybe.Absent} with a good exception if it should have applied but couldn't */ http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7fbd734a/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java ---------------------------------------------------------------------- diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java index ba4b1d3..201cd86 100644 --- a/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java +++ b/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java @@ -172,6 +172,12 @@ public class ReflectionsTest { Assert.assertFalse(Reflections.invokeConstructorFromArgs(CI1.class, new Object[] {"wrong", "args"}).isPresent()); } + @Test + public void testArrayToList() throws Exception { + assertEquals(Reflections.arrayToList(new String[] {"a", "b"}), ImmutableList.of("a", "b")); + assertEquals(Reflections.arrayToList((Object) new String[] {"a", "b"}), ImmutableList.of("a", "b")); + } + interface I { }; interface J extends I { }; interface K extends I, J { }; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7fbd734a/utils/common/src/test/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercionsTest.java ---------------------------------------------------------------------- diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercionsTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercionsTest.java index bd66c2e..e8ff59d 100644 --- a/utils/common/src/test/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercionsTest.java +++ b/utils/common/src/test/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercionsTest.java @@ -37,6 +37,7 @@ import java.util.TimeZone; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.util.collections.MutableSet; import org.apache.brooklyn.util.text.StringPredicates; +import org.apache.commons.lang3.builder.EqualsBuilder; import org.codehaus.groovy.runtime.GStringImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -197,6 +198,57 @@ public class TypeCoercionsTest { } @Test + public void testArrayToListCoercion() { + @SuppressWarnings("serial") + List<?> s = coerce(new String[] {"1", "2"}, new TypeToken<List<String>>() { }); + Assert.assertEquals(s, ImmutableList.of("1", "2")); + } + + @Test + public void testArrayEntryCoercion() { + @SuppressWarnings("serial") + Integer[] val = coerce(new String[] {"1", "2"}, new TypeToken<Integer[]>() { }); + Assert.assertTrue(Arrays.equals(val, new Integer[] {1, 2}), "val="+Arrays.toString(val)+" of type "+val.getClass()); + } + + @Test + public void testArrayEntryInvalidCoercion() { + try { + @SuppressWarnings("serial") + Integer[] val = coerce(new String[] {"myWrongVal"}, new TypeToken<Integer[]>() { }); + Asserts.shouldHaveFailedPreviously("val="+val); + } catch (ClassCoercionException e) { + Asserts.expectedFailureContains(e, "Cannot coerce", "myWrongVal", "to java.lang.Integer"); + } + } + + @Test + public void testListToArrayInvalidCoercion() { + try { + @SuppressWarnings("serial") + Integer[] val = coerce(ImmutableList.of("myWrongVal"), new TypeToken<Integer[]>() { }); + Asserts.shouldHaveFailedPreviously("val="+val); + } catch (ClassCoercionException e) { + Asserts.expectedFailureContains(e, "Cannot coerce", "myWrongVal", "to java.lang.Integer"); + } + } + + @Test + public void testArrayMultiDimensionEntryCoercion() { + @SuppressWarnings("serial") + Integer[][] val = coerce(new String[][] {{"1", "2"}, {"3", "4"}}, new TypeToken<Integer[][]>() { }); + Assert.assertTrue(EqualsBuilder.reflectionEquals(val, new Integer[][] {{1, 2}, {3, 4}}), + "val="+Arrays.toString(val)+" of type "+val.getClass()); + } + + @Test + public void testArrayEntryToListCoercion() { + @SuppressWarnings("serial") + List<?> s = coerce(new String[] {"1", "2"}, new TypeToken<List<Integer>>() { }); + Assert.assertEquals(s, ImmutableList.of(1, 2)); + } + + @Test public void testListToSetCoercion() { Set<?> s = coerce(ImmutableList.of(1), Set.class); Assert.assertEquals(s, ImmutableSet.of(1));
