/******************************************************************************* * 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.jdt.internal.ui.search; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTMatcher; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.CastExpression; import org.eclipse.jdt.core.dom.CatchClause; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ConstructorInvocation; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.Javadoc; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.ThrowStatement; import org.eclipse.jdt.core.dom.TryStatement; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclarationStatement; import org.eclipse.jdt.core.dom.UnionType; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.Bindings; import org.eclipse.jdt.internal.corext.util.Messages; import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; public class ExceptionOccurrencesFinder extends ASTVisitor implements IOccurrencesFinder { public static final String ID= "ExceptionOccurrencesFinder"; //$NON-NLS-1$ public static final String IS_EXCEPTION= "isException"; //$NON-NLS-1$ private CompilationUnit fASTRoot; private Name fSelectedName; private ITypeBinding fException; private ASTNode fStart; private TryStatement fTryStatement; private List<OccurrenceLocation> fResult; private String fDescription; public ExceptionOccurrencesFinder() { fResult= new ArrayList<OccurrenceLocation>(); } public String initialize(CompilationUnit root, int offset, int length) { return initialize(root, NodeFinder.perform(root, offset, length)); } public String initialize(CompilationUnit root, ASTNode node) { fASTRoot= root; if (!(node instanceof Name)) { return SearchMessages.ExceptionOccurrencesFinder_no_exception; } fSelectedName= ASTNodes.getTopMostName((Name)node); ASTNode parent= fSelectedName.getParent(); MethodDeclaration decl= resolveMethodDeclaration(parent); if (decl != null && methodThrowsException(decl, fSelectedName)) { fException= fSelectedName.resolveTypeBinding(); fStart= decl.getBody(); } else if (parent instanceof Type) { parent= parent.getParent(); if (parent instanceof UnionType) { parent= parent.getParent(); } if (parent instanceof SingleVariableDeclaration && parent.getParent() instanceof CatchClause) { CatchClause catchClause= (CatchClause)parent.getParent(); fTryStatement= (TryStatement)catchClause.getParent(); if (fTryStatement != null) { fException= fSelectedName.resolveTypeBinding(); fStart= fTryStatement.getBody(); } } } if (fException == null || fStart == null) return SearchMessages.ExceptionOccurrencesFinder_no_exception; fDescription= Messages.format(SearchMessages.ExceptionOccurrencesFinder_occurrence_description, BasicElementLabels.getJavaElementName(fException.getName())); return null; } private MethodDeclaration resolveMethodDeclaration(ASTNode node) { if (node instanceof MethodDeclaration) return (MethodDeclaration)node; Javadoc doc= (Javadoc) ASTNodes.getParent(node, ASTNode.JAVADOC); if (doc == null) return null; if (doc.getParent() instanceof MethodDeclaration) return (MethodDeclaration) doc.getParent(); return null; } private boolean methodThrowsException(MethodDeclaration method, Name exception) { ASTMatcher matcher = new ASTMatcher(); for (Iterator<Name> iter = method.thrownExceptions().iterator(); iter.hasNext();) { Name thrown = iter.next(); if (exception.subtreeMatch(matcher, thrown)) return true; } return false; } private void performSearch() { fStart.accept(this); if (fTryStatement != null) { visitResourceDeclarations(fTryStatement); handleImplicitResourceClosure(fTryStatement); } if (fSelectedName != null) { fResult.add(new OccurrenceLocation(fSelectedName.getStartPosition(), fSelectedName.getLength(), F_EXCEPTION_DECLARATION, fDescription)); } } private void visitResourceDeclarations(TryStatement tryStatement) { if (tryStatement.getAST().apiLevel() >= AST.JLS4) { List<VariableDeclarationExpression> resources= tryStatement.resources(); for (Iterator<VariableDeclarationExpression> iterator= resources.iterator(); iterator.hasNext();) { iterator.next().accept(this); } } } private void handleImplicitResourceClosure(TryStatement tryStatement) { //check if the exception is thrown as a result of resource#close() if (tryStatement.getAST().apiLevel() >= AST.JLS4) { List<VariableDeclarationExpression> resources= tryStatement.resources(); boolean exitMarked= false; for (VariableDeclarationExpression variable : resources) { Type type= variable.getType(); IMethodBinding methodBinding= Bindings.findMethodInHierarchy(type.resolveBinding(), "close", new ITypeBinding[0]); //$NON-NLS-1$ if (methodBinding != null) { ITypeBinding[] exceptionTypes= methodBinding.getExceptionTypes(); for (int j= 0; j < exceptionTypes.length; j++) { if (matches(exceptionTypes[j])) { // a close() throws the caught exception // mark name of resource for (VariableDeclarationFragment fragment : (List<VariableDeclarationFragment>) variable.fragments()) { SimpleName name= fragment.getName(); fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fDescription)); } if (!exitMarked) { // mark exit position exitMarked= true; Block body= tryStatement.getBody(); int offset= body.getStartPosition() + body.getLength() - 1; // closing bracket of try block fResult.add(new OccurrenceLocation(offset, 1, 0, Messages.format(SearchMessages.ExceptionOccurrencesFinder_occurrence_implicit_close_description, BasicElementLabels.getJavaElementName(fException.getName())))); } } } } } } } public OccurrenceLocation[] getOccurrences() { performSearch(); if (fResult.isEmpty()) return null; return fResult.toArray(new OccurrenceLocation[fResult.size()]); } public int getSearchKind() { return K_EXCEPTION_OCCURRENCE; } public CompilationUnit getASTRoot() { return fASTRoot; } public String getJobLabel() { return SearchMessages.ExceptionOccurrencesFinder_searchfor ; } public String getElementName() { if (fSelectedName != null) { return ASTNodes.asString(fSelectedName); } return null; } public String getUnformattedPluralLabel() { return SearchMessages.ExceptionOccurrencesFinder_label_plural; } public String getUnformattedSingularLabel() { return SearchMessages.ExceptionOccurrencesFinder_label_singular; } @Override public boolean visit(AnonymousClassDeclaration node) { return false; } @Override public boolean visit(CastExpression node) { if ("java.lang.ClassCastException".equals(fException.getQualifiedName())) { //$NON-NLS-1$ Type type= node.getType(); fResult.add(new OccurrenceLocation(type.getStartPosition(), type.getLength(), 0, fDescription)); } return super.visit(node); } @Override public boolean visit(ClassInstanceCreation node) { if (matches(node.resolveConstructorBinding())) { Type type= node.getType(); fResult.add(new OccurrenceLocation(type.getStartPosition(), type.getLength(), 0, fDescription)); } return super.visit(node); } @Override public boolean visit(ConstructorInvocation node) { if (matches(node.resolveConstructorBinding())) { // mark 'this' fResult.add(new OccurrenceLocation(node.getStartPosition(), 4, 0, fDescription)); } return super.visit(node); } @Override public boolean visit(MethodInvocation node) { if (matches(node.resolveMethodBinding())) { SimpleName name= node.getName(); fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fDescription)); } return super.visit(node); } @Override public boolean visit(SuperConstructorInvocation node) { if (matches(node.resolveConstructorBinding())) { // mark 'super' fResult.add(new OccurrenceLocation(node.getStartPosition(), 5, 0, fDescription)); } return super.visit(node); } @Override public boolean visit(SuperMethodInvocation node) { if (matches(node.resolveMethodBinding())) { SimpleName name= node.getName(); fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fDescription)); } return super.visit(node); } @Override public boolean visit(ThrowStatement node) { if (matches(node.getExpression().resolveTypeBinding())) { // mark 'throw' fResult.add(new OccurrenceLocation(node.getStartPosition(), 5, 0, fDescription)); } return super.visit(node); } @Override public boolean visit(TryStatement node) { handleImplicitResourceClosure(node); return super.visit(node); } @Override public boolean visit(TypeDeclarationStatement node) { // don't dive into local type declarations. return false; } private boolean matches(IMethodBinding binding) { if (binding == null) return false; ITypeBinding[] exceptions= binding.getExceptionTypes(); for (int i = 0; i < exceptions.length; i++) { ITypeBinding exception= exceptions[i]; if(matches(exception)) return true; } return false; } private boolean matches(ITypeBinding exception) { if (exception == null) return false; while (exception != null) { if (Bindings.equals(fException, exception)) return true; exception= exception.getSuperclass(); } return false; } public IOccurrencesFinder getNewInstance() { return new ExceptionOccurrencesFinder(); } public String getID() { return ID; } }