| /******************************************************************************* |
| * 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.*; |
| 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); |
| } |
| } |
| } |