/*******************************************************************************
* 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.IAnnotatable;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.util.HashtableOfIntValues;
import org.eclipse.jdt.internal.core.search.matching.MatchLocator.WrappedCoreException;
/**
* Specific visitor of field or method declaration which can identify and store the local and other
* elements of one or several matching nodes.
* <p>
* This visitor can also peek up local or anonymous type declaration and restart a new
* {@link MatchLocator} traverse on this type.
* </p>
*/
class MemberDeclarationVisitor extends ASTVisitor {
// Matches information
private final MatchLocator locator;
private final IJavaElement enclosingElement;
private final MatchingNodeSet nodeSet;
private final ASTNode[] matchingNodes;
private final ASTNode matchingNode;
// Local type storage
HashtableOfIntValues occurrencesCounts= new HashtableOfIntValues(); // key = class name (char[]), value = occurrenceCount (int)
int nodesCount= 0;
// Local and other elements storage
private Annotation annotation;
private LocalDeclaration localDeclaration;
IJavaElement localElement;
IJavaElement[] localElements, otherElements;
IJavaElement[][] allOtherElements;
int ptr= -1;
int[] ptrs;
public MemberDeclarationVisitor(IJavaElement element, ASTNode[] nodes, MatchingNodeSet set, MatchLocator locator) {
this.enclosingElement= element;
this.nodeSet= set;
this.locator= locator;
if (nodes == null) {
this.matchingNode= null;
this.matchingNodes= null;
} else {
this.nodesCount= nodes.length;
if (nodes.length == 1) {
this.matchingNode= nodes[0];
this.matchingNodes= null;
} else {
this.matchingNode= null;
this.matchingNodes= nodes;
this.localElements= new IJavaElement[this.nodesCount];
this.ptrs= new int[this.nodesCount];
this.allOtherElements= new IJavaElement[this.nodesCount][];
}
}
}
public void endVisit(Argument argument, BlockScope scope) {
this.localDeclaration= null;
}
public void endVisit(LocalDeclaration declaration, BlockScope scope) {
this.localDeclaration= null;
}
public void endVisit(MarkerAnnotation markerAnnotation, BlockScope unused) {
this.annotation= null;
}
public void endVisit(NormalAnnotation normalAnnotation, BlockScope unused) {
this.annotation= null;
}
public void endVisit(SingleMemberAnnotation singleMemberAnnotation, BlockScope unused) {
this.annotation= null;
}
IJavaElement getLocalElement(int idx) {
if (this.nodesCount == 1) {
return this.localElement;
}
if (this.localElements != null) {
return this.localElements[idx];
}
return null;
}
IJavaElement[] getOtherElements(int idx) {
if (this.nodesCount == 1) {
if (this.otherElements != null) {
int length= this.otherElements.length;
if (this.ptr < (length - 1)) {
System.arraycopy(this.otherElements, 0, this.otherElements= new IJavaElement[this.ptr + 1], 0, this.ptr + 1);
}
}
return this.otherElements;
}
IJavaElement[] elements= this.allOtherElements == null ? null : this.allOtherElements[idx];
if (elements != null) {
int length= elements.length;
if (this.ptrs[idx] < (length - 1)) {
System.arraycopy(elements, 0, elements= this.allOtherElements[idx]= new IJavaElement[this.ptrs[idx] + 1], 0, this.ptrs[idx] + 1);
}
}
return elements;
}
private int matchNode(ASTNode reference) {
if (this.matchingNode != null) {
if (this.matchingNode == reference)
return 0;
} else {
int length= this.matchingNodes.length;
for (int i= 0; i < length; i++) {
if (this.matchingNodes[i] == reference) { // == is intentional
return i;
}
}
}
return -1;
}
/*
* Store the handle for the reference of the given index (e.g. peek in #matchingNodes
* or #matchingNode).
* Note that for performance reason, matching node and associated handles are
* not stored in array when there's only one reference to identify.
*/
private void storeHandle(int idx) {
if (this.localDeclaration == null)
return;
IJavaElement handle= this.locator.createHandle(this.localDeclaration, this.enclosingElement);
if (this.nodesCount == 1) {
if (this.localElement == null) {
if (this.annotation == null) {
this.localElement= handle;
} else {
IJavaElement annotHandle= this.locator.createHandle(this.annotation, (IAnnotatable)handle);
if (annotHandle == null) {
annotHandle= this.locator.createHandle(this.annotation, (IAnnotatable)this.enclosingElement);
}
this.localElement= annotHandle == null ? handle : annotHandle;
}
} else {
if (++this.ptr == 0) {
this.otherElements= new IJavaElement[10];
} else {
int length= this.otherElements.length;
if (this.ptr == length) {
System.arraycopy(this.otherElements, 0, this.otherElements= new IJavaElement[length + 10], 0, length);
}
}
if (this.annotation == null) {
this.otherElements[this.ptr]= handle;
} else {
IJavaElement annotHandle= this.locator.createHandle(this.annotation, (IAnnotatable)handle);
if (annotHandle == null) {
annotHandle= this.locator.createHandle(this.annotation, (IAnnotatable)this.enclosingElement);
}
this.otherElements[this.ptr]= annotHandle == null ? handle : annotHandle;
}
}
} else {
if (this.localElements[idx] == null) {
if (this.annotation == null) {
this.localElements[idx]= handle;
} else {
IJavaElement annotHandle= this.locator.createHandle(this.annotation, (IAnnotatable)handle);
if (annotHandle == null) {
annotHandle= this.locator.createHandle(this.annotation, (IAnnotatable)this.enclosingElement);
}
this.localElements[idx]= annotHandle == null ? handle : annotHandle;
}
this.ptrs[idx]= -1;
} else {
int oPtr= ++this.ptrs[idx];
if (oPtr == 0) {
this.allOtherElements[idx]= new IJavaElement[10];
} else {
int length= this.allOtherElements[idx].length;
if (oPtr == length) {
System.arraycopy(this.allOtherElements[idx], 0, this.allOtherElements[idx]= new IJavaElement[length + 10], 0, length);
}
}
if (this.annotation == null) {
this.allOtherElements[idx][oPtr]= handle;
} else {
IJavaElement annotHandle= this.locator.createHandle(this.annotation, (IAnnotatable)handle);
if (annotHandle == null) {
annotHandle= this.locator.createHandle(this.annotation, (IAnnotatable)this.enclosingElement);
}
this.allOtherElements[idx][oPtr]= annotHandle == null ? handle : annotHandle;
}
}
}
}
public boolean visit(Argument argument, BlockScope scope) {
this.localDeclaration= argument;
return true;
}
public boolean visit(LocalDeclaration declaration, BlockScope scope) {
this.localDeclaration= declaration;
return true;
}
public boolean visit(MarkerAnnotation markerAnnotation, BlockScope unused) {
this.annotation= markerAnnotation;
return true;
}
public boolean visit(NormalAnnotation normalAnnotation, BlockScope unused) {
this.annotation= normalAnnotation;
return true;
}
public boolean visit(QualifiedNameReference nameReference, BlockScope unused) {
if (this.nodesCount > 0) {
int idx= matchNode(nameReference);
if (idx >= 0) {
storeHandle(idx);
}
}
return false;
}
public boolean visit(QualifiedTypeReference typeReference, BlockScope unused) {
if (this.nodesCount > 0) {
int idx= matchNode(typeReference);
if (idx >= 0) {
storeHandle(idx);
}
}
return false;
}
public boolean visit(SingleMemberAnnotation singleMemberAnnotation, BlockScope unused) {
this.annotation= singleMemberAnnotation;
return true;
}
public boolean visit(SingleNameReference nameReference, BlockScope unused) {
if (this.nodesCount > 0) {
int idx= matchNode(nameReference);
if (idx >= 0) {
storeHandle(idx);
}
}
return false;
}
public boolean visit(SingleTypeReference typeReference, BlockScope unused) {
if (this.nodesCount > 0) {
int idx= matchNode(typeReference);
if (idx >= 0) {
storeHandle(idx);
}
}
return false;
}
public boolean visit(TypeDeclaration typeDeclaration, BlockScope unused) {
try {
char[] simpleName;
if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) {
simpleName= CharOperation.NO_CHAR;
} else {
simpleName= typeDeclaration.name;
}
int occurrenceCount= this.occurrencesCounts.get(simpleName);
if (occurrenceCount == HashtableOfIntValues.NO_VALUE) {
occurrenceCount= 1;
} else {
occurrenceCount= occurrenceCount + 1;
}
this.occurrencesCounts.put(simpleName, occurrenceCount);
if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) {
this.locator.reportMatching(typeDeclaration, this.enclosingElement, -1, this.nodeSet, occurrenceCount);
} else {
Integer level= (Integer)this.nodeSet.matchingNodes.removeKey(typeDeclaration);
this.locator.reportMatching(typeDeclaration, this.enclosingElement, level != null ? level.intValue() : -1, this.nodeSet, occurrenceCount);
}
return false; // don't visit members as this was done during reportMatching(...)
} catch (CoreException e) {
throw new WrappedCoreException(e);
}
}
}