Repository: johnzon
Updated Branches:
  refs/heads/master a25adfaee -> fea9ac9f0


JOHNZON-155 better support of TypeVariables


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

Branch: refs/heads/master
Commit: fea9ac9f0b4e017378f67f57985a9621ce94457a
Parents: a25adfa
Author: Romain Manni-Bucau <rmannibu...@apache.org>
Authored: Sun Feb 4 17:15:12 2018 +0100
Committer: Romain Manni-Bucau <rmannibu...@apache.org>
Committed: Sun Feb 4 17:15:31 2018 +0100

----------------------------------------------------------------------
 johnzon-mapper/pom.xml                          |  5 +
 .../org/apache/johnzon/mapper/Mappings.java     | 28 +++---
 .../johnzon/mapper/reflection/Generics.java     | 98 ++++++++++++++++++++
 .../org/apache/johnzon/mapper/GenericsTest.java | 43 +++++++++
 .../src/test/java/org/superbiz/Model.java       | 29 ++++++
 .../src/test/java/org/superbiz/ModelBase.java   | 34 +++++++
 .../test/java/org/superbiz/ModelSuperBase.java  | 53 +++++++++++
 .../java/org/superbiz/ModelSuperSuperBase.java  | 48 ++++++++++
 8 files changed, 326 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/pom.xml
----------------------------------------------------------------------
diff --git a/johnzon-mapper/pom.xml b/johnzon-mapper/pom.xml
index 2104a3f..d4f0391 100644
--- a/johnzon-mapper/pom.xml
+++ b/johnzon-mapper/pom.xml
@@ -34,6 +34,11 @@
       <artifactId>johnzon-core</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>3.6</version>
+    </dependency>
 
     <dependency>
       <groupId>com.github.stefanbirkner</groupId>

http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
----------------------------------------------------------------------
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
index 44e13f1..8a04515 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
@@ -52,6 +52,7 @@ import java.util.concurrent.ConcurrentMap;
 
 import static java.util.Arrays.asList;
 import static org.apache.johnzon.mapper.reflection.Converters.matches;
+import static org.apache.johnzon.mapper.reflection.Generics.resolve;
 
 public class Mappings {
     public static class ClassMapping {
@@ -361,8 +362,8 @@ public class Mappings {
         Comparator<String> fieldComparator = 
accessMode.fieldComparator(inClazz);
         fieldComparator = fieldComparator == null ? config.getAttributeOrder() 
: fieldComparator;
 
-        final Map<String, Getter> getters = fieldComparator == null ? 
newOrderedMap(Getter.class) : new TreeMap<String, Getter>(fieldComparator);
-        final Map<String, Setter> setters = fieldComparator == null ? 
newOrderedMap(Setter.class) : new TreeMap<String, Setter>(fieldComparator);
+        final Map<String, Getter> getters = fieldComparator == null ? 
newOrderedMap(Getter.class) : new TreeMap<>(fieldComparator);
+        final Map<String, Setter> setters = fieldComparator == null ? 
newOrderedMap(Setter.class) : new TreeMap<>(fieldComparator);
 
         final Map<String, AccessMode.Reader> readers = 
accessMode.findReaders(clazz);
         final Map<String, AccessMode.Writer> writers = 
accessMode.findWriters(clazz);
@@ -372,13 +373,13 @@ public class Mappings {
             final JohnzonVirtualObjects virtualObjects = 
clazz.getAnnotation(JohnzonVirtualObjects.class);
             if (virtualObjects != null) {
                 for (final JohnzonVirtualObject virtualObject : 
virtualObjects.value()) {
-                    handleVirtualObject(virtualFields, virtualObject, getters, 
setters, readers, writers, copyDate);
+                    handleVirtualObject(virtualFields, virtualObject, getters, 
setters, readers, writers, copyDate, clazz);
                 }
             }
 
             final JohnzonVirtualObject virtualObject = 
clazz.getAnnotation(JohnzonVirtualObject.class);
             if (virtualObject != null) {
-                handleVirtualObject(virtualFields, virtualObject, getters, 
setters, readers, writers, copyDate);
+                handleVirtualObject(virtualFields, virtualObject, getters, 
setters, readers, writers, copyDate, clazz);
             }
         }
 
@@ -387,7 +388,7 @@ public class Mappings {
             if (virtualFields.contains(key)) {
                 continue;
             }
-            addGetterIfNeeded(getters, key, reader.getValue(), copyDate);
+            addGetterIfNeeded(getters, key, reader.getValue(), copyDate, 
clazz);
         }
 
         for (final Map.Entry<String, AccessMode.Writer> writer : 
writers.entrySet()) {
@@ -395,7 +396,7 @@ public class Mappings {
             if (virtualFields.contains(key)) {
                 continue;
             }
-            addSetterIfNeeded(setters, key, writer.getValue(), copyDate);
+            addSetterIfNeeded(setters, key, writer.getValue(), copyDate, 
clazz);
         }
 
         final Method anyGetter = accessMode.findAnyGetter(clazz);
@@ -435,7 +436,8 @@ public class Mappings {
     private void addSetterIfNeeded(final Map<String, Setter> setters,
                                    final String key,
                                    final AccessMode.Writer value,
-                                   final boolean copyDate) {
+                                   final boolean copyDate,
+                                   final Class<?> rootClass) {
         final JohnzonIgnore writeIgnore = 
value.getAnnotation(JohnzonIgnore.class);
         if (writeIgnore == null || writeIgnore.minVersion() >= 0) {
             if (key.equals("metaClass")) {
@@ -444,7 +446,7 @@ public class Mappings {
             final Type param = value.getType();
             final Class<?> returnType = Class.class.isInstance(param) ? 
Class.class.cast(param) : null;
             final Setter setter = new Setter(
-                    value, isPrimitive(param), returnType != null && 
returnType.isArray(), param,
+                    value, isPrimitive(param), returnType != null && 
returnType.isArray(), resolve(param, rootClass),
                     findConverter(copyDate, value), 
value.findObjectConverterReader(),
                     writeIgnore != null ? writeIgnore.minVersion() : -1);
             setters.put(key, setter);
@@ -454,7 +456,8 @@ public class Mappings {
     private void addGetterIfNeeded(final Map<String, Getter> getters,
                                    final String key,
                                    final AccessMode.Reader value,
-                                   final boolean copyDate) {
+                                   final boolean copyDate,
+                                   final Class<?> rootClass) {
         final JohnzonIgnore readIgnore = 
value.getAnnotation(JohnzonIgnore.class);
         final JohnzonIgnoreNested ignoreNested = 
value.getAnnotation(JohnzonIgnoreNested.class);
         if (readIgnore == null || readIgnore.minVersion() >= 0) {
@@ -480,7 +483,8 @@ public class Mappings {
                                      final Map<String, Setter> setters,
                                      final Map<String, AccessMode.Reader> 
readers,
                                      final Map<String, AccessMode.Writer> 
writers,
-                                     final boolean copyDate) {
+                                     final boolean copyDate,
+                                     final Class<?> rootClazz) {
         final String[] path = o.path();
         if (path.length < 1) {
             throw new IllegalArgumentException("@JohnzonVirtualObject need a 
path");
@@ -500,13 +504,13 @@ public class Mappings {
             if (f.read()) {
                 final AccessMode.Reader reader = readers.get(name);
                 if (reader != null) {
-                    addGetterIfNeeded(objectGetters, name, reader, copyDate);
+                    addGetterIfNeeded(objectGetters, name, reader, copyDate, 
rootClazz);
                 }
             }
             if (f.write()) {
                 final AccessMode.Writer writer = writers.get(name);
                 if (writer != null) {
-                    addSetterIfNeeded(objectSetters, name, writer, copyDate);
+                    addSetterIfNeeded(objectSetters, name, writer, copyDate, 
rootClazz);
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Generics.java
----------------------------------------------------------------------
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Generics.java
 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Generics.java
new file mode 100644
index 0000000..31ad7f2
--- /dev/null
+++ 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Generics.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper.reflection;
+
+import static java.util.Arrays.asList;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.ArrayList;
+import java.util.Collection;
+
+public final class Generics {
+    private Generics() {
+        // no-op
+    }
+
+    // todo: this piece of code needs to be enhanced a lot:
+    // - better handling of the hierarchy
+    // - wildcard support?
+    // - cycle handling (Foo<Foo>)
+    // - ....
+    public static Type resolve(final Type value, final Class<?> rootClass) {
+        if (TypeVariable.class.isInstance(value)) {
+            return resolveTypeVariable(value, rootClass);
+        }
+        if (ParameterizedType.class.isInstance(value)) {
+            return resolveParameterizedType(value, rootClass);
+        }
+        return value;
+    }
+
+    private static Type resolveParameterizedType(final Type value, final 
Class<?> rootClass) {
+        Collection<Type> args = null;
+        final ParameterizedType parameterizedType = 
ParameterizedType.class.cast(value);
+        int index = 0;
+        for (final Type arg : parameterizedType.getActualTypeArguments()) {
+            final Type type = resolve(arg, rootClass);
+            if (type != arg) {
+                if (args == null) {
+                    args = new ArrayList<>();
+                    if (index > 0) {
+                        
args.addAll(asList(parameterizedType.getActualTypeArguments()).subList(0, index 
+ 1));
+                    }
+                }
+            }
+            if (args != null) {
+                args.add(arg);
+            }
+            index++;
+        }
+        if (args != null) {
+            return new 
JohnzonParameterizedType(parameterizedType.getRawType(), args.toArray(new 
Type[args.size()]));
+        }
+        return value;
+    }
+
+    // for now the level is hardcoded to 2 with generic > concrete
+    private static Type resolveTypeVariable(final Type value, final Class<?> 
rootClass) {
+        final TypeVariable<?> tv = TypeVariable.class.cast(value);
+        final Type parent = rootClass.getGenericSuperclass();
+        if (ParameterizedType.class.isInstance(parent)) {
+            final ParameterizedType parentPt = 
ParameterizedType.class.cast(parent);
+            if (Class.class.isInstance(parentPt.getRawType())) {
+                final Type grandParent = 
Class.class.cast(parentPt.getRawType()).getGenericSuperclass();
+                if (ParameterizedType.class.isInstance(grandParent)) {
+                    final ParameterizedType grandParentPt = 
ParameterizedType.class.cast(grandParent);
+                    final Type[] grandParentArgs = 
grandParentPt.getActualTypeArguments();
+                    int index = 0;
+                    final String name = tv.getName();
+                    for (final Type t : grandParentArgs) {
+                        if (TypeVariable.class.isInstance(t) && 
TypeVariable.class.cast(t).getName().equals(name)) {
+                            return parentPt.getActualTypeArguments()[index];
+                        }
+                        index++;
+                    }
+                }
+            }
+        }
+        return value;
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GenericsTest.java
----------------------------------------------------------------------
diff --git 
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GenericsTest.java 
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GenericsTest.java
new file mode 100644
index 0000000..a5b0577
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GenericsTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.superbiz.Model;
+
+public class GenericsTest {
+    @Test
+    public void typeVariableMultiLevel() {
+        final String input = 
"{\"aalist\":[{\"detail\":\"something2\",\"name\":\"Na2\"}]," +
+                
"\"childA\":{\"detail\":\"something\",\"name\":\"Na\"},\"childB\":{}}";
+        final Mapper mapper = new 
MapperBuilder().setAttributeOrder(String::compareTo).build();
+        final Model model = mapper.readObject(input, Model.class);
+        assertNotNull(model.getChildA());
+        assertNotNull(model.getChildB());
+        assertNotNull(model.getAalist());
+        assertEquals("something", model.getChildA().detail);
+        assertEquals("Na", model.getChildA().name);
+        assertEquals(1, model.getAalist().size());
+        assertEquals("something2", model.getAalist().iterator().next().detail);
+        assertEquals(input, mapper.writeObjectAsString(model));
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/superbiz/Model.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/superbiz/Model.java 
b/johnzon-mapper/src/test/java/org/superbiz/Model.java
new file mode 100644
index 0000000..1b3a077
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/superbiz/Model.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.superbiz;
+
+public class Model extends ModelBase<Model.ChildA, Model.ChildB> {
+    public static class ChildA extends ModelSuperBase.ChildA {
+        public String detail;
+    }
+
+    public static class ChildB extends ModelSuperBase.ChildB {
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/superbiz/ModelBase.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/superbiz/ModelBase.java 
b/johnzon-mapper/src/test/java/org/superbiz/ModelBase.java
new file mode 100644
index 0000000..d7668df
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/superbiz/ModelBase.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.superbiz;
+
+import java.util.Collection;
+
+public abstract class ModelBase<A extends ModelSuperBase.ChildA, B extends 
ModelSuperBase.ChildB>
+        extends ModelSuperBase<A, B> {
+    private Collection<A> aalist;
+
+    public Collection<A> getAalist() {
+        return aalist;
+    }
+
+    public void setAalist(final Collection<A> aalist) {
+        this.aalist = aalist;
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/superbiz/ModelSuperBase.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/superbiz/ModelSuperBase.java 
b/johnzon-mapper/src/test/java/org/superbiz/ModelSuperBase.java
new file mode 100644
index 0000000..c39d80b
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/superbiz/ModelSuperBase.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.superbiz;
+
+public abstract class ModelSuperBase<A extends ModelSuperBase.ChildA, B 
extends ModelSuperBase.ChildB>
+        extends ModelSuperSuperBase<A, B> {
+    public static class ChildA extends ModelSuperSuperBase.ChildA {
+
+    }
+
+    public static class ChildB extends ModelSuperSuperBase.ChildB {
+
+    }
+
+    private A childA;
+    private B childB;
+
+    @Override
+    public A getChildA() {
+        return childA;
+    }
+
+    @Override
+    public void setChildA(A childA) {
+        this.childA = childA;
+    }
+
+    @Override
+    public B getChildB() {
+        return childB;
+    }
+
+    @Override
+    public void setChildB(B childB) {
+        this.childB = childB;
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/superbiz/ModelSuperSuperBase.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/superbiz/ModelSuperSuperBase.java 
b/johnzon-mapper/src/test/java/org/superbiz/ModelSuperSuperBase.java
new file mode 100644
index 0000000..72980b6
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/superbiz/ModelSuperSuperBase.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.superbiz;
+
+public abstract class ModelSuperSuperBase<A extends 
ModelSuperSuperBase.ChildA, B extends ModelSuperSuperBase.ChildB> {
+    public static class ChildA {
+        public String name;
+    }
+
+    public static class ChildB {
+
+    }
+
+    private A childA;
+    private B childB;
+
+    public A getChildA() {
+        return childA;
+    }
+
+    public void setChildA(A childA) {
+        this.childA = childA;
+    }
+
+    public B getChildB() {
+        return childB;
+    }
+
+    public void setChildB(B childB) {
+        this.childB = childB;
+    }
+}

Reply via email to