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));
    }