blob: 4ab37a60884daae5682dabdf437ea795c522aa5f [file] [log] [blame]
/*******************************************************************************
* 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;
}
}