You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2015/08/31 15:30:22 UTC

incubator-johnzon git commit: JOHNZON-56 basic one level support of generics

Repository: incubator-johnzon
Updated Branches:
  refs/heads/master a78c0cc5d -> 8841d7550


JOHNZON-56 basic one level support of generics


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/8841d755
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/8841d755
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/8841d755

Branch: refs/heads/master
Commit: 8841d755094293f658a5764149e9a2010e30da12
Parents: a78c0cc
Author: Romain Manni-Bucau <rm...@gmail.com>
Authored: Mon Aug 31 15:29:45 2015 +0200
Committer: Romain Manni-Bucau <rm...@gmail.com>
Committed: Mon Aug 31 15:29:45 2015 +0200

----------------------------------------------------------------------
 .../johnzon/mapper/access/BaseAccessMode.java   | 49 ++++++++++++++
 .../johnzon/mapper/access/FieldAccessMode.java  | 26 ++++---
 .../johnzon/mapper/access/MethodAccessMode.java | 38 +++++------
 .../johnzon/mapper/MapperGenericsTest.java      | 71 ++++++++++++++++++--
 4 files changed, 150 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/8841d755/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
index d8be3df..9d9619a 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
@@ -18,9 +18,17 @@
  */
 package org.apache.johnzon.mapper.access;
 
+import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
+import static java.util.Arrays.asList;
+
 // handle some specific types
 public abstract class BaseAccessMode implements AccessMode {
     private final Map<Class<?>, String[]> fieldsToRemove = new HashMap<Class<?>, String[]>();
@@ -58,4 +66,45 @@ public abstract class BaseAccessMode implements AccessMode {
         }
         return delegate;
     }
+
+    protected Type fixType(final Class<?> clazz, final Type type) { // to enhance
+        if (TypeVariable.class.isInstance(type)) { // we need to handle it on deserialization side, not needed on serialization side
+            return fixTypeVariable(clazz, type);
+        }
+        if (ParameterizedType.class.isInstance(type)) {
+            final ParameterizedType pt = ParameterizedType.class.cast(type);
+            final Type[] actualTypeArguments = pt.getActualTypeArguments();
+            if (actualTypeArguments.length == 1 && Class.class.isInstance(pt.getRawType())
+                && Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType()))
+                && Class.class.cast(pt.getRawType()).getName().startsWith("java.util.")
+                && TypeVariable.class.isInstance(actualTypeArguments[0])) {
+                return new JohnzonParameterizedType(pt.getRawType(), fixTypeVariable(clazz, actualTypeArguments[0]));
+            } else if (actualTypeArguments.length == 2 && Class.class.isInstance(pt.getRawType())
+                && Map.class.isAssignableFrom(Class.class.cast(pt.getRawType()))
+                && Class.class.cast(pt.getRawType()).getName().startsWith("java.util.")
+                && TypeVariable.class.isInstance(actualTypeArguments[1])) {
+                return new JohnzonParameterizedType(pt.getRawType(), actualTypeArguments[0], fixTypeVariable(clazz, actualTypeArguments[1]));
+            }
+        }
+        return type;
+    }
+
+    private Type fixTypeVariable(final Class<?> clazz, final Type type) {
+        final TypeVariable typeVariable = TypeVariable.class.cast(type);
+        if (typeVariable.getGenericDeclaration() == clazz.getSuperclass()) {
+            // try to match generic
+            final TypeVariable<? extends Class<?>>[] typeParameters = clazz.getSuperclass().getTypeParameters();
+            final int idx = asList(typeParameters).indexOf(typeVariable);
+            if (idx >= 0) {
+                final Type genParent = clazz.getGenericSuperclass();
+                if (ParameterizedType.class.isInstance(genParent)) {
+                    final ParameterizedType pt = ParameterizedType.class.cast(genParent);
+                    if (pt.getActualTypeArguments().length == typeParameters.length) {
+                        return pt.getActualTypeArguments()[idx];
+                    }
+                }
+            }
+        }
+        return type;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/8841d755/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAccessMode.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAccessMode.java
index fb85a69..3f1b54c 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAccessMode.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAccessMode.java
@@ -38,7 +38,8 @@ public class FieldAccessMode extends BaseAccessMode {
                 continue;
             }
 
-            readers.put(extractKey(f.getValue(), key), new FieldReader(f.getValue()));
+            final Field field = f.getValue();
+            readers.put(extractKey(field, key), new FieldReader(field, fixType(clazz, field.getGenericType())));
         }
         return readers;
     }
@@ -51,7 +52,9 @@ public class FieldAccessMode extends BaseAccessMode {
             if (isIgnored(key)) {
                 continue;
             }
-            writers.put(extractKey(f.getValue(), key), new FieldWriter(f.getValue()));
+
+            final Field field = f.getValue();
+            writers.put(extractKey(field, key), new FieldWriter(field, fixType(clazz, field.getGenericType())));
         }
         return writers;
     }
@@ -86,17 +89,19 @@ public class FieldAccessMode extends BaseAccessMode {
 
     protected static abstract class FieldDecoratedType implements DecoratedType {
         protected final Field field;
+        protected final Type type;
 
-        public FieldDecoratedType(final Field field) {
+        public FieldDecoratedType(final Field field, final Type type) {
             this.field = field;
             if (!field.isAccessible()) {
                 this.field.setAccessible(true);
             }
+            this.type = type;
         }
 
         @Override
         public Type getType() {
-            return field.getGenericType();
+            return type;
         }
 
         @Override
@@ -106,8 +111,8 @@ public class FieldAccessMode extends BaseAccessMode {
     }
 
     public static class FieldWriter extends FieldDecoratedType implements Writer {
-        public FieldWriter(final Field field) {
-            super(field);
+        public FieldWriter(final Field field, final Type type) {
+            super(field, type);
         }
 
         @Override
@@ -121,8 +126,8 @@ public class FieldAccessMode extends BaseAccessMode {
     }
 
     public static class FieldReader extends FieldDecoratedType  implements Reader {
-        public FieldReader(final Field field) {
-            super(field);
+        public FieldReader(final Field field, final Type type) {
+            super(field, type);
         }
 
         @Override
@@ -133,5 +138,10 @@ public class FieldAccessMode extends BaseAccessMode {
                 throw new MapperException(e);
             }
         }
+
+        @Override
+        public Type getType() {
+            return type;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/8841d755/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java
index d9cee8a..69f1af5 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java
@@ -48,7 +48,7 @@ public class MethodAccessMode extends BaseAccessMode {
                 if (isIgnored(descriptor.getName())) {
                     continue;
                 }
-                readers.put(extractKey(descriptor), new MethodReader(readMethod));
+                readers.put(extractKey(descriptor), new MethodReader(readMethod, fixType(clazz, readMethod.getGenericReturnType())));
             }
         }
         return readers;
@@ -64,11 +64,12 @@ public class MethodAccessMode extends BaseAccessMode {
             }
             final Method writeMethod = descriptor.getWriteMethod();
             if (writeMethod != null) {
-                writers.put(extractKey(descriptor), new MethodWriter(writeMethod));
+                writers.put(extractKey(descriptor), new MethodWriter(writeMethod, fixType(clazz, writeMethod.getGenericParameterTypes()[0])));
             } else if (supportGetterAsWritter
                     && Collection.class.isAssignableFrom(descriptor.getPropertyType())
                     && descriptor.getReadMethod() != null) {
-                writers.put(extractKey(descriptor), new MethodGetterAsWriter(descriptor.getReadMethod()));
+                final Method readMethod = descriptor.getReadMethod();
+                writers.put(extractKey(descriptor), new MethodGetterAsWriter(readMethod, fixType(clazz, readMethod.getGenericReturnType())));
             }
         }
         return writers;
@@ -95,12 +96,19 @@ public class MethodAccessMode extends BaseAccessMode {
 
     protected static abstract class MethodDecoratedType implements DecoratedType {
         protected final Method method;
+        protected final Type type;
 
-        public MethodDecoratedType(final Method method) {
+        public MethodDecoratedType(final Method method, final Type type) {
             this.method = method;
             if (!method.isAccessible()) {
                 method.setAccessible(true);
             }
+            this.type = type;
+        }
+
+        @Override
+        public Type getType() {
+            return type;
         }
 
         @Override
@@ -110,13 +118,8 @@ public class MethodAccessMode extends BaseAccessMode {
     }
 
     public static class MethodWriter extends MethodDecoratedType implements Writer {
-        public MethodWriter(final Method method) {
-            super(method);
-        }
-
-        @Override
-        public Type getType() {
-            return method.getGenericParameterTypes()[0];
+        public MethodWriter(final Method method, final Type type) {
+            super(method, type);
         }
 
         @Override
@@ -130,13 +133,8 @@ public class MethodAccessMode extends BaseAccessMode {
     }
 
     public static class MethodReader extends MethodDecoratedType implements Reader {
-        public MethodReader(final Method method) {
-            super(method);
-        }
-
-        @Override
-        public Type getType() {
-            return method.getGenericReturnType();
+        public MethodReader(final Method method, final Type type) {
+            super(method, type);
         }
 
         @Override
@@ -150,8 +148,8 @@ public class MethodAccessMode extends BaseAccessMode {
     }
 
     private class MethodGetterAsWriter extends MethodReader implements Writer {
-        public MethodGetterAsWriter(final Method readMethod) {
-            super(readMethod);
+        public MethodGetterAsWriter(final Method readMethod, final Type type) {
+            super(readMethod, type);
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/8841d755/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperGenericsTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperGenericsTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperGenericsTest.java
index 7e6f30d..42245fb 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperGenericsTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperGenericsTest.java
@@ -19,41 +19,59 @@
 package org.apache.johnzon.mapper;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
+import java.io.StringReader;
 import java.util.List;
 import java.util.Map;
 
+import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonMap;
 import static org.junit.Assert.assertEquals;
+
+@RunWith(Parameterized.class)
 public class MapperGenericsTest {
+    @Parameterized.Parameter
+    public String accessMode;
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<String> modes() {
+        return asList("field", "method", "both", "strict-method");
+    }
+
     @Test
     public void base() {
-        final Mapper mapper = new MapperBuilder().setAccessModeName("field").build();
+        final Mapper mapper = new MapperBuilder().setAccessModeName(accessMode).build();
 
         final Foo foo = new Foo();
         foo.name = "n";
         final Concrete concrete = new Concrete();
         concrete.value = foo;
 
-        assertEquals("{\"value\":{\"name\":\"n\"}}", mapper.writeObjectAsString(concrete));
+        final String asString = mapper.writeObjectAsString(concrete);
+        assertEquals("{\"value\":{\"name\":\"n\"}}", asString);
+        assertEquals("n", Concrete.class.cast(mapper.readObject(new StringReader(asString), Concrete.class)).getValue().name);
     }
 
     @Test
     public void list() {
-        final Mapper mapper = new MapperBuilder().setAccessModeName("field").build();
+        final Mapper mapper = new MapperBuilder().setAccessModeName(accessMode).build();
 
         final Foo foo = new Foo();
         foo.name = "n";
         final ConcreteList concrete = new ConcreteList();
         concrete.value = singletonList(foo);
 
-        assertEquals("{\"value\":[{\"name\":\"n\"}]}", mapper.writeObjectAsString(concrete));
+        final String asString = mapper.writeObjectAsString(concrete);
+        assertEquals("{\"value\":[{\"name\":\"n\"}]}", asString);
+        assertEquals("n", ConcreteList.class.cast(mapper.readObject(new StringReader(asString), ConcreteList.class)).getValue().iterator().next().name);
     }
 
     @Test
     public void map() {
-        final Mapper mapper = new MapperBuilder().setAccessModeName("field").build();
+        final Mapper mapper = new MapperBuilder().setAccessModeName(accessMode).build();
 
         final Foo foo = new Foo();
         foo.name = "n";
@@ -61,12 +79,18 @@ public class MapperGenericsTest {
         final ConcreteMap concrete = new ConcreteMap();
         concrete.value = singletonMap("k", foo);
 
-        assertEquals("{\"value\":{\"k\":{\"name\":\"n\"}}}", mapper.writeObjectAsString(concrete));
+        final String asString = mapper.writeObjectAsString(concrete);
+        assertEquals("{\"value\":{\"k\":{\"name\":\"n\"}}}", asString);
+        assertEquals(concrete.value, ConcreteMap.class.cast(mapper.readObject(new StringReader(asString), ConcreteMap.class)).getValue());
     }
 
     public static abstract class Base<T> {
         protected T value;
 
+        public void setValue(final T value) {
+            this.value = value;
+        }
+
         public T getValue() {
             return value;
         }
@@ -75,6 +99,10 @@ public class MapperGenericsTest {
     public static abstract class BaseList<T> {
         protected List<T> value;
 
+        public void setValue(final List<T> value) {
+            this.value = value;
+        }
+
         public List<T> getValue() {
             return value;
         }
@@ -83,6 +111,10 @@ public class MapperGenericsTest {
     public static abstract class BaseMap<T> {
         protected Map<String, T> value;
 
+        public void setValue(final Map<String, T> value) {
+            this.value = value;
+        }
+
         public Map<String, T> getValue() {
             return value;
         }
@@ -99,5 +131,32 @@ public class MapperGenericsTest {
 
     public static class Foo {
         private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(final String name) {
+            this.name = name;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Foo foo = (Foo) o;
+            return name.equals(foo.name);
+
+        }
+
+        @Override
+        public int hashCode() {
+            return name.hashCode();
+        }
     }
 }