You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ad...@apache.org on 2014/10/26 20:31:18 UTC
[2/4] git commit: JCLOUDS-750 Allow abstract value types to be
constructed from json using static factory methods.
JCLOUDS-750 Allow abstract value types to be constructed from json using static factory methods.
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/671749d6
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/671749d6
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/671749d6
Branch: refs/heads/master
Commit: 671749d6b72340493244f4136dec2ba6ed57ddf4
Parents: 2a1dff2
Author: Adrian Cole <ad...@gmail.com>
Authored: Sat Oct 25 23:16:45 2014 -0700
Committer: Adrian Cole <ad...@gmail.com>
Committed: Sun Oct 26 12:31:05 2014 -0700
----------------------------------------------------------------------
.../java/org/jclouds/reflect/Reflection2.java | 29 ++++-------
...ctorAndReflectiveTypeAdapterFactoryTest.java | 54 +++++++++++++++++---
.../org/jclouds/reflect/Reflection2Test.java | 19 +++++++
3 files changed, 78 insertions(+), 24 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds/blob/671749d6/core/src/main/java/org/jclouds/reflect/Reflection2.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/reflect/Reflection2.java b/core/src/main/java/org/jclouds/reflect/Reflection2.java
index 7246f64..03db79f 100644
--- a/core/src/main/java/org/jclouds/reflect/Reflection2.java
+++ b/core/src/main/java/org/jclouds/reflect/Reflection2.java
@@ -23,6 +23,7 @@ import static com.google.common.collect.Iterables.tryFind;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
@@ -71,23 +72,6 @@ public class Reflection2 {
}
/**
- * returns an {@link Invokable} object that reflects a constructor present in the {@link TypeToken} type.
- *
- * @param ownerType
- * corresponds to {@link Invokable#getOwnerType()}
- * @param parameterTypes
- * corresponds to {@link Constructor#getParameterTypes()}
- *
- * @throws IllegalArgumentException
- * if the constructor doesn't exist or a security exception occurred
- */
- @SuppressWarnings("unchecked")
- public static <T> Invokable<T, T> constructor(Class<T> ownerType, Class<?>... parameterTypes) {
- return (Invokable<T, T>) get(constructorForParams, new TypeTokenAndParameterTypes(typeToken(ownerType),
- parameterTypes));
- }
-
- /**
* return all constructors present in the class as {@link Invokable}s.
*
* @param ownerType
@@ -144,7 +128,8 @@ public class Reflection2 {
}
/**
- * this gets all declared constructors, not just public ones. makes them accessible, as well.
+ * This gets all declared constructors, not just public ones. makes them accessible, as well.
+ * This also includes factory methods on abstract types, defined static methods returning the same type.
*/
private static LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> constructorsForTypeToken = CacheBuilder
.newBuilder().build(new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() {
@@ -154,6 +139,14 @@ public class Reflection2 {
ctor.setAccessible(true);
builder.add(key.constructor(ctor));
}
+ // Look for factory methods, if this is an abstract type.
+ if (Modifier.isAbstract(key.getRawType().getModifiers())) {
+ for (Invokable<?, Object> method : methods(key.getRawType())){
+ if (method.isStatic() && method.getReturnType().equals(key)) {
+ builder.add(method);
+ }
+ }
+ }
return builder.build();
}
});
http://git-wip-us.apache.org/repos/asf/jclouds/blob/671749d6/core/src/test/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactoryTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactoryTest.java b/core/src/test/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactoryTest.java
index 665fc2a..6567125 100644
--- a/core/src/test/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactoryTest.java
+++ b/core/src/test/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactoryTest.java
@@ -30,6 +30,7 @@ import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
+import org.jclouds.json.internal.NamingStrategies.AnnotationConstructorNamingStrategy;
import org.jclouds.json.internal.NamingStrategies.AnnotationOrNameFieldNamingStrategy;
import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
@@ -61,7 +62,7 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
static DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory() {
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(
new ExtractSerializedName(), new ExtractNamed()));
- NamingStrategies.AnnotationConstructorNamingStrategy deserializationPolicy = new NamingStrategies.AnnotationConstructorNamingStrategy(
+ AnnotationConstructorNamingStrategy deserializationPolicy = new AnnotationConstructorNamingStrategy(
ImmutableSet.of(ConstructorProperties.class, Inject.class), ImmutableSet.of(new ExtractNamed()));
return new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(ImmutableMap.<Type, InstanceCreator<?>>of()),
@@ -152,15 +153,58 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
gson.fromJson("{\"bar\":1}", ValidatedConstructor.class);
}
- private static class GenericParamsCopiedIn {
+ private abstract static class ValueTypeWithFactory {
+ abstract List<String> foo();
+ abstract Map<String, String> bar();
+
+ @Inject
+ static ValueTypeWithFactory create(@Named("foo") List<String> foo, @Named("bar") Map<String, String> bar) {
+ return new GenericParamsCopiedIn(foo, bar);
+ }
+ }
+
+ private static class GenericParamsCopiedIn extends ValueTypeWithFactory {
final List<String> foo;
final Map<String, String> bar;
- @Inject
- GenericParamsCopiedIn(@Named("foo") List<String> foo, @Named("bar") Map<String, String> bar) {
+ @Inject GenericParamsCopiedIn(@Named("foo") List<String> foo, @Named("bar") Map<String, String> bar) {
this.foo = Lists.newArrayList(foo);
this.bar = Maps.newHashMap(bar);
}
+
+ @Override List<String> foo() {
+ return foo;
+ }
+
+ @Override Map<String, String> bar() {
+ return bar;
+ }
+ }
+
+ public void testBuilderOnAbstractValueType() throws IOException {
+ TypeAdapter<ValueTypeWithFactory> adapter = parameterizedCtorFactory.create(gson,
+ TypeToken.get(ValueTypeWithFactory.class));
+ List<String> inputFoo = Lists.newArrayList();
+ inputFoo.add("one");
+ Map<String, String> inputBar = Maps.newHashMap();
+ inputBar.put("2", "two");
+
+ ValueTypeWithFactory toTest = adapter.fromJson("{ \"foo\":[\"one\"], \"bar\":{ \"2\":\"two\"}}");
+ assertEquals(inputFoo, toTest.foo());
+ assertNotSame(inputFoo, toTest.foo());
+ assertEquals(inputBar, toTest.bar());
+ }
+
+ private abstract static class ValueTypeWithFactoryMissingSerializedNames {
+ @Inject
+ static ValueTypeWithFactoryMissingSerializedNames create(List<String> foo, Map<String, String> bar) {
+ return null;
+ }
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = ".* parameter 0 failed to be named by AnnotationBasedNamingStrategy requiring one of javax.inject.Named")
+ public void testSerializedNameRequiredOnAllFactoryMethodParameters() {
+ parameterizedCtorFactory.create(gson, TypeToken.get(ValueTypeWithFactoryMissingSerializedNames.class));
}
public void testGenericParamsCopiedIn() throws IOException {
@@ -175,7 +219,6 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
assertEquals(inputFoo, toTest.foo);
assertNotSame(inputFoo, toTest.foo);
assertEquals(inputBar, toTest.bar);
-
}
private static class RenamedFields {
@@ -259,5 +302,4 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
.create(gson, TypeToken.get(ComposedObjects.class));
assertNull(adapter.fromJson("{\"x\":{\"foo\":0,\"bar\":1}}"));
}
-
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/671749d6/core/src/test/java/org/jclouds/reflect/Reflection2Test.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/reflect/Reflection2Test.java b/core/src/test/java/org/jclouds/reflect/Reflection2Test.java
index 340e32d..a51e0da 100644
--- a/core/src/test/java/org/jclouds/reflect/Reflection2Test.java
+++ b/core/src/test/java/org/jclouds/reflect/Reflection2Test.java
@@ -65,6 +65,25 @@ public class Reflection2Test {
"[int arg0, float arg1]", "[int arg0]", "[int arg0, float arg1, boolean arg2]"));
}
+ private abstract static class MyValue {
+ abstract String foo();
+
+ static MyValue create(String foo){
+ return null;
+ }
+ }
+
+ public void testConstructorsReturnsFactoryMethods() {
+ Set<String> ctorParams = FluentIterable.from(constructors(TypeToken.of(MyValue.class)))
+ .transform(new Function<Invokable<?, ?>, Iterable<Parameter>>() {
+ public Iterable<Parameter> apply(Invokable<?, ?> input) {
+ return input.getParameters();
+ }
+ }).transform(toStringFunction()).toSet();
+
+ assertEquals(ctorParams, ImmutableSet.of("[]", "[java.lang.String arg0]"));
+ }
+
public void testTypeTokenForClass() {
assertEquals(typeToken(String.class), TypeToken.of(String.class));
}