blob: fb5befed6063cc65a0655ab6f12cf1cdd4b6ccf3 [file] [log] [blame]
package org.eclipse.jdt.internal.core.search.matching;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.search.*;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.util.CharOperation;
import org.eclipse.jdt.internal.core.search.matching.*;
import org.eclipse.jdt.internal.core.Util;
import java.util.*;
/**
* A set of matches and potential matches.
*/
public class MatchSet {
private static final char[][] EMPTY_CHAR_CHAR = new char[0][];
private MatchLocator locator;
int matchContainer;
boolean cuHasBeenResolved = false;
int accuracy = IJavaSearchResultCollector.POTENTIAL_MATCH;
/**
* Set of matching ast nodes that don't need to be resolved.
*/
private Hashtable matchingNodes = new Hashtable(5);
/**
* Set of potential matching ast nodes. They need to be resolved
* to determine if they really match the search pattern.
*/
private Hashtable potentialMatchingNodes = new Hashtable(5);
public MatchSet(MatchLocator locator) {
this.locator = locator;
this.matchContainer = locator.pattern.matchContainer();
}
public void addPossibleMatch(AstNode node) {
this.potentialMatchingNodes.put(node, node);
}
public void addTrustedMatch(AstNode node) {
this.matchingNodes.put(node, node);
}
public void checkMatching(AstNode node) {
int matchLevel = this.locator.pattern.matchLevel(node);
switch (matchLevel) {
case SearchPattern.POSSIBLE_MATCH:
this.addPossibleMatch(node);
break;
case SearchPattern.TRUSTED_MATCH:
this.addTrustedMatch(node);
}
}
/**
* Returns the matching nodes that are in the given range.
*/
private AstNode[] matchingNodes(int start, int end) {
return this.nodesInRange(start, end, this.matchingNodes);
}
public boolean needsResolve() {
return this.potentialMatchingNodes.size() > 0;
}
/**
* Returns the matching nodes that are in the given range in the source order.
*/
private AstNode[] nodesInRange(int start, int end, Hashtable set) {
// collect nodes in the given range
Vector nodes = new Vector();
for (Enumeration keys = set.keys(); keys.hasMoreElements();) {
AstNode node = (AstNode)keys.nextElement();
if (start <= node.sourceStart && node.sourceEnd <= end) {
nodes.addElement(node);
}
}
AstNode[] result = new AstNode[nodes.size()];
nodes.copyInto(result);
// sort nodes by source starts
Util.Comparer comparer = new Util.Comparer() {
public int compare(Object o1, Object o2) {
AstNode node1 = (AstNode) o1;
AstNode node2 = (AstNode) o2;
return node1.sourceStart - node2.sourceStart;
}
};
Util.sort(result, comparer);
return result;
}
/**
* Returns the potential matching nodes that are in the given range.
*/
private AstNode[] potentialMatchingNodes(int start, int end) {
return this.nodesInRange(start, end, this.potentialMatchingNodes);
}
/**
* Visit the given method declaration and report the nodes that match exactly the
* search pattern (ie. the ones in the matching nodes set)
* Note that the method declaration has already been checked.
*/
private void reportMatching(AbstractMethodDeclaration method, char[][] definingTypeNames) throws CoreException {
// references in this method
AstNode[] nodes = this.matchingNodes(method.declarationSourceStart, method.declarationSourceEnd);
for (int i = 0; i < nodes.length; i++) {
AstNode node = nodes[i];
this.matchingNodes.remove(node);
if ((this.matchContainer & SearchPattern.METHOD) != 0) {
this.locator.reportReference(node, method, definingTypeNames, this.accuracy);
}
}
if (this.potentialMatchingNodes(method.declarationSourceStart, method.declarationSourceEnd).length == 0) {
// no need to resolve the statements in the method
method.statements = null;
}
}
/**
* Visit the given parse tree and report the nodes that match exactly the
* search pattern.
*/
public void reportMatching(CompilationUnitDeclaration unit) throws CoreException {
if (this.cuHasBeenResolved) {
// move the potential matching nodes that exactly match the search pattern to the matching nodes set
for (Enumeration potentialMatches = this.potentialMatchingNodes.keys(); potentialMatches.hasMoreElements();) {
AstNode node = (AstNode) potentialMatches.nextElement();
if (this.locator.pattern.matches(node)) {
this.matchingNodes.put(node, node);
}
}
this.potentialMatchingNodes = new Hashtable();
}
// package declaration
ImportReference pkg = unit.currentPackage;
if (pkg != null && this.matchingNodes.get(pkg) == pkg) {
this.matchingNodes.remove(pkg);
if ((this.matchContainer & SearchPattern.COMPILATION_UNIT) != 0) {
this.locator.reportPackageDeclaration(pkg);
}
}
// import declarations
ImportReference[] imports = unit.imports;
if (imports != null) {
for (int i = 0; i < imports.length; i++) {
ImportReference importRef = imports[i];
if (this.matchingNodes.get(importRef) == importRef) {
this.matchingNodes.remove(importRef);
if ((this.matchContainer & SearchPattern.COMPILATION_UNIT) != 0) {
this.locator.reportImport(importRef, this.accuracy);
}
}
}
}
// types
TypeDeclaration[] types = unit.types;
if (types != null) {
for (int i = 0; i < types.length; i++) {
TypeDeclaration type = types[i];
if (this.matchingNodes.get(type) == type) {
this.matchingNodes.remove(type);
if ((this.matchContainer & SearchPattern.COMPILATION_UNIT) != 0) {
this.locator.reportTypeDeclaration(type, new char[][] {type.name}, this.accuracy);
}
}
this.reportMatching(type, EMPTY_CHAR_CHAR);
}
}
}
/**
* Visit the given field declaration and report the nodes that match exactly the
* search pattern (ie. the ones in the matching nodes set)
* Note that the field declaration has already been checked.
*/
private void reportMatching(FieldDeclaration field, char[][] definingTypeNames, TypeDeclaration type) throws CoreException {
AstNode[] nodes = this.matchingNodes(field.declarationSourceStart, field.declarationSourceEnd);
for (int i = 0; i < nodes.length; i++) {
AstNode node = nodes[i];
this.matchingNodes.remove(node);
if ((this.matchContainer & SearchPattern.FIELD) != 0) {
this.locator.reportReference(node, type, field, definingTypeNames, this.accuracy);
}
}
}
/**
* Visit the given type declaration and report the nodes that match exactly the
* search pattern (ie. the ones in the matching nodes set)
* Note that the type declaration has already been checked.
*/
private void reportMatching(TypeDeclaration type, char[][] enclosingTypeNames) throws CoreException {
char[][] definingTypeNames = CharOperation.arrayConcat(enclosingTypeNames, type.name);
// fields
FieldDeclaration[] fields = type.fields;
if (fields != null) {
for (int i = 0; i < fields.length; i++) {
FieldDeclaration field = fields[i];
if (this.matchingNodes.get(field) == field) {
this.matchingNodes.remove(field);
if ((this.matchContainer & SearchPattern.CLASS) != 0) {
this.locator.reportFieldDeclaration(field, definingTypeNames, this.accuracy);
}
}
this.reportMatching(field, definingTypeNames, type);
}
}
// methods
AbstractMethodDeclaration[] methods = type.methods;
if (methods != null) {
for (int i = 0; i < methods.length; i++) {
AbstractMethodDeclaration method = methods[i];
if (this.matchingNodes.get(method) == method) {
this.matchingNodes.remove(method);
if ((this.matchContainer & SearchPattern.CLASS) != 0) {
this.locator.reportMethodDeclaration(method, definingTypeNames, this.accuracy);
}
}
this.reportMatching(method, definingTypeNames);
}
}
// member types
MemberTypeDeclaration[] memberTypes = type.memberTypes;
if (memberTypes != null) {
for (int i = 0; i < memberTypes.length; i++) {
MemberTypeDeclaration memberType = memberTypes[i];
if (this.matchingNodes.get(memberType) == memberType) {
this.matchingNodes.remove(memberType);
if ((this.matchContainer & SearchPattern.CLASS) != 0) {
char[][] memberTypeNames = CharOperation.arrayConcat(definingTypeNames, memberType.name);
this.locator.reportTypeDeclaration(memberType, memberTypeNames, this.accuracy);
}
}
this.reportMatching(memberType, definingTypeNames);
}
}
// super types
TypeReference superClass = type.superclass;
if (superClass != null && this.matchingNodes.get(superClass) == superClass) {
this.matchingNodes.remove(superClass);
if ((this.matchContainer & SearchPattern.CLASS) != 0) {
this.locator.reportSuperTypeReference(superClass, definingTypeNames, this.accuracy);
}
}
TypeReference[] superInterfaces = type.superInterfaces;
if (superInterfaces != null) {
for (int i = 0; i < superInterfaces.length; i++) {
TypeReference superInterface = superInterfaces[i];
if (this.matchingNodes.get(superInterface) == superInterface) {
this.matchingNodes.remove(superInterface);
if ((this.matchContainer & SearchPattern.CLASS) != 0) {
this.locator.reportSuperTypeReference(superInterface, definingTypeNames, this.accuracy);
}
}
}
}
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("Exact matches:"/*nonNLS*/);
for (Enumeration enum = this.matchingNodes.keys(); enum.hasMoreElements();) {
result.append("\n"/*nonNLS*/);
AstNode node = (AstNode)enum.nextElement();
result.append(node.toString(1));
}
result.append("\nPotential matches:"/*nonNLS*/);
for (Enumeration enum = this.potentialMatchingNodes.keys(); enum.hasMoreElements();) {
result.append("\n"/*nonNLS*/);
AstNode node = (AstNode)enum.nextElement();
result.append(node.toString(1));
}
return result.toString();
}
}