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