/******************************************************************************* * Copyright (c) 2000, 2011 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.che.ide.ext.java.jdt.internal.corext.dom; import org.eclipse.che.ide.ext.java.jdt.core.compiler.IProblem; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTVisitor; import org.eclipse.che.ide.ext.java.jdt.core.dom.BreakStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit; import org.eclipse.che.ide.ext.java.jdt.core.dom.ContinueStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.IBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IMethodBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.ITypeBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IVariableBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.LabeledStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.NodeFinder; import org.eclipse.che.ide.ext.java.jdt.core.dom.SimpleName; import java.util.ArrayList; /** * 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) * * @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<SimpleName>(); 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. * * @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<SimpleName>(); 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) { ArrayList<SimpleName> res = new ArrayList<SimpleName>(); 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((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) { 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; } } }