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

org.evosuite.utils.generic.GenericClass Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * This file is part of EvoSuite.
 *
 * EvoSuite is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3.0 of the License, or
 * (at your option) any later version.
 *
 * EvoSuite is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with EvoSuite. If not, see .
 */
package org.evosuite.utils.generic;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.*;

import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.evosuite.Properties;
import org.evosuite.TestGenerationContext;
import org.evosuite.ga.ConstructionFailedException;
import org.evosuite.seeding.CastClassManager;
import org.evosuite.utils.LoggingUtils;
import org.evosuite.utils.ParameterizedTypeImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.gentyref.CaptureType;
import com.googlecode.gentyref.GenericTypeReflector;

public class GenericClass implements Serializable {

	private static final Logger logger = LoggerFactory.getLogger(GenericClass.class);

	private static List primitiveClasses = Arrays.asList("char", "int", "short",
	                                                             "long", "boolean",
	                                                             "float", "double",
	                                                             "byte");

	private static final long serialVersionUID = -3307107227790458308L;

	/**
	 * Set of wrapper classes
	 */
	private static final Set> WRAPPER_TYPES = new LinkedHashSet>(
	        Arrays.asList(Boolean.class, Character.class, Byte.class, Short.class,
	                      Integer.class, Long.class, Float.class, Double.class,
	                      Void.class));

	protected static Type addTypeParameters(Class clazz) {
		if (clazz.isArray()) {
			return GenericArrayTypeImpl.createArrayType(addTypeParameters(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
			        : addTypeParameters(clazz.getDeclaringClass());
			return new ParameterizedTypeImpl(clazz, vars, owner);
		} else {
			return clazz;
		}
	}

	/**
	 * Returns the erasure of the given type.
	 */
	private 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 if (type instanceof CaptureType) {
			CaptureType captureType = (CaptureType) type;
			if (captureType.getUpperBounds().length == 0)
				return Object.class;
			else
				return erase(captureType.getUpperBounds()[0]);
		} else {
			// TODO at least support CaptureType here
			throw new RuntimeException("not supported: " + type.getClass());
		}
	}

	private static Class getClass(String name) throws ClassNotFoundException {
		return getClass(name, TestGenerationContext.getInstance().getClassLoaderForSUT());
	}

	private static Class getClass(String name, ClassLoader loader)
	        throws ClassNotFoundException {
		if (name.equals("void"))
			return void.class;
		else if (name.equals("int") || name.equals("I"))
			return int.class;
		else if (name.equals("short") || name.equals("S"))
			return short.class;
		else if (name.equals("long") || name.equals("J"))
			return long.class;
		else if (name.equals("float") || name.equals("F"))
			return float.class;
		else if (name.equals("double") || name.equals("D"))
			return double.class;
		else if (name.equals("boolean") || name.equals("Z"))
			return boolean.class;
		else if (name.equals("byte") || name.equals("B"))
			return byte.class;
		else if (name.equals("char") || name.equals("C"))
			return char.class;
		else if (name.startsWith("[")) {
			Class componentType = getClass(name.substring(1, name.length()), loader);
			Object array = Array.newInstance(componentType, 0);
			return array.getClass();
		} else if (name.startsWith("L") && name.endsWith(";")) {
			return getClass(name.substring(1, name.length() - 1), loader);
		} else if (name.endsWith(";")) {
			return getClass(name.substring(0, name.length() - 1), loader);
		} else if (name.endsWith(".class")) {
			return getClass(name.replace(".class", ""), loader);
		} else
			return loader.loadClass(name);
	}

	/**
	 * 

* isAssignable *

* * @param lhsType * a {@link java.lang.reflect.Type} object. * @param rhsType * a {@link java.lang.reflect.Type} object. * @return a boolean. */ public static boolean isAssignable(Type lhsType, Type rhsType) { if (rhsType == null || lhsType == null) return false; try { return TypeUtils.isAssignable(rhsType, lhsType); } catch (Throwable e) { logger.debug("Found unassignable type: " + e); return false; } } public 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 if(type instanceof GenericArrayType) { return false; } else if(type instanceof TypeVariable) { return false; } else if(type instanceof WildcardType) { return false; } else { throw new AssertionError("Unexpected type " + type.getClass()); } } /** *

* isSubclass *

* * @param superclass * a {@link java.lang.reflect.Type} object. * @param subclass * a {@link java.lang.reflect.Type} object. * @return a boolean. */ public static boolean isSubclass(Type superclass, Type subclass) { List> superclasses = ClassUtils.getAllSuperclasses((Class) subclass); List> interfaces = ClassUtils.getAllInterfaces((Class) subclass); if (superclasses.contains(superclass) || interfaces.contains(superclass)) { return true; } return false; } transient Class rawClass = null; transient Type type = null; /** * Generate a generic class by setting all generic parameters to their * parameter types * * @param clazz * a {@link java.lang.Class} object. */ public GenericClass(Class clazz) { this.type = addTypeParameters(clazz); //GenericTypeReflector.addWildcardParameters(clazz); this.rawClass = clazz; } public GenericClass(GenericClass copy) { this.type = copy.type; this.rawClass = copy.rawClass; } /** * Generate a generic class by from a type * * @param type * a {@link java.lang.reflect.Type} object. */ public GenericClass(Type type) { if (type instanceof Class) { this.type = addTypeParameters((Class) type); //GenericTypeReflector.addWildcardParameters((Class) type); this.rawClass = (Class) type; } else { if (!handleGenericArraySpecialCase(type)) { this.type = type; try { this.rawClass = erase(type); } catch (RuntimeException e) { // If there is an unresolved capture type in here // we delete it and replace with a wildcard this.rawClass = Object.class; } } } } /** * Generate a GenericClass with this exact generic type and raw class * * @param type * @param clazz */ public GenericClass(Type type, Class clazz) { this.type = type; this.rawClass = clazz; handleGenericArraySpecialCase(type); } /** * Determine if there exists an instantiation of the type variables such * that the class matches otherType * * @param otherType * is the class we want to generate * @return */ public boolean canBeInstantiatedTo(GenericClass otherType) { if (isPrimitive() && otherType.isWrapperType()) return false; if (isAssignableTo(otherType)) return true; if (!isTypeVariable() && !otherType.isTypeVariable()) { try { if (otherType.isGenericSuperTypeOf(this)) return true; } catch (RuntimeException e) { // FIXME: GentyRef sometimes throws: // java.lang.RuntimeException: not implemented: class sun.reflect.generics.reflectiveObjects.TypeVariableImpl // While I have no idea why, it should be safe to proceed if we can ignore this type return false; } } Class otherRawClass = otherType.getRawClass(); if (otherRawClass.isAssignableFrom(rawClass)) { //logger.debug("Raw classes are assignable: " + otherType + ", have: " // + toString()); Map, Type> typeMap = otherType.getTypeVariableMap(); if (otherType.isParameterizedType()) { typeMap.putAll(TypeUtils.determineTypeArguments(rawClass, (ParameterizedType) otherType.getType())); } //logger.debug(typeMap.toString()); try { GenericClass instantiation = getGenericInstantiation(typeMap); if (equals(instantiation)) { //logger.debug("Instantiation is equal to original, so I think we can't assign: " // + instantiation); if (hasWildcardOrTypeVariables()) return false; else return true; } //logger.debug("Checking instantiation: " + instantiation); return instantiation.canBeInstantiatedTo(otherType); } catch (ConstructionFailedException e) { logger.debug("Failed to instantiate " + toString()); return false; } } return false; } /** *

* changeClassLoader *

* * @param loader * a {@link java.lang.ClassLoader} object. */ public void changeClassLoader(ClassLoader loader) { try { if (rawClass != null) rawClass = getClass(rawClass.getName(), loader); if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; // GenericClass rawType = new GenericClass(pt.getRawType()); // rawType.changeClassLoader(loader); GenericClass ownerType = null; if (pt.getOwnerType() != null) { ownerType = new GenericClass(pt.getOwnerType()); // ownerType.type = pt.getOwnerType(); ownerType.changeClassLoader(loader); } List parameterClasses = new ArrayList(); for (Type parameterType : pt.getActualTypeArguments()) { GenericClass parameter = new GenericClass(parameterType); // parameter.type = parameterType; parameter.changeClassLoader(loader); parameterClasses.add(parameter); } Type[] parameterTypes = new Type[parameterClasses.size()]; for (int i = 0; i < parameterClasses.size(); i++) parameterTypes[i] = parameterClasses.get(i).getType(); this.type = new ParameterizedTypeImpl(rawClass, parameterTypes, ownerType != null ? ownerType.getType() : null); } else if (type instanceof GenericArrayType) { GenericClass componentClass = getComponentClass(); componentClass.changeClassLoader(loader); this.type = GenericArrayTypeImpl.createArrayType(componentClass.getType()); } else if (type instanceof WildcardType) { Type[] oldUpperBounds = ((WildcardType) type).getUpperBounds(); Type[] oldLowerBounds = ((WildcardType) type).getLowerBounds(); Type[] upperBounds = new Type[oldUpperBounds.length]; Type[] lowerBounds = new Type[oldLowerBounds.length]; for (int i = 0; i < oldUpperBounds.length; i++) { GenericClass bound = new GenericClass(oldUpperBounds[i]); // bound.type = oldUpperBounds[i]; bound.changeClassLoader(loader); upperBounds[i] = bound.getType(); } for (int i = 0; i < oldLowerBounds.length; i++) { GenericClass bound = new GenericClass(oldLowerBounds[i]); // bound.type = oldLowerBounds[i]; bound.changeClassLoader(loader); lowerBounds[i] = bound.getType(); } this.type = new WildcardTypeImpl(upperBounds, lowerBounds); } else if (type instanceof TypeVariable) { for (TypeVariable newVar : rawClass.getTypeParameters()) { if (newVar.getName().equals(((TypeVariable) type).getName())) { this.type = newVar; break; } } } else { this.type = addTypeParameters(rawClass); //GenericTypeReflector.addWildcardParameters(raw_class); } } catch (ClassNotFoundException e) { logger.warn("Class not found: " + rawClass + " - keeping old class loader ", e); } catch (SecurityException e) { logger.warn("Class not found: " + rawClass + " - keeping old class loader ", e); } } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; GenericClass other = (GenericClass) obj; //return type.equals(other.type); return getTypeName().equals(other.getTypeName()); /* if (raw_class == null) { if (other.raw_class != null) return false; } else if (!raw_class.equals(other.raw_class)) return false; */ /* if (type == null) { if (other.type != null) return false; } else if (!type.equals(other.type)) return false; */ // return true; } public Class getBoxedType() { if (isPrimitive()) { if (rawClass.equals(int.class)) return Integer.class; else if (rawClass.equals(byte.class)) return Byte.class; else if (rawClass.equals(short.class)) return Short.class; else if (rawClass.equals(long.class)) return Long.class; else if (rawClass.equals(float.class)) return Float.class; else if (rawClass.equals(double.class)) return Double.class; else if (rawClass.equals(char.class)) return Character.class; else if (rawClass.equals(boolean.class)) return Boolean.class; else if (rawClass.equals(void.class)) return Void.class; else throw new RuntimeException("Unknown unboxed type: " + rawClass); } return rawClass; } /** *

* getClassName *

* * @return a {@link java.lang.String} object. */ public String getClassName() { return rawClass.getName(); } public GenericClass getComponentClass() { if (type instanceof GenericArrayType) { GenericArrayType arrayType = (GenericArrayType) type; Type componentType = arrayType.getGenericComponentType(); Class rawComponentType = rawClass.getComponentType(); return new GenericClass(componentType, rawComponentType); } else { return new GenericClass(rawClass.getComponentType()); } } /** *

* getComponentName *

* * @return a {@link java.lang.String} object. */ public String getComponentName() { return rawClass.getComponentType().getSimpleName(); } /** *

* getComponentType *

* * @return a {@link java.lang.reflect.Type} object. */ public Type getComponentType() { return GenericTypeReflector.getArrayComponentType(type); } public Collection getGenericBounds() { Set bounds = new LinkedHashSet(); if (isRawClass() || !hasWildcardOrTypeVariables()) { return bounds; } if (isWildcardType()) { getGenericWildcardBounds(bounds); } else if (isArray()) { bounds.addAll(getComponentClass().getGenericBounds()); } else if (isTypeVariable()) { getGenericTypeVarBounds(bounds); } else if (isParameterizedType()) { getGenericParameterizedTypeBounds(bounds); } return bounds; } private void getGenericWildcardBounds(Collection bounds) { for(Type t : ((WildcardType)type).getUpperBounds()) { bounds.add(new GenericClass(t)); } for(Type t : ((WildcardType)type).getLowerBounds()) { bounds.add(new GenericClass(t)); } } private void getGenericTypeVarBounds(Collection bounds) { for(Type t : ((TypeVariable)type).getBounds()) { bounds.add(new GenericClass(t)); } } private void getGenericParameterizedTypeBounds(Collection bounds) { for(TypeVariable typeVar : getTypeVariables()) { for(Type t : typeVar.getBounds()) { bounds.add(new GenericClass(t)); } } } /** * Instantiate all type variables randomly, but adhering to type boundaries * * @return * @throws ConstructionFailedException */ public GenericClass getGenericInstantiation() throws ConstructionFailedException { return getGenericInstantiation(new HashMap, Type>()); } /** * Instantiate type variables using map, and anything not contained in the * map randomly * * @param typeMap * @return * @throws ConstructionFailedException */ public GenericClass getGenericInstantiation(Map, Type> typeMap) throws ConstructionFailedException { return getGenericInstantiation(typeMap, 0); } private GenericClass getGenericInstantiation(Map, Type> typeMap, int recursionLevel) throws ConstructionFailedException { logger.debug("Instantiation " + toString() + " with type map " + typeMap); // If there are no type variables, create copy if (isRawClass() || !hasWildcardOrTypeVariables() || recursionLevel > Properties.MAX_GENERIC_DEPTH) { logger.debug("Nothing to replace: " + toString() + ", " + isRawClass() + ", " + hasWildcardOrTypeVariables()); return new GenericClass(this); } if (isWildcardType()) { logger.debug("Is wildcard type."); return getGenericWildcardInstantiation(typeMap, recursionLevel); } else if (isArray()) { return getGenericArrayInstantiation(typeMap, recursionLevel); } else if (isTypeVariable()) { logger.debug("Is type variable "); return getGenericTypeVariableInstantiation(typeMap, recursionLevel); } else if (isParameterizedType()) { logger.debug("Is parameterized type"); return getGenericParameterizedTypeInstantiation(typeMap, recursionLevel); } // TODO return null; } /** * Instantiate generic component type * * @param typeMap * @throws ConstructionFailedException * evel * @return */ private GenericClass getGenericArrayInstantiation(Map, Type> typeMap, int recursionLevel) throws ConstructionFailedException { GenericClass componentClass = getComponentClass().getGenericInstantiation(); return getWithComponentClass(componentClass); } /** * Instantiate type variable * * @param typeMap * @param recursionLevel * @return * @throws ConstructionFailedException */ private GenericClass getGenericTypeVariableInstantiation( Map, Type> typeMap, int recursionLevel) throws ConstructionFailedException { if (typeMap.containsKey(type)) { logger.debug("Type contains {}: {}", toString(), typeMap); if(typeMap.get(type) == type) { // FIXXME: How does this happen? throw new ConstructionFailedException("Type points to itself"); } //TODO: If typeMap.get(type) is a wildcard we need to keep the bounds of the // type variable in mind anyway, so this needs to be rewritten/fixed. GenericClass selectedClass = new GenericClass(typeMap.get(type)).getGenericInstantiation(typeMap, recursionLevel + 1); if (!selectedClass.satisfiesBoundaries((TypeVariable) type)) { logger.debug("Cannot be instantiated to: {}", selectedClass); } else { logger.debug("Can be instantiated to: {}", selectedClass); return selectedClass; } } logger.debug("Type map does not contain {}: {}", toString(), typeMap); GenericClass selectedClass = CastClassManager.getInstance().selectCastClass((TypeVariable) type, recursionLevel < Properties.MAX_GENERIC_DEPTH, typeMap); if (selectedClass == null) { throw new ConstructionFailedException("Unable to instantiate " + toString()); } logger.debug("Getting instantiation of type variable {}: {}", toString(), selectedClass); Map, Type> extendedMap = new HashMap, Type>( typeMap); extendedMap.putAll(getTypeVariableMap()); for (Type bound : ((TypeVariable) type).getBounds()) { logger.debug("Current bound of variable {}: {}", type, bound); GenericClass boundClass = new GenericClass(bound); extendedMap.putAll(boundClass.getTypeVariableMap()); if(boundClass.isParameterizedType()) { Class boundRawClass = boundClass.getRawClass(); if(boundRawClass.isAssignableFrom(selectedClass.getRawClass())) { Map, Type> xmap = TypeUtils.determineTypeArguments(selectedClass.getRawClass(), (ParameterizedType) boundClass.getType()); extendedMap.putAll(xmap); } } } logger.debug("Updated type variable map to {}", extendedMap); GenericClass instantiation = selectedClass.getGenericInstantiation(extendedMap, recursionLevel + 1); typeMap.put((TypeVariable) type, instantiation.getType()); return instantiation; } /** * Instantiate wildcard type * * @param typeMap * @param recursionLevel * @return * @throws ConstructionFailedException */ private GenericClass getGenericWildcardInstantiation( Map, Type> typeMap, int recursionLevel) throws ConstructionFailedException { GenericClass selectedClass = CastClassManager.getInstance().selectCastClass((WildcardType) type, recursionLevel < Properties.MAX_GENERIC_DEPTH, typeMap); return selectedClass.getGenericInstantiation(typeMap, recursionLevel + 1); } public List getInterfaces() { List ret = new ArrayList(); for(Class intf : rawClass.getInterfaces()) { ret.add(new GenericClass(intf)); } return ret; } /** * Instantiate all type parameters of a parameterized type * * @param typeMap * @param recursionLevel * @return * @throws ConstructionFailedException */ private GenericClass getGenericParameterizedTypeInstantiation( Map, Type> typeMap, int recursionLevel) throws ConstructionFailedException { // FIXME: This negatively affects coverage. Why was it added? // // if(isClass() && !hasTypeVariables()) { // return this; // } List> typeParameters = getTypeVariables(); Type[] parameterTypes = new Type[typeParameters.size()]; Type ownerType = null; int numParam = 0; for (GenericClass parameterClass : getParameterClasses()) { logger.debug("Current parameter to instantiate", parameterClass); /* * If the parameter is a parameterized type variable such as T extends Map * then the boundaries of the parameters of the type variable need to be respected */ if (!parameterClass.hasWildcardOrTypeVariables()) { logger.debug("Parameter has no wildcard or type variable"); parameterTypes[numParam++] = parameterClass.getType(); } else { logger.debug("Current parameter has type variables: " + parameterClass); Map, Type> extendedMap = new HashMap, Type>( typeMap); extendedMap.putAll(parameterClass.getTypeVariableMap()); if(!extendedMap.containsKey(typeParameters.get(numParam)) && !parameterClass.isTypeVariable()) extendedMap.put(typeParameters.get(numParam), parameterClass.getType()); logger.debug("New type map: " + extendedMap); if (parameterClass.isWildcardType()) { logger.debug("Is wildcard type, here we should value the wildcard boundaries"); logger.debug("Wildcard boundaries: "+parameterClass.getGenericBounds()); logger.debug("Boundaries of underlying var: "+Arrays.asList(typeParameters.get(numParam).getBounds())); GenericClass parameterInstance = parameterClass.getGenericWildcardInstantiation(extendedMap, recursionLevel + 1); //GenericClass parameterTypeClass = new GenericClass(typeParameters.get(numParam)); // if(!parameterTypeClass.isAssignableFrom(parameterInstance)) { if(!parameterInstance.satisfiesBoundaries(typeParameters.get(numParam))) { throw new ConstructionFailedException("Invalid generic instance"); } //GenericClass parameterInstance = new GenericClass( // typeParameters.get(numParam)).getGenericInstantiation(extendedMap, // recursionLevel + 1); parameterTypes[numParam++] = parameterInstance.getType(); } else { logger.debug("Is not wildcard but type variable? " + parameterClass.isTypeVariable()); GenericClass parameterInstance = parameterClass.getGenericInstantiation(extendedMap, recursionLevel + 1); parameterTypes[numParam++] = parameterInstance.getType(); } } } if (hasOwnerType()) { GenericClass ownerClass = getOwnerType().getGenericInstantiation(typeMap, recursionLevel); ownerType = ownerClass.getType(); } return new GenericClass(new ParameterizedTypeImpl(rawClass, parameterTypes, ownerType)); } /** * Retrieve number of generic type parameters * * @return */ public int getNumParameters() { if (type instanceof ParameterizedType) { return Arrays.asList(((ParameterizedType) type).getActualTypeArguments()).size(); } return 0; } /** * Retrieve the generic owner * * @return */ public GenericClass getOwnerType() { return new GenericClass(((ParameterizedType) type).getOwnerType()); } /** * Retrieve list of actual parameters * * @return */ public List getParameterTypes() { if (type instanceof ParameterizedType) { return Arrays.asList(((ParameterizedType) type).getActualTypeArguments()); } return new ArrayList(); } /** * Retrieve list of parameter classes * * @return */ public List getParameterClasses() { if (type instanceof ParameterizedType) { List parameters = new ArrayList(); for (Type parameterType : ((ParameterizedType) type).getActualTypeArguments()) { parameters.add(new GenericClass(parameterType)); } return parameters; } return new ArrayList(); } /** *

* getRawClass *

* * @return a {@link java.lang.Class} object. */ public Class getRawClass() { return rawClass; } /** *

* getComponentClass *

* * @return a {@link java.lang.reflect.Type} object. */ public Type getRawComponentClass() { return GenericTypeReflector.erase(rawClass.getComponentType()); } public GenericClass getRawGenericClass() { return new GenericClass(rawClass); } /** *

* getSimpleName *

* * @return a {@link java.lang.String} object. */ public String getSimpleName() { // return raw_class.getSimpleName(); String name = ClassUtils.getShortClassName(rawClass).replace(";", "[]"); if (!isPrimitive() && primitiveClasses.contains(name)) return rawClass.getSimpleName().replace(";", "[]"); return name; } public GenericClass getSuperClass() { return new GenericClass( GenericTypeReflector.getExactSuperType(type, rawClass.getSuperclass())); } /** *

* Getter for the field type. *

* * @return a {@link java.lang.reflect.Type} object. */ public Type getType() { return type; } /** *

* getTypeName *

* * @return a {@link java.lang.String} object. */ public String getTypeName() { return GenericTypeReflector.getTypeName(type); } private Map, Type> typeVariableMap = null; public Map, Type> getTypeVariableMap() { if(typeVariableMap != null) return typeVariableMap; //logger.debug("Getting type variable map for " + type); List> typeVariables = getTypeVariables(); List types = getParameterTypes(); Map, Type> typeMap = new LinkedHashMap, Type>(); try { if (rawClass.getSuperclass() != null && !rawClass.isAnonymousClass() && !rawClass.getSuperclass().isAnonymousClass() && !(hasOwnerType() && getOwnerType().getRawClass().isAnonymousClass())) { GenericClass superClass = getSuperClass(); //logger.debug("Superclass of " + type + ": " + superClass); Map, Type> superMap = superClass.getTypeVariableMap(); //logger.debug("Super map after " + superClass + ": " + superMap); typeMap.putAll(superMap); } for(Class interFace : rawClass.getInterfaces()) { GenericClass interFaceClass = new GenericClass(interFace); //logger.debug("Interface of " + type + ": " + interFaceClass); Map, Type> superMap = interFaceClass.getTypeVariableMap(); //logger.debug("Super map after " + superClass + ": " + superMap); typeMap.putAll(superMap); } if(isTypeVariable()) { for(Type boundType : ((TypeVariable)type).getBounds()) { GenericClass boundClass = new GenericClass(boundType); typeMap.putAll(boundClass.getTypeVariableMap()); } } } catch (Exception e) { logger.debug("Exception while getting type map: " + e); } for (int i = 0; i < typeVariables.size(); i++) { if (types.get(i) != typeVariables.get(i)) { typeMap.put(typeVariables.get(i), types.get(i)); } } //logger.debug("Type map: " + typeMap); typeVariableMap = typeMap; return typeMap; } /** * Return a list of type variables of this type, or an empty list if this is * not a parameterized type * * @return */ public List> getTypeVariables() { List> typeVariables = new ArrayList>(); if (type instanceof ParameterizedType) { //logger.debug("Type variables of "+rawClass+": "); //for(TypeVariable var : rawClass.getTypeParameters()) { // logger.debug("Var "+var+" of "+var.getGenericDeclaration()); //} typeVariables.addAll(Arrays.asList(rawClass.getTypeParameters())); } return typeVariables; } public Class getUnboxedType() { if (isWrapperType()) { if (rawClass.equals(Integer.class)) return int.class; else if (rawClass.equals(Byte.class)) return byte.class; else if (rawClass.equals(Short.class)) return short.class; else if (rawClass.equals(Long.class)) return long.class; else if (rawClass.equals(Float.class)) return float.class; else if (rawClass.equals(Double.class)) return double.class; else if (rawClass.equals(Character.class)) return char.class; else if (rawClass.equals(Boolean.class)) return boolean.class; else if (rawClass.equals(Void.class)) return void.class; else throw new RuntimeException("Unknown boxed type: " + rawClass); } return rawClass; } public GenericClass getWithComponentClass(GenericClass componentClass) { if (type instanceof GenericArrayType) { return new GenericClass( GenericArrayTypeImpl.createArrayType(componentClass.getType()), rawClass); } else { return new GenericClass(type, rawClass); } } public GenericClass getWithGenericParameterTypes(List parameters) { Type[] typeArray = new Type[parameters.size()]; for (int i = 0; i < parameters.size(); i++) { typeArray[i] = parameters.get(i).getType(); } Type ownerType = null; if (type instanceof ParameterizedType) { ownerType = ((ParameterizedType) type).getOwnerType(); } return new GenericClass(new ParameterizedTypeImpl(rawClass, typeArray, ownerType)); } public GenericClass getWithOwnerType(GenericClass ownerClass) { if (type instanceof ParameterizedType) { ParameterizedType currentType = (ParameterizedType) type; return new GenericClass(new ParameterizedTypeImpl(rawClass, currentType.getActualTypeArguments(), ownerClass.getType())); } return new GenericClass(type); } /** * If this is a LinkedList and the super class is a List then * this returns a LinkedList * * @param superClass * @return * @throws ConstructionFailedException */ public GenericClass getWithParametersFromSuperclass(GenericClass superClass) throws ConstructionFailedException { GenericClass exactClass = new GenericClass(type); if (!(type instanceof ParameterizedType)) { exactClass.type = type; return exactClass; } ParameterizedType pType = (ParameterizedType) type; if (superClass.isParameterizedType()) { Map, Type> typeMap = TypeUtils.determineTypeArguments(rawClass, (ParameterizedType) superClass.getType()); return getGenericInstantiation(typeMap); } Class targetClass = superClass.getRawClass(); Class currentClass = rawClass; Type[] parameterTypes = new Type[superClass.getNumParameters()]; superClass.getParameterTypes().toArray(parameterTypes); if (targetClass.equals(currentClass)) { logger.info("Raw classes match, setting parameters to: " + superClass.getParameterTypes()); exactClass.type = new ParameterizedTypeImpl(currentClass, parameterTypes, pType.getOwnerType()); } else { Type ownerType = pType.getOwnerType(); Map, Type> superTypeMap = superClass.getTypeVariableMap(); Type[] origArguments = pType.getActualTypeArguments(); Type[] arguments = new Type[origArguments.length]; // For some reason, doing this would lead to arguments being // of component type TypeVariable, which would lead to // ArrayStoreException if we try to assign a WildcardType //Type[] arguments = Arrays.copyOf(origArguments, origArguments.length); for(int i = 0; i < origArguments.length; i++) arguments[i] = origArguments[i]; List> variables = getTypeVariables(); for (int i = 0; i < arguments.length; i++) { TypeVariable var = variables.get(i); if (superTypeMap.containsKey(var)) { arguments[i] = superTypeMap.get(var); logger.info("Setting type variable " + var + " to " + superTypeMap.get(var)); } else if (arguments[i] instanceof WildcardType && i < parameterTypes.length) { logger.info("Replacing wildcard with " + parameterTypes[i]); logger.info("Lower Bounds: " + Arrays.asList(TypeUtils.getImplicitLowerBounds((WildcardType) arguments[i]))); logger.info("Upper Bounds: " + Arrays.asList(TypeUtils.getImplicitUpperBounds((WildcardType) arguments[i]))); logger.info("Type variable: " + variables.get(i)); if (!TypeUtils.isAssignable(parameterTypes[i], arguments[i])) { logger.info("Not assignable to bounds!"); return null; } else { boolean assignable = false; for (Type bound : variables.get(i).getBounds()) { if (TypeUtils.isAssignable(parameterTypes[i], bound)) { assignable = true; break; } } if (!assignable) { logger.info("Not assignable to type variable!"); return null; } } arguments[i] = parameterTypes[i]; } } GenericClass ownerClass = new GenericClass(ownerType).getWithParametersFromSuperclass(superClass); if (ownerClass == null) return null; exactClass.type = new ParameterizedTypeImpl(currentClass, arguments, ownerClass.getType()); } return exactClass; } public GenericClass getWithParameterTypes(List parameters) { Type[] typeArray = new Type[parameters.size()]; for (int i = 0; i < parameters.size(); i++) { typeArray[i] = parameters.get(i); } Type ownerType = null; if (type instanceof ParameterizedType) { ownerType = ((ParameterizedType) type).getOwnerType(); } return new GenericClass(new ParameterizedTypeImpl(rawClass, typeArray, ownerType)); } public GenericClass getWithParameterTypes(Type[] parameters) { Type ownerType = null; if (type instanceof ParameterizedType) { ownerType = ((ParameterizedType) type).getOwnerType(); } return new GenericClass( new ParameterizedTypeImpl(rawClass, parameters, ownerType)); } public GenericClass getWithWildcardTypes() { Type ownerType = GenericTypeReflector.addWildcardParameters(rawClass); return new GenericClass(ownerType); } private boolean handleGenericArraySpecialCase(Type type) { if (type instanceof GenericArrayType) { // There is some weird problem with generic methods and the component type can be null Type componentType = ((GenericArrayType) type).getGenericComponentType(); if (componentType == null) { this.rawClass = Object[].class; this.type = this.rawClass; return true; } } return false; } /** * Determine if this class is a subclass of superType * * @param superType * @return */ public boolean hasGenericSuperType(GenericClass superType) { return GenericTypeReflector.isSuperType(superType.getType(), type); } /** * Determine if this class is a subclass of superType * * @param superType * @return */ public boolean hasGenericSuperType(Type superType) { return GenericTypeReflector.isSuperType(superType, type); } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getTypeName().hashCode(); //result = prime * result + ((raw_class == null) ? 0 : raw_class.hashCode()); //result = prime * result + ((type == null) ? 0 : type.hashCode()); return result; } public boolean hasOwnerType() { if (type instanceof ParameterizedType) return ((ParameterizedType) type).getOwnerType() != null; else return false; } public boolean hasTypeVariables() { if (isParameterizedType()) { return hasTypeVariables((ParameterizedType) type); } if (isTypeVariable()) return true; return false; } private boolean hasTypeVariables(ParameterizedType parameterType) { for (Type t : parameterType.getActualTypeArguments()) { if (t instanceof TypeVariable) return true; else if (t instanceof ParameterizedType) { if (hasTypeVariables((ParameterizedType) t)) return true; } } return false; } public boolean hasWildcardOrTypeVariables() { if (isTypeVariable() || isWildcardType()) return true; if (hasWildcardTypes()) return true; if (hasTypeVariables()) return true; if (hasOwnerType()) { if (getOwnerType().hasWildcardOrTypeVariables()) return true; } if (type instanceof GenericArrayType) { if (getComponentClass().hasWildcardOrTypeVariables()) return true; } return false; } private boolean hasWildcardType(ParameterizedType parameterType) { for (Type t : parameterType.getActualTypeArguments()) { if (t instanceof WildcardType) return true; else if (t instanceof ParameterizedType) { if (hasWildcardType((ParameterizedType) t)) return true; } } return false; } public boolean hasWildcardTypes() { if (isParameterizedType()) { return hasWildcardType((ParameterizedType) type); } if (isWildcardType()) return true; return false; } /** * True if this represents an abstract class * * @return */ public boolean isAbstract() { return Modifier.isAbstract(rawClass.getModifiers()); } /** * True if this is an anonymous class * * @return */ public boolean isAnonymous() { return rawClass.isAnonymousClass(); } /** * Return true if variable is an array * * @return a boolean. */ public boolean isArray() { return rawClass.isArray(); } /** *

* isAssignableFrom *

* * @param rhsType * a {@link GenericClass} object. * @return a boolean. */ public boolean isAssignableFrom(GenericClass rhsType) { return isAssignable(type, rhsType.type); } /** *

* isAssignableFrom *

* * @param rhsType * a {@link java.lang.reflect.Type} object. * @return a boolean. */ public boolean isAssignableFrom(Type rhsType) { return isAssignable(type, rhsType); } /** *

* isAssignableTo *

* * @param lhsType * a {@link GenericClass} object. * @return a boolean. */ public boolean isAssignableTo(GenericClass lhsType) { return isAssignable(lhsType.type, type); } /** *

* isAssignableTo *

* * @param lhsType * a {@link java.lang.reflect.Type} object. * @return a boolean. */ public boolean isAssignableTo(Type lhsType) { return isAssignable(lhsType, type); } /** * True if this represents java.lang.Class * * @return */ public boolean isClass() { return rawClass.equals(Class.class); } /** * Return true if variable is an enumeration * * @return a boolean. */ public boolean isEnum() { return rawClass.isEnum(); } public boolean isGenericArray() { GenericClass componentClass = new GenericClass(rawClass.getComponentType()); return componentClass.hasWildcardOrTypeVariables(); } /** * Determine if subType is a generic subclass * * @param subType * @return */ public boolean isGenericSuperTypeOf(GenericClass subType) { return GenericTypeReflector.isSuperType(type, subType.getType()); } /** * Determine if subType is a generic subclass * * @param subType * @return */ public boolean isGenericSuperTypeOf(Type subType) { return GenericTypeReflector.isSuperType(type, subType); } /** * True is this represents java.lang.Object * * @return */ public boolean isObject() { return rawClass.equals(Object.class); } /** * True if this represents a parameterized generic type * * @return */ public boolean isParameterizedType() { return type instanceof ParameterizedType; } /** * Return true if variable is a primitive type * * @return a boolean. */ public boolean isPrimitive() { return rawClass.isPrimitive(); } /** * True if this is a non-generic type * * @return */ public boolean isRawClass() { return type instanceof Class; } /** * True if this is a type variable * * @return */ public boolean isTypeVariable() { return type instanceof TypeVariable; } /** * True if this is a wildcard type * * @return */ public boolean isWildcardType() { return type instanceof WildcardType; } /** * True if this represents java.lang.String * * @return a boolean. */ public boolean isString() { return rawClass.equals(String.class); } /** * Return true if variable is void * * @return a boolean. */ public boolean isVoid() { return rawClass.equals(Void.class) || rawClass.equals(void.class); } /** * Return true if type of variable is a primitive wrapper * * @return a boolean. */ public boolean isWrapperType() { return WRAPPER_TYPES.contains(rawClass); } public boolean satisfiesBoundaries(TypeVariable typeVariable) { return satisfiesBoundaries(typeVariable, getTypeVariableMap()); } /** * Determine whether the boundaries of the type variable are satisfied by * this class * * @param typeVariable * @return */ public boolean satisfiesBoundaries(TypeVariable typeVariable, Map, Type> typeMap) { boolean isAssignable = true; // logger.debug("Checking class: " + type + " against type variable " + typeVariable+" with map "+typeMap); Map, Type> ownerVariableMap = getTypeVariableMap(); for(Type bound : typeVariable.getBounds()) { if(bound instanceof ParameterizedType) { Class boundClass = GenericTypeReflector.erase(bound); if(boundClass.isAssignableFrom(rawClass)) { Map, Type> xmap = TypeUtils.determineTypeArguments(rawClass, (ParameterizedType) bound); ownerVariableMap.putAll(xmap); } } } ownerVariableMap.putAll(typeMap); boolean changed = true; while(changed) { changed = false; for (TypeVariable var : ownerVariableMap.keySet()) { // If the type variable points to a typevariable, let it point to what the other typevariable points to // A -> B // B -> C // ==> A -> C if(ownerVariableMap.get(var) instanceof TypeVariable) { // Other type variable, i.e., the one this is currently pointing to TypeVariable value = (TypeVariable)ownerVariableMap.get(var); if(ownerVariableMap.containsKey(value)) { Type other = ownerVariableMap.get(value); if (other instanceof TypeVariable) { // If the value (C) is also a typevariable, check we don't have a recursion here if (ownerVariableMap.containsKey(other)) { Type x = ownerVariableMap.get(other); if (x == var || x == value || x == other) { continue; } } } if (var != other && value != other) { ownerVariableMap.put(var, other); changed = true; } } } } } GenericClass concreteClass = new GenericClass(GenericUtils.replaceTypeVariables(type, ownerVariableMap)); //logger.debug("Concrete class after variable replacement: " + concreteClass); for (Type theType : typeVariable.getBounds()) { //logger.debug("Current boundary of " + typeVariable + ": " + theType); // Special case: Enum is defined as Enum if (GenericTypeReflector.erase(theType).equals(Enum.class)) { //logger.debug("Is ENUM case"); // if this is an enum then it's ok. if (isEnum()) { //logger.debug("Class " + toString() + " is an enum!"); continue; } else { // If it's not an enum, it cannot be assignable to enum! //logger.debug("Class " + toString() + " is not an enum."); isAssignable = false; break; } } Type boundType = GenericUtils.replaceTypeVariables(theType, ownerVariableMap); boundType = GenericUtils.replaceTypeVariable(boundType, typeVariable, getType()); boundType = GenericUtils.replaceTypeVariablesWithWildcards(boundType); //logger.debug("Bound after variable replacement: " + boundType); if (!concreteClass.isAssignableTo(boundType) && !(boundType instanceof WildcardType)) { //logger.debug("Not assignable: " + type + " and " + boundType); // If the boundary is not assignable it may still be possible // to instantiate the generic to an assignable type if (GenericTypeReflector.erase(boundType).isAssignableFrom(getRawClass())) { //logger.debug("Raw classes are assignable: " + boundType + ", " // + getRawClass()); Type instanceType = GenericTypeReflector.getExactSuperType(boundType, getRawClass()); if (instanceType == null) { // This happens when the raw class is not a supertype // of the boundary //logger.debug("Instance type is null"); isAssignable = false; break; } // GenericClass instanceClass = new GenericClass(instanceType, // getRawClass()); // logger.debug("Instance type is " + instanceType); // if (instanceClass.hasTypeVariables()) // logger.debug("Instance type has type variables"); // if (instanceClass.hasWildcardTypes()) // logger.debug("Instance type has wildcard variables"); boundType = GenericUtils.replaceTypeVariable(theType, typeVariable, instanceType); // logger.debug("Instance type after replacement is " + boundType); if (GenericClass.isAssignable(boundType, instanceType)) { //logger.debug("Found assignable generic exact type: " // + instanceType); continue; } else { //logger.debug("Is not assignable: " + boundType + " and " // + instanceType); } } isAssignable = false; break; } } //logger.debug("Result: is assignable " + isAssignable); return isAssignable; } public boolean satisfiesBoundaries(WildcardType wildcardType) { return satisfiesBoundaries(wildcardType, getTypeVariableMap()); } /** * Determine whether the upper and lower boundaries are satisfied by this * class * * @param wildcardType * @return */ public boolean satisfiesBoundaries(WildcardType wildcardType, Map, Type> typeMap) { boolean isAssignable = true; Map, Type> ownerVariableMap = getTypeVariableMap(); ownerVariableMap.putAll(typeMap); // ? extends X for (Type theType : wildcardType.getUpperBounds()) { logger.debug("Checking upper bound "+theType); // Special case: Enum is defined as Enum if (GenericTypeReflector.erase(theType).equals(Enum.class)) { // if this is an enum then it's ok. if (isEnum()) continue; else { // If it's not an enum, it cannot be assignable to enum! isAssignable = false; break; } } Type type = GenericUtils.replaceTypeVariables(theType, ownerVariableMap); //logger.debug("Bound after variable replacement: " + type); if (!isAssignableTo(type)) { // If the boundary is not assignable it may still be possible // to instantiate the generic to an assignable type if (GenericTypeReflector.erase(type).isAssignableFrom(getRawClass())) { Type instanceType = GenericTypeReflector.getExactSuperType(type, getRawClass()); if (instanceType == null) { // This happens when the raw class is not a supertype // of the boundary isAssignable = false; break; } if (GenericClass.isAssignable(type, instanceType)) { logger.debug("Found assignable generic exact type: " + instanceType); continue; } } isAssignable = false; break; } } // ? super X Type[] lowerBounds = wildcardType.getLowerBounds(); if (lowerBounds != null && lowerBounds.length > 0) { for (Type theType : wildcardType.getLowerBounds()) { logger.debug("Checking lower bound "+theType); Type type = GenericUtils.replaceTypeVariables(theType, ownerVariableMap); logger.debug("Bound after variable replacement: " + type); logger.debug("Is assignable from "+toString()+"?"); if (!isAssignableFrom(type)) { logger.debug("Not assignable from "+toString()); // If the boundary is not assignable it may still be possible // to instantiate the generic to an assignable type if(type instanceof WildcardType) continue; if (GenericTypeReflector.erase(type).isAssignableFrom(getRawClass())) { Type instanceType = GenericTypeReflector.getExactSuperType(type, getRawClass()); if (instanceType == null) { // This happens when the raw class is not a supertype // of the boundary isAssignable = false; break; } if (GenericClass.isAssignable(type, instanceType)) { logger.debug("Found assignable generic exact type: " + instanceType); continue; } } isAssignable = false; break; } else { logger.debug("Is assignable from "+toString()); } } } return isAssignable; } /** {@inheritDoc} */ @Override public String toString() { if (type == null) { LoggingUtils.getEvoLogger().info("Type is null for raw class " + rawClass); for (StackTraceElement elem : Thread.currentThread().getStackTrace()) { LoggingUtils.getEvoLogger().info(elem.toString()); } assert (false); } return type.toString(); } /** * De-serialize. Need to use current classloader. * * @param ois * @throws ClassNotFoundException * @throws IOException */ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { String name = (String) ois.readObject(); if (name == null) { this.rawClass = null; this.type = null; return; } this.rawClass = getClass(name); Boolean isParameterized = (Boolean) ois.readObject(); if (isParameterized) { // GenericClass rawType = (GenericClass) ois.readObject(); GenericClass ownerType = (GenericClass) ois.readObject(); @SuppressWarnings("unchecked") List parameterClasses = (List) ois.readObject(); Type[] parameterTypes = new Type[parameterClasses.size()]; for (int i = 0; i < parameterClasses.size(); i++) parameterTypes[i] = parameterClasses.get(i).getType(); this.type = new ParameterizedTypeImpl(rawClass, parameterTypes, ownerType.getType()); } else { this.type = addTypeParameters(rawClass); //GenericTypeReflector.addWildcardParameters(raw_class); } } /** * Serialize, but need to abstract classloader away * * @param oos * @throws IOException */ private void writeObject(ObjectOutputStream oos) throws IOException { if (rawClass == null) { oos.writeObject(null); } else { oos.writeObject(rawClass.getName()); if (type instanceof ParameterizedType) { oos.writeObject(Boolean.TRUE); ParameterizedType pt = (ParameterizedType) type; // oos.writeObject(new GenericClass(pt.getRawType())); oos.writeObject(new GenericClass(pt.getOwnerType())); List parameterClasses = new ArrayList(); for (Type parameterType : pt.getActualTypeArguments()) { parameterClasses.add(new GenericClass(parameterType)); } oos.writeObject(parameterClasses); } else { oos.writeObject(Boolean.FALSE); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy