Copyright (c) 2000, 2018 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2000, 2018 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * 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.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; 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.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.NodeFinder; import org.eclipse.jdt.core.dom.SimpleName;
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), for constructors also the type and all other constructors.
/** * 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), for constructors also the type and all other constructors. */
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)
Params:
  • root – The root of the AST tree to search
  • binding – The binding of the searched nodes
Returns:Return
/** * 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<SimpleName> res= new ArrayList<>(); BindingFinder nodeFinder= new BindingFinder(binding, res); root.accept(nodeFinder); return 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.
Params:
  • root – The root of the AST tree to search
  • name – The node to find linked nodes for
Returns:Return
/** * 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<SimpleName> res= new ArrayList<>(); LabelFinder nodeFinder= new LabelFinder(name, res); root.accept(nodeFinder); return 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: case IProblem.UnresolvedVariable: 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) { if (nameNode.getAST().apiLevel() >= AST.JLS10 && nameNode.isVar()) { return null; } ArrayList<SimpleName> 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())) { if (node instanceof SimpleName && name.equals(((SimpleName) node).getIdentifier())) { if (node.getAST().apiLevel() < AST.JLS10 || !((SimpleName) node).isVar()) res.add((SimpleName) node); } } } } } return res.toArray(new SimpleName[res.size()]); } private static class LabelFinder extends ASTVisitor { private SimpleName fLabel; private ASTNode fDefiningLabel; private ArrayList<SimpleName> fResult; public LabelFinder(SimpleName label, ArrayList<SimpleName> result) { super(true); fLabel= label; fResult= result; fDefiningLabel= null; } private boolean isSameLabel(SimpleName label) { return label != null && fLabel.getIdentifier().equals(label.getIdentifier()); } @Override public boolean visit(BreakStatement node) { SimpleName label= node.getLabel(); if (fDefiningLabel != null && isSameLabel(label) && ASTNodes.isParent(label, fDefiningLabel)) { fResult.add(label); } return false; } @Override public boolean visit(ContinueStatement node) { SimpleName label= node.getLabel(); if (fDefiningLabel != null && isSameLabel(label) && ASTNodes.isParent(label, fDefiningLabel)) { fResult.add(label); } return false; } @Override 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<SimpleName> fResult; public BindingFinder(IBinding binding, ArrayList<SimpleName> result) { super(true); fBinding= getDeclaration(binding); fResult= result; } @Override public boolean visit(SimpleName node) { if (node.getAST().apiLevel() >= AST.JLS10 && node.isVar()) { return false; } IBinding binding= node.resolveBinding(); if (binding == null) { return false; } binding= getDeclaration(binding); if (fBinding == binding) { fResult.add(node); } else if (binding.getKind() != fBinding.getKind()) { return false; } else if (binding.getKind() == IBinding.METHOD) { IMethodBinding curr= (IMethodBinding) binding; IMethodBinding methodBinding= (IMethodBinding) fBinding; if (methodBinding.overrides(curr) || curr.overrides(methodBinding)) { fResult.add(node); } } return false; } private static IBinding getDeclaration(IBinding binding) { if (binding instanceof ITypeBinding) { return ((ITypeBinding) binding).getTypeDeclaration(); } else if (binding instanceof IMethodBinding) { IMethodBinding methodBinding= (IMethodBinding) binding; if (methodBinding.isConstructor()) { // link all constructors with their type return methodBinding.getDeclaringClass().getTypeDeclaration(); } else { return methodBinding.getMethodDeclaration(); } } else if (binding instanceof IVariableBinding) { return ((IVariableBinding) binding).getVariableDeclaration(); } return binding; } } }