/*******************************************************************************
* Copyright (c) 2000, 2008 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.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.search.FieldDeclarationMatch;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.Reference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedFieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.util.SimpleSet;
import org.eclipse.jdt.internal.core.JavaElement;
public class FieldLocator extends VariableLocator {
protected boolean isDeclarationOfAccessedFieldsPattern;
public FieldLocator(FieldPattern pattern) {
super(pattern);
this.isDeclarationOfAccessedFieldsPattern= this.pattern instanceof DeclarationOfAccessedFieldsPattern;
}
protected int fineGrain() {
return this.pattern.fineGrain;
}
public int match(ASTNode node, MatchingNodeSet nodeSet) {
int declarationsLevel= IMPOSSIBLE_MATCH;
if (this.pattern.findReferences) {
if (node instanceof ImportReference) {
// With static import, we can have static field reference in import reference
ImportReference importRef= (ImportReference)node;
int length= importRef.tokens.length - 1;
if (importRef.isStatic() && ((importRef.bits & ASTNode.OnDemand) == 0) && matchesName(this.pattern.name, importRef.tokens[length])) {
char[][] compoundName= new char[length][];
System.arraycopy(importRef.tokens, 0, compoundName, 0, length);
FieldPattern fieldPattern= (FieldPattern)this.pattern;
char[] declaringType= CharOperation.concat(fieldPattern.declaringQualification, fieldPattern.declaringSimpleName, '.');
if (matchesName(declaringType, CharOperation.concatWith(compoundName, '.'))) {
declarationsLevel= this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
}
}
}
}
return nodeSet.addMatch(node, declarationsLevel);
}
//public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
public int match(FieldDeclaration node, MatchingNodeSet nodeSet) {
int referencesLevel= IMPOSSIBLE_MATCH;
if (this.pattern.findReferences)
// must be a write only access with an initializer
if (this.pattern.writeAccess && !this.pattern.readAccess && node.initialization != null)
if (matchesName(this.pattern.name, node.name))
referencesLevel= this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
int declarationsLevel= IMPOSSIBLE_MATCH;
if (this.pattern.findDeclarations) {
switch (node.getKind()) {
case AbstractVariableDeclaration.FIELD:
case AbstractVariableDeclaration.ENUM_CONSTANT:
if (matchesName(this.pattern.name, node.name))
if (matchesTypeReference(((FieldPattern)this.pattern).typeSimpleName, node.type))
declarationsLevel= this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
break;
}
}
return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match
}
//public int match(MethodDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
//public int match(MessageSend node, MatchingNodeSet nodeSet) - SKIP IT
//public int match(TypeDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
//public int match(TypeReference node, MatchingNodeSet nodeSet) - SKIP IT
protected int matchContainer() {
if (this.pattern.findReferences || this.pattern.fineGrain != 0) {
// need to look everywhere to find in javadocs and static import
return ALL_CONTAINER;
}
return CLASS_CONTAINER;
}
protected int matchField(FieldBinding field, boolean matchName) {
if (field == null)
return INACCURATE_MATCH;
if (matchName && !matchesName(this.pattern.name, field.readableName()))
return IMPOSSIBLE_MATCH;
FieldPattern fieldPattern= (FieldPattern)this.pattern;
ReferenceBinding receiverBinding= field.declaringClass;
if (receiverBinding == null) {
if (field == ArrayBinding.ArrayLength)
// optimized case for length field of an array
return fieldPattern.declaringQualification == null && fieldPattern.declaringSimpleName == null
? ACCURATE_MATCH
: IMPOSSIBLE_MATCH;
return INACCURATE_MATCH;
}
// Note there is no dynamic lookup for field access
int declaringLevel= resolveLevelForType(fieldPattern.declaringSimpleName, fieldPattern.declaringQualification, receiverBinding);
if (declaringLevel == IMPOSSIBLE_MATCH)
return IMPOSSIBLE_MATCH;
// look at field type only if declaring type is not specified
if (fieldPattern.declaringSimpleName == null)
return declaringLevel;
// get real field binding
FieldBinding fieldBinding= field;
if (field instanceof ParameterizedFieldBinding) {
fieldBinding= ((ParameterizedFieldBinding)field).originalField;
}
int typeLevel= resolveLevelForType(fieldBinding.type);
return declaringLevel > typeLevel ? typeLevel : declaringLevel; // return the weaker match
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.core.search.matching.PatternLocator#matchLevelAndReportImportRef(org.eclipse.jdt.internal.compiler.ast.ImportReference, org.eclipse.jdt.internal.compiler.lookup.Binding, org.eclipse.jdt.internal.core.search.matching.MatchLocator)
* Accept to report match of static field on static import
*/
protected void matchLevelAndReportImportRef(ImportReference importRef, Binding binding, MatchLocator locator) throws CoreException {
if (importRef.isStatic() && binding instanceof FieldBinding) {
super.matchLevelAndReportImportRef(importRef, binding, locator);
}
}
protected int matchReference(Reference node, MatchingNodeSet nodeSet, boolean writeOnlyAccess) {
if (node instanceof FieldReference) {
if (matchesName(this.pattern.name, ((FieldReference)node).token))
return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
return IMPOSSIBLE_MATCH;
}
return super.matchReference(node, nodeSet, writeOnlyAccess);
}
protected void matchReportReference(ASTNode reference, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException {
matchReportReference(reference, element, null, null, elementBinding, accuracy, locator);
}
protected void matchReportReference(ASTNode reference, IJavaElement element, IJavaElement localElement, IJavaElement[] otherElements, Binding elementBinding, int accuracy, MatchLocator locator)
throws CoreException {
if (this.isDeclarationOfAccessedFieldsPattern) {
// need exact match to be able to open on type ref
if (accuracy != SearchMatch.A_ACCURATE)
return;
// element that references the field must be included in the enclosing element
DeclarationOfAccessedFieldsPattern declPattern= (DeclarationOfAccessedFieldsPattern)this.pattern;
while (element != null && !declPattern.enclosingElement.equals(element))
element= element.getParent();
if (element != null) {
if (reference instanceof FieldReference) {
reportDeclaration(((FieldReference)reference).binding, locator, declPattern.knownFields);
} else if (reference instanceof QualifiedNameReference) {
QualifiedNameReference qNameRef= (QualifiedNameReference)reference;
Binding nameBinding= qNameRef.binding;
if (nameBinding instanceof FieldBinding)
reportDeclaration((FieldBinding)nameBinding, locator, declPattern.knownFields);
int otherMax= qNameRef.otherBindings == null ? 0 : qNameRef.otherBindings.length;
for (int i= 0; i < otherMax; i++)
reportDeclaration(qNameRef.otherBindings[i], locator, declPattern.knownFields);
} else if (reference instanceof SingleNameReference) {
reportDeclaration((FieldBinding)((SingleNameReference)reference).binding, locator, declPattern.knownFields);
}
}
} else if (reference instanceof ImportReference) {
ImportReference importRef= (ImportReference)reference;
long[] positions= importRef.sourcePositions;
int lastIndex= importRef.tokens.length - 1;
int start= (int)((positions[lastIndex]) >>> 32);
int end= (int)positions[lastIndex];
this.match= locator.newFieldReferenceMatch(element, localElement, elementBinding, accuracy, start, end - start + 1, importRef);
locator.report(this.match);
} else if (reference instanceof FieldReference) {
FieldReference fieldReference= (FieldReference)reference;
long position= fieldReference.nameSourcePosition;
int start= (int)(position >>> 32);
int end= (int)position;
this.match= locator.newFieldReferenceMatch(element, localElement, elementBinding, accuracy, start, end - start + 1, fieldReference);
locator.report(this.match);
} else if (reference instanceof SingleNameReference) {
int offset= reference.sourceStart;
this.match= locator.newFieldReferenceMatch(element, localElement, elementBinding, accuracy, offset, reference.sourceEnd - offset + 1, reference);
locator.report(this.match);
} else if (reference instanceof QualifiedNameReference) {
QualifiedNameReference qNameRef= (QualifiedNameReference)reference;
int length= qNameRef.tokens.length;
SearchMatch[] matches= new SearchMatch[length];
Binding nameBinding= qNameRef.binding;
int indexOfFirstFieldBinding= qNameRef.indexOfFirstFieldBinding > 0 ? qNameRef.indexOfFirstFieldBinding - 1 : 0;
// first token
if (matchesName(this.pattern.name, qNameRef.tokens[indexOfFirstFieldBinding]) && !(nameBinding instanceof LocalVariableBinding)) {
FieldBinding fieldBinding= nameBinding instanceof FieldBinding ? (FieldBinding)nameBinding : null;
if (fieldBinding == null) {
matches[indexOfFirstFieldBinding]= locator.newFieldReferenceMatch(element, localElement, elementBinding, accuracy, -1, -1, reference);
} else {
switch (matchField(fieldBinding, false)) {
case ACCURATE_MATCH:
matches[indexOfFirstFieldBinding]= locator.newFieldReferenceMatch(element, localElement, elementBinding, SearchMatch.A_ACCURATE, -1, -1, reference);
break;
case INACCURATE_MATCH:
this.match= locator.newFieldReferenceMatch(element, localElement, elementBinding, SearchMatch.A_INACCURATE, -1, -1, reference);
if (fieldBinding.type != null && fieldBinding.type.isParameterizedType() && this.pattern.hasTypeArguments()) {
updateMatch((ParameterizedTypeBinding)fieldBinding.type, this.pattern.getTypeArguments(), locator);
}
matches[indexOfFirstFieldBinding]= this.match;
break;
}
}
}
// other tokens
for (int i= indexOfFirstFieldBinding + 1; i < length; i++) {
char[] token= qNameRef.tokens[i];
if (matchesName(this.pattern.name, token)) {
FieldBinding otherBinding= qNameRef.otherBindings == null ? null : qNameRef.otherBindings[i - (indexOfFirstFieldBinding + 1)];
if (otherBinding == null) {
matches[i]= locator.newFieldReferenceMatch(element, localElement, elementBinding, accuracy, -1, -1, reference);
} else {
switch (matchField(otherBinding, false)) {
case ACCURATE_MATCH:
matches[i]= locator.newFieldReferenceMatch(element, localElement, elementBinding, SearchMatch.A_ACCURATE, -1, -1, reference);
break;
case INACCURATE_MATCH:
this.match= locator.newFieldReferenceMatch(element, localElement, elementBinding, SearchMatch.A_INACCURATE, -1, -1, reference);
if (otherBinding.type != null && otherBinding.type.isParameterizedType() && this.pattern.hasTypeArguments()) {
updateMatch((ParameterizedTypeBinding)otherBinding.type, this.pattern.getTypeArguments(), locator);
}
matches[i]= this.match;
break;
}
}
}
}
locator.reportAccurateFieldReference(matches, qNameRef);
}
}
/* (non-Javadoc)
* Overridden to reject unexact matches.
* @see org.eclipse.jdt.internal.core.search.matching.PatternLocator#updateMatch(org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding, char[][][], org.eclipse.jdt.internal.core.search.matching.MatchLocator)
*
*/
protected void updateMatch(ParameterizedTypeBinding parameterizedBinding, char[][][] patternTypeArguments, MatchLocator locator) {
// We can only refine if locator has an unit scope.
if (locator.unitScope == null)
return;
updateMatch(parameterizedBinding, patternTypeArguments, false, 0, locator);
if (!this.match.isExact()) {
// cannot accept neither erasure nor compatible match
this.match.setRule(0);
}
}
protected void reportDeclaration(FieldBinding fieldBinding, MatchLocator locator, SimpleSet knownFields) throws CoreException {
// ignore length field
if (fieldBinding == ArrayBinding.ArrayLength)
return;
ReferenceBinding declaringClass= fieldBinding.declaringClass;
IType type= locator.lookupType(declaringClass);
if (type == null)
return; // case of a secondary type
char[] bindingName= fieldBinding.name;
IField field= type.getField(new String(bindingName));
if (knownFields.addIfNotIncluded(field) == null)
return;
IResource resource= type.getResource();
boolean isBinary= type.isBinary();
IBinaryType info= null;
if (isBinary) {
if (resource == null)
resource= type.getJavaProject().getProject();
info= locator.getBinaryInfo((org.eclipse.jdt.internal.core.ClassFile)type.getClassFile(), resource);
locator.reportBinaryMemberDeclaration(resource, field, fieldBinding, info, SearchMatch.A_ACCURATE);
} else {
if (declaringClass instanceof ParameterizedTypeBinding)
declaringClass= ((ParameterizedTypeBinding)declaringClass).genericType();
ClassScope scope= ((SourceTypeBinding)declaringClass).scope;
if (scope != null) {
TypeDeclaration typeDecl= scope.referenceContext;
FieldDeclaration fieldDecl= null;
FieldDeclaration[] fieldDecls= typeDecl.fields;
for (int i= 0, length= fieldDecls.length; i < length; i++) {
if (CharOperation.equals(bindingName, fieldDecls[i].name)) {
fieldDecl= fieldDecls[i];
break;
}
}
if (fieldDecl != null) {
int offset= fieldDecl.sourceStart;
this.match= new FieldDeclarationMatch(((JavaElement)field).resolved(fieldBinding), SearchMatch.A_ACCURATE, offset, fieldDecl.sourceEnd - offset + 1, locator.getParticipant(),
resource);
locator.report(this.match);
}
}
}
}
protected int referenceType() {
return IJavaElement.FIELD;
}
public int resolveLevel(ASTNode possiblelMatchingNode) {
if (this.pattern.findReferences || this.pattern.fineGrain != 0) {
if (possiblelMatchingNode instanceof FieldReference)
return matchField(((FieldReference)possiblelMatchingNode).binding, true);
else if (possiblelMatchingNode instanceof NameReference)
return resolveLevel((NameReference)possiblelMatchingNode);
}
if (possiblelMatchingNode instanceof FieldDeclaration)
return matchField(((FieldDeclaration)possiblelMatchingNode).binding, true);
return IMPOSSIBLE_MATCH;
}
public int resolveLevel(Binding binding) {
if (binding == null)
return INACCURATE_MATCH;
if (!(binding instanceof FieldBinding))
return IMPOSSIBLE_MATCH;
return matchField((FieldBinding)binding, true);
}
protected int resolveLevel(NameReference nameRef) {
if (nameRef instanceof SingleNameReference)
return resolveLevel(nameRef.binding);
Binding binding= nameRef.binding;
QualifiedNameReference qNameRef= (QualifiedNameReference)nameRef;
FieldBinding fieldBinding= null;
if (binding instanceof FieldBinding) {
fieldBinding= (FieldBinding)binding;
char[] bindingName= fieldBinding.name;
int lastDot= CharOperation.lastIndexOf('.', bindingName);
if (lastDot > -1)
bindingName= CharOperation.subarray(bindingName, lastDot + 1, bindingName.length);
if (matchesName(this.pattern.name, bindingName)) {
int level= matchField(fieldBinding, false);
if (level != IMPOSSIBLE_MATCH)
return level;
}
}
int otherMax= qNameRef.otherBindings == null ? 0 : qNameRef.otherBindings.length;
for (int i= 0; i < otherMax; i++) {
char[] token= qNameRef.tokens[i + qNameRef.indexOfFirstFieldBinding];
if (matchesName(this.pattern.name, token)) {
FieldBinding otherBinding= qNameRef.otherBindings[i];
int level= matchField(otherBinding, false);
if (level != IMPOSSIBLE_MATCH)
return level;
}
}
return IMPOSSIBLE_MATCH;
}
/* (non-Javadoc)
* Resolve level for type with a given binding.
*/
protected int resolveLevelForType(TypeBinding typeBinding) {
FieldPattern fieldPattern= (FieldPattern)this.pattern;
TypeBinding fieldTypeBinding= typeBinding;
if (fieldTypeBinding != null && fieldTypeBinding.isParameterizedType()) {
fieldTypeBinding= typeBinding.erasure();
}
return resolveLevelForType(
fieldPattern.typeSimpleName,
fieldPattern.typeQualification,
fieldPattern.getTypeArguments(),
0,
fieldTypeBinding);
}
}