blob: bee6a31223d6aa0efb91def93439a9b77e60dfb2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2020, 2021 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:
* Red Hat Inc. - initial version based on SurroundWithTryCatchAnalyzer
*******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.surround;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.CompilationUnit;
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.LambdaExpression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodReference;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.UnionType;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.Selection;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.util.SurroundWithAnalyzer;
public class SurroundWithTryWithResourcesAnalyzer extends SurroundWithAnalyzer {
private ITypeBinding[] fExceptions;
private ASTNode fEnclosingNode;
private CompilationUnit fCompilationUnit;
public SurroundWithTryWithResourcesAnalyzer(ICompilationUnit unit, Selection selection) throws CoreException {
super(unit, selection, true);
}
public ITypeBinding[] getExceptions(Selection selection) {
if (fEnclosingNode != null && !getStatus().hasFatalError()) {
fExceptions= ExceptionAnalyzer.perform(fEnclosingNode, selection, false);
if (fExceptions == null || fExceptions.length == 0) {
if (fEnclosingNode instanceof MethodReference) {
invalidSelection(RefactoringCoreMessages.SurroundWithTryCatchAnalyzer_doesNotContain);
} else {
List<ASTNode> autoClosableNodes= getCoveredAutoClosableNodes();
if (autoClosableNodes.isEmpty()) {
fExceptions= new ITypeBinding[] { fCompilationUnit.getAST().resolveWellKnownType("java.lang.Exception") }; //$NON-NLS-1$
} else {
fExceptions= new ITypeBinding[0];
}
}
}
}
return fExceptions;
}
public ITypeBinding[] getCaughtExceptions() {
List<ITypeBinding> exceptions= new ArrayList<>();
TryStatement enclosingTry= (TryStatement)ASTResolving.findAncestor(getFirstSelectedNode(), ASTNode.TRY_STATEMENT);
while (enclosingTry != null) {
List<CatchClause> catchClauses= enclosingTry.catchClauses();
for (CatchClause catchClause : catchClauses) {
SingleVariableDeclaration sdv= catchClause.getException();
Type type= sdv.getType();
if (type instanceof UnionType) {
UnionType unionType= (UnionType)type;
List<Type> types= unionType.types();
for (Type t : types) {
ITypeBinding binding= t.resolveBinding();
if (binding == null) {
break;
}
exceptions.add(binding);
}
} else {
ITypeBinding binding= type.resolveBinding();
if (binding != null) {
exceptions.add(binding);
}
}
}
enclosingTry= (TryStatement)ASTResolving.findAncestor(enclosingTry.getParent(), ASTNode.TRY_STATEMENT);
}
return exceptions.toArray(new ITypeBinding[0]);
}
public ITypeBinding[] getThrownExceptions() {
List<ITypeBinding> exceptions= new ArrayList<>();
if (fEnclosingNode.getNodeType() == ASTNode.METHOD_DECLARATION) {
List<Type> thrownExceptions= ((MethodDeclaration) fEnclosingNode).thrownExceptionTypes();
for (Type type : thrownExceptions) {
ITypeBinding thrownException= type.resolveBinding();
if (thrownException != null) {
exceptions.add(thrownException);
}
}
} else {
ITypeBinding typeBinding= null;
if (fEnclosingNode.getLocationInParent() == LambdaExpression.BODY_PROPERTY) {
typeBinding= ((LambdaExpression) fEnclosingNode.getParent()).resolveTypeBinding();
} else if (fEnclosingNode instanceof MethodReference) {
typeBinding= ((MethodReference) fEnclosingNode).resolveTypeBinding();
}
if (typeBinding != null) {
IMethodBinding methodBinding= typeBinding.getFunctionalInterfaceMethod();
if (methodBinding != null) {
Collections.addAll(exceptions, methodBinding.getExceptionTypes());
}
}
}
return exceptions.toArray(new ITypeBinding[0]);
}
public List<ITypeBinding> calculateCatchesAndRethrows(List<ITypeBinding> exceptions, List<ITypeBinding> mustRethrowList) {
List<ITypeBinding> exceptionList= new ArrayList<>(exceptions);
ITypeBinding[] caughtExceptions= getCaughtExceptions();
if (caughtExceptions.length > 0) {
for (Iterator<ITypeBinding> iter= exceptionList.iterator(); iter.hasNext();) {
ITypeBinding binding= iter.next();
for (ITypeBinding caughtException : caughtExceptions) {
if (binding.isAssignmentCompatible(caughtException)) {
iter.remove();
break;
}
}
}
for (ITypeBinding binding : exceptionList) {
for (ITypeBinding caughtException : caughtExceptions) {
if (caughtException.isAssignmentCompatible(binding)) {
mustRethrowList.add(caughtException);
break;
}
}
}
}
ITypeBinding[] thrownExceptions= getThrownExceptions();
for (Iterator<ITypeBinding> iter= exceptionList.iterator(); iter.hasNext();) {
ITypeBinding binding= iter.next();
for (ITypeBinding thrownException : thrownExceptions) {
if (binding.isAssignmentCompatible(thrownException)) {
iter.remove();
break;
}
}
}
for (ITypeBinding binding : exceptionList) {
for (ITypeBinding thrownException : thrownExceptions) {
if (thrownException.isAssignmentCompatible(binding)) {
mustRethrowList.add(thrownException);
break;
}
}
}
return exceptionList;
}
public ASTNode getEnclosingNode() {
return fEnclosingNode;
}
@Override
public void endVisit(CompilationUnit node) {
fEnclosingNode= null;
fCompilationUnit= node;
if (!getStatus().hasFatalError() && hasSelectedNodes())
fEnclosingNode= SurroundWithAnalyzer.getEnclosingNode(getFirstSelectedNode());
super.endVisit(node);
}
/**
* Return the first auto closable nodes. When a node that isn't Autoclosable is found the method
* returns.
*
* @return List of the first AutoClosable nodes found
*/
public List<ASTNode> getCoveredAutoClosableNodes() {
ASTNode[] astNodes= getSelectedNodes();
List<ASTNode> autoClosableNodes= new ArrayList<>();
for (ASTNode astNode : astNodes) {
if (isAutoClosable(astNode)) {
autoClosableNodes.add(astNode);
} else {
return autoClosableNodes;
}
}
return autoClosableNodes;
}
private boolean isAutoClosable(ASTNode astNode) {
Map<SimpleName, IVariableBinding> simpleNames= getVariableStatementBinding(astNode);
for (Entry<SimpleName, IVariableBinding> entry : simpleNames.entrySet()) {
ITypeBinding typeBinding= null;
switch (entry.getKey().getParent().getNodeType()) {
case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
case ASTNode.VARIABLE_DECLARATION_STATEMENT:
case ASTNode.ASSIGNMENT:
typeBinding= entry.getValue().getType();
break;
default:
continue;
}
if (typeBinding == null) {
continue;
}
for (ITypeBinding superType : Bindings.getAllSuperTypes(typeBinding)) {
if (superType.getQualifiedName().equals("java.lang.AutoCloseable")) { //$NON-NLS-1$
return true;
}
}
}
return false;
}
public Map<SimpleName, IVariableBinding> getVariableStatementBinding(ASTNode astNode) {
Map<SimpleName, IVariableBinding> variableBindings= new HashMap<>();
astNode.accept(new ASTVisitor() {
@Override
public boolean visit(VariableDeclarationStatement node) {
for (Object o : node.fragments()) {
if (o instanceof VariableDeclarationFragment) {
VariableDeclarationFragment vdf= (VariableDeclarationFragment) o;
SimpleName name= vdf.getName();
IBinding binding= name.resolveBinding();
if (binding instanceof IVariableBinding) {
variableBindings.put(name, (IVariableBinding) binding);
break;
}
}
}
return false;
}
});
return variableBindings;
}
}