/* ******************************************************************* * Copyright (c) 2005 Contributors. * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * ******************************************************************/ package org.aspectj.weaver.reflect; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; import java.util.HashMap; import java.util.Map; import org.aspectj.weaver.BoundedReferenceType; import org.aspectj.weaver.ReferenceType; import org.aspectj.weaver.ResolvedType; import org.aspectj.weaver.TypeFactory; import org.aspectj.weaver.TypeVariable; import org.aspectj.weaver.TypeVariableReferenceType; import org.aspectj.weaver.UnresolvedType; import org.aspectj.weaver.World; /** * Handles the translation of java.lang.reflect.Type objects into AspectJ UnresolvedTypes. * * @author Adrian Colyer */ public class JavaLangTypeToResolvedTypeConverter { // Used to prevent recursion - we record what we are working on and return it if asked again *whilst* working on it private Map typeVariablesInProgress = new HashMap<>(); private final World world; public JavaLangTypeToResolvedTypeConverter(World aWorld) { this.world = aWorld; } private World getWorld() { return this.world; } public ResolvedType fromType(Type type) { if (type instanceof Class) { Class clazz = (Class) type; String name = clazz.getName(); /** * getName() can return: * * 1. If this class object represents a reference type that is not an * array type then the binary name of the class is returned * 2. If this class object represents a primitive type or void, then * the name returned is a String equal to the Java language keyword * corresponding to the primitive type or void. * 3. If this class object represents a class of arrays, then the internal * form of the name consists of the name of the element type preceded by * one or more '[' characters representing the depth of the array nesting. */ if (clazz.isArray()) { UnresolvedType ut = UnresolvedType.forSignature(name.replace('.', '/')); return getWorld().resolve(ut); } else { return getWorld().resolve(name); } } else if (type instanceof ParameterizedType) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=509327 // TODO should deal with the ownerType if it set, indicating this is possibly an inner type of a parameterized type Type ownerType = ((ParameterizedType) type).getOwnerType(); ParameterizedType parameterizedType = (ParameterizedType) type; ResolvedType baseType = fromType(parameterizedType.getRawType()); Type[] typeArguments = parameterizedType.getActualTypeArguments(); if (baseType.isSimpleType() && typeArguments.length == 0 && ownerType != null) { // 'type' is an inner type of some outer parameterized type // For now just return the base type - in future create the parameterized form of the outer // and use it with the inner. We return the base type to be compatible with what the // code does that accesses the info from the bytecode (unlike this code which accesses it // reflectively). return baseType; } ResolvedType[] resolvedTypeArguments = fromTypes(typeArguments); return TypeFactory.createParameterizedType(baseType, resolvedTypeArguments, getWorld()); } else if (type instanceof java.lang.reflect.TypeVariable) { TypeVariableReferenceType inprogressVar = typeVariablesInProgress.get(type); if (inprogressVar != null) { return inprogressVar; } java.lang.reflect.TypeVariable tv = (java.lang.reflect.TypeVariable) type; TypeVariable rt_tv = new TypeVariable(tv.getName()); TypeVariableReferenceType tvrt = new TypeVariableReferenceType(rt_tv, getWorld()); typeVariablesInProgress.put(type, tvrt); // record what we are working on, for recursion case Type[] bounds = tv.getBounds(); ResolvedType[] resBounds = fromTypes(bounds); ResolvedType upperBound = resBounds[0]; ResolvedType[] additionalBounds = ResolvedType.EMPTY_RESOLVED_TYPE_ARRAY; if (resBounds.length > 1) { additionalBounds = new ResolvedType[resBounds.length - 1]; System.arraycopy(resBounds, 1, additionalBounds, 0, additionalBounds.length); } rt_tv.setUpperBound(upperBound); rt_tv.setAdditionalInterfaceBounds(additionalBounds); typeVariablesInProgress.remove(type); // we have finished working on it return tvrt; } else if (type instanceof WildcardType) { WildcardType wildType = (WildcardType) type; Type[] lowerBounds = wildType.getLowerBounds(); Type[] upperBounds = wildType.getUpperBounds(); ResolvedType bound = null; boolean isExtends = lowerBounds.length == 0; if (isExtends) { bound = fromType(upperBounds[0]); } else { bound = fromType(lowerBounds[0]); } return new BoundedReferenceType((ReferenceType) bound, isExtends, getWorld()); } else if (type instanceof GenericArrayType) { GenericArrayType genericArrayType = (GenericArrayType) type; Type componentType = genericArrayType.getGenericComponentType(); return UnresolvedType.makeArray(fromType(componentType), 1).resolve(getWorld()); } return ResolvedType.MISSING; } public ResolvedType[] fromTypes(Type[] types) { ResolvedType[] ret = new ResolvedType[types.length]; for (int i = 0; i < ret.length; i++) { ret[i] = fromType(types[i]); } return ret; } }