/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.core.search.matching; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchPattern; 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.Argument; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; public class ConstructorLocator extends PatternLocator { protected ConstructorPattern pattern; public ConstructorLocator(ConstructorPattern pattern) { super(pattern); this.pattern= pattern; } public int match(ASTNode node, MatchingNodeSet nodeSet) { // interested in ExplicitConstructorCall if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; if (!(node instanceof ExplicitConstructorCall)) return IMPOSSIBLE_MATCH; if (!matchParametersCount(node, ((ExplicitConstructorCall)node).arguments)) return IMPOSSIBLE_MATCH; return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) { int referencesLevel= this.pattern.findReferences ? matchLevelForReferences(node) : IMPOSSIBLE_MATCH; int declarationsLevel= this.pattern.findDeclarations ? matchLevelForDeclarations(node) : IMPOSSIBLE_MATCH; return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match } public int match(Expression node, MatchingNodeSet nodeSet) { // interested in AllocationExpression if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; if (!(node instanceof AllocationExpression)) return IMPOSSIBLE_MATCH; // constructor name is simple type name AllocationExpression allocation= (AllocationExpression)node; char[][] typeName= allocation.type.getTypeName(); if (this.pattern.declaringSimpleName != null && !matchesName(this.pattern.declaringSimpleName, typeName[typeName.length - 1])) return IMPOSSIBLE_MATCH; if (!matchParametersCount(node, allocation.arguments)) return IMPOSSIBLE_MATCH; return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } public int match(FieldDeclaration field, MatchingNodeSet nodeSet) { if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; // look only for enum constant if (field.type != null || !(field.initialization instanceof AllocationExpression)) return IMPOSSIBLE_MATCH; AllocationExpression allocation= (AllocationExpression)field.initialization; if (field.binding != null && field.binding.declaringClass != null) { if (this.pattern.declaringSimpleName != null && !matchesName(this.pattern.declaringSimpleName, field.binding.declaringClass.sourceName())) return IMPOSSIBLE_MATCH; } if (!matchParametersCount(field, allocation.arguments)) return IMPOSSIBLE_MATCH; return nodeSet.addMatch(field, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } //public int match(MethodDeclaration node, MatchingNodeSet nodeSet) - SKIP IT /** * Special case for message send in javadoc comment. They can be in fact bound to a contructor. * * @see "http://bugs.eclipse.org/bugs/show_bug.cgi?id=83285" */ public int match(MessageSend msgSend, MatchingNodeSet nodeSet) { if ((msgSend.bits & ASTNode.InsideJavadoc) == 0) return IMPOSSIBLE_MATCH; if (this.pattern.declaringSimpleName == null || CharOperation.equals(msgSend.selector, this.pattern.declaringSimpleName)) { return nodeSet.addMatch(msgSend, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } return IMPOSSIBLE_MATCH; } //public int match(Reference node, MatchingNodeSet nodeSet) - SKIP IT public int match(TypeDeclaration node, MatchingNodeSet nodeSet) { if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; // need to look for a generated default constructor return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } //public int match(TypeReference node, MatchingNodeSet nodeSet) - SKIP IT protected int matchConstructor(MethodBinding constructor) { if (!constructor.isConstructor()) return IMPOSSIBLE_MATCH; // declaring type, simple name has already been matched by matchIndexEntry() int level= resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification, constructor.declaringClass); if (level == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH; // parameter types int parameterCount= this.pattern.parameterCount; if (parameterCount > -1) { if (constructor.parameters == null) return INACCURATE_MATCH; if (parameterCount != constructor.parameters.length) return IMPOSSIBLE_MATCH; for (int i= 0; i < parameterCount; i++) { // TODO (frederic) use this call to refine accuracy on parameter types // int newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], this.pattern.parametersTypeArguments[i], 0, constructor.parameters[i]); int newLevel= resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], constructor.parameters[i]); if (level > newLevel) { if (newLevel == IMPOSSIBLE_MATCH) { // if (isErasureMatch) { // return ERASURE_MATCH; // } return IMPOSSIBLE_MATCH; } level= newLevel; // can only be downgraded } } } return level; } protected int matchContainer() { if (this.pattern.findReferences) return ALL_CONTAINER; // handles both declarations + references & just references // COMPILATION_UNIT_CONTAINER - implicit constructor call: case of Y extends X and Y doesn't define any constructor // CLASS_CONTAINER - implicit constructor call: case of constructor declaration with no explicit super call // METHOD_CONTAINER - reference in another constructor // FIELD_CONTAINER - anonymous in a field initializer // declarations are only found in Class return CLASS_CONTAINER; } protected int matchLevelForReferences(ConstructorDeclaration constructor) { ExplicitConstructorCall constructorCall= constructor.constructorCall; if (constructorCall == null || constructorCall.accessMode != ExplicitConstructorCall.ImplicitSuper) return IMPOSSIBLE_MATCH; if (this.pattern.parameterSimpleNames != null) { int length= this.pattern.parameterSimpleNames.length; Expression[] args= constructorCall.arguments; int argsLength= args == null ? 0 : args.length; if (length != argsLength) return IMPOSSIBLE_MATCH; } return this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; } protected int matchLevelForDeclarations(ConstructorDeclaration constructor) { // constructor name is stored in selector field if (this.pattern.declaringSimpleName != null && !matchesName(this.pattern.declaringSimpleName, constructor.selector)) return IMPOSSIBLE_MATCH; if (this.pattern.parameterSimpleNames != null) { int length= this.pattern.parameterSimpleNames.length; Argument[] args= constructor.arguments; int argsLength= args == null ? 0 : args.length; if (length != argsLength) return IMPOSSIBLE_MATCH; } // Verify type arguments (do not reject if pattern has no argument as it can be an erasure match) if (this.pattern.hasConstructorArguments()) { if (constructor.typeParameters == null || constructor.typeParameters.length != this.pattern.constructorArguments.length) return IMPOSSIBLE_MATCH; } return this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; } boolean matchParametersCount(ASTNode node, Expression[] args) { if (this.pattern.parameterSimpleNames != null && (!this.pattern.varargs || ((node.bits & ASTNode.InsideJavadoc) != 0))) { int length= this.pattern.parameterCount; if (length < 0) length= this.pattern.parameterSimpleNames.length; int argsLength= args == null ? 0 : args.length; if (length != argsLength) { return false; } } return true; } protected void matchReportReference(ASTNode reference, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { MethodBinding constructorBinding= null; boolean isSynthetic= false; if (reference instanceof ExplicitConstructorCall) { ExplicitConstructorCall call= (ExplicitConstructorCall)reference; isSynthetic= call.isImplicitSuper(); constructorBinding= call.binding; } else if (reference instanceof AllocationExpression) { AllocationExpression alloc= (AllocationExpression)reference; constructorBinding= alloc.binding; } else if (reference instanceof TypeDeclaration || reference instanceof FieldDeclaration) { super.matchReportReference(reference, element, elementBinding, accuracy, locator); if (this.match != null) return; } // Create search match this.match= locator.newMethodReferenceMatch(element, elementBinding, accuracy, -1, -1, true, isSynthetic, reference); // Look to refine accuracy if (constructorBinding instanceof ParameterizedGenericMethodBinding) { // parameterized generic method // Update match regarding constructor type arguments ParameterizedGenericMethodBinding parameterizedMethodBinding= (ParameterizedGenericMethodBinding)constructorBinding; this.match.setRaw(parameterizedMethodBinding.isRaw); TypeBinding[] typeBindings= parameterizedMethodBinding.isRaw ? null : parameterizedMethodBinding.typeArguments; updateMatch(typeBindings, locator, this.pattern.constructorArguments, this.pattern.hasConstructorParameters()); // Update match regarding declaring class type arguments if (constructorBinding.declaringClass.isParameterizedType() || constructorBinding.declaringClass.isRawType()) { ParameterizedTypeBinding parameterizedBinding= (ParameterizedTypeBinding)constructorBinding.declaringClass; if (!this.pattern.hasTypeArguments() && this.pattern.hasConstructorArguments() || parameterizedBinding.isParameterizedWithOwnVariables()) { // special case for constructor pattern which defines arguments but no type // in this case, we only use refined accuracy for constructor } else if (this.pattern.hasTypeArguments() && !this.pattern.hasConstructorArguments()) { // special case for constructor pattern which defines no constructor arguments but has type ones // in this case, we do not use refined accuracy updateMatch(parameterizedBinding, this.pattern.getTypeArguments(), this.pattern.hasTypeParameters(), 0, locator); } else { updateMatch(parameterizedBinding, this.pattern.getTypeArguments(), this.pattern.hasTypeParameters(), 0, locator); } } else if (this.pattern.hasTypeArguments()) { this.match.setRule(SearchPattern.R_ERASURE_MATCH); } // Update match regarding constructor parameters // TODO ? (frederic) } else if (constructorBinding instanceof ParameterizedMethodBinding) { // Update match regarding declaring class type arguments if (constructorBinding.declaringClass.isParameterizedType() || constructorBinding.declaringClass.isRawType()) { ParameterizedTypeBinding parameterizedBinding= (ParameterizedTypeBinding)constructorBinding.declaringClass; if (!this.pattern.hasTypeArguments() && this.pattern.hasConstructorArguments()) { // special case for constructor pattern which defines arguments but no type updateMatch(parameterizedBinding, new char[][][] { this.pattern.constructorArguments }, this.pattern.hasTypeParameters(), 0, locator); } else if (!parameterizedBinding.isParameterizedWithOwnVariables()) { updateMatch(parameterizedBinding, this.pattern.getTypeArguments(), this.pattern.hasTypeParameters(), 0, locator); } } else if (this.pattern.hasTypeArguments()) { this.match.setRule(SearchPattern.R_ERASURE_MATCH); } // Update match regarding constructor parameters // TODO ? (frederic) } else if (this.pattern.hasConstructorArguments()) { // binding has no type params, compatible erasure if pattern does this.match.setRule(SearchPattern.R_ERASURE_MATCH); } // See whether it is necessary to report or not if (this.match.getRule() == 0) return; // impossible match boolean report= (this.isErasureMatch && this.match.isErasure()) || (this.isEquivalentMatch && this.match.isEquivalent()) || this.match.isExact(); if (!report) return; // Report match int offset= reference.sourceStart; this.match.setOffset(offset); this.match.setLength(reference.sourceEnd - offset + 1); if (reference instanceof FieldDeclaration) { // enum declaration FieldDeclaration enumConstant= (FieldDeclaration)reference; if (enumConstant.initialization instanceof QualifiedAllocationExpression) { locator.reportAccurateEnumConstructorReference(this.match, enumConstant, (QualifiedAllocationExpression)enumConstant.initialization); return; } } locator.report(this.match); } public SearchMatch newDeclarationMatch(ASTNode reference, IJavaElement element, Binding binding, int accuracy, int length, MatchLocator locator) { this.match= null; int offset= reference.sourceStart; if (this.pattern.findReferences) { if (reference instanceof TypeDeclaration) { TypeDeclaration type= (TypeDeclaration)reference; AbstractMethodDeclaration[] methods= type.methods; if (methods != null) { for (int i= 0, max= methods.length; i < max; i++) { AbstractMethodDeclaration method= methods[i]; boolean synthetic= method.isDefaultConstructor() && method.sourceStart < type.bodyStart; this.match= locator.newMethodReferenceMatch(element, binding, accuracy, offset, length, method.isConstructor(), synthetic, method); } } } else if (reference instanceof ConstructorDeclaration) { ConstructorDeclaration constructor= (ConstructorDeclaration)reference; ExplicitConstructorCall call= constructor.constructorCall; boolean synthetic= call != null && call.isImplicitSuper(); this.match= locator.newMethodReferenceMatch(element, binding, accuracy, offset, length, constructor.isConstructor(), synthetic, constructor); } } if (this.match != null) { return this.match; } // super implementation... return locator.newDeclarationMatch(element, binding, accuracy, reference.sourceStart, length); } public int resolveLevel(ASTNode node) { if (this.pattern.findReferences) { if (node instanceof AllocationExpression) return resolveLevel((AllocationExpression)node); if (node instanceof ExplicitConstructorCall) return resolveLevel(((ExplicitConstructorCall)node).binding); if (node instanceof TypeDeclaration) return resolveLevel((TypeDeclaration)node); if (node instanceof FieldDeclaration) return resolveLevel((FieldDeclaration)node); if (node instanceof JavadocMessageSend) { return resolveLevel(((JavadocMessageSend)node).binding); } } if (node instanceof ConstructorDeclaration) return resolveLevel((ConstructorDeclaration)node, true); return IMPOSSIBLE_MATCH; } protected int referenceType() { return IJavaElement.METHOD; } protected int resolveLevel(AllocationExpression allocation) { // constructor name is simple type name char[][] typeName= allocation.type.getTypeName(); if (this.pattern.declaringSimpleName != null && !matchesName(this.pattern.declaringSimpleName, typeName[typeName.length - 1])) return IMPOSSIBLE_MATCH; return resolveLevel(allocation.binding); } protected int resolveLevel(FieldDeclaration field) { // only accept enum constants if (field.type != null || field.binding == null) return IMPOSSIBLE_MATCH; if (this.pattern.declaringSimpleName != null && !matchesName(this.pattern.declaringSimpleName, field.binding.type.sourceName())) return IMPOSSIBLE_MATCH; if (!(field.initialization instanceof AllocationExpression) || field.initialization.resolvedType.isLocalType()) return IMPOSSIBLE_MATCH; return resolveLevel(((AllocationExpression)field.initialization).binding); } public int resolveLevel(Binding binding) { if (binding == null) return INACCURATE_MATCH; if (!(binding instanceof MethodBinding)) return IMPOSSIBLE_MATCH; MethodBinding constructor= (MethodBinding)binding; int level= matchConstructor(constructor); if (level == IMPOSSIBLE_MATCH) { if (constructor != constructor.original()) { level= matchConstructor(constructor.original()); } } return level; } protected int resolveLevel(ConstructorDeclaration constructor, boolean checkDeclarations) { int referencesLevel= IMPOSSIBLE_MATCH; if (this.pattern.findReferences) { ExplicitConstructorCall constructorCall= constructor.constructorCall; if (constructorCall != null && constructorCall.accessMode == ExplicitConstructorCall.ImplicitSuper) { // eliminate explicit super call as it will be treated with matchLevel(ExplicitConstructorCall, boolean) int callCount= (constructorCall.arguments == null) ? 0 : constructorCall.arguments.length; int patternCount= (this.pattern.parameterSimpleNames == null) ? 0 : this.pattern.parameterSimpleNames.length; if (patternCount != callCount) { referencesLevel= IMPOSSIBLE_MATCH; } else { referencesLevel= resolveLevel(constructorCall.binding); if (referencesLevel == ACCURATE_MATCH) return ACCURATE_MATCH; // cannot get better } } } if (!checkDeclarations) return referencesLevel; int declarationsLevel= this.pattern.findDeclarations ? resolveLevel(constructor.binding) : IMPOSSIBLE_MATCH; return referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel; // answer the stronger match } protected int resolveLevel(TypeDeclaration type) { // find default constructor AbstractMethodDeclaration[] methods= type.methods; if (methods != null) { for (int i= 0, length= methods.length; i < length; i++) { AbstractMethodDeclaration method= methods[i]; if (method.isDefaultConstructor() && method.sourceStart < type.bodyStart) // if synthetic return resolveLevel((ConstructorDeclaration)method, false); } } return IMPOSSIBLE_MATCH; } public String toString() { return "Locator for " + this.pattern.toString(); //$NON-NLS-1$ } }