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/30 15:19:23 UTC

git commit: JCLOUDS-750 support serializing auto-value types without custom type adapters.

Repository: jclouds
Updated Branches:
  refs/heads/master 5641efa0c -> e21cbd09c


JCLOUDS-750 support serializing auto-value types without custom type adapters.


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/e21cbd09
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/e21cbd09
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/e21cbd09

Branch: refs/heads/master
Commit: e21cbd09cb568eef6076033da99448b128310518
Parents: 5641efa
Author: Adrian Cole <ac...@twitter.com>
Authored: Wed Oct 29 21:04:06 2014 -0700
Committer: Adrian Cole <ad...@apache.org>
Committed: Thu Oct 30 07:19:01 2014 -0700

----------------------------------------------------------------------
 core/pom.xml                                    |  5 +
 ...structorAndReflectiveTypeAdapterFactory.java | 22 ++++-
 .../test/java/org/jclouds/json/JsonTest.java    | 99 +++++++++++++++-----
 project/pom.xml                                 |  6 ++
 4 files changed, 103 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/e21cbd09/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index fb8f86d..eb0c915 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -108,6 +108,11 @@
       <version>4.2.0</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>com.google.auto.value</groupId>
+      <artifactId>auto-value</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/jclouds/blob/e21cbd09/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java b/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java
index 87e9d32..de985fd 100644
--- a/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java
+++ b/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java
@@ -22,6 +22,7 @@ import static org.jclouds.reflect.Reflection2.typeToken;
 
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
 import java.util.List;
 import java.util.Map;
 
@@ -118,14 +119,25 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
 
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
       com.google.common.reflect.TypeToken<T> token = typeToken(type.getType());
-      Invokable<T, T> deserializationCtor = constructorFieldNamingPolicy.getDeserializer(token);
+      Invokable<T, T> deserializationTarget = constructorFieldNamingPolicy.getDeserializer(token);
 
-      if (deserializationCtor == null) {
+      if (deserializationTarget == null) {
          return null; // allow GSON to choose the correct Adapter (can't simply return delegateFactory.create())
-      } else {
-         return new DeserializeIntoParameterizedConstructor<T>(delegateFactory.create(gson, type), deserializationCtor,
-               getParameterReaders(gson, deserializationCtor));
       }
+      // @AutoValue is SOURCE retention, which means it cannot be looked up at runtime.
+      // Assume abstract types built by static methods are AutoValue.
+      if (Modifier.isAbstract(type.getRawType().getModifiers()) && deserializationTarget.isStatic()) {
+         // Lookup the generated AutoValue class, whose fields must be read for serialization.
+         String packageName = type.getRawType().getPackage().getName();
+         String autoClassName = type.getRawType().getName().replace('$', '_')
+               .replace(packageName + ".", packageName + ".AutoValue_");
+         try {
+            type = (TypeToken<T>) TypeToken.get(Class.forName(autoClassName));
+         } catch (ClassNotFoundException ignored) {
+         }
+      }
+      return new DeserializeIntoParameterizedConstructor<T>(delegateFactory.create(gson, type), deserializationTarget,
+            getParameterReaders(gson, deserializationTarget));
    }
 
    private final class DeserializeIntoParameterizedConstructor<T> extends TypeAdapter<T> {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/e21cbd09/core/src/test/java/org/jclouds/json/JsonTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/json/JsonTest.java b/core/src/test/java/org/jclouds/json/JsonTest.java
index 6a0b6aa..cf477b4 100644
--- a/core/src/test/java/org/jclouds/json/JsonTest.java
+++ b/core/src/test/java/org/jclouds/json/JsonTest.java
@@ -20,24 +20,35 @@ import static com.google.common.io.BaseEncoding.base16;
 import static com.google.common.primitives.Bytes.asList;
 import static org.testng.Assert.assertEquals;
 
+import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
 
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.json.config.GsonModule.DefaultExclusionStrategy;
 import org.testng.annotations.Test;
 
-import com.google.common.base.Objects;
+import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.gson.FieldAttributes;
 import com.google.gson.FieldNamingPolicy;
 import com.google.gson.FieldNamingStrategy;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
+import com.google.inject.Provides;
 import com.google.inject.TypeLiteral;
 
 @Test
@@ -214,7 +225,8 @@ public class JsonTest {
                EnumInsideWithParser.Test.UNRECOGNIZED);
    }
 
-   private abstract static class UpperCamelCasedType {
+   @AutoValue
+   abstract static class UpperCamelCasedType {
       abstract String id();
 
       @Nullable abstract Map<String, String> volumes();
@@ -222,7 +234,7 @@ public class JsonTest {
       // Currently, this only works for deserialization. Need to set a naming policy for serialization!
       @SerializedNames({ "Id", "Volumes" })
       private static UpperCamelCasedType create(String id, Map<String, String> volumes) {
-         return new UpperCamelCasedTypeImpl(id, volumes);
+         return new AutoValue_JsonTest_UpperCamelCasedType(id, volumes);
       }
    }
 
@@ -251,36 +263,75 @@ public class JsonTest {
             UpperCamelCasedType.create("1234", null));
    }
 
-   private static class UpperCamelCasedTypeImpl extends UpperCamelCasedType {
-      private final String id;
-      private final Map<String, String> volumes;
+   @AutoValue
+   abstract static class NestedCamelCasedType {
+      abstract UpperCamelCasedType item();
 
-      private UpperCamelCasedTypeImpl(String id, Map<String, String> volumes) {
-         this.id = id;
-         this.volumes = volumes;
-      }
+      abstract List<UpperCamelCasedType> items();
 
-      @Override String id() {
-         return id;
+      // Currently, this only works for deserialization. Need to set a naming policy for serialization!
+      @SerializedNames({ "Item", "Items" })
+      private static NestedCamelCasedType create(UpperCamelCasedType item, List<UpperCamelCasedType> items) {
+         return new AutoValue_JsonTest_NestedCamelCasedType(item, items);
       }
+   }
 
-      @Override @Nullable Map<String, String> volumes() {
-         return volumes;
-      }
+   private final NestedCamelCasedType nested = NestedCamelCasedType
+         .create(UpperCamelCasedType.create("1234", Collections.<String, String>emptyMap()),
+               Arrays.asList(UpperCamelCasedType.create("5678", ImmutableMap.of("Foo", "Bar"))));
 
-      @Override public boolean equals(Object o) {
-         if (o == this) {
-            return true;
+   public void nestedCamelCasedType() {
+      Json json = Guice.createInjector(new GsonModule(), new AbstractModule() {
+         @Override protected void configure() {
+            bind(FieldNamingStrategy.class).toInstance(FieldNamingPolicy.UPPER_CAMEL_CASE);
          }
-         if (o instanceof UpperCamelCasedType) {
-            UpperCamelCasedType that = (UpperCamelCasedType) o;
-            return Objects.equal(this.id, that.id()) && Objects.equal(this.volumes, that.volumes());
+      }).getInstance(Json.class);
+
+      String spinalJson = "{\"Item\":{\"Id\":\"1234\",\"Volumes\":{}},\"Items\":[{\"Id\":\"5678\",\"Volumes\":{\"Foo\":\"Bar\"}}]}";
+
+      assertEquals(json.toJson(nested), spinalJson);
+      assertEquals(json.fromJson(spinalJson, NestedCamelCasedType.class), nested);
+   }
+
+   public void nestedCamelCasedTypeOverriddenTypeAdapterFactory() {
+      Json json = Guice.createInjector(new GsonModule(), new AbstractModule() {
+         @Override protected void configure() {
+         }
+
+         @Provides public Set<TypeAdapterFactory> typeAdapterFactories() {
+            return ImmutableSet.<TypeAdapterFactory>of(new NestedCamelCasedTypeAdapterFactory());
+         }
+      }).getInstance(Json.class);
+
+      assertEquals(json.toJson(nested), "{\"id\":\"1234\",\"count\":1}");
+      assertEquals(json.fromJson("{\"id\":\"1234\",\"count\":1}", NestedCamelCasedType.class), nested);
+   }
+
+   private class NestedCamelCasedTypeAdapterFactory extends TypeAdapter<NestedCamelCasedType>
+         implements TypeAdapterFactory {
+
+      @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+         if (!(NestedCamelCasedType.class.isAssignableFrom(typeToken.getRawType()))) {
+            return null;
          }
-         return false;
+         return (TypeAdapter<T>) this;
+      }
+
+      @Override public void write(JsonWriter out, NestedCamelCasedType value) throws IOException {
+         out.beginObject();
+         out.name("id").value(value.item().id());
+         out.name("count").value(value.items().size());
+         out.endObject();
       }
 
-      @Override public int hashCode() {
-         return Objects.hashCode(id, volumes);
+      @Override public NestedCamelCasedType read(JsonReader in) throws IOException {
+         in.beginObject();
+         in.nextName();
+         in.nextString();
+         in.nextName();
+         in.nextInt();
+         in.endObject();
+         return nested;
       }
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/e21cbd09/project/pom.xml
----------------------------------------------------------------------
diff --git a/project/pom.xml b/project/pom.xml
index 833a447..d324ae4 100644
--- a/project/pom.xml
+++ b/project/pom.xml
@@ -210,6 +210,7 @@
     <surefire.version>2.17</surefire.version>
     <assertj-core.version>1.6.1</assertj-core.version>
     <assertj-guava.version>1.2.0</assertj-guava.version>
+    <auto-value.version>1.0-rc2</auto-value.version>
     <http.proxyHost />
     <http.proxyPort />
     <jclouds.wire.httpstream.url>http://archive.apache.org/dist/commons/logging/binaries/commons-logging-1.1.1-bin.tar.gz</jclouds.wire.httpstream.url>
@@ -304,6 +305,11 @@
         <artifactId>assertj-guava</artifactId>
         <version>${assertj-guava.version}</version>
       </dependency>
+      <dependency>
+        <groupId>com.google.auto.value</groupId>
+        <artifactId>auto-value</artifactId>
+        <version>${auto-value.version}</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
   <dependencies>