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

commit c57060f1cec2cf66373e51d0a2ee21edcdf40a7c
Author: Xavier Dury <[email protected]>
AuthorDate: Wed Apr 3 17:22:58 2019 +0200

    Clarified Types.findParameterizedType()
---
 .../main/java/org/apache/johnzon/core/Types.java   | 71 ++++++++++++++--------
 .../java/org/apache/johnzon/core/TypesTest.java    | 58 +++++++++++++-----
 2 files changed, 88 insertions(+), 41 deletions(-)

diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java
index 037dc93..3e00e1d 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java
@@ -24,30 +24,60 @@ import java.lang.reflect.TypeVariable;
 import java.util.Arrays;
 
 public class Types {
-    
-    private static Type[] resolveArgumentTypes(Class<?> type, Class<?> 
superType) {
-        if (superType.equals(type)) {
+
+    /**
+     * This method helps reconstructing the resulting ParameterizedType for a 
specific generic type across its hierarchy.
+     *
+     * Example: Let's create some interface Converter[X, Y] and try to 
determine the actual type arguments for that
+     * interface from classes/interfaces which inherit from it (directly or 
indirectly).
+     *
+     * <ul>
+     * <li>Converter[X, Y] will yield Converter[X, Y]</li>
+     * <li>StringConverter[Z] extends Converter[Z, String] will yield 
Converter[Z, String]</li>
+     * <li>AbstractStringConverter[A] implements StringConverter[A] will yield 
Converter[A, String]</li>
+     * <li>UUIDStringConverter extends AbstractStringConverter[UUID] will 
yield Converter[UUID, String]</li>
+     * </ul>
+     *
+     * For the last example (UUIDStringConverter), nowhere in its hierarchy is 
a type directly implementing
+     * Converter[UUID, String] but this method is capable of reconstructing 
that information.
+     */
+    public static ParameterizedType findParameterizedType(Class<?> klass, 
Class<?> parameterizedClass) {
+        return new ParameterizedTypeImpl(parameterizedClass, 
resolveArgumentTypes(klass, parameterizedClass));
+    }
+
+    private static Type[] resolveArgumentTypes(Type type, Class<?> 
parameterizedClass) {
+        if (type instanceof Class<?>) {
+            return resolveArgumentTypes((Class<?>) type, parameterizedClass);
+        }
+        if (type instanceof ParameterizedType) {
+            return resolveArgumentTypes((ParameterizedType) type, 
parameterizedClass);
+        }
+        throw new IllegalArgumentException("Cannot resolve argument types from 
" + type.getClass().getSimpleName());
+    }
+
+    private static Type[] resolveArgumentTypes(Class<?> type, Class<?> 
parameterizedClass) {
+        if (parameterizedClass.equals(type)) {
             // May return Class[] instead of Type[], so copy it as a Type[] to 
avoid
             // problems in visit(ParameterizedType)
-            return Arrays.copyOf(type.getTypeParameters(), 
superType.getTypeParameters().length, Type[].class);
+            return Arrays.copyOf(type.getTypeParameters(), 
parameterizedClass.getTypeParameters().length, Type[].class);
         }
-        if (type.getSuperclass() != null && 
superType.isAssignableFrom(type.getSuperclass())) {
-            return resolveArgumentTypes(type.getGenericSuperclass(), 
superType);
+        if (type.getSuperclass() != null && 
parameterizedClass.isAssignableFrom(type.getSuperclass())) {
+            return resolveArgumentTypes(type.getGenericSuperclass(), 
parameterizedClass);
         }
         Class<?>[] interfaces = type.getInterfaces();
         Type[] genericInterfaces = type.getGenericInterfaces();
         for (int i = 0; i < interfaces.length; i++) {
-            if (superType.isAssignableFrom(interfaces[i])) {
-                return resolveArgumentTypes(genericInterfaces[i], superType);
+            if (parameterizedClass.isAssignableFrom(interfaces[i])) {
+                return resolveArgumentTypes(genericInterfaces[i], 
parameterizedClass);
             }
         }
-        throw new IllegalArgumentException(String.format("%s is not assignable 
from %s", type, superType));
+        throw new IllegalArgumentException(String.format("%s is not assignable 
from %s", type, parameterizedClass));
     }
-    
-    private static Type[] resolveArgumentTypes(ParameterizedType type, 
Class<?> superType) {
+
+    private static Type[] resolveArgumentTypes(ParameterizedType type, 
Class<?> parameterizedClass) {
         Class<?> rawType = (Class<?>) type.getRawType(); // always a Class
         TypeVariable<?>[] typeVariables = rawType.getTypeParameters();
-        Type[] types = resolveArgumentTypes(rawType, superType);
+        Type[] types = resolveArgumentTypes(rawType, parameterizedClass);
         for (int i = 0; i < types.length; i++) {
             if (types[i] instanceof TypeVariable<?>) {
                 TypeVariable<?> typeVariable = (TypeVariable<?>) types[i];
@@ -61,20 +91,6 @@ public class Types {
         return types;
     }
 
-    private static Type[] resolveArgumentTypes(Type type, Class<?> superClass) 
{
-        if (type instanceof Class<?>) {
-            return resolveArgumentTypes((Class<?>) type, superClass);
-        }
-        if (type instanceof ParameterizedType) {
-            return resolveArgumentTypes((ParameterizedType) type, superClass);
-        }
-        throw new IllegalArgumentException("Cannot resolve argument types from 
" + type.getClass().getSimpleName());
-    }
-
-    public static ParameterizedType findParameterizedType(Type type, Class<?> 
superClass) {
-        return new ParameterizedTypeImpl(superClass, 
resolveArgumentTypes(type, superClass));
-    }
-
     private Types() {
         // no-op
     }
@@ -84,7 +100,7 @@ public class Types {
         private final Type rawType;
         private final Type[] arguments;
 
-        public ParameterizedTypeImpl(Type rawType, Type... arguments) {
+        ParameterizedTypeImpl(Type rawType, Type... arguments) {
             this.rawType = rawType;
             this.arguments = arguments;
         }
@@ -104,5 +120,6 @@ public class Types {
             return arguments;
         }
 
+        // TODO equals, hashcode, toString if needed
     }
 }
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java 
b/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java
index 8e1fc20..d7ff83d 100644
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java
@@ -18,31 +18,61 @@
  */
 package org.apache.johnzon.core;
 
+import org.junit.Assert;
+import org.junit.Test;
+
 import java.io.Serializable;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
-import java.util.Arrays;
-
-import org.junit.Assert;
-import org.junit.Test;
+import java.lang.reflect.TypeVariable;
+import java.util.UUID;
 
 public class TypesTest {
     
-    interface GenericInterface<X, Y> {}
+    interface Converter<From, To> {}
     
-    interface PartialInterface<X> extends Serializable, 
GenericInterface<Integer, X> {}
+    interface ToStringConverter<X> extends Serializable, Converter<X, String> 
{}
     
-    static abstract class AbstractClass<Z, Y> implements PartialInterface<Y> {}
+    static abstract class AbstractToStringConverter<NotUsed, Z> implements 
ToStringConverter<Z> {}
     
-    static class ConcreteClass extends AbstractClass<String, Boolean> {}
+    static class UUIDToStringConverter extends AbstractToStringConverter<Void, 
UUID> {}
     
     @Test
     public void test() {
-        ParameterizedType parameterizedType = 
Types.findParameterizedType(ConcreteClass.class, GenericInterface.class);
-        
-        
Assert.assertTrue(Arrays.deepEquals(parameterizedType.getActualTypeArguments(), 
new Type[] {
-                Integer.class,
-                Boolean.class
-        }));
+        assertTypeParameters(Converter.class, Converter.class, 
variable("From"), variable("To"));
+        assertTypeParameters(ToStringConverter.class, Converter.class, 
variable("X"), String.class);
+        assertTypeParameters(AbstractToStringConverter.class, Converter.class, 
variable("Z"), String.class);
+        assertTypeParameters(UUIDToStringConverter.class, Converter.class, 
UUID.class, String.class);
+    }
+
+    private static void assertTypeParameters(Class<?> klass, Class<?> 
parameterizedClass, Type... types) {
+        ParameterizedType parameterizedType = 
Types.findParameterizedType(klass, parameterizedClass);
+        Assert.assertNotNull(parameterizedType);
+        Assert.assertEquals(parameterizedType.getRawType(), 
parameterizedClass);
+        Assert.assertArrayEquals(types, 
parameterizedType.getActualTypeArguments());
+    }
+
+    private static Type variable(String name) {
+        return new SimplifiedTypeVariable(name);
+    }
+
+    // Serves as a placeholder to hold variable name (and not reimplementing 
the whole TypeVariable)
+    private static class SimplifiedTypeVariable implements Type {
+
+        private final String name;
+
+        public SimplifiedTypeVariable(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public int hashCode() {
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof TypeVariable<?> && ((TypeVariable<?>) 
obj).getName().equals(this.name);
+        }
     }
 }

Reply via email to