/*******************************************************************************
* 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.wst.jsdt.internal.core.search.matching;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.wst.jsdt.core.IField;
import org.eclipse.wst.jsdt.core.IJavaScriptElement;
import org.eclipse.wst.jsdt.core.IType;
import org.eclipse.wst.jsdt.core.compiler.CharOperation;
import org.eclipse.wst.jsdt.core.infer.InferredAttribute;
import org.eclipse.wst.jsdt.core.search.FieldDeclarationMatch;
import org.eclipse.wst.jsdt.core.search.SearchMatch;
import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode;
import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.ast.FieldReference;
import org.eclipse.wst.jsdt.internal.compiler.ast.ImportReference;
import org.eclipse.wst.jsdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.ast.NameReference;
import org.eclipse.wst.jsdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.wst.jsdt.internal.compiler.ast.Reference;
import org.eclipse.wst.jsdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.wst.jsdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.env.IBinaryType;
import org.eclipse.wst.jsdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.Binding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.ClassScope;
import org.eclipse.wst.jsdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.Scope;
import org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.wst.jsdt.internal.compiler.util.SimpleSet;
import org.eclipse.wst.jsdt.internal.core.JavaElement;
public class FieldLocator extends VariableLocator {
protected boolean isDeclarationOfAccessedFieldsPattern;
FieldPattern fieldPattern;
public FieldLocator(FieldPattern pattern) {
super(pattern);
this.fieldPattern=pattern;
this.isDeclarationOfAccessedFieldsPattern = this.pattern instanceof DeclarationOfAccessedFieldsPattern;
}
public int match(ASTNode node, MatchingNodeSet nodeSet) {
int declarationsLevel = IMPOSSIBLE_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 = ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
int declarationsLevel = IMPOSSIBLE_MATCH;
if (this.pattern.findDeclarations) {
switch (node.getKind()) {
case AbstractVariableDeclaration.FIELD :
if (matchesName(this.pattern.name, node.name))
if (matchesTypeReference(((FieldPattern)this.pattern).typeSimpleName, node.type))
declarationsLevel = ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
break;
}
}
return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match
}
public int match(LocalDeclaration node, MatchingNodeSet nodeSet) {
if (!this.fieldPattern.isVar)
return IMPOSSIBLE_MATCH;
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 = ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
int declarationsLevel = IMPOSSIBLE_MATCH;
if (this.pattern.findDeclarations)
if (matchesName(this.pattern.name, node.name))
// if (node.declarationSourceStart == this.pattern.getVariableStart())
declarationsLevel = ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match
}
//public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
public int match(InferredAttribute node, MatchingNodeSet nodeSet) {
if (this.fieldPattern.isVar)
return IMPOSSIBLE_MATCH;
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 = ((InternalSearchPattern)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 = ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
// break;
// }
}
return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match
}
//public int match(FunctionDeclaration 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) {
// need to look everywhere to find in javadocs and static import
return ALL_CONTAINER;
}
return (this.fieldPattern.isVar)? COMPILATION_UNIT_CONTAINER : 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;
int typeLevel = resolveLevelForType(fieldBinding.type);
return declaringLevel > typeLevel ? typeLevel : declaringLevel; // return the weaker match
}
protected int matchLocalVariable(LocalVariableBinding variable, boolean matchName) {
if (variable == null) return INACCURATE_MATCH;
if (matchName && !matchesName(this.pattern.name, variable.readableName())) return IMPOSSIBLE_MATCH;
return ACCURATE_MATCH;
}
/* (non-Javadoc)
* @see org.eclipse.wst.jsdt.internal.core.search.matching.PatternLocator#matchLevelAndReportImportRef(org.eclipse.wst.jsdt.internal.compiler.ast.ImportReference, org.eclipse.wst.jsdt.internal.compiler.lookup.Binding, org.eclipse.wst.jsdt.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 {
}
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, ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
return IMPOSSIBLE_MATCH;
}
return super.matchReference(node, nodeSet, writeOnlyAccess);
}
protected void matchReportReference(ASTNode reference, IJavaScriptElement element, 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];
match = locator.newFieldReferenceMatch(element, elementBinding, accuracy, start, end-start+1, importRef);
locator.report(match);
} else if (reference instanceof FieldReference) {
FieldReference fieldReference = (FieldReference) reference;
long position = fieldReference.nameSourcePosition;
int start = (int) (position >>> 32);
int end = (int) position;
match = locator.newFieldReferenceMatch(element, elementBinding, accuracy, start, end-start+1, fieldReference);
locator.report(match);
} else if (reference instanceof SingleNameReference) {
int offset = reference.sourceStart;
match = locator.newFieldReferenceMatch(element, elementBinding, accuracy, offset, reference.sourceEnd-offset+1, reference);
locator.report(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, elementBinding, accuracy, -1, -1, reference);
} else {
switch (matchField(fieldBinding, false)) {
case ACCURATE_MATCH:
matches[indexOfFirstFieldBinding] = locator.newFieldReferenceMatch(element, elementBinding, SearchMatch.A_ACCURATE, -1, -1, reference);
break;
case INACCURATE_MATCH:
match = locator.newFieldReferenceMatch(element, elementBinding, SearchMatch.A_INACCURATE, -1, -1, reference);
matches[indexOfFirstFieldBinding] = 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, elementBinding, accuracy, -1, -1, reference);
} else {
switch (matchField(otherBinding, false)) {
case ACCURATE_MATCH:
matches[i] = locator.newFieldReferenceMatch(element, elementBinding, SearchMatch.A_ACCURATE, -1, -1, reference);
break;
case INACCURATE_MATCH:
match = locator.newFieldReferenceMatch(element, elementBinding, SearchMatch.A_INACCURATE, -1, -1, reference);
matches[i] = match;
break;
}
}
}
}
locator.reportAccurateFieldReference(matches, qNameRef);
}
}
/* (non-Javadoc)
* Overridden to reject unexact matches.
* @see org.eclipse.wst.jsdt.internal.core.search.matching.PatternLocator#updateMatch(org.eclipse.wst.jsdt.internal.compiler.lookup.ParameterizedTypeBinding, char[][][], org.eclipse.wst.jsdt.internal.core.search.matching.MatchLocator)
*
*/
protected void updateMatch(char[][][] patternTypeArguments, MatchLocator locator) {
// We can only refine if locator has an unit scope.
if (locator.unitScope == null) return;
if (!match.isExact()) {
// cannot accept neither erasure nor compatible match
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.getJavaScriptProject().getProject();
info = locator.getBinaryInfo((org.eclipse.wst.jsdt.internal.core.ClassFile) type.getClassFile(), resource);
locator.reportBinaryMemberDeclaration(resource, field, fieldBinding, info, SearchMatch.A_ACCURATE);
} else {
Scope scp = ((SourceTypeBinding) declaringClass).scope;
if (scp instanceof ClassScope) {
ClassScope scope=(ClassScope)scp;
TypeDeclaration typeDecl = scope.referenceContext;
if (typeDecl!=null) {
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;
match = new FieldDeclarationMatch(((JavaElement) field)
.resolved(fieldBinding), SearchMatch.A_ACCURATE,
offset, fieldDecl.sourceEnd - offset + 1, locator
.getParticipant(), resource);
locator.report(match);
}
} else if (scope.inferredType!=null)
{
InferredAttribute attribute=null;
for (int attributeInx=0; attributeInx<scope.inferredType.numberAttributes; attributeInx++) {
InferredAttribute element = scope.inferredType.attributes[attributeInx];
if (CharOperation.equals(bindingName, element.name)) {
attribute =element;
break;
}
}
if (attribute != null) {
int offset = attribute.sourceStart;
match = new FieldDeclarationMatch(((JavaElement) field)
.resolved(fieldBinding), SearchMatch.A_ACCURATE,
offset, attribute.sourceEnd - offset + 1, locator
.getParticipant(), resource);
locator.report(match);
}
}
}
else if (scp !=null)
//TODO: could be compilation unit scope
throw new org.eclipse.wst.jsdt.core.UnimplementedException();
}
}
protected int referenceType() {
return IJavaScriptElement.FIELD;
}
public int resolveLevel(ASTNode possiblelMatchingNode) {
if (this.pattern.findReferences) {
if (possiblelMatchingNode instanceof FieldReference)
if (!this.fieldPattern.isVar)
return matchField(((FieldReference) possiblelMatchingNode).binding, true);
else return IMPOSSIBLE_MATCH;
else if (possiblelMatchingNode instanceof NameReference)
return resolveLevel((NameReference) possiblelMatchingNode);
}
if (possiblelMatchingNode instanceof FieldDeclaration)
return matchField(((FieldDeclaration) possiblelMatchingNode).binding, true);
else if (possiblelMatchingNode instanceof LocalDeclaration)
return matchLocalVariable(((LocalDeclaration) possiblelMatchingNode).binding, true);
return IMPOSSIBLE_MATCH;
}
public int resolveLevel(Binding binding) {
if (binding == null) return INACCURATE_MATCH;
if (fieldPattern.isVar)
{
if (!(binding instanceof LocalVariableBinding)) return IMPOSSIBLE_MATCH;
LocalVariableBinding localVariableBinding=(LocalVariableBinding) binding;
if (localVariableBinding.declaringScope.kind!=Scope.COMPILATION_UNIT_SCOPE)
return IMPOSSIBLE_MATCH;
return matchLocalVariable((LocalVariableBinding) binding, true);
}
else
{
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;
return resolveLevelForType(
fieldPattern.typeSimpleName,
fieldPattern.typeQualification,
fieldPattern.getTypeArguments(),
0,
fieldTypeBinding);
}
public int matchLocalDeclaration(LocalDeclaration 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 = ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
int declarationsLevel = IMPOSSIBLE_MATCH;
if (this.pattern.findDeclarations)
if (matchesName(this.pattern.name, node.name))
declarationsLevel = ((InternalSearchPattern)this.pattern).mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match
}
}