Package com.fasterxml.classmate

Source Code of com.fasterxml.classmate.TypeResolver

package com.fasterxml.classmate;

import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;

import com.fasterxml.classmate.types.*;
import com.fasterxml.classmate.util.ClassKey;
import com.fasterxml.classmate.util.ResolvedTypeCache;

/**
* Object that is used for resolving generic type information of a class
* so that it is accessible using simple API. Resolved types are also starting
* point for accessing resolved (generics aware) return and argument types
* of class members (methods, fields, constructors).
*<p>
* Note that resolver instances are stateful in that resolvers cache resolved
* types for efficiency. Since this is internal state and not directly visible
* to callers, access to state is fully synchronized so that access from
* multiple threads is safe.
*/
@SuppressWarnings("serial")
public class TypeResolver implements Serializable
{
    private final static ResolvedType[] NO_TYPES = new ResolvedType[0];
   
    /*
    /**********************************************************************
    /* Pre-created instances
    /**********************************************************************
     */

    /**
     * We will also need to return "unknown" type for cases where type variable binding
     * is not found ('raw' instances of generic types); easiest way is to
     * pre-create type for <code>java.lang.Object</code>
     */
    private final static ResolvedObjectType sJavaLangObject =
        new ResolvedObjectType(Object.class, null, null, NO_TYPES);

    /**
     * Since number of primitive types is small, and they are frequently needed,
     * let's actually pre-create them for efficient reuse. Same goes for limited number
     * of other "standard" types...
     */
    protected final static HashMap<ClassKey, ResolvedType> _primitiveTypes;
    static {
        _primitiveTypes = new HashMap<ClassKey, ResolvedType>(16);
        for (ResolvedPrimitiveType type : ResolvedPrimitiveType.all()) {
            _primitiveTypes.put(new ClassKey(type.getErasedType()), type);
        }
        // should we include "void"? might as well...
        _primitiveTypes.put(new ClassKey(Void.TYPE), ResolvedPrimitiveType.voidType());
        // and at least java.lang.Object should be added too.
        _primitiveTypes.put(new ClassKey(Object.class), sJavaLangObject);
        // but most other types can be added dynamically
    }

    /*
    /**********************************************************************
    /* Caching
    /**********************************************************************
     */
   
    /**
     * Simple cache of types resolved by this resolved; capped to last 200 resolved types.
     * Caching works because type instances themselves are mostly immutable;
     * and properly synchronized in cases where transient data (raw members) are
     * accessed.
     */
    protected final ResolvedTypeCache _resolvedTypes = new ResolvedTypeCache(200);

    /*
    /**********************************************************************
    /* Life cycle
    /**********************************************************************
     */
   
    public TypeResolver() { }

    /*
    /**********************************************************************
    /* Factory methods, with explicit parameterization
    /**********************************************************************
     */
   
    /**
     * Factory method for resolving given base type
     * using specified types as type parameters.
     * Sample usage would be:
     *<pre>
     *  ResolvedType type = TypeResolver.resolve(List.class, Integer.class);
     *</pre>
     * which would be equivalent to
     *<pre>
     *  ResolvedType type = TypeResolver.resolve(new GenericType&lt;List&lt;Integer>>() { });
     *</pre>
     * Note that you can mix different types of type parameters, whether already
     * resolved ({@link ResolvedType}), type-erased ({@link java.lang.Class}) or
     * generic type reference ({@link GenericType}).
     */
    public ResolvedType resolve(Type type, Type... typeParameters)
    {
        boolean noParams = (typeParameters == null || typeParameters.length == 0);
        TypeBindings bindings;
        Class<?> rawBase;

        if (type instanceof Class<?>) {
            bindings = TypeBindings.emptyBindings();
            if (noParams) {
                return _fromClass(null, (Class<?>) type, bindings);
            }
            rawBase = (Class<?>) type;
        } else if (type instanceof GenericType<?>) {
            bindings = TypeBindings.emptyBindings();
            if (noParams) {
                return _fromGenericType(null, (GenericType<?>) type, bindings);
            }
            ResolvedType rt = _fromAny(null, type, bindings);
            rawBase = rt.getErasedType();
        } else if (type instanceof ResolvedType) {
            ResolvedType rt = (ResolvedType) type;
            if (noParams) {
                return rt;
            }
            bindings = rt.getTypeBindings();
            rawBase = rt.getErasedType();
        } else {
            bindings = TypeBindings.emptyBindings();
            if (noParams) {
                return resolve(bindings, type);
            }
            // Quite convoluted... but necessary to find Class<?> underlying it all
            ResolvedType rt = _fromAny(null, type, bindings);
            rawBase = rt.getErasedType();
        }

        // Next: resolve type parameters
        int len = typeParameters.length;
        ResolvedType[] resolvedParams = new ResolvedType[len];
        for (int i = 0; i < len; ++i) {
            resolvedParams[i] = _fromAny(null, typeParameters[i], bindings);
        }
        return _fromClass(null, rawBase, TypeBindings.create(rawBase, resolvedParams));
    }

    /**
     * Factory method for constructing array type of given element type.
     */
    public ResolvedArrayType arrayType(Type elementType)
    {
        ResolvedType resolvedElementType = resolve(TypeBindings.emptyBindings(), elementType);
        // Arrays are cumbersome for some reason:
        Object emptyArray = Array.newInstance(resolvedElementType.getErasedType(), 0);
        // Should we try to use cache? It's bit tricky, so let's not bother yet
        return new ResolvedArrayType(emptyArray.getClass(), TypeBindings.emptyBindings(),
                resolvedElementType);
    }

    /**
     * Factory method for resolving specified Java {@link java.lang.reflect.Type}, given
     * {@link TypeBindings} needed to resolve any type variables.
     *<p>
     * Use of this method is discouraged (use if and only if you really know what you
     * are doing!); but if used, type bindings passed should come from {@link ResolvedType}
     * instance of declaring class (or interface).
     *<p>
     * NOTE: order of arguments was reversed for 0.8, to avoid problems with
     * overload varargs method.
     */
    public ResolvedType resolve(TypeBindings typeBindings, Type jdkType)
    {
        return _fromAny(null, jdkType, typeBindings);
    }

    /**
     * Factory method for constructing sub-classing specified type; class specified
     * as sub-class must be compatible according to basic Java inheritance rules
     * (subtype must propery extend or implement specified supertype).
     *<p>
     * A typical use case here is to refine a generic type; for example, given
     * that we have generic type like <code>List&ltInteger></code>, but we want
     * a more specific implementation type like
     * class <code>ArrayList</code> but with same parameterization (here just <code>Integer</code>),
     * we could achieve it by:
     *<pre>
     *  ResolvedType mapType = typeResolver.resolve(List.class, Integer.class);
     *  ResolveType concreteMapType = typeResolver.resolveSubType(mapType, ArrayList.class);
     *</pre>
     * (in this case, it would have been simpler to resolve directly; but in some
     * cases we are handled supertype and want to refine it, in which case steps
     * would be the same but separated by other code)
     *<p>
     * Note that this method will fail if extension can not succeed; either because
     * this type is not extendable (sub-classable) -- which is true for primitive
     * and array types -- or because given class is not a subtype of this type.
     * To check whether subtyping could succeed, you can call
     * {@link ResolvedType#canCreateSubtypes()} to see if supertype can ever
     * be extended.
     *
     * @param supertype Type to subtype (extend)
     * @param subtype Type-erased sub-class or sub-interface
     *
     * @return Resolved subtype
     *
     * @throws IllegalArgumentException If this type can be extended in general, but not into specified sub-class
     * @throws UnsupportedOperationException If this type can not be sub-classed
     */
    public ResolvedType resolveSubtype(ResolvedType supertype, Class<?> subtype)
        throws IllegalArgumentException, UnsupportedOperationException
    {
        // first: if it's a recursive reference, find out referred-to type
        ResolvedType refType = supertype.getSelfReferencedType();
        if (refType != null) {
            supertype = refType;
        }
        // Then, trivial check for case where subtype is supertype...
        if (supertype.getErasedType() == subtype) {
            return supertype;
        }
       
        if (!supertype.canCreateSubtypes()) {
            throw new UnsupportedOperationException("Can not subtype primitive or array types (type "+supertype.getFullDescription()+")");
        }
        // In general, must be able to subtype as per JVM rules:
        Class<?> superclass = supertype.getErasedType();
        if (!superclass.isAssignableFrom(subtype)) {
            throw new IllegalArgumentException("Can not sub-class "+supertype.getBriefDescription()
                    +" into "+subtype.getName());
        }
        // Ok, then, let us instantiate type with placeholders
        ResolvedType resolvedSubtype;
        int paramCount = subtype.getTypeParameters().length;
        TypePlaceHolder[] placeholders;

        if (paramCount == 0) { // no generics
            placeholders = null;
            resolvedSubtype = resolve(subtype);
        } else {
            placeholders = new TypePlaceHolder[paramCount];
            for (int i = 0; i < paramCount; ++i) {
                placeholders[i] = new TypePlaceHolder(i);
            }
            resolvedSubtype = resolve(subtype, placeholders);           
        }
        ResolvedType rawSupertype = resolvedSubtype.findSupertype(superclass);
        if (rawSupertype == null) { // sanity check, should never occur
            throw new IllegalArgumentException("Internal error: unable to locate supertype ("+subtype.getName()+") for type "+supertype.getBriefDescription());
        }
        // Ok, then, let's find and verify type assignments
        _resolveTypePlaceholders(supertype, rawSupertype);
       
        // And then re-construct, if necessary
        if (paramCount == 0) { // if no type parameters, fine as is
            return resolvedSubtype;
        }
        // but with type parameters, need to reconstruct
        ResolvedType[] typeParams = new ResolvedType[paramCount];
        for (int i = 0; i < paramCount; ++i) {
            ResolvedType t = placeholders[i].actualType();
            /* Is it ok for it to be left unassigned? For now let's not
             * allow that
             */
            if (t == null) {
                throw new IllegalArgumentException("Failed to find type parameter #"+(i+1)+"/"
                        +paramCount+" for "+subtype.getName());
            }
            typeParams[i] = t;
        }
        return resolve(subtype, typeParams);
    }
   
    /*
    /**********************************************************************
    /* Misc other methods
    /**********************************************************************
     */

    /**
     * Helper method that can be used to checked whether given resolved type
     * (with erased type of <code>java.lang.Object</code>) is a placeholder
     * for "self-reference"; these are nasty recursive ("self") types
     * needed with some interfaces
     */
    public static boolean isSelfReference(ResolvedType type)
    {
        return (type instanceof ResolvedRecursiveType);
    }
   
    /*
    /**********************************************************************
    /* Internal methods, second-level factory methods
    /**********************************************************************
     */

    private ResolvedType _fromAny(ClassStack context, Type mainType, TypeBindings typeBindings)
    {
        if (mainType instanceof Class<?>) {
            return _fromClass(context, (Class<?>) mainType, typeBindings);
        }
        if (mainType instanceof ResolvedType) {
            return (ResolvedType) mainType;
        }
        if (mainType instanceof ParameterizedType) {
            return _fromParamType(context, (ParameterizedType) mainType, typeBindings);
        }
        if (mainType instanceof GenericType<?>) {
            return _fromGenericType(context, (GenericType<?>) mainType, typeBindings);
        }
        if (mainType instanceof GenericArrayType) {
            return _fromArrayType(context, (GenericArrayType) mainType, typeBindings);
        }
        if (mainType instanceof TypeVariable<?>) {
            return _fromVariable(context, (TypeVariable<?>) mainType, typeBindings);
        }
        if (mainType instanceof WildcardType) {
            return _fromWildcard(context, (WildcardType) mainType, typeBindings);
        }
        // should never get here...
        throw new IllegalArgumentException("Unrecognized type class: "+mainType.getClass().getName());
    }

    private ResolvedType _fromClass(ClassStack context, Class<?> rawType, TypeBindings typeBindings)
    {
        // First: a primitive type perhaps?
        ResolvedType type = _primitiveTypes.get(new ClassKey(rawType));
        if (type != null) {
            return type;
        }
        // Second: recursive reference?
        if (context == null) {
            context = new ClassStack(rawType);
        } else {
            ClassStack prev = context.find(rawType);
            if (prev != null) {
                // Self-reference: needs special handling, then...
                ResolvedRecursiveType selfRef = new ResolvedRecursiveType(rawType, typeBindings);
                prev.addSelfReference(selfRef);
                return selfRef;
            }
            // no, can just add
            context = context.child(rawType);
        }
       
        // If not, already recently resolved?
        ResolvedType[] typeParameters = typeBindings.typeParameterArray();
        ResolvedTypeCache.Key key = _resolvedTypes.key(rawType, typeParameters);
               
        type = _resolvedTypes.find(key);
        if (type == null) {
            type = _constructType(context, rawType, typeBindings);
            _resolvedTypes.put(key, type);
        }
        context.resolveSelfReferences(type);
        return type;
    }

    /**
     * Factory method for resolving given generic type, defined by using sub-class
     * instance of {@link GenericType}
     */
    private ResolvedType _fromGenericType(ClassStack context, GenericType<?> generic, TypeBindings typeBindings)
    {
        /* To allow multiple levels of inheritance (just in case someone
         * wants to go to town with inheritance of GenericType),
         * we better resolve the whole thing; then dig out
         * type parameterization...
         */
        ResolvedType type = _fromClass(context, generic.getClass(), typeBindings);
        ResolvedType genType = type.findSupertype(GenericType.class);
        if (genType == null) { // sanity check; shouldn't occur
            throw new IllegalArgumentException("Unparameterized GenericType instance ("+generic.getClass().getName()+")");
        }
        TypeBindings b = genType.getTypeBindings();
        ResolvedType[] params = b.typeParameterArray();
        if (params.length == 0) {
            throw new IllegalArgumentException("Unparameterized GenericType instance ("+generic.getClass().getName()+")");
        }
        return params[0];
    }
   
    private ResolvedType _constructType(ClassStack context, Class<?> rawType, TypeBindings typeBindings)
    {
        // Ok: no easy shortcut, let's figure out type of type...
        if (rawType.isArray()) {
            ResolvedType elementType = _fromAny(context, rawType.getComponentType(), typeBindings);
            return new ResolvedArrayType(rawType, typeBindings, elementType);
        }
        // For other types super interfaces are needed...
        if (rawType.isInterface()) {
            return new ResolvedInterfaceType(rawType, typeBindings,
                    _resolveSuperInterfaces(context, rawType, typeBindings));
           
        }
        return new ResolvedObjectType(rawType, typeBindings,
                _resolveSuperClass(context, rawType, typeBindings),
                _resolveSuperInterfaces(context, rawType, typeBindings));
    }

    private ResolvedType[] _resolveSuperInterfaces(ClassStack context, Class<?> rawType, TypeBindings typeBindings)
    {
        Type[] types = rawType.getGenericInterfaces();
        if (types == null || types.length == 0) {
            return NO_TYPES;
        }
        int len = types.length;
        ResolvedType[] resolved = new ResolvedType[len];
        for (int i = 0; i < len; ++i) {
            resolved[i] = _fromAny(context, types[i], typeBindings);
        }
        return resolved;
    }

    /**
     * NOTE: return type changed in 1.0.1 from {@link ResolvedObjectType} to
     *    {@link ResolvedType}, since it was found that other types may
     *    be returned...
     *
     * @return Usually a {@link ResolvedObjectType}, but possibly also
     *    {@link ResolvedRecursiveType}
     */
    private ResolvedType _resolveSuperClass(ClassStack context, Class<?> rawType, TypeBindings typeBindings)
    {
        Type parent = rawType.getGenericSuperclass();
        if (parent == null) {
            return null;
        }
        return _fromAny(context, parent, typeBindings);
    }
   
    private ResolvedType _fromParamType(ClassStack context, ParameterizedType ptype, TypeBindings parentBindings)
    {
        /* First: what is the actual base type? One odd thing is that 'getRawType'
         * returns Type, not Class<?> as one might expect. But let's assume it is
         * always of type Class: if not, need to add more code to resolve it...
         */
        Class<?> rawType = (Class<?>) ptype.getRawType();
        Type[] params = ptype.getActualTypeArguments();
        int len = params.length;
        ResolvedType[] types = new ResolvedType[len];

        for (int i = 0; i < len; ++i) {
            types[i] = _fromAny(context, params[i], parentBindings);
        }
        // Ok: this gives us current bindings for this type:
        TypeBindings newBindings = TypeBindings.create(rawType, types);
        return _fromClass(context, rawType, newBindings);
    }

    private ResolvedType _fromArrayType(ClassStack context, GenericArrayType arrayType, TypeBindings typeBindings)
    {
        ResolvedType elementType = _fromAny(context, arrayType.getGenericComponentType(), typeBindings);
        // Figuring out raw class for generic array is actually bit tricky...
        Object emptyArray = Array.newInstance(elementType.getErasedType(), 0);
        return new ResolvedArrayType(emptyArray.getClass(), typeBindings, elementType);
    }

    private ResolvedType _fromWildcard(ClassStack context, WildcardType wildType, TypeBindings typeBindings)
    {
        /* Similar to challenges with TypeVariable, we may have multiple upper bounds.
         * But it is also possible that if upper bound defaults to Object, we might want to
         * consider lower bounds instead?
         * For now, we won't try anything more advanced; above is just for future reference.
         */
        return _fromAny(context, wildType.getUpperBounds()[0], typeBindings);
    }
   
    private ResolvedType _fromVariable(ClassStack context, TypeVariable<?> variable, TypeBindings typeBindings)
    {
        // ideally should find it via bindings:
        String name = variable.getName();
        ResolvedType type = typeBindings.findBoundType(name);
        if (type != null) {
            return type;
        }
        /* but if not, use bounds... note that approach here is simplistics; not taking
         * into account possible multiple bounds, nor consider upper bounds.
         */
        /* 02-Mar-2011, tatu: As per issue#4, need to avoid self-reference cycles here;
         *   can be handled by (temporarily) adding binding:
         */
        typeBindings = typeBindings.withAdditionalBinding(name, sJavaLangObject);
        Type[] bounds = variable.getBounds();
        return _fromAny(context, bounds[0], typeBindings);
    }

    /*
    /**********************************************************************
    /* Internal methods, replacing and verifying type placeholders
    /**********************************************************************
     */

    /**
     * Method called to verify that types match; and if there are
     */
    private void _resolveTypePlaceholders(ResolvedType expectedType, ResolvedType actualType)
        throws IllegalArgumentException
    {
        List<ResolvedType> expectedTypes = expectedType.getTypeParameters();
        List<ResolvedType> actualTypes = actualType.getTypeParameters();
        for (int i = 0, len = expectedTypes.size(); i < len; ++i) {
            ResolvedType exp = expectedTypes.get(i);
            ResolvedType act = actualTypes.get(i);
            if (!_typesMatch(exp, act)) {
                throw new IllegalArgumentException("Type parameter #"+(i+1)+"/"+len+" differs; expected "
                        +exp.getBriefDescription()+", got "+act.getBriefDescription());
            }
        }
    }

    private boolean _typesMatch(ResolvedType exp, ResolvedType act)
    {
        // Simple equality check, except for one thing: place holders for 'act'
        if (act instanceof TypePlaceHolder) {
            ((TypePlaceHolder) act).actualType(exp);
            return true;
        }
        // but due to recursive nature can't call equality...
        if (exp.getErasedType() != act.getErasedType()) {
            return false;
        }
        // But we can check type parameters "blindly"
        List<ResolvedType> expectedTypes = exp.getTypeParameters();
        List<ResolvedType> actualTypes = act.getTypeParameters();
        for (int i = 0, len = expectedTypes.size(); i < len; ++i) {
            ResolvedType exp2 = expectedTypes.get(i);
            ResolvedType act2 = actualTypes.get(i);
            if (!_typesMatch(exp2, act2)) {
                return false;
            }
        }
        return true;
    }
   
    /*
    /**********************************************************************
    /* Helper classes
    /**********************************************************************
     */
   
    /**
     * Simple helper class used to keep track of 'call stack' for classes being referenced
     * (as well as unbound variables)
     */
    private final static class ClassStack
    {
        private final ClassStack _parent;
        private final Class<?> _current;
       
        private ArrayList<ResolvedRecursiveType> _selfRefs;
       
        public ClassStack(Class<?> rootType) {
            this(null, rootType);
        }
       
        private ClassStack(ClassStack parent, Class<?> curr) {
            _parent = parent;
            _current = curr;
        }

        /**
         * @return New stack frame, if addition is ok; null if not
         */
        public ClassStack child(Class<?> cls)
        {
            return new ClassStack(this, cls);
        }
       
        /**
         * Method called to indicate that there is a self-reference from
         * deeper down in stack pointing into type this stack frame represents.
         */
        public void addSelfReference(ResolvedRecursiveType ref)
        {
            if (_selfRefs == null) {
                _selfRefs = new ArrayList<ResolvedRecursiveType>();
            }
            _selfRefs.add(ref);
        }

        /**
         * Method called when type that this stack frame represents is
         * fully resolved, allowing self-references to be completed
         * (if there are any)
         */
        public void resolveSelfReferences(ResolvedType resolved)
        {
            if (_selfRefs != null) {
                for (ResolvedRecursiveType ref : _selfRefs) {
                    ref.setReference(resolved);
                }
            }
        }
       
        public ClassStack find(Class<?> cls)
        {
            if (_current == cls) return this;
            if (_parent != null) {
                return _parent.find(cls);
            }
            return null;
        }
    }
}
TOP

Related Classes of com.fasterxml.classmate.TypeResolver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.