Package com.vercer.engine.persist.util.generic

Source Code of com.vercer.engine.persist.util.generic.GenericTypeReflector

/*
* Copied from Gentyref project http://code.google.com/p/gentyref/
* Reformatted and moved to fit package structure
*/
package com.vercer.engine.persist.util.generic;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
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.Arrays;
import java.util.List;

/**
* Utility class for doing reflection on types.
*
* @author Wouter Coekaerts <wouter@coekaerts.be>
*/
public class GenericTypeReflector
{
  private static final Type UNBOUND_WILDCARD = new WildcardTypeImpl(new Type[] { Object.class },
      new Type[] {});

  /**
   * Returns the erasure of the given type.
   */
  public static Class<?> erase(Type type)
  {
    if (type instanceof Class<?>)
    {
      return (Class<?>) type;
    }
    else if (type instanceof ParameterizedType)
    {
      return (Class<?>) ((ParameterizedType) type).getRawType();
    }
    else if (type instanceof TypeVariable<?>)
    {
      TypeVariable<?> tv = (TypeVariable<?>) type;
      if (tv.getBounds().length == 0)
        return Object.class;
      else
        return erase(tv.getBounds()[0]);
    }
    else if (type instanceof GenericArrayType)
    {
      GenericArrayType aType = (GenericArrayType) type;
      return GenericArrayTypeImpl.createArrayType(erase(aType.getGenericComponentType()));
    }
    else
    {
      // TODO at least support CaptureType here
      throw new RuntimeException("not supported: " + type.getClass());
    }
  }

  /**
   * Maps type parameters in a type to their values.
   *
   * @param toMapType
   *            Type possibly containing type arguments
   * @param typeAndParams
   *            must be either ParameterizedType, or (in case there are no
   *            type arguments, or it's a raw type) Class
   * @return toMapType, but with type parameters from typeAndParams replaced.
   */
  private static Type mapTypeParameters(Type toMapType, Type typeAndParams)
  {
    if (isMissingTypeParameters(typeAndParams))
    {
      return erase(toMapType);
    }
    else
    {
      VarMap varMap = new VarMap();
      Type handlingTypeAndParams = typeAndParams;
      while (handlingTypeAndParams instanceof ParameterizedType)
      {
        ParameterizedType pType = (ParameterizedType) handlingTypeAndParams;
        Class<?> clazz = (Class<?>) pType.getRawType(); // getRawType
                                // should always
                                // be Class
        varMap.addAll(clazz.getTypeParameters(), pType.getActualTypeArguments());
        handlingTypeAndParams = pType.getOwnerType();
      }
      return varMap.map(toMapType);
    }
  }

  /**
   * Checks if the given type is a class that is supposed to have type
   * parameters, but doesn't. In other words, if it's a really raw type.
   */
  private static boolean isMissingTypeParameters(Type type)
  {
    if (type instanceof Class<?>)
    {
      for (Class<?> clazz = (Class<?>) type; clazz != null; clazz = clazz.getEnclosingClass())
      {
        if (clazz.getTypeParameters().length != 0)
          return true;
      }
      return false;
    }
    else if (type instanceof ParameterizedType)
    {
      return false;
    }
    else
    {
      throw new AssertionError("Unexpected type " + type.getClass());
    }
  }

  /**
   * Returns a type representing the class, with all type parameters the
   * unbound wildcard ("?"). For example,
   * <tt>addWildcardParameters(Map.class)</tt> returns a type representing
   * <tt>Map&lt;?,?&gt;</tt>.
   *
   * @return <ul>
   *         <li>If clazz is a class or interface without type parameters,
   *         clazz itself is returned.</li>
   *         <li>If clazz is a class or interface with type parameters, an
   *         instance of ParameterizedType is returned.</li>
   *         <li>if clazz is an array type, an array type is returned with
   *         unbound wildcard parameters added in the the component type.
   *         </ul>
   */
  public static Type addWildcardParameters(Class<?> clazz)
  {
    if (clazz.isArray())
    {
      return GenericArrayTypeImpl.createArrayType(addWildcardParameters(clazz
          .getComponentType()));
    }
    else if (isMissingTypeParameters(clazz))
    {
      TypeVariable<?>[] vars = clazz.getTypeParameters();
      Type[] arguments = new Type[vars.length];
      Arrays.fill(arguments, UNBOUND_WILDCARD);
      Type owner = clazz.getDeclaringClass() == null ? null : addWildcardParameters(clazz
          .getDeclaringClass());
      return new ParameterizedTypeImpl(clazz, arguments, owner);
    }
    else
    {
      return clazz;
    }
  }

  /**
   * With type a supertype of searchClass, returns the exact supertype of the
   * given class, including type parameters. For example, with
   * <tt>class StringList implements List&lt;String&gt;</tt>,
   * <tt>getExactSuperType(StringList.class, Collection.class)</tt> returns a
   * {@link ParameterizedType} representing <tt>Collection&lt;String&gt;</tt>.
   * <ul>
   * <li>Returns null if <tt>searchClass</tt> is not a superclass of type.</li>
   * <li>Returns an instance of {@link Class} if <tt>type</tt> if it is a raw
   * type, or has no type parameters</li>
   * <li>Returns an instance of {@link ParameterizedType} if the type does
   * have parameters</li>
   * <li>Returns an instance of {@link GenericArrayType} if
   * <tt>searchClass</tt> is an array type, and the actual type has type
   * parameters</li>
   * </ul>
   */
  public static Type getExactSuperType(Type type, Class<?> searchClass)
  {
    if (type instanceof ParameterizedType || type instanceof Class<?>
        || type instanceof GenericArrayType)
    {
      Class<?> clazz = erase(type);

      if (searchClass == clazz)
      {
        return type;
      }

      if (!searchClass.isAssignableFrom(clazz))
        return null;
    }

    for (Type superType : getExactDirectSuperTypes(type))
    {
      Type result = getExactSuperType(superType, searchClass);
      if (result != null)
        return result;
    }

    return null;
  }

  /**
   * Gets the type parameter for a given type that is the value for a given
   * type variable. For example, with
   * <tt>class StringList implements List&lt;String&gt;</tt>,
   * <tt>getTypeParameter(StringList.class, Collection.class.getTypeParameters()[0])</tt>
   * returns <tt>String</tt>.
   *
   * @param type
   *            The type to inspect.
   * @param variable
   *            The type variable to find the value for.
   * @return The type parameter for the given variable. Or null if type is not
   *         a subtype of the type that declares the variable, or if the
   *         variable isn't known (because of raw types).
   */
  public static Type getTypeParameter(Type type, TypeVariable<? extends Class<?>> variable)
  {
    Class<?> clazz = variable.getGenericDeclaration();
    Type superType = getExactSuperType(type, clazz);
    if (superType instanceof ParameterizedType)
    {
      int index = Arrays.asList(clazz.getTypeParameters()).indexOf(variable);
      return ((ParameterizedType) superType).getActualTypeArguments()[index];
    }
    else
    {
      return null;
    }
  }

  /**
   * Checks if the capture of subType is a subtype of superType
   */
  public static boolean isSuperType(Type superType, Type subType)
  {
    if (superType instanceof ParameterizedType || superType instanceof Class<?>
        || superType instanceof GenericArrayType)
    {
      Class<?> superClass = erase(superType);
      Type mappedSubType = getExactSuperType(capture(subType), superClass);
      if (mappedSubType == null)
      {
        return false;
      }
      else if (superType instanceof Class<?>)
      {
        return true;
      }
      else if (mappedSubType instanceof Class<?>)
      {
        // TODO treat supertype by being raw type differently
        // ("supertype, but with warnings")
        return true; // class has no parameters, or it's a raw type
      }
      else if (mappedSubType instanceof GenericArrayType)
      {
        Type superComponentType = getArrayComponentType(superType);
        assert superComponentType != null;
        Type mappedSubComponentType = getArrayComponentType(mappedSubType);
        assert mappedSubComponentType != null;
        return isSuperType(superComponentType, mappedSubComponentType);
      }
      else
      {
        assert mappedSubType instanceof ParameterizedType;
        ParameterizedType pMappedSubType = (ParameterizedType) mappedSubType;
        assert pMappedSubType.getRawType() == superClass;
        ParameterizedType pSuperType = (ParameterizedType) superType;

        Type[] superTypeArgs = pSuperType.getActualTypeArguments();
        Type[] subTypeArgs = pMappedSubType.getActualTypeArguments();
        assert superTypeArgs.length == subTypeArgs.length;
        for (int i = 0; i < superTypeArgs.length; i++)
        {
          if (!contains(superTypeArgs[i], subTypeArgs[i]))
          {
            return false;
          }
        }
        // params of the class itself match, so if the owner types are
        // supertypes too, it's a supertype.
        return pSuperType.getOwnerType() == null
            || isSuperType(pSuperType.getOwnerType(), pMappedSubType.getOwnerType());
      }
    }
    else if (superType instanceof CaptureType)
    {
      if (superType.equals(subType))
        return true;
      for (Type lowerBound : ((CaptureType) superType).getLowerBounds())
      {
        if (isSuperType(lowerBound, subType))
        {
          return true;
        }
      }
      return false;
    }
    else if (superType instanceof GenericArrayType)
    {
      return isArraySupertype(superType, subType);
    }
    else
    {
      throw new RuntimeException("not implemented: " + superType.getClass());
    }
  }

  private static boolean isArraySupertype(Type arraySuperType, Type subType)
  {
    Type superTypeComponent = getArrayComponentType(arraySuperType);
    assert superTypeComponent != null;
    Type subTypeComponent = getArrayComponentType(subType);
    if (subTypeComponent == null)
    { // subType is not an array type
      return false;
    }
    else
    {
      return isSuperType(superTypeComponent, subTypeComponent);
    }
  }

  /**
   * If type is an array type, returns the type of the component of the array.
   * Otherwise, returns null.
   */
  public static Type getArrayComponentType(Type type)
  {
    if (type instanceof Class<?>)
    {
      Class<?> clazz = (Class<?>) type;
      return clazz.getComponentType();
    }
    else if (type instanceof GenericArrayType)
    {
      GenericArrayType aType = (GenericArrayType) type;
      return aType.getGenericComponentType();
    }
    else
    {
      return null;
    }
  }

  private static boolean contains(Type containingType, Type containedType)
  {
    if (containingType instanceof WildcardType)
    {
      WildcardType wContainingType = (WildcardType) containingType;
      for (Type upperBound : wContainingType.getUpperBounds())
      {
        if (!isSuperType(upperBound, containedType))
        {
          return false;
        }
      }
      for (Type lowerBound : wContainingType.getLowerBounds())
      {
        if (!isSuperType(containedType, lowerBound))
        {
          return false;
        }
      }
      return true;
    }
    else
    {
      return containingType.equals(containedType);
    }
  }

  /**
   * Returns the direct supertypes of the given type. Resolves type
   * parameters.
   */
  public static Type[] getExactDirectSuperTypes(Type type)
  {
    if (type instanceof ParameterizedType || type instanceof Class<?>)
    {
      Class<?> clazz;
      if (type instanceof ParameterizedType)
      {
        clazz = (Class<?>) ((ParameterizedType) type).getRawType();
      }
      else
      {
        // TODO primitive types?
        clazz = (Class<?>) type;
        if (clazz.isArray())
          return getArrayExactDirectSuperTypes(clazz);
      }

      Type[] superInterfaces = clazz.getGenericInterfaces();
      Type superClass = clazz.getGenericSuperclass();
      Type[] result;
      int resultIndex;
      if (superClass == null)
      {
        result = new Type[superInterfaces.length];
        resultIndex = 0;
      }
      else
      {
        result = new Type[superInterfaces.length + 1];
        resultIndex = 1;
        result[0] = mapTypeParameters(superClass, type);
      }
      for (Type superInterface : superInterfaces)
      {
        result[resultIndex++] = mapTypeParameters(superInterface, type);
      }

      return result;
    }
    else if (type instanceof TypeVariable<?>)
    {
      TypeVariable<?> tv = (TypeVariable<?>) type;
      return tv.getBounds();
    }
    else if (type instanceof WildcardType)
    {
      // This should be a rare case: normally this wildcard is already
      // captured.
      // But it does happen if the upper bound of a type variable contains
      // a wildcard
      // TODO shouldn't upper bound of type variable have been captured
      // too? (making this case impossible?)
      return ((WildcardType) type).getUpperBounds();
    }
    else if (type instanceof CaptureType)
    {
      return ((CaptureType) type).getUpperBounds();
    }
    else if (type instanceof GenericArrayType)
    {
      return getArrayExactDirectSuperTypes(type);
    }
    else
    {
      throw new RuntimeException("not implemented type: " + type);
    }
  }

  private static Type[] getArrayExactDirectSuperTypes(Type arrayType)
  {
    // see
    // http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10.3
    Type typeComponent = getArrayComponentType(arrayType);

    Type[] result;
    int resultIndex;
    if (typeComponent instanceof Class<?> && ((Class<?>) typeComponent).isPrimitive())
    {
      resultIndex = 0;
      result = new Type[3];
    }
    else
    {
      Type[] componentSupertypes = getExactDirectSuperTypes(typeComponent);
      result = new Type[componentSupertypes.length + 3];
      for (resultIndex = 0; resultIndex < componentSupertypes.length; resultIndex++)
      {
        result[resultIndex] = GenericArrayTypeImpl
            .createArrayType(componentSupertypes[resultIndex]);
      }
    }
    result[resultIndex++] = Object.class;
    result[resultIndex++] = Cloneable.class;
    result[resultIndex++] = Serializable.class;
    return result;
  }

  /**
   * Returns the exact return type of the given method in the given type. This
   * may be different from <tt>m.getGenericReturnType()</tt> when the method
   * was declared in a superclass, of <tt>type</tt> is a raw type.
   */
  public static Type getExactReturnType(Method m, Type type)
  {
    Type returnType = m.getGenericReturnType();
    Type exactDeclaringType = getExactSuperType(capture(type), m.getDeclaringClass());
    return mapTypeParameters(returnType, exactDeclaringType);
  }

  /**
   * Returns the exact type of the given field in the given type. This may be
   * different from <tt>f.getGenericType()</tt> when the field was declared in
   * a superclass, of <tt>type</tt> is a raw type.
   */
  public static Type getExactFieldType(Field f, Type type)
  {
    Type returnType = f.getGenericType();
    Type exactDeclaringType = getExactSuperType(capture(type), f.getDeclaringClass());
    return mapTypeParameters(returnType, exactDeclaringType);
  }

  /**
   * Applies capture conversion to the given type.
   */
  public static Type capture(Type type)
  {
    VarMap varMap = new VarMap();
    List<CaptureTypeImpl> toInit = new ArrayList<CaptureTypeImpl>();
    if (type instanceof ParameterizedType)
    {
      ParameterizedType pType = (ParameterizedType) type;
      Class<?> clazz = (Class<?>) pType.getRawType();
      Type[] arguments = pType.getActualTypeArguments();
      TypeVariable<?>[] vars = clazz.getTypeParameters();
      Type[] capturedArguments = new Type[arguments.length];
      assert arguments.length == vars.length;
      for (int i = 0; i < arguments.length; i++)
      {
        Type argument = arguments[i];
        if (argument instanceof WildcardType)
        {
          CaptureTypeImpl captured = new CaptureTypeImpl((WildcardType) argument, vars[i]);
          argument = captured;
          toInit.add(captured);
        }
        capturedArguments[i] = argument;
        varMap.add(vars[i], argument);
      }
      for (CaptureTypeImpl captured : toInit)
      {
        captured.init(varMap);
      }
      Type ownerType = (pType.getOwnerType() == null) ? null : capture(pType.getOwnerType());
      return new ParameterizedTypeImpl(clazz, capturedArguments, ownerType);
    }
    else
    {
      return type;
    }
  }

  /**
   * Returns the display name of a Type.
   */
  public static String getTypeName(Type type)
  {
    if (type instanceof Class<?>)
    {
      Class<?> clazz = (Class<?>) type;
      return clazz.isArray() ? (getTypeName(clazz.getComponentType()) + "[]") : clazz.getName();
    }
    else
    {
      return type.toString();
    }
  }
}
TOP

Related Classes of com.vercer.engine.persist.util.generic.GenericTypeReflector

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.