This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git


The following commit(s) were added to refs/heads/master by this push:
     new 4523937  JOHNZON-262 resolved first generics level for parameterized 
types
4523937 is described below

commit 452393789070e24a9dd92bb2e57dcfa9ad988abd
Author: Romain Manni-Bucau <[email protected]>
AuthorDate: Fri Aug 16 15:38:03 2019 +0200

    JOHNZON-262 resolved first generics level for parameterized types
---
 .../org/apache/johnzon/jsonb/GenericsTest.java     | 36 ++++++++
 .../java/org/apache/johnzon/mapper/Mappings.java   | 99 +++++++++++++++-------
 2 files changed, 106 insertions(+), 29 deletions(-)

diff --git 
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/GenericsTest.java 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/GenericsTest.java
new file mode 100644
index 0000000..7f68b72
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/GenericsTest.java
@@ -0,0 +1,36 @@
+package org.apache.johnzon.jsonb;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.johnzon.jsonb.model.Holder;
+import org.apache.johnzon.jsonb.test.JsonbRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class GenericsTest {
+    @Rule
+    public final JsonbRule jsonb = new JsonbRule();
+
+    @Test
+    public void genericModel() {
+        final String json = jsonb.toJson(new StillGeneric<String>() {{ 
setInstance("Test String"); } });
+        assertEquals("{\"instance\":\"Test String\"}", json);
+        final StillGeneric<String> deserialized = jsonb.fromJson(json, new 
StillGeneric<String>() {
+        }.getClass().getGenericSuperclass());
+        assertEquals("Test String", deserialized.getInstance());
+    }
+
+    public static class StillGeneric<T> implements Holder<T> {
+        private T value;
+
+        @Override
+        public T getInstance() {
+            return value;
+        }
+
+        @Override
+        public void setInstance(final T instance) {
+            this.value = instance;
+        }
+    }
+}
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 1ecbe44..142fa3f 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
@@ -18,14 +18,10 @@
  */
 package org.apache.johnzon.mapper;
 
-import org.apache.johnzon.mapper.access.AccessMode;
-import org.apache.johnzon.mapper.access.MethodAccessMode;
-import org.apache.johnzon.mapper.converter.DateWithCopyConverter;
-import org.apache.johnzon.mapper.converter.EnumConverter;
-import org.apache.johnzon.mapper.internal.AdapterKey;
-import org.apache.johnzon.mapper.internal.ConverterAdapter;
-import org.apache.johnzon.mapper.reflection.Generics;
-import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyMap;
+import static org.apache.johnzon.mapper.reflection.Converters.matches;
+import static org.apache.johnzon.mapper.reflection.Generics.resolve;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Array;
@@ -33,6 +29,7 @@ import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.Collection;
@@ -56,9 +53,14 @@ import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 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;
+import org.apache.johnzon.mapper.access.AccessMode;
+import org.apache.johnzon.mapper.access.MethodAccessMode;
+import org.apache.johnzon.mapper.converter.DateWithCopyConverter;
+import org.apache.johnzon.mapper.converter.EnumConverter;
+import org.apache.johnzon.mapper.internal.AdapterKey;
+import org.apache.johnzon.mapper.internal.ConverterAdapter;
+import org.apache.johnzon.mapper.reflection.Generics;
+import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
 
 public class Mappings {
     public static class ClassMapping {
@@ -363,8 +365,17 @@ public class Mappings {
     }
 
     public ClassMapping findOrCreateClassMapping(final Type clazz) {
+        return doFindOrCreateClassMapping(clazz, emptyMap());
+    }
+
+    private ClassMapping doFindOrCreateClassMapping(final Type clazz, final 
Map<Type, Type> args) {
         ClassMapping classMapping = classes.get(clazz);
         if (classMapping == null) {
+            if (ParameterizedType.class.isInstance(clazz)) {
+                final ParameterizedType pt = 
ParameterizedType.class.cast(clazz);
+                final ClassMapping mapping = 
doFindOrCreateClassMapping(pt.getRawType(), toResolvedTypes(pt));
+                return putOrGetClassMapping(clazz, mapping);
+            }
             if (!Class.class.isInstance(clazz)) {
                 return null;
             }
@@ -372,24 +383,52 @@ public class Mappings {
             if (Map.class.isAssignableFrom(asClass) || asClass.isInterface()) {
                 final Class<?> mapping = 
config.getInterfaceImplementationMapping().get(clazz);
                 if (mapping != null) {
-                    classMapping = createClassMapping(mapping);
+                    classMapping = createClassMapping(mapping, args);
                 } else if (asClass.getName().startsWith("java.")) { // we'll 
not be able to map it with pojo rules
                     return null;
                 } else { // assume that it can be written with pojo rules but 
not deserialized
-                    classMapping = createClassMapping(asClass);
+                    classMapping = createClassMapping(asClass, args);
                 }
             } else {
-                classMapping = createClassMapping(asClass);
+                classMapping = createClassMapping(asClass, args);
             }
-            final ClassMapping existing = classes.putIfAbsent(clazz, 
classMapping);
-            if (existing != null) {
-                classMapping = existing;
+            classMapping = putOrGetClassMapping(clazz, classMapping);
+        }
+        return classMapping;
+    }
+
+    private Map<Type, Type> toResolvedTypes(final Type clazz) {
+        if (ParameterizedType.class.isInstance(clazz)) {
+            final ParameterizedType parameterizedType = 
ParameterizedType.class.cast(clazz);
+            if (!Class.class.isInstance(parameterizedType.getRawType())) {
+                return emptyMap(); // not yet supported
+            }
+            final Class<?> raw = 
Class.class.cast(parameterizedType.getRawType());
+            final Type[] arguments = 
parameterizedType.getActualTypeArguments();
+            if (arguments.length > 0) {
+                final TypeVariable<? extends Class<?>>[] parameters = 
raw.getTypeParameters();
+                final Map<Type, Type> map = new HashMap<>(parameters.length);
+                for (int i = 0; i < parameters.length && i < arguments.length; 
i++) {
+                    map.put(parameters[i], arguments[i]);
+                }
+                return map;
             }
         }
+        return emptyMap();
+    }
+
+    private ClassMapping putOrGetClassMapping(final Type clazz, final 
ClassMapping classMapping) {
+        if (classMapping == null) {
+            return null;
+        }
+        final ClassMapping existing = classes.putIfAbsent(clazz, classMapping);
+        if (existing != null) {
+            return existing;
+        }
         return classMapping;
     }
 
-    protected ClassMapping createClassMapping(final Class<?> inClazz) {
+    protected ClassMapping createClassMapping(final Class<?> inClazz, final 
Map<Type, Type> resolvedTypes) {
         boolean copyDate = false;
         for (final Class<?> itf : inClazz.getInterfaces()) {
             if 
("org.apache.openjpa.enhance.PersistenceCapable".equals(itf.getName())) {
@@ -430,7 +469,7 @@ public class Mappings {
             if (virtualFields.contains(key)) {
                 continue;
             }
-            addGetterIfNeeded(getters, key, reader.getValue(), copyDate, 
clazz);
+            addGetterIfNeeded(getters, key, reader.getValue(), copyDate, 
resolvedTypes);
         }
 
         for (final Map.Entry<String, AccessMode.Writer> writer : 
writers.entrySet()) {
@@ -438,7 +477,7 @@ public class Mappings {
             if (virtualFields.contains(key)) {
                 continue;
             }
-            addSetterIfNeeded(setters, key, writer.getValue(), copyDate, 
clazz);
+            addSetterIfNeeded(setters, key, writer.getValue(), copyDate, 
clazz, resolvedTypes);
         }
 
         final Method anyGetter = accessMode.findAnyGetter(clazz);
@@ -473,20 +512,21 @@ public class Mappings {
     }
 
     private <T> Map<String, T> newOrderedMap(final Class<T> value) {
-        return config.getAttributeOrder() != null ? new TreeMap<String, 
T>(config.getAttributeOrder()) : new HashMap<String, T>();
+        return config.getAttributeOrder() != null ? new 
TreeMap<>(config.getAttributeOrder()) : new HashMap<>();
     }
 
     private void addSetterIfNeeded(final Map<String, Setter> setters,
                                    final String key,
                                    final AccessMode.Writer value,
                                    final boolean copyDate,
-                                   final Class<?> rootClass) {
+                                   final Class<?> rootClass,
+                                   final Map<Type, Type> resolvedTypes) {
         final JohnzonIgnore writeIgnore = 
value.getAnnotation(JohnzonIgnore.class);
         if (writeIgnore == null || writeIgnore.minVersion() >= 0) {
             if (key.equals("metaClass")) {
                 return;
             }
-            final Type param = value.getType();
+            final Type param = resolvedTypes.getOrDefault(value.getType(), 
value.getType());
             final Class<?> returnType = Class.class.isInstance(param) ? 
Class.class.cast(param) : null;
             final Setter setter = new Setter(
                     value, isPrimitive(param),
@@ -502,14 +542,15 @@ public class Mappings {
                                    final String key,
                                    final AccessMode.Reader value,
                                    final boolean copyDate,
-                                   final Class<?> rootClass) {
+                                   final Map<Type, Type> resolvedTypes) {
         final JohnzonIgnore readIgnore = 
value.getAnnotation(JohnzonIgnore.class);
         final JohnzonIgnoreNested ignoreNested = 
value.getAnnotation(JohnzonIgnoreNested.class);
         if (readIgnore == null || readIgnore.minVersion() >= 0) {
-            final Class<?> returnType = 
Class.class.isInstance(value.getType()) ? Class.class.cast(value.getType()) : 
null;
-            final ParameterizedType pt = 
ParameterizedType.class.isInstance(value.getType()) ? 
ParameterizedType.class.cast(value.getType()) : null;
+            final Type type = resolvedTypes.getOrDefault(value.getType(), 
value.getType());
+            final Class<?> returnType = Class.class.isInstance(type) ? 
Class.class.cast(type) : null;
+            final ParameterizedType pt = 
ParameterizedType.class.isInstance(type) ? ParameterizedType.class.cast(type) : 
null;
             final Getter getter = new Getter(value, returnType == 
Object.class, isPrimitive(returnType),
-                    (returnType != null && returnType.isArray()) || 
GenericArrayType.class.isInstance(value.getType()),
+                    (returnType != null && returnType.isArray()) || 
GenericArrayType.class.isInstance(type),
                     (pt != null && 
Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
                             || (returnType != null && 
Collection.class.isAssignableFrom(returnType)),
                     (pt != null && 
Map.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
@@ -549,13 +590,13 @@ public class Mappings {
             if (f.read()) {
                 final AccessMode.Reader reader = readers.get(name);
                 if (reader != null) {
-                    addGetterIfNeeded(objectGetters, name, reader, copyDate, 
rootClazz);
+                    addGetterIfNeeded(objectGetters, name, reader, copyDate, 
emptyMap());
                 }
             }
             if (f.write()) {
                 final AccessMode.Writer writer = writers.get(name);
                 if (writer != null) {
-                    addSetterIfNeeded(objectSetters, name, writer, copyDate, 
rootClazz);
+                    addSetterIfNeeded(objectSetters, name, writer, copyDate, 
rootClazz, emptyMap());
                 }
             }
         }

Reply via email to