zhys513 / jenabean

Automatically exported from code.google.com/p/jenabean
0 stars 0 forks source link

ValuesContext#getGenericType(ParameterizedType) not working in all cases #16

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Hi, I got a 
java.lang.ClassCastException: 
sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to 
java.lang.Class
    at thewebsemantic.ValuesContext.getGenericType(ValuesContext.java:69)

When using field that's type is a List of generic types. I changed the code to 
the following to make it work:

    public Class<?> getGenericType(final ParameterizedType type) {
        // unfortunately we don't have the TypeVariable information from the reference to this type,
        // so some TypeVariable->Type mappings are missing
        final Map<TypeVariable<?>, Type> typeVariableMap = new HashMap<TypeVariable<?>, Type>();
        final Type actualType = type.getActualTypeArguments()[0];
        return getClassForType(actualType, typeVariableMap);
    }

    public Class<?> getGenericType(final ParameterizedType type, final HashMap<TypeVariable<?>, Type> typeVariableMap) {
        final Type actualType = type.getActualTypeArguments()[0];
        return getClassForType(actualType, typeVariableMap);
    }

    private Class<?> getClassForType(final Type type, final Map<TypeVariable<?>, Type> typeVariableMap) {
        if (type == null) {
            return NullType.class;
        }
        if(type instanceof Class) {
            return (Class<?>)type;
        }
        if(type instanceof ParameterizedType) {
            final Type rawType = ((ParameterizedType) type).getRawType();
            return getClassForType(rawType, typeVariableMap);
        }
        if(type instanceof GenericArrayType) {
            final Type genericType = ((GenericArrayType) type).getGenericComponentType();
            // find class object of type the array is of
            final Class<?> arrayClass = getClassForType(genericType, typeVariableMap);
            // didn't find another way to get Class for array of type arrayClass
            return Array.newInstance(arrayClass, 0).getClass();
        }
        if(type instanceof TypeVariable<?>) {
            final Type actualType = typeVariableMap.get(type);
            // if we don't have mapping to actual type, we use most global class Object.class
            if(actualType == null) {
                return Object.class;
            }
            return getClassForType(actualType,typeVariableMap);
        }
        // don't know how to handle WildcardType and TypeVariable
        throw new IllegalArgumentException();
    }

    public Map<TypeVariable<?>, Type> getGenericTypeMap(final Type type) {
        final Map<TypeVariable<?>, Type> result = new HashMap<TypeVariable<?>, Type>();
        findTypeVariableMappingsRecursive(type, result);
        return result;
    }

    private void findTypeVariableMappingsRecursive(final Type type, final Map<TypeVariable<?>, Type> typeVariableMap) {
        // mappings for current type
        findTypeVariableMappings(type, typeVariableMap);
        // now mappings for supertypes and generic interfaces
        if(type instanceof Class<?>) {
            final Class<?> clazz = (Class<?>) type;
            final Type superType = clazz.getGenericSuperclass();
            if(superType != null) {
                findTypeVariableMappingsRecursive(superType,typeVariableMap);
            }
            final Type[] interfaceTypes = clazz.getGenericInterfaces();
            for (Type interfaceType : interfaceTypes) {
                findTypeVariableMappingsRecursive(interfaceType, typeVariableMap);
            }
        }
    }

    private void findTypeVariableMappings(final Type type,
            final Map<TypeVariable<?>, Type> map) {
        if (type instanceof ParameterizedType) {
            final ParameterizedType parameterizedType = (ParameterizedType) type;
            final GenericDeclaration genericDeclaration = (GenericDeclaration) parameterizedType.getRawType();
            final TypeVariable<?>[] typeVariables = genericDeclaration.getTypeParameters();
            final Type[] actualTypes = parameterizedType.getActualTypeArguments();
            for (int i = 0; i < actualTypes.length; ++i) {
                map.put(typeVariables[i], actualTypes[i]);
            }
        }
    }

I did also add support for generic arrays, although the framework do not seem 
to support arrays at the moment. Unfortunately the caller does not (and maybe 
cannot) provide information which TypeVariables are mapped to which actual 
types, or a ParameterizedType this information can be read from. I added 
methods for retrieving this information from a Type (see getGenericTypeMap).

Original issue reported on code.google.com by maxbur...@googlemail.com on 8 Jun 2012 at 1:58