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

org.apache.webbeans.util.GenericsUtil Maven / Gradle / Ivy

There is a newer version: 10.0.0-M3
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.webbeans.util;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.webbeans.config.OwbGenericArrayTypeImpl;
import org.apache.webbeans.config.OwbParametrizedTypeImpl;
import org.apache.webbeans.config.OwbTypeVariableImpl;
import org.apache.webbeans.config.OwbWildcardTypeImpl;

/**
 * Utility classes for generic type operations.
 */
public final class GenericsUtil
{
    public static boolean satisfiesDependency(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType)
    {
        if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType)
        {
            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
        }
        else
        {
            Type injectionPointRawType = injectionPointType instanceof ParameterizedType? ((ParameterizedType)injectionPointType).getRawType(): injectionPointType;
            Type beanRawType = beanType instanceof ParameterizedType? ((ParameterizedType)beanType).getRawType(): beanType;
            
            if  (ClassUtil.isSame(injectionPointRawType, beanRawType))
            {
                return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
            }
        }

        return false;
    }

    public static boolean satisfiesDependencyRaw(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType)
    {
        if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType)
        {
            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
        }
        else
        {
            Type injectionPointRawType = injectionPointType instanceof ParameterizedType? ((ParameterizedType)injectionPointType).getRawType(): injectionPointType;
            Type beanRawType = beanType instanceof ParameterizedType? ((ParameterizedType)beanType).getRawType(): beanType;

            if  (ClassUtil.isSame(injectionPointRawType, beanRawType))
            {
                return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointRawType, beanRawType);
            }
            else
            {
                Class bean = (Class) beanType;
                if (bean.getSuperclass() != null && ClassUtil.isRawClassEquals(injectionPointType, bean.getSuperclass()))
                {
                    return true;
                }

                Class[] interfaces = bean.getInterfaces();
                if (interfaces == null || interfaces.length == 0)
                {
                    return false;
                }

                for (Class clazz : interfaces)
                {
                    if (ClassUtil.isRawClassEquals(injectionPointType, clazz))
                    {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * 5.2.3 and 5.2.4
     */
    public static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, Type requiredType, Type beanType)
    {
        if (requiredType instanceof Class)
        {
            return isAssignableFrom(isDelegateOrEvent, (Class)requiredType, beanType);
        }
        else if (requiredType instanceof ParameterizedType)
        {
            return isAssignableFrom(isDelegateOrEvent, isProducer, (ParameterizedType)requiredType, beanType);
        }
        else if (requiredType instanceof TypeVariable)
        {
            return isAssignableFrom(isDelegateOrEvent, (TypeVariable)requiredType, beanType);
        }
        else if (requiredType instanceof GenericArrayType)
        {
            return Class.class.isInstance(beanType) && Class.class.cast(beanType).isArray()
                    && isAssignableFrom(isDelegateOrEvent, (GenericArrayType)requiredType, beanType);
        }
        else if (requiredType instanceof WildcardType)
        {
            return isAssignableFrom(isDelegateOrEvent, (WildcardType)requiredType, beanType);
        }
        else
        {
            throw new IllegalArgumentException("Unsupported type " + requiredType.getClass());
        }
    }

    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class injectionPointType, Type beanType)
    {
        if (beanType instanceof Class)
        {
            return isAssignableFrom(injectionPointType, (Class)beanType);
        }
        else if (beanType instanceof TypeVariable)
        {
            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable)beanType);
        }
        else if (beanType instanceof ParameterizedType)
        {
            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType)beanType);
        }
        else if (beanType instanceof GenericArrayType)
        {
            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (GenericArrayType)beanType);
        }
        else if (beanType instanceof WildcardType)
        {
            return isAssignableFrom(isDelegateOrEvent, (Type)injectionPointType, (WildcardType)beanType);
        }
        else
        {
            throw new IllegalArgumentException("Unsupported type " + injectionPointType.getClass());
        }
    }

    private static boolean isAssignableFrom(Class injectionPointType, Class beanType)
    {
        return ClassUtil.isClassAssignableFrom(injectionPointType, beanType);
    }

    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class injectionPointType, TypeVariable beanType)
    {
        for (Type bounds: beanType.getBounds())
        {
            if (isAssignableFrom(isDelegateOrEvent, injectionPointType, bounds))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * CDI Spec. 5.2.4: "A parameterized bean type is considered assignable to a raw required type
     * if the raw types are identical and all type parameters of the bean type are either unbounded type variables or java.lang.Object." 
     */
    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class injectionPointType, ParameterizedType beanType)
    {
        if (beanType.getRawType() != injectionPointType)
        {
            return false; //raw types don't match
        }

        if (isDelegateOrEvent)
        {
            // for delegate and events we match 'in reverse' kind off
            // @Observes ProcessInjectionPoint does also match Instance
            return isAssignableFrom(true, injectionPointType, beanType.getRawType());
        }

        for (Type typeArgument: beanType.getActualTypeArguments())
        {
            if (typeArgument == Object.class)
            {
                continue;
            }
            if (!(typeArgument instanceof TypeVariable))
            {
                return false; //neither object nor type variable
            }
            TypeVariable typeVariable = (TypeVariable)typeArgument;
            for (Type bounds: typeVariable.getBounds())
            {
                if (bounds != Object.class)
                {
                    return false; //bound type variable
                }
            }
        }
        return true;
    }

    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class injectionPointType, GenericArrayType beanType)
    {
        return injectionPointType.isArray() && isAssignableFrom(isDelegateOrEvent, injectionPointType.getComponentType(), beanType.getGenericComponentType());
    }
    
    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Type injectionPointType, WildcardType beanType)
    {
        for (Type bounds: beanType.getLowerBounds())
        {
            if (!isAssignableFrom(isDelegateOrEvent, false, bounds, injectionPointType))
            {
                return false;
            }
        }
        for (Type bounds: beanType.getUpperBounds())
        {
            if (isAssignableFrom(isDelegateOrEvent, false, injectionPointType, bounds))
            {
                return true;
            }
        }
        return false;
    }

    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Type beanType)
    {
        if (beanType instanceof Class)
        {
            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (Class)beanType);
        }
        else if (beanType instanceof TypeVariable)
        {
            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (TypeVariable)beanType);
        }
        else if (beanType instanceof ParameterizedType)
        {
            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType)beanType);
        }
        else if (beanType instanceof WildcardType)
        {
            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (WildcardType)beanType);
        }
        else if (beanType instanceof GenericArrayType)
        {
            return false;
        }
        else
        {
            throw new IllegalArgumentException("Unsupported type " + beanType.getClass());
        }
    }

    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Class beanType)
    {
        Class rawInjectionPointType = getRawType(injectionPointType);
        if (rawInjectionPointType.equals(beanType))
        {
            if (isProducer)
            {
                for (final Type t : injectionPointType.getActualTypeArguments())
                {
                    if (!TypeVariable.class.isInstance(t) || !isNotBound(TypeVariable.class.cast(t).getBounds()))
                    {
                        if (!Class.class.isInstance(t) || Object.class != t)
                        {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
        if (!rawInjectionPointType.isAssignableFrom(beanType))
        {
            return false;
        }
        if (beanType.getSuperclass() != null && isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType.getGenericSuperclass()))
        {
            return true;
        }
        for (Type genericInterface: beanType.getGenericInterfaces())
        {
            if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, genericInterface))
            {
                return true;
            }
        }
        return false;
    }

    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, TypeVariable beanType)
    {
        final Type[] types = beanType.getBounds();
        if (isNotBound(types))
        {
            return true;
        }
        for (final Type bounds: types)
        {
            if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, bounds))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * CDI Spec. 5.2.4
     */
    private static boolean isAssignableFrom(boolean isDelegateOrEvent, ParameterizedType injectionPointType, ParameterizedType beanType)
    {
        if (injectionPointType.getRawType() != beanType.getRawType())
        {
            return false;
        }
        boolean swapParams = !isDelegateOrEvent;
        Type[] injectionPointTypeArguments = injectionPointType.getActualTypeArguments();
        Type[] beanTypeArguments = beanType.getActualTypeArguments();
        for (int i = 0; i < injectionPointTypeArguments.length; i++)
        {
            Type injectionPointTypeArgument = injectionPointTypeArguments[i];
            Type beanTypeArgument = beanTypeArguments[i];

            // for this special case it's actually an 'assignable to', thus we swap the params, see CDI-389
            // but this special rule does not apply to Delegate injection points...
            if (swapParams &&
                (injectionPointTypeArgument instanceof Class || injectionPointTypeArgument instanceof TypeVariable) &&
                beanTypeArgument instanceof TypeVariable)
            {
                final Type[] bounds = ((TypeVariable) beanTypeArgument).getBounds();
                final boolean isNotBound = isNotBound(bounds);
                if (!isNotBound)
                {
                    for (final Type upperBound : bounds)
                    {
                        if (!isAssignableFrom(true, false, upperBound, injectionPointTypeArgument))
                        {
                            return false;
                        }
                    }
                }
            }
            else if (swapParams && injectionPointTypeArgument instanceof TypeVariable)
            {
                return false;
            }
            else if (isDelegateOrEvent && injectionPointTypeArgument instanceof Class && beanTypeArgument instanceof Class)
            {
                // if no wildcard type was given then we require a real exact match.
                return injectionPointTypeArgument.equals(beanTypeArgument);

            }
            else if (!isAssignableFrom(isDelegateOrEvent, false, injectionPointTypeArgument, beanTypeArgument))
            {
                return false;
            }
        }
        return true;
    }

    private static boolean isNotBound(final Type... bounds)
    {
        return bounds == null || bounds.length == 0 || (bounds.length == 1 && Object.class == bounds[0]);
    }

    private static boolean isAssignableFrom(boolean isDelegateOrEvent, TypeVariable injectionPointType, Type beanType)
    {
        for (Type bounds: injectionPointType.getBounds())
        {
            if (!isAssignableFrom(isDelegateOrEvent, false, bounds, beanType))
            {
                return false;
            }
        }
        return true;
    }

    // rules are a bit different when in an array so we handle ParameterizedType manually (not reusing isAssignableFrom)
    private static boolean isAssignableFrom(boolean isDelegateOrEvent, GenericArrayType injectionPointType, Type beanType)
    {
        final Type genericComponentType = injectionPointType.getGenericComponentType();
        final Class componentType = Class.class.cast(beanType).getComponentType();
        if (Class.class.isInstance(genericComponentType))
        {
            return Class.class.cast(genericComponentType).isAssignableFrom(componentType);
        }
        if (ParameterizedType.class.isInstance(genericComponentType))
        {
            return isAssignableFrom(isDelegateOrEvent, false, ParameterizedType.class.cast(genericComponentType).getRawType(), componentType);
        }
        return isAssignableFrom(isDelegateOrEvent, false, genericComponentType, componentType);
    }

    private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, Type beanType)
    {
        if (beanType instanceof TypeVariable)
        {
            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable)beanType);
        }
        for (Type bounds: injectionPointType.getLowerBounds())
        {
            if (!isAssignableFrom(isDelegateOrEvent, false, beanType, bounds))
            {
                return false;
            }
        }
        for (Type bounds: injectionPointType.getUpperBounds())
        {
            Set beanTypeClosure = getTypeClosure(beanType);
            boolean isAssignable = false;
            for (Type beanSupertype: beanTypeClosure)
            {
                if (isAssignableFrom(isDelegateOrEvent, false, bounds, beanSupertype)
                    || (Class.class.isInstance(bounds)
                        && ParameterizedType.class.isInstance(beanSupertype)
                        && bounds == ParameterizedType.class.cast(beanSupertype).getRawType()))
                {
                    isAssignable = true;
                    break;
                }
            }
            if (!isAssignable)
            {
                return false;
            }
        }
        return true;
    }
    
    /**
     * CDI 1.1 Spec. 5.2.4, third bullet point
     */
    private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, TypeVariable beanType)
    {
        for (Type upperBound: injectionPointType.getUpperBounds())
        {
            for (Type bound: beanType.getBounds())
            {
                if (!isAssignableFrom(isDelegateOrEvent, false, upperBound, bound) && !isAssignableFrom(isDelegateOrEvent, false, bound, upperBound))
                {
                    return false;
                }
            }
        }
        for (Type lowerBound: injectionPointType.getLowerBounds())
        {
            for (Type bound: beanType.getBounds())
            {
                if (!isAssignableFrom(isDelegateOrEvent, false, bound, lowerBound))
                {
                    return false;
                }
            }
        }
        return true;
    }
    
    /**
     * @return true, if the specified type declaration contains an unresolved type variable.
     */
    public static boolean containsTypeVariable(Type type)
    {
        if (type instanceof Class)
        {
            return false;
        }
        else if (type instanceof TypeVariable)
        {
            return true;
        }
        else if (type instanceof ParameterizedType)
        {
            ParameterizedType parameterizedType = (ParameterizedType)type;
            return containTypeVariable(parameterizedType.getActualTypeArguments());
        }
        else if (type instanceof WildcardType)
        {
            WildcardType wildcardType = (WildcardType)type;
            return containTypeVariable(wildcardType.getUpperBounds()) || containTypeVariable(wildcardType.getLowerBounds());
        }
        else if (type instanceof GenericArrayType)
        {
            GenericArrayType arrayType = (GenericArrayType)type;
            return containsTypeVariable(arrayType.getGenericComponentType());
        }
        else
        {
            throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
        }

    }
    
    public static boolean containTypeVariable(Collection types)
    {
        return containTypeVariable(types.toArray(new Type[types.size()]));
    }
    
    public static boolean containTypeVariable(Type[] types)
    {
        for (Type type: types)
        {
            if (containsTypeVariable(type))
            {
                return true;
            }
        }
        return false;
    }

    /**
     *
     * @param type to check
     *
     * @return {@code true} if the given type contains a {@link java.lang.reflect.WildcardType}
     *         {@code false} otherwise
     */
    public static boolean containsWildcardType(Type type)
    {
        if (!(type instanceof ParameterizedType))
        {
            return false;
        }

        for (Type typeArgument : getParameterizedType(type).getActualTypeArguments())
        {
            if (ClassUtil.isParametrizedType(typeArgument))
            {
                if (containsWildcardType(typeArgument))
                {
                    return true;
                }
            }
            else
            {
                if (ClassUtil.isWildCardType(typeArgument))
                {
                    return true;
                }
            }
        }

        return false;
    }


    /**
     * Resolves the actual type of the specified field for the type hierarchy specified by the given subclass
     */
    public static Type resolveType(Class subclass, Field field)
    {
        return resolveType(field.getGenericType(), subclass, newSeenList());
    }

    /**
     * Resolves the actual return type of the specified method for the type hierarchy specified by the given subclass
     */
    public static Type resolveReturnType(Class subclass, Method method)
    {
        return resolveType(method.getGenericReturnType(), subclass, newSeenList());
    }

    /**
     * Resolves the actual parameter types of the specified constructor for the type hierarchy specified by the given subclass
     */
    public static Type[] resolveParameterTypes(Class subclass, Constructor constructor)
    {
        return resolveTypes(constructor.getGenericParameterTypes(), subclass);
    }

    /**
     * Resolves the actual parameter types of the specified method for the type hierarchy specified by the given subclass
     */
    public static Type[] resolveParameterTypes(Class subclass, Method method)
    {
        return resolveTypes(method.getGenericParameterTypes(), subclass);
    }

    /**
     * Resolves the actual type of the specified type for the type hierarchy specified by the given subclass
     */
    public static Type resolveType(Type type, Class subclass, Member member)
    {
        return resolveType(type, subclass, newSeenList());
    }

    public static Type resolveType(Type type, Class subclass, Member member, Collection> seen)
    {
        return resolveType(type, subclass, seen);
    }

    public static Type resolveType(Type type, Type actualType, Collection> seen)
    {
        if (type instanceof Class)
        {
            return type;
        }
        else if (type instanceof ParameterizedType)
        {
            ParameterizedType parameterizedType = (ParameterizedType)type;

            Type[] resolvedTypeArguments;
            if (Enum.class.equals(parameterizedType.getRawType()))
            {
                // Enums derive from themselves, which would create an infinite loop
                // we directly escape the loop if we detect this.
                resolvedTypeArguments = new Type[]{new OwbWildcardTypeImpl(new Type[]{Enum.class}, ClassUtil.NO_TYPES)};
            }
            else
            {
                resolvedTypeArguments = resolveTypes(parameterizedType.getActualTypeArguments(), actualType, seen);

            }

            return new OwbParametrizedTypeImpl(parameterizedType.getOwnerType(), parameterizedType.getRawType(), resolvedTypeArguments);
        }
        else if (type instanceof TypeVariable)
        {
            TypeVariable variable = (TypeVariable)type;
            return resolveTypeVariable(variable, actualType, seen);
        }
        else if (type instanceof WildcardType)
        {
            WildcardType wildcardType = (WildcardType)type;
            Type[] upperBounds = resolveTypes(wildcardType.getUpperBounds(), actualType, seen);
            Type[] lowerBounds = resolveTypes(wildcardType.getLowerBounds(), actualType, seen);
            return new OwbWildcardTypeImpl(upperBounds, lowerBounds);
        }
        else if (type instanceof GenericArrayType)
        {
            GenericArrayType arrayType = (GenericArrayType)type;
            return createArrayType(resolveType(arrayType.getGenericComponentType(), actualType, seen));
        }
        else
        {
            throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
        }
    }
    
    public static Type[] resolveTypes(Type[] types, Type actualType, Collection> seen)
    {
        Type[] resolvedTypeArguments = new Type[types.length];
        for (int i = 0; i < types.length; i++)
        {
            final Type type = resolveType(types[i], actualType, seen);
            if (type != null) // means a stackoverflow was avoided, just keep what we have
            {
                resolvedTypeArguments[i] = type;
            }
        }
        return resolvedTypeArguments;
    }

    public static Type[] resolveTypes(Type[] types, Type actualType)
    {
        Type[] resolvedTypeArguments = new Type[types.length];
        for (int i = 0; i < types.length; i++)
        {
            resolvedTypeArguments[i] = resolveType(types[i], actualType, newSeenList());
        }
        return resolvedTypeArguments;
    }

    public static Set getTypeClosure(Class type)
    {
        return getTypeClosure(type, type);
    }

    public static Set getTypeClosure(Type actualType)
    {
        return getTypeClosure(actualType, actualType);
    }

    /**
     * Returns the type closure for the specified parameters.
     * 

Example 1:

*

* Take the following classes: *

* * public class Foo { * private T t; * } * public class Bar extends Foo { * } * *

* To get the type closure of T in the context of Bar (which is {Number.class, Object.class}), you have to call this method like *

* * GenericUtil.getTypeClosure(Foo.class.getDeclaredField("t").getType(), Bar.class, Foo.class); * *

Example 2:

*

* Take the following classes: *

* * public class Foo { * private T t; * } * public class Bar extends Foo { * } * *

* To get the type closure of Bar in the context of Foo (which are besides Object.class the ParameterizedTypes Bar and Foo), * you have to call this method like *

* * GenericUtil.getTypeClosure(Foo.class, new TypeLiteral>() {}.getType(), Bar.class); * * * @param type the type to get the closure for * @param actualType the context to bind type variables * @return the type closure */ public static Set getTypeClosure(Type type, Type actualType) { Class rawType = getRawType(type); Class actualRawType = getRawType(actualType); if (rawType.isAssignableFrom(actualRawType) && rawType != actualRawType) { return getTypeClosure(actualType, type); } if (hasTypeParameters(type)) { type = getParameterizedType(type); } return getDirectTypeClosure(type, actualType); } public static Set getDirectTypeClosure(final Type type, final Type actualType) { Set typeClosure = new HashSet(); typeClosure.add(Object.class); fillTypeHierarchy(typeClosure, type, actualType); return typeClosure; } private static void fillTypeHierarchy(Set set, Type type, Type actualType) { if (type == null) { return; } Type resolvedType = GenericsUtil.resolveType(type, actualType, newSeenList()); set.add(resolvedType); Class resolvedClass = GenericsUtil.getRawType(resolvedType, actualType); if (resolvedClass.getSuperclass() != null) { fillTypeHierarchy(set, resolvedClass.getGenericSuperclass(), resolvedType); } for (Type interfaceType: resolvedClass.getGenericInterfaces()) { fillTypeHierarchy(set, interfaceType, resolvedType); } } private static Collection> newSeenList() { return new ArrayList>(); } public static boolean hasTypeParameters(Type type) { if (type instanceof Class) { Class classType = (Class)type; return classType.getTypeParameters().length > 0; } return false; } public static ParameterizedType getParameterizedType(Type type) { if (type instanceof ParameterizedType) { return (ParameterizedType)type; } else if (type instanceof Class) { Class classType = (Class)type; return new OwbParametrizedTypeImpl(classType.getDeclaringClass(), classType, classType.getTypeParameters()); } else { throw new IllegalArgumentException(type.getClass().getSimpleName() + " is not supported"); } } public static Class getRawType(Type type) { return getRawType(type, null); } static Class getRawType(Type type, Type actualType) { if (type instanceof Class) { return (Class)type; } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)type; return getRawType(parameterizedType.getRawType(), actualType); } else if (type instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable)type; Type mostSpecificType = getMostSpecificType(getRawTypes(typeVariable.getBounds(), actualType), typeVariable.getBounds()); return getRawType(mostSpecificType, actualType); } else if (type instanceof WildcardType) { WildcardType wildcardType = (WildcardType)type; Type mostSpecificType = getMostSpecificType(getRawTypes(wildcardType.getUpperBounds(), actualType), wildcardType.getUpperBounds()); return getRawType(mostSpecificType, actualType); } else if (type instanceof GenericArrayType) { GenericArrayType arrayType = (GenericArrayType)type; return getRawType(createArrayType(getRawType(arrayType.getGenericComponentType(), actualType)), actualType); } else { throw new IllegalArgumentException("Unsupported type " + type.getClass().getName()); } } private static Class[] getRawTypes(Type[] types) { return getRawTypes(types, null); } private static Class[] getRawTypes(Type[] types, Type actualType) { Class[] rawTypes = new Class[types.length]; for (int i = 0; i < types.length; i++) { rawTypes[i] = getRawType(types[i], actualType); } return rawTypes; } private static Type getMostSpecificType(Class[] types, Type[] genericTypes) { Class mostSpecificType = types[0]; int mostSpecificIndex = 0; for (int i = 0; i < types.length; i++) { if (mostSpecificType.isAssignableFrom(types[i])) { mostSpecificType = types[i]; mostSpecificIndex = i; } } return genericTypes[mostSpecificIndex]; } private static Class[] getClassTypes(Class[] rawTypes) { List> classTypes = new ArrayList>(); for (Class rawType : rawTypes) { if (!rawType.isInterface()) { classTypes.add(rawType); } } return classTypes.toArray(new Class[classTypes.size()]); } private static Type resolveTypeVariable(TypeVariable variable, Type actualType, Collection> seen) { if (actualType == null) { return variable; } Class declaringClass = getDeclaringClass(variable.getGenericDeclaration()); Class actualClass = getRawType(actualType); if (actualClass == declaringClass) { return resolveTypeVariable(variable, variable.getGenericDeclaration(), getParameterizedType(actualType), seen); } else if (actualClass.isAssignableFrom(declaringClass)) { Class directSubclass = getDirectSubclass(declaringClass, actualClass); Type[] typeArguments = resolveTypeArguments(directSubclass, actualType); Type directSubtype = new OwbParametrizedTypeImpl(directSubclass.getDeclaringClass(), directSubclass, typeArguments); return resolveTypeVariable(variable, directSubtype, seen); } else // if (declaringClass.isAssignableFrom(actualClass)) { Type genericSuperclass = getGenericSuperclass(actualClass, declaringClass); if (genericSuperclass == null) { return variable; } else if (genericSuperclass instanceof Class) { // special handling for type erasure Class superclass = (Class)genericSuperclass; genericSuperclass = new OwbParametrizedTypeImpl(superclass.getDeclaringClass(), superclass, getRawTypes(superclass.getTypeParameters())); } else { ParameterizedType genericSupertype = getParameterizedType(genericSuperclass); Type[] typeArguments = resolveTypeArguments(getParameterizedType(actualType), genericSupertype); genericSuperclass = new OwbParametrizedTypeImpl(genericSupertype.getOwnerType(), genericSupertype.getRawType(), typeArguments); } Type resolvedType = resolveTypeVariable(variable, genericSuperclass, seen); if (resolvedType instanceof TypeVariable) { TypeVariable resolvedTypeVariable = (TypeVariable)resolvedType; TypeVariable[] typeParameters = actualClass.getTypeParameters(); for (int i = 0; i < typeParameters.length; i++) { if (typeParameters[i].getName().equals(resolvedTypeVariable.getName())) { resolvedType = getParameterizedType(actualType).getActualTypeArguments()[i]; break; } } } return resolvedType; } } private static Class getDeclaringClass(GenericDeclaration declaration) { if (declaration instanceof Class) { return (Class)declaration; } else if (declaration instanceof Member) { return ((Member)declaration).getDeclaringClass(); } else { throw new IllegalArgumentException("Unsupported type " + declaration.getClass()); } } private static Type resolveTypeVariable(TypeVariable variable, GenericDeclaration declaration, ParameterizedType type, Collection> seen) { int index = getIndex(declaration, variable); if (declaration instanceof Class) { if (index >= 0) { return type.getActualTypeArguments()[index]; } else { index = getIndex(type, variable); if (index >= 0) { return declaration.getTypeParameters()[index]; } } } else { if (seen.contains(variable)) { return null; } seen.add(variable); Type[] resolvedBounds = resolveTypes(declaration.getTypeParameters()[index].getBounds(), type, seen); return OwbTypeVariableImpl.createTypeVariable(variable, resolvedBounds); } return variable; } private static int getIndex(GenericDeclaration declaration, TypeVariable variable) { Type[] typeParameters = declaration.getTypeParameters(); for (int i = 0; i < typeParameters.length; i++) { if (typeParameters[i] instanceof TypeVariable) { TypeVariable variableArgument = (TypeVariable)typeParameters[i]; if (variableArgument.getName().equals(variable.getName())) { return i; } } } return -1; } private static int getIndex(ParameterizedType type, TypeVariable variable) { Type[] actualTypeArguments = type.getActualTypeArguments(); for (int i = 0; i < actualTypeArguments.length; i++) { if (actualTypeArguments[i] instanceof TypeVariable) { TypeVariable variableArgument = (TypeVariable)actualTypeArguments[i]; if (variableArgument.getName().equals(variable.getName())) { return i; } } } return -1; } private static Class getDirectSubclass(Class declaringClass, Class actualClass) { if (actualClass.isInterface()) { Class subclass = declaringClass; for (Class iface: declaringClass.getInterfaces()) { if (iface == actualClass) { return subclass; } if (actualClass.isAssignableFrom(iface)) { subclass = iface; } else { subclass = declaringClass.getSuperclass(); } } return getDirectSubclass(subclass, actualClass); } else { Class directSubclass = declaringClass; while (directSubclass.getSuperclass() != actualClass) { directSubclass = directSubclass.getSuperclass(); } return directSubclass; } } private static Type getGenericSuperclass(Class subclass, Class superclass) { if (!superclass.isInterface()) { return subclass.getGenericSuperclass(); } else { for (Type genericInterface: subclass.getGenericInterfaces()) { if (getRawType(genericInterface) == superclass) { return genericInterface; } } } return superclass; } private static Type[] resolveTypeArguments(Class subclass, Type supertype) { if (supertype instanceof ParameterizedType) { ParameterizedType parameterizedSupertype = (ParameterizedType)supertype; return resolveTypeArguments(subclass, parameterizedSupertype); } else { return subclass.getTypeParameters(); } } private static Type[] resolveTypeArguments(Class subclass, ParameterizedType parameterizedSupertype) { Type genericSuperclass = getGenericSuperclass(subclass, getRawType(parameterizedSupertype)); if (!(genericSuperclass instanceof ParameterizedType)) { return subclass.getTypeParameters(); } ParameterizedType parameterizedSuperclass = (ParameterizedType)genericSuperclass; Type[] typeParameters = subclass.getTypeParameters(); Type[] actualTypeArguments = parameterizedSupertype.getActualTypeArguments(); return resolveTypeArguments(parameterizedSuperclass, typeParameters, actualTypeArguments); } private static Type[] resolveTypeArguments(ParameterizedType subtype, ParameterizedType parameterizedSupertype) { return resolveTypeArguments(getParameterizedType(getRawType(subtype)), parameterizedSupertype.getActualTypeArguments(), subtype.getActualTypeArguments()); } private static Type[] resolveTypeArguments(ParameterizedType parameterizedType, Type[] typeParameters, Type[] actualTypeArguments) { Type[] resolvedTypeArguments = new Type[typeParameters.length]; for (int i = 0; i < typeParameters.length; i++) { resolvedTypeArguments[i] = resolveTypeArgument(parameterizedType, typeParameters[i], actualTypeArguments); } return resolvedTypeArguments; } private static Type resolveTypeArgument(ParameterizedType parameterizedType, Type typeParameter, Type[] actualTypeArguments) { if (typeParameter instanceof TypeVariable) { TypeVariable variable = (TypeVariable)typeParameter; int index = getIndex(parameterizedType, variable); if (index == -1) { return typeParameter; } else { return actualTypeArguments[index]; } } else if (typeParameter instanceof GenericArrayType) { GenericArrayType array = (GenericArrayType)typeParameter; return createArrayType(resolveTypeArgument(parameterizedType, array.getGenericComponentType(), actualTypeArguments)); } else { return typeParameter; } } private static Type createArrayType(Type componentType) { if (componentType instanceof Class) { return Array.newInstance((Class)componentType, 0).getClass(); } else { return new OwbGenericArrayTypeImpl(componentType); } } public static Type resolveType(ParameterizedType parameterizedType, Type metadataType) { return resolveType(parameterizedType, metadataType, newSeenList()); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy