| /******************************************************************************* |
| * Copyright (c) 2000, 2006 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.corext.dom; |
| |
| import java.util.ArrayList; |
| |
| import org.eclipse.jdt.core.compiler.IProblem; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTVisitor; |
| import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; |
| import org.eclipse.jdt.core.dom.BreakStatement; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.ContinueStatement; |
| import org.eclipse.jdt.core.dom.EnumDeclaration; |
| import org.eclipse.jdt.core.dom.IBinding; |
| import org.eclipse.jdt.core.dom.IMethodBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.IVariableBinding; |
| import org.eclipse.jdt.core.dom.LabeledStatement; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.TypeDeclaration; |
| |
| |
| /** |
| * Find all nodes connected to a given binding or node. e.g. Declaration of a field and all references. |
| * For types this includes also the constructor declaration, for methods also overridden methods |
| * or methods overriding (if existing in the same AST) |
| */ |
| |
| public class LinkedNodeFinder { |
| |
| private LinkedNodeFinder() { |
| } |
| |
| |
| /** |
| * Find all nodes connected to the given binding. e.g. Declaration of a field and all references. |
| * For types this includes also the constructor declaration, for methods also overridden methods |
| * or methods overriding (if existing in the same AST) |
| * @param root The root of the AST tree to search |
| * @param binding The binding of the searched nodes |
| * @return Return |
| */ |
| public static SimpleName[] findByBinding(ASTNode root, IBinding binding) { |
| ArrayList res= new ArrayList(); |
| BindingFinder nodeFinder= new BindingFinder(binding, res); |
| root.accept(nodeFinder); |
| return (SimpleName[]) res.toArray(new SimpleName[res.size()]); |
| } |
| |
| /** |
| * Find all nodes connected to the given name node. If the node has a binding then all nodes connected |
| * to this binding are returned. If the node has no binding, then all nodes that also miss a binding and have |
| * the same name are returned. |
| * @param root The root of the AST tree to search |
| * @param name The node to find linked nodes for |
| * @return Return |
| */ |
| public static SimpleName[] findByNode(ASTNode root, SimpleName name) { |
| IBinding binding = name.resolveBinding(); |
| if (binding != null) { |
| return findByBinding(root, binding); |
| } |
| SimpleName[] names= findByProblems(root, name); |
| if (names != null) { |
| return names; |
| } |
| int parentKind= name.getParent().getNodeType(); |
| if (parentKind == ASTNode.LABELED_STATEMENT || parentKind == ASTNode.BREAK_STATEMENT || parentKind == ASTNode.CONTINUE_STATEMENT) { |
| ArrayList res= new ArrayList(); |
| LabelFinder nodeFinder= new LabelFinder(name, res); |
| root.accept(nodeFinder); |
| return (SimpleName[]) res.toArray(new SimpleName[res.size()]); |
| } |
| return new SimpleName[] { name }; |
| } |
| |
| |
| |
| private static final int FIELD= 1; |
| private static final int METHOD= 2; |
| private static final int TYPE= 4; |
| private static final int LABEL= 8; |
| private static final int NAME= FIELD | TYPE; |
| |
| private static int getProblemKind(IProblem problem) { |
| switch (problem.getID()) { |
| case IProblem.UndefinedField: |
| return FIELD; |
| case IProblem.UndefinedMethod: |
| return METHOD; |
| case IProblem.UndefinedLabel: |
| return LABEL; |
| case IProblem.UndefinedName: |
| return NAME; |
| case IProblem.UndefinedType: |
| return TYPE; |
| } |
| return 0; |
| } |
| |
| private static int getNameNodeProblemKind(IProblem[] problems, SimpleName nameNode) { |
| int nameOffset= nameNode.getStartPosition(); |
| int nameInclEnd= nameOffset + nameNode.getLength() - 1; |
| |
| for (int i= 0; i < problems.length; i++) { |
| IProblem curr= problems[i]; |
| if (curr.getSourceStart() == nameOffset && curr.getSourceEnd() == nameInclEnd) { |
| int kind= getProblemKind(curr); |
| if (kind != 0) { |
| return kind; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| |
| public static SimpleName[] findByProblems(ASTNode parent, SimpleName nameNode) { |
| ArrayList res= new ArrayList(); |
| |
| ASTNode astRoot = parent.getRoot(); |
| if (!(astRoot instanceof CompilationUnit)) { |
| return null; |
| } |
| |
| IProblem[] problems= ((CompilationUnit) astRoot).getProblems(); |
| int nameNodeKind= getNameNodeProblemKind(problems, nameNode); |
| if (nameNodeKind == 0) { // no problem on node |
| return null; |
| } |
| |
| int bodyStart= parent.getStartPosition(); |
| int bodyEnd= bodyStart + parent.getLength(); |
| |
| String name= nameNode.getIdentifier(); |
| |
| for (int i= 0; i < problems.length; i++) { |
| IProblem curr= problems[i]; |
| int probStart= curr.getSourceStart(); |
| int probEnd= curr.getSourceEnd() + 1; |
| |
| if (probStart > bodyStart && probEnd < bodyEnd) { |
| int currKind= getProblemKind(curr); |
| if ((nameNodeKind & currKind) != 0) { |
| ASTNode node= NodeFinder.perform(parent, probStart, probEnd - probStart); |
| if (node instanceof SimpleName && name.equals(((SimpleName) node).getIdentifier())) { |
| res.add(node); |
| } |
| } |
| } |
| } |
| return (SimpleName[]) res.toArray(new SimpleName[res.size()]); |
| } |
| |
| private static class LabelFinder extends ASTVisitor { |
| |
| private SimpleName fLabel; |
| private ASTNode fDefiningLabel; |
| private ArrayList fResult; |
| |
| public LabelFinder(SimpleName label, ArrayList result) { |
| super(true); |
| fLabel= label; |
| fResult= result; |
| fDefiningLabel= null; |
| } |
| |
| private boolean isSameLabel(SimpleName label) { |
| return label != null && fLabel.getIdentifier().equals(label.getIdentifier()); |
| } |
| |
| public boolean visit(BreakStatement node) { |
| SimpleName label= node.getLabel(); |
| if (fDefiningLabel != null && isSameLabel(label) && ASTNodes.isParent(label, fDefiningLabel)) { |
| fResult.add(label); |
| } |
| return false; |
| } |
| |
| public boolean visit(ContinueStatement node) { |
| SimpleName label= node.getLabel(); |
| if (fDefiningLabel != null && isSameLabel(label) && ASTNodes.isParent(label, fDefiningLabel)) { |
| fResult.add(label); |
| } |
| return false; |
| } |
| |
| public boolean visit(LabeledStatement node) { |
| if (fDefiningLabel == null) { |
| SimpleName label= node.getLabel(); |
| if (fLabel == label || isSameLabel(label) && ASTNodes.isParent(fLabel, node)) { |
| fDefiningLabel= node; |
| fResult.add(label); |
| } |
| } |
| node.getBody().accept(this); |
| return false; |
| } |
| } |
| |
| private static class BindingFinder extends ASTVisitor { |
| |
| private IBinding fBinding; |
| private ArrayList fResult; |
| |
| public BindingFinder(IBinding binding, ArrayList result) { |
| super(true); |
| fBinding= getDeclaration(binding); |
| fResult= result; |
| } |
| |
| public boolean visit(MethodDeclaration node) { |
| if (node.isConstructor() && fBinding.getKind() == IBinding.TYPE) { |
| ASTNode typeNode= node.getParent(); |
| if (typeNode instanceof AbstractTypeDeclaration) { |
| if (fBinding == ((AbstractTypeDeclaration) typeNode).resolveBinding()) { |
| fResult.add(node.getName()); |
| } |
| } |
| } |
| return true; |
| } |
| |
| public boolean visit(TypeDeclaration node) { |
| if (fBinding.getKind() == IBinding.METHOD) { |
| IMethodBinding binding= (IMethodBinding) fBinding; |
| if (binding.isConstructor() && binding.getDeclaringClass() == node.resolveBinding()) { |
| fResult.add(node.getName()); |
| } |
| } |
| return true; |
| } |
| |
| public boolean visit(EnumDeclaration node) { |
| if (fBinding.getKind() == IBinding.METHOD) { |
| IMethodBinding binding= (IMethodBinding) fBinding; |
| if (binding.isConstructor() && binding.getDeclaringClass() == node.resolveBinding()) { |
| fResult.add(node.getName()); |
| } |
| } |
| return true; |
| } |
| |
| public boolean visit(AnnotationTypeDeclaration node) { |
| // annotation types can not have a constructor |
| return true; |
| } |
| |
| public boolean visit(SimpleName node) { |
| IBinding binding= node.resolveBinding(); |
| if (binding == null || binding.getKind() != fBinding.getKind()) { |
| return false; |
| } |
| binding= getDeclaration(binding); |
| |
| if (fBinding == binding) { |
| fResult.add(node); |
| } else if (binding.getKind() == IBinding.METHOD) { |
| if (isConnectedMethod((IMethodBinding) binding, (IMethodBinding) fBinding)) { |
| fResult.add(node); |
| } |
| } |
| return false; |
| } |
| |
| private static IBinding getDeclaration(IBinding binding) { |
| if (binding instanceof ITypeBinding) { |
| return ((ITypeBinding) binding).getTypeDeclaration(); |
| } else if (binding instanceof IMethodBinding) { |
| return ((IMethodBinding) binding).getMethodDeclaration(); |
| } else if (binding instanceof IVariableBinding) { |
| return ((IVariableBinding) binding).getVariableDeclaration(); |
| } |
| return binding; |
| } |
| |
| private boolean isConnectedMethod(IMethodBinding meth1, IMethodBinding meth2) { |
| if (Bindings.isEqualMethod(meth1, meth2.getName(), meth2.getParameterTypes())) { |
| ITypeBinding type1= meth1.getDeclaringClass(); |
| ITypeBinding type2= meth2.getDeclaringClass(); |
| if (Bindings.isSuperType(type2, type1) || Bindings.isSuperType(type1, type2)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| } |