/** * Copyright (C) 2006-2017 INRIA and contributors * Spoon - http://spoon.gforge.inria.fr/ * * This software is governed by the CeCILL-C License under French law and * abiding by the rules of distribution of free software. You can use, modify * and/or redistribute the software under the terms of the CeCILL-C license as * circulated by CEA, CNRS and INRIA at http://www.cecill.info. * * This program 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 CeCILL-C License for more details. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ package spoon.support.compiler.jdt; import static spoon.support.compiler.jdt.JDTTreeBuilderQuery.searchPackage; import static spoon.support.compiler.jdt.JDTTreeBuilderQuery.searchType; import static spoon.support.compiler.jdt.JDTTreeBuilderQuery.searchTypeBinding; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding; import org.eclipse.jdt.internal.compiler.lookup.CatchParameterBinding; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18; import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; import spoon.reflect.code.CtLambda; import spoon.reflect.declaration.CtPackage; import spoon.reflect.declaration.CtParameter; import spoon.reflect.declaration.ModifierKind; import spoon.reflect.factory.PackageFactory; import spoon.reflect.reference.CtArrayTypeReference; import spoon.reflect.reference.CtCatchVariableReference; import spoon.reflect.reference.CtExecutableReference; import spoon.reflect.reference.CtFieldReference; import spoon.reflect.reference.CtLocalVariableReference; import spoon.reflect.reference.CtPackageReference; import spoon.reflect.reference.CtParameterReference; import spoon.reflect.reference.CtReference; import spoon.reflect.reference.CtTypeParameterReference; import spoon.reflect.reference.CtTypeReference; import spoon.reflect.reference.CtVariableReference; public class ReferenceBuilder { private Map<String, CtTypeReference<?>> basestypes = new TreeMap<>(); private boolean bounds = false; private final JDTTreeBuilder jdtTreeBuilder; ReferenceBuilder(JDTTreeBuilder jdtTreeBuilder) { this.jdtTreeBuilder = jdtTreeBuilder; } private CtTypeReference<?> getBoundedTypeReference(TypeBinding binding) { bounds = true; CtTypeReference<?> ref = getTypeReference(binding); bounds = false; return ref; } /** * Builds a type reference from a {@link TypeReference}. * * @param type Type from JDT. * @param scope Scope of the parent element. * @param <T> Type of the type reference. * @return a type reference. */ <T> CtTypeReference<T> buildTypeReference(TypeReference type, Scope scope) { if (type == null) { return null; } return buildTypeReferenceInternal(this.<T>getTypeReference(type.resolvedType, type), type, scope); } /** * Builds a qualified type reference from a {@link TypeReference}. * * @param type Qualified type from JDT. * @param scope Scope of the parent element. * @return */ <T> CtTypeReference<T> buildTypeReference(QualifiedTypeReference type, Scope scope) { CtTypeReference<T> accessedType = buildTypeReference((TypeReference) type, scope); final TypeBinding receiverType = type != null ? type.resolvedType : null; if (receiverType != null) { final CtTypeReference<T> ref = getQualifiedTypeReference(type.tokens, receiverType, receiverType.enclosingType(), new JDTTreeBuilder.OnAccessListener() { @Override public boolean onAccess(char[][] tokens, int index) { return true; } }); if (ref != null) { accessedType = ref; } } return accessedType; } /** * Builds a type parameter reference from a {@link TypeReference} * * @param type Type from JDT. * @param scope Scope of the parent element. * @return a type parameter reference. */ private CtTypeParameterReference buildTypeParameterReference(TypeReference type, Scope scope) { if (type == null) { return null; } return (CtTypeParameterReference) this.buildTypeReferenceInternal(this.getTypeParameterReference(type.resolvedType, type), type, scope); } private <T> CtTypeReference<T> buildTypeReferenceInternal(CtTypeReference<T> typeReference, TypeReference type, Scope scope) { if (type == null) { return null; } CtTypeReference<?> currentReference = typeReference; for (int position = type.getTypeName().length - 1; position >= 0; position--) { if (currentReference == null) { break; } this.jdtTreeBuilder.getContextBuilder().enter(currentReference, type); if (type.annotations != null && type.annotations.length - 1 <= position && type.annotations[position] != null && type.annotations[position].length > 0) { for (Annotation annotation : type.annotations[position]) { if (scope instanceof ClassScope) { annotation.traverse(this.jdtTreeBuilder, (ClassScope) scope); } else if (scope instanceof BlockScope) { annotation.traverse(this.jdtTreeBuilder, (BlockScope) scope); } else { annotation.traverse(this.jdtTreeBuilder, (BlockScope) null); } } } if (type.getTypeArguments() != null && type.getTypeArguments().length - 1 <= position && type.getTypeArguments()[position] != null && type.getTypeArguments()[position].length > 0) { currentReference.getActualTypeArguments().clear(); for (TypeReference typeArgument : type.getTypeArguments()[position]) { if (typeArgument instanceof Wildcard || typeArgument.resolvedType instanceof WildcardBinding || typeArgument.resolvedType instanceof TypeVariableBinding) { currentReference.addActualTypeArgument(buildTypeParameterReference(typeArgument, scope)); } else { currentReference.addActualTypeArgument(buildTypeReference(typeArgument, scope)); } } } else if ((type instanceof ParameterizedSingleTypeReference || type instanceof ParameterizedQualifiedTypeReference) && !isTypeArgumentExplicit(type.getTypeArguments())) { for (CtTypeReference<?> actualTypeArgument : currentReference.getActualTypeArguments()) { actualTypeArgument.setImplicit(true); if (actualTypeArgument instanceof CtArrayTypeReference) { ((CtArrayTypeReference) actualTypeArgument).getComponentType().setImplicit(true); } } } if (type instanceof Wildcard && typeReference instanceof CtTypeParameterReference) { ((CtTypeParameterReference) typeReference).setBoundingType(buildTypeReference(((Wildcard) type).bound, scope)); } this.jdtTreeBuilder.getContextBuilder().exit(type); currentReference = currentReference.getDeclaringType(); } return typeReference; } private boolean isTypeArgumentExplicit(TypeReference[][] typeArguments) { if (typeArguments == null) { return true; } boolean isGenericTypeExplicit = true; // This loop is necessary because it is the only way to know if the generic type // is implicit or not. for (TypeReference[] typeArgument : typeArguments) { isGenericTypeExplicit = typeArgument != null && typeArgument.length > 0; if (isGenericTypeExplicit) { break; } } return isGenericTypeExplicit; } /** * Builds a type reference from a qualified name when a type specified in the name isn't available. * * @param tokens Qualified name. * @param receiverType Last type in the qualified name. * @param enclosingType Enclosing type of the type name. * @param listener Listener to know if we must build the type reference. * @return a type reference. */ <T> CtTypeReference<T> getQualifiedTypeReference(char[][] tokens, TypeBinding receiverType, ReferenceBinding enclosingType, JDTTreeBuilder.OnAccessListener listener) { if (enclosingType != null && Collections.disjoint(Arrays.asList(ModifierKind.PUBLIC, ModifierKind.PROTECTED), JDTTreeBuilderQuery.getModifiers(enclosingType.modifiers))) { String access = ""; int i = 0; final CompilationUnitDeclaration[] units = ((TreeBuilderCompiler) this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.scope.environment.typeRequestor).unitsToProcess; for (; i < tokens.length; i++) { final char[][] qualified = Arrays.copyOfRange(tokens, 0, i + 1); if (searchPackage(qualified, units) == null) { access = CharOperation.toString(qualified); break; } } if (!access.contains(CtPackage.PACKAGE_SEPARATOR)) { access = searchType(access, this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.imports); } final TypeBinding accessBinding = searchTypeBinding(access, units); if (accessBinding != null && listener.onAccess(tokens, i)) { final TypeBinding superClassBinding = searchTypeBinding(accessBinding.superclass(), CharOperation.charToString(tokens[i + 1])); if (superClassBinding != null) { return this.getTypeReference(superClassBinding.clone(accessBinding)); } else { return this.getTypeReference(receiverType); } } else { return this.getTypeReference(receiverType); } } return null; } /** * Try to get the declaring reference (package or type) from imports of the current * compilation unit declaration (current class). This method returns a CtReference * which can be a CtTypeReference if it retrieves the information in an static import, * a CtPackageReference if it retrieves the information in an standard import, otherwise * it returns null. * * @param expectedName Name expected in imports. * @return CtReference which can be a CtTypeReference, a CtPackageReference or null. */ CtReference getDeclaringReferenceFromImports(char[] expectedName) { if (this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration != null && this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.imports != null) { for (ImportReference anImport : this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.imports) { if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], expectedName)) { if (anImport.isStatic()) { int indexDeclaring = 2; if ((anImport.bits & ASTNode.OnDemand) != 0) { // With .* indexDeclaring = 1; } char[][] packageName = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - indexDeclaring); char[][] className = CharOperation.subarray(anImport.getImportName(), anImport.getImportName().length - indexDeclaring, anImport.getImportName().length - (indexDeclaring - 1)); PackageBinding aPackage; if (packageName.length != 0) { aPackage = this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.scope.environment.createPackage(packageName); } else { aPackage = null; } final MissingTypeBinding declaringType = this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.scope.environment.createMissingType(aPackage, className); this.jdtTreeBuilder.getContextBuilder().ignoreComputeImports = true; final CtTypeReference<Object> typeReference = getTypeReference(declaringType); this.jdtTreeBuilder.getContextBuilder().ignoreComputeImports = false; return typeReference; } else { PackageBinding packageBinding = null; char[][] chars = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 1); // `findImport(chars, false, false);` and `createPackage(chars)` require // an array with a minimum length of 1 and throw an // ArrayIndexOutOfBoundsException if `chars.length == 0`. Fixes #759. if (chars.length > 0) { Binding someBinding = this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.scope.findImport(chars, false, false); if (someBinding != null && someBinding.isValidBinding() && someBinding instanceof PackageBinding) { packageBinding = (PackageBinding) someBinding; } else { packageBinding = this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.scope.environment.createPackage(chars); } } if (packageBinding == null) { // Big crisis here. We are already in noclasspath mode but JDT doesn't support always // creation of a package in this mode. So, if we are in this brace, we make the job of JDT... packageBinding = new PackageBinding(chars, null, this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.scope.environment); } return getPackageReference(packageBinding); } } } } return null; } @SuppressWarnings("unchecked") <T> CtExecutableReference<T> getExecutableReference(MethodBinding exec) { if (exec == null) { return null; } final CtExecutableReference ref = this.jdtTreeBuilder.getFactory().Core().createExecutableReference(); if (exec.isConstructor()) { ref.setSimpleName(CtExecutableReference.CONSTRUCTOR_NAME); ref.setType(getTypeReference(exec.declaringClass)); } else { ref.setSimpleName(new String(exec.selector)); ref.setType(getTypeReference(exec.returnType)); } if (exec instanceof ProblemMethodBinding) { if (exec.declaringClass != null && Arrays.asList(exec.declaringClass.methods()).contains(exec)) { ref.setDeclaringType(getTypeReference(exec.declaringClass)); } else { final CtReference declaringType = getDeclaringReferenceFromImports(exec.constantPoolName()); if (declaringType instanceof CtTypeReference) { ref.setDeclaringType((CtTypeReference<?>) declaringType); } } if (exec.isConstructor()) { // super() invocation have a good declaring class. ref.setDeclaringType(getTypeReference(exec.declaringClass)); } ref.setStatic(true); } else { ref.setDeclaringType(getTypeReference(exec.declaringClass)); ref.setStatic(exec.isStatic()); } if (exec.declaringClass instanceof ParameterizedTypeBinding) { ref.setDeclaringType(getTypeReference(exec.declaringClass.actualType())); } // original() method returns a result not null when the current method is generic. if (exec.original() != null) { final List<CtTypeReference<?>> parameters = new ArrayList<>(exec.original().parameters.length); for (TypeBinding b : exec.original().parameters) { parameters.add(getTypeReference(b)); } ref.setParameters(parameters); } else if (exec.parameters != null) { // This is a method without a generic argument. final List<CtTypeReference<?>> parameters = new ArrayList<>(); for (TypeBinding b : exec.parameters) { parameters.add(getTypeReference(b)); } ref.setParameters(parameters); } return ref; } <T> CtExecutableReference<T> getExecutableReference(AllocationExpression allocationExpression) { CtExecutableReference<T> ref; if (allocationExpression.binding != null) { ref = getExecutableReference(allocationExpression.binding); } else { ref = jdtTreeBuilder.getFactory().Core().createExecutableReference(); ref.setSimpleName(CtExecutableReference.CONSTRUCTOR_NAME); ref.setDeclaringType(getTypeReference(null, allocationExpression.type)); final List<CtTypeReference<?>> parameters = new ArrayList<>(allocationExpression.argumentTypes.length); for (TypeBinding b : allocationExpression.argumentTypes) { parameters.add(getTypeReference(b)); } ref.setParameters(parameters); } if (allocationExpression.type == null) { ref.setType(this.<T>getTypeReference(allocationExpression.expectedType())); } return ref; } <T> CtExecutableReference<T> getExecutableReference(MessageSend messageSend) { if (messageSend.binding != null) { return getExecutableReference(messageSend.binding); } CtExecutableReference<T> ref = jdtTreeBuilder.getFactory().Core().createExecutableReference(); ref.setSimpleName(CharOperation.charToString(messageSend.selector)); ref.setType(this.<T>getTypeReference(messageSend.expectedType())); if (messageSend.receiver.resolvedType == null) { // It is crisis dude! static context, we don't have much more information. if (messageSend.receiver instanceof SingleNameReference) { ref.setDeclaringType(jdtTreeBuilder.getHelper().createTypeAccessNoClasspath((SingleNameReference) messageSend.receiver).getAccessedType()); } else if (messageSend.receiver instanceof QualifiedNameReference) { ref.setDeclaringType(jdtTreeBuilder.getHelper().createTypeAccessNoClasspath((QualifiedNameReference) messageSend.receiver).getAccessedType()); } } else { ref.setDeclaringType(getTypeReference(messageSend.receiver.resolvedType)); } if (messageSend.arguments != null) { final List<CtTypeReference<?>> parameters = new ArrayList<>(); for (Expression expression : messageSend.arguments) { parameters.add(getTypeReference(expression.resolvedType)); } ref.setParameters(parameters); } return ref; } private CtPackageReference getPackageReference(PackageBinding reference) { String name = new String(reference.shortReadableName()); if (name.length() == 0) { return this.jdtTreeBuilder.getFactory().Package().topLevel(); } CtPackageReference ref = this.jdtTreeBuilder.getFactory().Core().createPackageReference(); ref.setSimpleName(name); return ref; } final Map<TypeBinding, CtTypeReference> bindingCache = new HashMap<>(); <T> CtTypeReference<T> getTypeReference(TypeBinding binding, TypeReference ref) { CtTypeReference<T> ctRef = getTypeReference(binding); if (ctRef != null && isCorrectTypeReference(ref)) { insertGenericTypesInNoClasspathFromJDTInSpoon(ref, ctRef); return ctRef; } return getTypeReference(ref); } CtTypeReference<Object> getTypeParameterReference(TypeBinding binding, TypeReference ref) { CtTypeReference<Object> ctRef = getTypeReference(binding); if (ctRef != null && isCorrectTypeReference(ref)) { if (!(ctRef instanceof CtTypeParameterReference)) { CtTypeParameterReference typeParameterRef = this.jdtTreeBuilder.getFactory().Core().createTypeParameterReference(); typeParameterRef.setSimpleName(ctRef.getSimpleName()); typeParameterRef.setDeclaringType(ctRef.getDeclaringType()); typeParameterRef.setPackage(ctRef.getPackage()); ctRef = typeParameterRef; } insertGenericTypesInNoClasspathFromJDTInSpoon(ref, ctRef); return ctRef; } return getTypeParameterReference(CharOperation.toString(ref.getParameterizedTypeName())); } /** * In no classpath, the model of the super interface isn't always correct. */ private boolean isCorrectTypeReference(TypeReference ref) { if (ref.resolvedType == null) { return false; } if (!(ref.resolvedType instanceof ProblemReferenceBinding)) { return true; } final String[] compoundName = CharOperation.charArrayToStringArray(((ProblemReferenceBinding) ref.resolvedType).compoundName); final String[] typeName = CharOperation.charArrayToStringArray(ref.getTypeName()); if (compoundName.length == 0 || typeName.length == 0) { return false; } return compoundName[compoundName.length - 1].equals(typeName[typeName.length - 1]); } private <T> void insertGenericTypesInNoClasspathFromJDTInSpoon(TypeReference original, CtTypeReference<T> type) { if (original.resolvedType instanceof ProblemReferenceBinding && original.getTypeArguments() != null) { for (TypeReference[] typeReferences : original.getTypeArguments()) { if (typeReferences != null) { for (TypeReference typeReference : typeReferences) { type.addActualTypeArgument(this.getTypeReference(typeReference.resolvedType)); } } } } } /** * JDT doesn't return a correct AST with the resolved type of the reference. * This method try to build a correct Spoon AST from the name of the JDT * reference, thanks to the parsing of the string, the name parameterized from * the JDT reference and java convention. * Returns a complete Spoon AST when the name is correct, otherwise a spoon type * reference with a name that correspond to the name of the JDT type reference. */ <T> CtTypeReference<T> getTypeReference(TypeReference ref) { CtTypeReference<T> res = null; CtTypeReference inner = null; final String[] namesParameterized = CharOperation.charArrayToStringArray(ref.getParameterizedTypeName()); int index = namesParameterized.length - 1; for (; index >= 0; index--) { // Start at the end to get the class name first. CtTypeReference main = getTypeReference(namesParameterized[index]); if (main == null) { break; } if (res == null) { res = (CtTypeReference<T>) main; } else { inner.setDeclaringType((CtTypeReference<?>) main); } inner = main; } if (res == null) { return this.jdtTreeBuilder.getFactory().Type().createReference(CharOperation.toString(ref.getParameterizedTypeName())); } if (inner.getPackage() == null) { PackageFactory packageFactory = this.jdtTreeBuilder.getFactory().Package(); CtPackageReference packageReference = index >= 0 ? packageFactory.getOrCreate(concatSubArray(namesParameterized, index)).getReference() : packageFactory.topLevel(); inner.setPackage(packageReference); } return res; } private String concatSubArray(String[] a, int endIndex) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < endIndex; i++) { sb.append(a[i]).append('.'); } sb.append(a[endIndex]); return sb.toString(); } /** * Try to build a CtTypeReference from a simple name with specified generic types but * returns null if the name doesn't correspond to a type (not start by an upper case). */ private <T> CtTypeReference<T> getTypeReference(String name) { CtTypeReference<T> main = null; if (name.matches(".*(<.+>)")) { Pattern pattern = Pattern.compile("([^<]+)<(.+)>"); Matcher m = pattern.matcher(name); if (name.startsWith("?")) { main = (CtTypeReference) this.jdtTreeBuilder.getFactory().Core().createWildcardReference(); } else { main = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); } if (m.find()) { main.setSimpleName(m.group(1)); final String[] split = m.group(2).split(","); for (String parameter : split) { ((CtTypeReference) main).addActualTypeArgument(getTypeParameterReference(parameter.trim())); } } } else if (Character.isUpperCase(name.charAt(0))) { main = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); main.setSimpleName(name); final CtReference declaring = this.getDeclaringReferenceFromImports(name.toCharArray()); setPackageOrDeclaringType(main, declaring); } else if (name.startsWith("?")) { return (CtTypeReference) this.jdtTreeBuilder.getFactory().Core().createWildcardReference(); } return main; } /** * Try to build a CtTypeParameterReference from a single name with specified generic types but * keep in mind that if you give wrong data in the strong, reference will be wrong. */ private CtTypeReference<Object> getTypeParameterReference(String name) { CtTypeReference<Object> param = null; if (name.contains("extends") || name.contains("super")) { String[] split = name.contains("extends") ? name.split("extends") : name.split("super"); param = getTypeParameterReference(split[0].trim()); ((CtTypeParameterReference) param).setBoundingType(getTypeReference(split[split.length - 1].trim())); } else if (name.matches(".*(<.+>)")) { Pattern pattern = Pattern.compile("([^<]+)<(.+)>"); Matcher m = pattern.matcher(name); if (m.find()) { param = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); param.setSimpleName(m.group(1)); final String[] split = m.group(2).split(","); for (String parameter : split) { param.addActualTypeArgument(getTypeParameterReference(parameter.trim())); } } } else if (name.contains("?")) { param = this.jdtTreeBuilder.getFactory().Core().createWildcardReference(); } else { param = this.jdtTreeBuilder.getFactory().Core().createTypeParameterReference(); param.setSimpleName(name); } return param; } @SuppressWarnings("unchecked") <T> CtTypeReference<T> getTypeReference(TypeBinding binding) { if (binding == null) { return null; } CtTypeReference<?> ref = null; if (binding instanceof RawTypeBinding) { ref = getTypeReference(((ParameterizedTypeBinding) binding).genericType()); } else if (binding instanceof ParameterizedTypeBinding) { if (binding.actualType() != null && binding.actualType() instanceof LocalTypeBinding) { // When we define a nested class in a method and when the enclosing class of this method // is a parameterized type binding, JDT give a ParameterizedTypeBinding for the nested class // and hide the real class in actualType(). ref = getTypeReference(binding.actualType()); } else { ref = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); if (binding.isAnonymousType()) { ref.setSimpleName(""); } else { ref.setSimpleName(String.valueOf(binding.sourceName())); if (binding.enclosingType() != null) { ref.setDeclaringType(getTypeReference(binding.enclosingType())); } else { ref.setPackage(getPackageReference(binding.getPackage())); } } } if (binding.actualType() instanceof MissingTypeBinding) { ref = getTypeReference(binding.actualType()); } if (((ParameterizedTypeBinding) binding).arguments != null) { for (TypeBinding b : ((ParameterizedTypeBinding) binding).arguments) { if (bindingCache.containsKey(b)) { ref.addActualTypeArgument(getCtCircularTypeReference(b)); } else { ref.addActualTypeArgument(getTypeReference(b)); } } } } else if (binding instanceof MissingTypeBinding) { ref = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); ref.setSimpleName(new String(binding.sourceName())); ref.setPackage(getPackageReference(binding.getPackage())); if (!this.jdtTreeBuilder.getContextBuilder().ignoreComputeImports) { final CtReference declaring = this.getDeclaringReferenceFromImports(binding.sourceName()); if (declaring instanceof CtPackageReference) { ref.setPackage((CtPackageReference) declaring); } else if (declaring instanceof CtTypeReference) { ref.setDeclaringType((CtTypeReference) declaring); } } } else if (binding instanceof BinaryTypeBinding) { ref = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); if (binding.enclosingType() != null) { ref.setDeclaringType(getTypeReference(binding.enclosingType())); } else { ref.setPackage(getPackageReference(binding.getPackage())); } ref.setSimpleName(new String(binding.sourceName())); } else if (binding instanceof TypeVariableBinding) { boolean oldBounds = bounds; ref = this.jdtTreeBuilder.getFactory().Core().createTypeParameterReference(); if (binding instanceof CaptureBinding) { ref = this.jdtTreeBuilder.getFactory().Core().createWildcardReference(); bounds = true; } else { ref.setSimpleName(new String(binding.sourceName())); } TypeVariableBinding b = (TypeVariableBinding) binding; if (bounds) { if (b instanceof CaptureBinding && ((CaptureBinding) b).wildcard != null) { bounds = oldBounds; return getTypeReference(((CaptureBinding) b).wildcard); } else if (b.superclass != null && b.firstBound == b.superclass) { bounds = false; bindingCache.put(binding, ref); ((CtTypeParameterReference) ref).setBoundingType(getTypeReference(b.superclass)); bounds = oldBounds; } } if (bounds && b.superInterfaces != null && b.superInterfaces != Binding.NO_SUPERINTERFACES) { bounds = false; bindingCache.put(binding, ref); List<CtTypeReference<?>> bounds = new ArrayList<>(); if (((CtTypeParameterReference) ref).getBoundingType() != null) { bounds.add(((CtTypeParameterReference) ref).getBoundingType()); } for (ReferenceBinding superInterface : b.superInterfaces) { bounds.add(getTypeReference(superInterface)); } ((CtTypeParameterReference) ref).setBoundingType(this.jdtTreeBuilder.getFactory().Type().createIntersectionTypeReferenceWithBounds(bounds)); } if (binding instanceof CaptureBinding) { bounds = false; } } else if (binding instanceof BaseTypeBinding) { String name = new String(binding.sourceName()); ref = basestypes.get(name); if (ref == null) { ref = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); ref.setSimpleName(name); basestypes.put(name, ref); } else { ref = ref == null ? ref : ref.clone(); } } else if (binding instanceof WildcardBinding) { ref = this.jdtTreeBuilder.getFactory().Core().createWildcardReference(); if (((WildcardBinding) binding).boundKind == Wildcard.SUPER && ref instanceof CtTypeParameterReference) { ((CtTypeParameterReference) ref).setUpper(false); } if (((WildcardBinding) binding).bound != null && ref instanceof CtTypeParameterReference) { if (bindingCache.containsKey(((WildcardBinding) binding).bound)) { ((CtTypeParameterReference) ref).setBoundingType(getCtCircularTypeReference(((WildcardBinding) binding).bound)); } else { ((CtTypeParameterReference) ref).setBoundingType(getTypeReference(((WildcardBinding) binding).bound)); } } } else if (binding instanceof LocalTypeBinding) { ref = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); if (binding.isAnonymousType()) { ref.setSimpleName(JDTTreeBuilderHelper.computeAnonymousName(((SourceTypeBinding) binding).constantPoolName())); ref.setDeclaringType(getTypeReference((binding.enclosingType()))); } else { ref.setSimpleName(new String(binding.sourceName())); if (((LocalTypeBinding) binding).enclosingMethod == null && binding.enclosingType() != null && binding.enclosingType() instanceof LocalTypeBinding) { ref.setDeclaringType(getTypeReference(binding.enclosingType())); } else if (binding.enclosingMethod() != null) { ref.setSimpleName(JDTTreeBuilderHelper.computeAnonymousName(((SourceTypeBinding) binding).constantPoolName())); ref.setDeclaringType(getTypeReference(binding.enclosingType())); } } } else if (binding instanceof SourceTypeBinding) { ref = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); if (binding.isAnonymousType()) { ref.setSimpleName(JDTTreeBuilderHelper.computeAnonymousName(((SourceTypeBinding) binding).constantPoolName())); ref.setDeclaringType(getTypeReference((binding.enclosingType()))); } else { ref.setSimpleName(new String(binding.sourceName())); if (binding.enclosingType() != null) { ref.setDeclaringType(getTypeReference(binding.enclosingType())); } else { ref.setPackage(getPackageReference(binding.getPackage())); } // if(((SourceTypeBinding) binding).typeVariables!=null && // ((SourceTypeBinding) binding).typeVariables.length>0){ // for (TypeBinding b : ((SourceTypeBinding) // binding).typeVariables) { // ref.getActualTypeArguments().add(getTypeReference(b)); // } // } } } else if (binding instanceof ArrayBinding) { CtArrayTypeReference<Object> arrayref; arrayref = this.jdtTreeBuilder.getFactory().Core().createArrayTypeReference(); ref = arrayref; for (int i = 1; i < binding.dimensions(); i++) { CtArrayTypeReference<Object> tmp = this.jdtTreeBuilder.getFactory().Core().createArrayTypeReference(); arrayref.setComponentType(tmp); arrayref = tmp; } arrayref.setComponentType(getTypeReference(binding.leafComponentType())); } else if (binding instanceof PolyTypeBinding) { // JDT can't resolve the type of this binding and we only have a string. // In this case, we return a type Object because we can't know more about it. ref = this.jdtTreeBuilder.getFactory().Type().objectType(); } else if (binding instanceof ProblemReferenceBinding) { // Spoon is able to analyze also without the classpath ref = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); ref.setSimpleName(new String(binding.readableName())); final CtReference declaring = this.getDeclaringReferenceFromImports(binding.sourceName()); setPackageOrDeclaringType(ref, declaring); } else if (binding instanceof JDTTreeBuilder.SpoonReferenceBinding) { ref = this.jdtTreeBuilder.getFactory().Core().createTypeReference(); ref.setSimpleName(new String(binding.sourceName())); ref.setDeclaringType(getTypeReference(binding.enclosingType())); } else if (binding instanceof IntersectionTypeBinding18) { List<CtTypeReference<?>> bounds = new ArrayList<>(); for (ReferenceBinding superInterface : binding.getIntersectingTypes()) { bounds.add(getTypeReference(superInterface)); } ref = this.jdtTreeBuilder.getFactory().Type().createIntersectionTypeReferenceWithBounds(bounds); } else { throw new RuntimeException("Unknown TypeBinding: " + binding.getClass() + " " + binding); } bindingCache.remove(binding); return (CtTypeReference<T>) ref; } private CtTypeReference<?> getCtCircularTypeReference(TypeBinding b) { return bindingCache.get(b).clone(); } @SuppressWarnings("unchecked") <T> CtVariableReference<T> getVariableReference(MethodBinding methbin) { CtFieldReference<T> ref = this.jdtTreeBuilder.getFactory().Core().createFieldReference(); ref.setSimpleName(new String(methbin.selector)); ref.setType((CtTypeReference<T>) getTypeReference(methbin.returnType)); if (methbin.declaringClass != null) { ref.setDeclaringType(getTypeReference(methbin.declaringClass)); } else { ref.setDeclaringType(ref.getType()); } return ref; } <T> CtFieldReference<T> getVariableReference(FieldBinding varbin) { CtFieldReference<T> ref = this.jdtTreeBuilder.getFactory().Core().createFieldReference(); if (varbin == null) { return ref; } ref.setSimpleName(new String(varbin.name)); ref.setType(this.<T>getTypeReference(varbin.type)); if (varbin.declaringClass != null) { ref.setDeclaringType(getTypeReference(varbin.declaringClass)); } else { ref.setDeclaringType(ref.getType()); } ref.setFinal(varbin.isFinal()); ref.setStatic((varbin.modifiers & ClassFileConstants.AccStatic) != 0); return ref; } <T> CtFieldReference<T> getVariableReference(FieldBinding fieldBinding, char[] tokens) { final CtFieldReference<T> ref = getVariableReference(fieldBinding); if (fieldBinding != null) { return ref; } ref.setSimpleName(CharOperation.charToString(tokens)); return ref; } @SuppressWarnings("unchecked") <T> CtVariableReference<T> getVariableReference(VariableBinding varbin) { if (varbin instanceof FieldBinding) { return getVariableReference((FieldBinding) varbin); } else if (varbin instanceof LocalVariableBinding) { final LocalVariableBinding localVariableBinding = (LocalVariableBinding) varbin; if (localVariableBinding.declaration instanceof Argument && localVariableBinding.declaringScope instanceof MethodScope) { CtParameterReference<T> ref = this.jdtTreeBuilder.getFactory().Core().createParameterReference(); ref.setSimpleName(new String(varbin.name)); ref.setType((CtTypeReference<T>) getTypeReference(varbin.type)); final ReferenceContext referenceContext = localVariableBinding.declaringScope.referenceContext(); if (referenceContext instanceof LambdaExpression) { ref.setDeclaringExecutable(getExecutableReference(((LambdaExpression) referenceContext).binding)); } else { ref.setDeclaringExecutable(getExecutableReference(((AbstractMethodDeclaration) referenceContext).binding)); } return ref; } else if (localVariableBinding.declaration.binding instanceof CatchParameterBinding) { CtCatchVariableReference<T> ref = this.jdtTreeBuilder.getFactory().Core().createCatchVariableReference(); ref.setSimpleName(new String(varbin.name)); CtTypeReference<T> ref2 = getTypeReference(varbin.type); ref.setType(ref2); return ref; } else { CtLocalVariableReference<T> ref = this.jdtTreeBuilder.getFactory().Core().createLocalVariableReference(); ref.setSimpleName(new String(varbin.name)); CtTypeReference<T> ref2 = getTypeReference(varbin.type); ref.setType(ref2); return ref; } } else { // unknown VariableBinding, the caller must do something return null; } } <T> CtVariableReference<T> getVariableReference(ProblemBinding binding) { CtFieldReference<T> ref = this.jdtTreeBuilder.getFactory().Core().createFieldReference(); if (binding == null) { return ref; } ref.setSimpleName(new String(binding.name)); ref.setType((CtTypeReference<T>) getTypeReference(binding.searchType)); return ref; } List<CtTypeReference<?>> getBoundedTypesReferences(TypeBinding[] genericTypeArguments) { List<CtTypeReference<?>> res = new ArrayList<>(genericTypeArguments.length); for (TypeBinding tb : genericTypeArguments) { res.add(getBoundedTypeReference(tb)); } return res; } /** * Sets {@code declaring} as inner of {@code ref}, as either the package or the declaring type */ void setPackageOrDeclaringType(CtTypeReference<?> ref, CtReference declaring) { if (declaring instanceof CtPackageReference) { ref.setPackage((CtPackageReference) declaring); } else if (declaring instanceof CtTypeReference) { ref.setDeclaringType((CtTypeReference) declaring); } else if (declaring == null) { ref.setPackage(jdtTreeBuilder.getFactory().Package().topLevel()); } else { throw new AssertionError("unexpected declaring type: " + declaring.getClass() + " of " + declaring); } } /** * In noclasspath, lambda doesn't have always a binding for their variables accesses in their block/expression. * Here, we make the job of JDT and bind their variables accesses to their parameters. * * @param singleNameReference Name of the variable access. * @return executable reference which corresponds to the lambda. */ public CtExecutableReference<?> getLambdaExecutableReference(SingleNameReference singleNameReference) { ASTPair potentialLambda = null; for (ASTPair astPair : jdtTreeBuilder.getContextBuilder().stack) { if (astPair.node instanceof LambdaExpression) { potentialLambda = astPair; // stop at innermost lambda, fixes #1100 break; } } if (potentialLambda == null) { return null; } LambdaExpression lambdaJDT = (LambdaExpression) potentialLambda.node; for (Argument argument : lambdaJDT.arguments()) { if (CharOperation.equals(argument.name, singleNameReference.token)) { CtTypeReference<?> declaringType = null; if (lambdaJDT.enclosingScope instanceof MethodScope) { declaringType = jdtTreeBuilder.getReferencesBuilder().getTypeReference(((MethodScope) lambdaJDT.enclosingScope).parent.enclosingSourceType()); } CtLambda<?> ctLambda = (CtLambda<?>) potentialLambda.element; List<CtTypeReference<?>> parametersType = new ArrayList<>(); List<CtParameter<?>> parameters = ctLambda.getParameters(); for (CtParameter<?> parameter : parameters) { parametersType.add(parameter.getType() != null ? parameter.getType().clone() // it's the best match :( : jdtTreeBuilder.getFactory().Type().OBJECT.clone() ); } return jdtTreeBuilder.getFactory().Executable().createReference(declaringType, ctLambda.getType(), ctLambda.getSimpleName(), parametersType); } } return null; } }