All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.fortnox.reactivewizard.util.AccessorUtil Maven / Gradle / Ivy

package se.fortnox.reactivewizard.util;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

class AccessorUtil {
    /**
     * Create a map of all generic type parameters on a super class to the actual types in a subclass.
     *
     * @param subClass The subclass to find out type information for
     * @param member   The member (field or method) to find out type information for
     * @return a mapping of the generic type names (ie. "T") to the actual types
     */
    private static Map> typesByGenericName(Class subClass, Member member) {
        Map> typeByName = new HashMap<>();
        for (int i = 0; i < member.getDeclaringClass().getTypeParameters().length; i++) {
            String typeName = member.getDeclaringClass().getTypeParameters()[i].getTypeName();
            Class superclass = subClass.getSuperclass();
            Type genericSuperclass = subClass.getGenericSuperclass();
            while (superclass != null && superclass != Object.class) {
                if (genericSuperclass instanceof ParameterizedType) {
                    Class type = (Class) ((ParameterizedType) genericSuperclass).getActualTypeArguments()[i];
                    typeByName.put(typeName, type);
                }
                genericSuperclass = superclass.getGenericSuperclass();
                superclass = superclass.getSuperclass();
            }
        }
        return typeByName;
    }

    static MemberTypeInfo fieldTypeInfo(Class cls, Field field) {
        Map> genericTypenameToType = AccessorUtil.typesByGenericName(cls, field);
        Class genericType = genericTypenameToType.get(field.getGenericType().getTypeName());

        Class returnType;
        Type genericReturnType;
        if (genericType == null) {
            returnType = field.getType();
            genericReturnType = field.getGenericType();
        } else {
            returnType = genericType;
            genericReturnType = genericType;
        }

        return new MemberTypeInfo(returnType, genericReturnType);
    }

    static MemberTypeInfo getterTypeInfo(Class cls, Method method) {
        Map> genericTypenameToType = AccessorUtil.typesByGenericName(cls, method);
        Class genericType = genericTypenameToType.get(method.getGenericReturnType().getTypeName());

        Class returnType;
        Type genericReturnType;
        if (genericType == null) {
            returnType = method.getReturnType();
            genericReturnType = method.getGenericReturnType();
        } else {
            returnType = genericType;
            genericReturnType = genericType;
        }

        return new MemberTypeInfo(returnType, genericReturnType);
    }

    static  MemberTypeInfo setterTypeInfo(Class cls, Method method) {
        Map> genericTypenameToType = AccessorUtil.typesByGenericName(cls, method);
        Class genericType = genericTypenameToType.get(method.getGenericParameterTypes()[0].getTypeName());

        Class returnType;
        Type genericReturnType;
        if (genericType == null) {
            returnType = method.getParameterTypes()[0];
            genericReturnType = method.getGenericParameterTypes()[0];
        } else {
            returnType = genericType;
            genericReturnType = genericType;
        }

        return new MemberTypeInfo<>(returnType, genericReturnType);
    }

    static class MemberTypeInfo {
        private final Class returnType;
        private final Type genericReturnType;

        private MemberTypeInfo(Class returnType, Type genericReturnType) {
            this.returnType = returnType;
            this.genericReturnType = genericReturnType;
        }

        public Class getReturnType() {
            return returnType;
        }

        public Type getGenericReturnType() {
            return genericReturnType;
        }
    }
}