| /******************************************************************************* |
| * 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 code based on SurroundWithTryCatchRefactoring |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.refactoring.surround; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| |
| import org.eclipse.text.edits.MultiTextEdit; |
| import org.eclipse.text.edits.TextEdit; |
| import org.eclipse.text.edits.TextEditGroup; |
| |
| import org.eclipse.jface.text.ITextSelection; |
| |
| import org.eclipse.ltk.core.refactoring.Change; |
| import org.eclipse.ltk.core.refactoring.Refactoring; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| import org.eclipse.ltk.core.refactoring.TextFileChange; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| 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.Assignment; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.CatchClause; |
| import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.ExpressionStatement; |
| import org.eclipse.jdt.core.dom.IExtendedModifier; |
| 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.MethodReference; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.jdt.core.dom.Statement; |
| 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.UnionType; |
| import org.eclipse.jdt.core.dom.VariableDeclaration; |
| import org.eclipse.jdt.core.dom.VariableDeclarationExpression; |
| import org.eclipse.jdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.jdt.core.dom.VariableDeclarationStatement; |
| import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation; |
| import org.eclipse.jdt.core.dom.rewrite.ListRewrite; |
| import org.eclipse.jdt.core.refactoring.CompilationUnitChange; |
| |
| import org.eclipse.jdt.internal.core.manipulation.StubUtility; |
| import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; |
| import org.eclipse.jdt.internal.core.manipulation.util.Strings; |
| import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder; |
| import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; |
| import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder; |
| import org.eclipse.jdt.internal.corext.dom.Selection; |
| import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel; |
| import org.eclipse.jdt.internal.corext.refactoring.Checks; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; |
| import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; |
| |
| import org.eclipse.jdt.internal.ui.text.correction.QuickAssistProcessor; |
| |
| /** |
| * Surround a set of statements with a try-with-resources block. |
| * |
| * Special case: |
| * |
| * URL url= file.toURL(); |
| * |
| * In this case the variable declaration statement gets convert into a |
| * declaration without initializer. So the body of the try/catch block |
| * only consists of new assignments. In this case we can't move the |
| * selected nodes (e.g. the declaration) into the try block. |
| */ |
| public class SurroundWithTryWithResourcesRefactoring extends Refactoring { |
| |
| public final String GROUP_EXC_TYPE= "exc_type"; //$NON-NLS-1$ |
| public final String GROUP_EXC_NAME= "exc_name"; //$NON-NLS-1$ |
| public final String GROUP_TRY_STATEMENT= "try_stmt"; //$NON-NLS-1$ |
| |
| private Selection fSelection; |
| private SurroundWithTryWithResourcesAnalyzer fAnalyzer; |
| private boolean fLeaveDirty; |
| |
| private ICompilationUnit fCUnit; |
| private CompilationUnit fRootNode; |
| private ASTRewrite fRewriter; |
| private ImportRewrite fImportRewrite; |
| private CodeScopeBuilder.Scope fScope; |
| private ASTNode[] fSelectedNodes; |
| private List<ASTNode> fAutoClosableNodes; |
| |
| private LinkedProposalModel fLinkedProposalModel; |
| |
| private SurroundWithTryWithResourcesRefactoring(ICompilationUnit cu, Selection selection) { |
| fCUnit= cu; |
| fSelection= selection; |
| fLeaveDirty= false; |
| } |
| |
| public static SurroundWithTryWithResourcesRefactoring create(ICompilationUnit cu, int offset, int length) { |
| return new SurroundWithTryWithResourcesRefactoring(cu, Selection.createFromStartLength(offset, length)); |
| } |
| |
| public static SurroundWithTryWithResourcesRefactoring create(ICompilationUnit cu, ITextSelection selection) { |
| return new SurroundWithTryWithResourcesRefactoring(cu, Selection.createFromStartLength(selection.getOffset(), selection.getLength())); |
| } |
| |
| public LinkedProposalModel getLinkedProposalModel() { |
| return fLinkedProposalModel; |
| } |
| |
| public void setLeaveDirty(boolean leaveDirty) { |
| fLeaveDirty= leaveDirty; |
| } |
| |
| public boolean stopExecution() { |
| if (fAnalyzer == null) |
| return true; |
| ITypeBinding[] exceptions= fAnalyzer.getExceptions(fAnalyzer.getSelection()); |
| List<ASTNode> autoClosableNodes= fAnalyzer.getCoveredAutoClosableNodes(); |
| return (exceptions == null || exceptions.length == 0) && (autoClosableNodes == null || autoClosableNodes.isEmpty()); |
| } |
| |
| /* non Java-doc |
| * @see IRefactoring#getName() |
| */ |
| @Override |
| public String getName() { |
| return RefactoringCoreMessages.SurroundWithTryWithResourcesRefactoring_name; |
| } |
| |
| public RefactoringStatus checkActivationBasics(CompilationUnit rootNode) throws CoreException { |
| RefactoringStatus result= new RefactoringStatus(); |
| fRootNode= rootNode; |
| |
| fAnalyzer= new SurroundWithTryWithResourcesAnalyzer(fCUnit, fSelection); |
| fRootNode.accept(fAnalyzer); |
| result.merge(fAnalyzer.getStatus()); |
| fAutoClosableNodes= fAnalyzer.getCoveredAutoClosableNodes(); |
| if (fAutoClosableNodes == null || fAutoClosableNodes.isEmpty()) { |
| result.merge(RefactoringStatus.createWarningStatus(RefactoringCoreMessages.SurroundWithTryWithResourcesRefactoring_notAutoclosable)); |
| } |
| return result; |
| } |
| |
| |
| /* |
| * @see Refactoring#checkActivation(IProgressMonitor) |
| */ |
| @Override |
| public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { |
| CompilationUnit rootNode= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL).parse(fCUnit, true, pm); |
| return checkActivationBasics(rootNode); |
| } |
| |
| /* |
| * @see Refactoring#checkInput(IProgressMonitor) |
| */ |
| @Override |
| public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { |
| return Checks.validateModifiesFiles( |
| ResourceUtil.getFiles(new ICompilationUnit[]{fCUnit}), |
| getValidationContext(), pm); |
| } |
| |
| /* non Java-doc |
| * @see IRefactoring#createChange(IProgressMonitor) |
| */ |
| @Override |
| public Change createChange(IProgressMonitor pm) throws CoreException { |
| final String NN= ""; //$NON-NLS-1$ |
| if (pm == null) pm= new NullProgressMonitor(); |
| pm.beginTask(NN, 2); |
| try { |
| final CompilationUnitChange result= new CompilationUnitChange(getName(), fCUnit); |
| if (fLeaveDirty) |
| result.setSaveMode(TextFileChange.LEAVE_DIRTY); |
| MultiTextEdit root= new MultiTextEdit(); |
| result.setEdit(root); |
| fRewriter= ASTRewrite.create(fAnalyzer.getEnclosingBodyDeclaration().getAST()); |
| fImportRewrite= StubUtility.createImportRewrite(fRootNode, true); |
| |
| fLinkedProposalModel= new LinkedProposalModel(); |
| |
| fScope= CodeScopeBuilder.perform(fAnalyzer.getEnclosingBodyDeclaration(), fSelection). |
| findScope(fSelection.getOffset(), fSelection.getLength()); |
| fScope.setCursor(fSelection.getOffset()); |
| |
| fSelectedNodes= fAnalyzer.getSelectedNodes(); |
| |
| createTryWithResourcesStatement(fCUnit.getBuffer(), fCUnit.findRecommendedLineSeparator()); |
| |
| if (fImportRewrite.hasRecordedChanges()) { |
| TextEdit edit= fImportRewrite.rewriteImports(null); |
| root.addChild(edit); |
| result.addTextEditGroup(new TextEditGroup(NN, new TextEdit[] {edit} )); |
| } |
| TextEdit change= fRewriter.rewriteAST(); |
| root.addChild(change); |
| result.addTextEditGroup(new TextEditGroup(NN, new TextEdit[] {change} )); |
| return result; |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private AST getAST() { |
| return fRootNode.getAST(); |
| } |
| |
| private void createTryWithResourcesStatement(org.eclipse.jdt.core.IBuffer buffer, String lineDelimiter) throws CoreException { |
| List<Statement> result= new ArrayList<>(1); |
| TryStatement tryStatement= getAST().newTryStatement(); |
| ITypeBinding[] exceptions= fAnalyzer.getExceptions(fAnalyzer.getSelection()); |
| ImportRewriteContext context= new ContextSensitiveImportRewriteContext(fAnalyzer.getEnclosingBodyDeclaration(), fImportRewrite); |
| |
| CatchClause catchClause= getAST().newCatchClause(); |
| SingleVariableDeclaration decl= getAST().newSingleVariableDeclaration(); |
| String varName= StubUtility.getExceptionVariableName(fCUnit.getJavaProject()); |
| String name= fScope.createName(varName, false); |
| decl.setName(getAST().newSimpleName(name)); |
| |
| |
| List<ASTNode> variableDeclarations= getSpecialVariableDeclarationStatements(); |
| ListRewrite statements= fRewriter.getListRewrite(tryStatement.getBody(), Block.STATEMENTS_PROPERTY); |
| boolean selectedNodeRemoved= false; |
| ASTNode expressionStatement= null; |
| for (ASTNode node : fSelectedNodes) { |
| if (fAutoClosableNodes.contains(node)) { |
| continue; |
| } |
| if (node instanceof VariableDeclarationStatement && variableDeclarations.contains(node)) { |
| AST ast= getAST(); |
| VariableDeclarationStatement statement= (VariableDeclarationStatement)node; |
| // Create a copy and remove the initializer |
| VariableDeclarationStatement copy= (VariableDeclarationStatement)ASTNode.copySubtree(ast, statement); |
| List<IExtendedModifier> modifiers= copy.modifiers(); |
| for (Iterator<IExtendedModifier> iter= modifiers.iterator(); iter.hasNext();) { |
| IExtendedModifier modifier= iter.next(); |
| if (modifier.isModifier() && Modifier.isFinal(((Modifier)modifier).getKeyword().toFlagValue())) { |
| iter.remove(); |
| } |
| } |
| List<VariableDeclarationFragment> fragments= copy.fragments(); |
| for (VariableDeclarationFragment fragment : fragments) { |
| fragment.setInitializer(null); |
| } |
| |
| // "var" type cannot have null initializer, so change to inferred type |
| if (ASTNodes.isVarType(statement, fRootNode)) { |
| ITypeBinding binding= statement.getType().resolveBinding(); |
| if (binding != null) { |
| Type varType= fImportRewrite.addImport(binding, getAST(), context, TypeLocation.LOCAL_VARIABLE); |
| copy.setType(varType); |
| } |
| } |
| |
| CompilationUnit root= (CompilationUnit)statement.getRoot(); |
| int extendedStart= root.getExtendedStartPosition(statement); |
| // we have a leading comment and the comment is covered by the selection |
| if (extendedStart != statement.getStartPosition() && extendedStart >= fSelection.getOffset()) { |
| String commentToken= buffer.getText(extendedStart, statement.getStartPosition() - extendedStart); |
| commentToken= Strings.trimTrailingTabsAndSpaces(commentToken); |
| Type type= statement.getType(); |
| String typeName= buffer.getText(type.getStartPosition(), type.getLength()); |
| copy.setType((Type)fRewriter.createStringPlaceholder(commentToken + typeName, type.getNodeType())); |
| } |
| result.add(copy); |
| // convert the fragments into expression statements |
| fragments= statement.fragments(); |
| if (!fragments.isEmpty()) { |
| List<ExpressionStatement> newExpressionStatements= new ArrayList<>(); |
| for (VariableDeclarationFragment fragment : fragments) { |
| Expression initializer= fragment.getInitializer(); |
| if (initializer != null) { |
| Assignment assignment= ast.newAssignment(); |
| assignment.setLeftHandSide((Expression)fRewriter.createCopyTarget(fragment.getName())); |
| assignment.setRightHandSide((Expression)fRewriter.createCopyTarget(initializer)); |
| newExpressionStatements.add(ast.newExpressionStatement(assignment)); |
| } |
| } |
| if (!newExpressionStatements.isEmpty()) { |
| if (fSelectedNodes.length == 1) { |
| expressionStatement= fRewriter.createGroupNode(newExpressionStatements.toArray(new ASTNode[newExpressionStatements.size()])); |
| } else { |
| fRewriter.replace( |
| statement, |
| fRewriter.createGroupNode(newExpressionStatements.toArray(new ASTNode[newExpressionStatements.size()])), |
| null); |
| } |
| } else { |
| fRewriter.remove(statement, null); |
| selectedNodeRemoved= true; |
| } |
| } else { |
| fRewriter.remove(statement, null); |
| selectedNodeRemoved= true; |
| } |
| } |
| } |
| result.add(tryStatement); |
| ASTNode replacementNode; |
| if (result.size() == 1) { |
| replacementNode= result.get(0); |
| } else { |
| replacementNode= fRewriter.createGroupNode(result.toArray(new ASTNode[result.size()])); |
| } |
| |
| ASTNode node= fSelectedNodes[0]; |
| List<ASTNode> coveredStatements= new ArrayList<>(); |
| for (ASTNode coveredNode : fSelectedNodes) { |
| Statement statement= ASTResolving.findParentStatement(coveredNode); |
| if (statement == null) { |
| continue; |
| } |
| if (!coveredStatements.contains(statement)) { |
| coveredStatements.add(statement); |
| } |
| } |
| |
| Selection nodesInRangeSelection= fAnalyzer.getSelection(); |
| List<ASTNode> nodesInRange= new ArrayList<>(); |
| if (!fAutoClosableNodes.isEmpty()) { |
| ASTNode parentBodyDeclaration= ASTResolving.findParentBodyDeclaration(node); |
| int start= fAutoClosableNodes.get(0).getStartPosition(); |
| ASTNode lastSelectedNode= fSelectedNodes[fSelectedNodes.length - 1]; |
| int end= lastSelectedNode.getStartPosition() + lastSelectedNode.getLength(); |
| |
| for (ASTNode astNode : fAutoClosableNodes) { |
| int endPosition= findEndPosition(astNode); |
| end= Math.max(end, endPosition); |
| } |
| |
| // recursive loop to find all nodes affected by wrapping in try block |
| nodesInRange= findNodesInRange(parentBodyDeclaration, start, end); |
| int oldEnd= end; |
| int newEnd= end; |
| while (true) { |
| newEnd= oldEnd; |
| for (ASTNode astNode : nodesInRange) { |
| int endPosition= findEndPosition(astNode); |
| newEnd= Math.max(newEnd, endPosition); |
| } |
| if (newEnd > oldEnd) { |
| oldEnd= newEnd; |
| nodesInRange= findNodesInRange(parentBodyDeclaration, start, newEnd); |
| continue; |
| } |
| break; |
| } |
| if (nodesInRange.size() > 0) { |
| // must recalculate exceptions as additional lines are now in try statement |
| ASTNode lastNode= nodesInRange.get(nodesInRange.size() - 1); |
| nodesInRangeSelection= Selection.createFromStartEnd(start, lastNode.getStartPosition() + lastNode.getLength()); |
| exceptions= fAnalyzer.getExceptions(nodesInRangeSelection); |
| } |
| nodesInRange.removeAll(fAutoClosableNodes); |
| nodesInRange.removeAll(Arrays.asList(fSelectedNodes)); |
| } |
| |
| // add required resource statements |
| CompilationUnit cu= (CompilationUnit)fSelectedNodes[0].getRoot(); |
| AST ast= fSelectedNodes[0].getAST(); |
| Set<String> resourceNameList= new HashSet<>(); |
| List<ITypeBinding> allExceptions= new ArrayList<>(Arrays.asList(exceptions)); |
| for (ASTNode coveredNode : fAutoClosableNodes) { |
| ASTNode findAncestor= ASTResolving.findAncestor(coveredNode, ASTNode.VARIABLE_DECLARATION_STATEMENT); |
| if (findAncestor == null) { |
| findAncestor= ASTResolving.findAncestor(coveredNode, ASTNode.ASSIGNMENT); |
| } |
| if (findAncestor instanceof VariableDeclarationStatement) { |
| VariableDeclarationStatement vds= (VariableDeclarationStatement) findAncestor; |
| String commentToken= null; |
| int extendedStatementStart= cu.getExtendedStartPosition(vds); |
| if(vds.getStartPosition() > extendedStatementStart) { |
| commentToken= buffer.getText(extendedStatementStart, vds.getStartPosition() - extendedStatementStart); |
| } |
| Type type= vds.getType(); |
| ITypeBinding typeBinding= type.resolveBinding(); |
| if (typeBinding != null) { |
| IMethodBinding close= findAutocloseMethod(typeBinding); |
| if (close != null) { |
| for (ITypeBinding exceptionType : close.getExceptionTypes()) { |
| if (!allExceptions.contains(exceptionType)) { |
| allExceptions.add(exceptionType); |
| } |
| } |
| } |
| } |
| String typeName= buffer.getText(type.getStartPosition(), type.getLength()); |
| |
| for (Object object : vds.fragments()) { |
| VariableDeclarationFragment variableDeclarationFragment= (VariableDeclarationFragment) object; |
| VariableDeclarationFragment newVariableDeclarationFragment= ast.newVariableDeclarationFragment(); |
| SimpleName vdsName= variableDeclarationFragment.getName(); |
| |
| if(commentToken == null) { |
| int extendedStart= cu.getExtendedStartPosition(variableDeclarationFragment); |
| commentToken= buffer.getText(extendedStart, variableDeclarationFragment.getStartPosition() - extendedStart); |
| } |
| commentToken= Strings.trimTrailingTabsAndSpaces(commentToken); |
| commentToken += commentToken.isEmpty() ? "" : " "; //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| newVariableDeclarationFragment.setName(ast.newSimpleName(vdsName.getIdentifier())); |
| Expression newExpression= null; |
| Expression initializer= variableDeclarationFragment.getInitializer(); |
| if (initializer == null) { |
| fRewriter.remove(coveredNode, null); |
| continue; |
| } else { |
| newExpression= (Expression) fRewriter.createMoveTarget(initializer); |
| } |
| resourceNameList.add(vdsName.getIdentifier()); |
| newVariableDeclarationFragment.setInitializer(newExpression); |
| VariableDeclarationExpression newVariableDeclarationExpression= ast.newVariableDeclarationExpression(newVariableDeclarationFragment); |
| newVariableDeclarationExpression.setType( |
| (Type) fRewriter.createStringPlaceholder(commentToken + typeName, type.getNodeType())); |
| tryStatement.resources().add(newVariableDeclarationExpression); |
| commentToken= null; |
| } |
| // String commentToken2= ""; //$NON-NLS-1$ |
| // int extendedStart= cu.getExtendedStartPosition(vds); |
| // int extendedLength= cu.getExtendedLength(vds); |
| // int endCommentLength= extendedLength - (vds.getStartPosition() - extendedStart) - vds.getLength(); |
| // if (endCommentLength > 0) { |
| // commentToken2= buffer.getText(vds.getStartPosition() + vds.getLength(), |
| // endCommentLength); |
| // commentToken2= Strings.trimLeadingTabsAndSpaces(commentToken2); |
| // } |
| } |
| } |
| |
| List<ITypeBinding> mustRethrowList= new ArrayList<>(); |
| List<ITypeBinding> catchExceptions= fAnalyzer.calculateCatchesAndRethrows(ASTNodes.filterSubtypes(allExceptions), mustRethrowList); |
| List<ITypeBinding> filteredExceptions= ASTNodes.filterSubtypes(catchExceptions); |
| if (catchExceptions.size() > 0) { |
| LinkedProposalModel linkedProposalModel= new LinkedProposalModel(); |
| int i= 0; |
| for (ITypeBinding mustThrow : mustRethrowList) { |
| CatchClause newClause= ast.newCatchClause(); |
| SingleVariableDeclaration newDecl= ast.newSingleVariableDeclaration(); |
| newDecl.setName(ast.newSimpleName(name)); |
| Type importType= fImportRewrite.addImport(mustThrow, ast, context, TypeLocation.EXCEPTION); |
| newDecl.setType(importType); |
| newClause.setException(newDecl); |
| ThrowStatement newThrowStatement= ast.newThrowStatement(); |
| newThrowStatement.setExpression(ast.newSimpleName(name)); |
| linkedProposalModel.getPositionGroup(GROUP_EXC_NAME + i, true).addPosition(fRewriter.track(decl.getName()), false); |
| newClause.getBody().statements().add(newThrowStatement); |
| tryStatement.catchClauses().add(newClause); |
| ++i; |
| } |
| UnionType unionType= getAST().newUnionType(); |
| List<Type> types= unionType.types(); |
| for (ITypeBinding exception : filteredExceptions) { |
| Type type= fImportRewrite.addImport(exception, getAST(), context, TypeLocation.EXCEPTION); |
| types.add(type); |
| fLinkedProposalModel.getPositionGroup(GROUP_EXC_TYPE + i, true).addPosition(fRewriter.track(type), i == 0); |
| i++; |
| } |
| |
| decl.setType(unionType); |
| catchClause.setException(decl); |
| fLinkedProposalModel.getPositionGroup(GROUP_EXC_NAME + 0, true).addPosition(fRewriter.track(decl.getName()), false); |
| Statement st= getCatchBody("Exception", name, lineDelimiter); //$NON-NLS-1$ |
| if (st != null) { |
| catchClause.getBody().statements().add(st); |
| } |
| tryStatement.catchClauses().add(catchClause); |
| } |
| |
| |
| if (fSelectedNodes.length == 1 && fAutoClosableNodes.isEmpty()) { |
| ASTNode selectedNode= fSelectedNodes[0]; |
| |
| if (selectedNode instanceof MethodReference) { |
| MethodReference methodReference= (MethodReference) selectedNode; |
| IMethodBinding functionalMethod= QuickAssistProcessor.getFunctionalMethodForMethodReference(methodReference); |
| // functionalMethod is non-null and non-generic. See ExceptionAnalyzer.handleMethodReference(MethodReference node). |
| Assert.isTrue(functionalMethod != null && !functionalMethod.isGenericMethod()); |
| LambdaExpression lambda= QuickAssistProcessor.convertMethodRefernceToLambda(methodReference, functionalMethod, fRootNode, fRewriter, null, true); |
| ASTNode statementInBlock= (ASTNode) ((Block) lambda.getBody()).statements().get(0); |
| fRewriter.replace(statementInBlock, replacementNode, null); |
| statements.insertLast(statementInBlock, null); |
| return; |
| } |
| |
| LambdaExpression enclosingLambda= ASTResolving.findEnclosingLambdaExpression(selectedNode); |
| if (enclosingLambda != null && selectedNode.getLocationInParent() == LambdaExpression.BODY_PROPERTY && enclosingLambda.resolveMethodBinding() != null) { |
| QuickAssistProcessor.changeLambdaBodyToBlock(enclosingLambda, getAST(), fRewriter); |
| Block blockBody= (Block) fRewriter.get(enclosingLambda, LambdaExpression.BODY_PROPERTY); |
| ASTNode statementInBlock= (ASTNode) blockBody.statements().get(0); |
| fRewriter.replace(statementInBlock, replacementNode, null); |
| statements.insertLast(statementInBlock, null); |
| return; |
| } |
| |
| if (expressionStatement != null) { |
| statements.insertLast(expressionStatement, null); |
| } else { |
| if (!selectedNodeRemoved) |
| statements.insertLast(fRewriter.createMoveTarget(selectedNode), null); |
| } |
| fRewriter.replace(selectedNode, replacementNode, null); |
| } else { |
| ListRewrite source= fRewriter.getListRewrite( |
| fSelectedNodes[0].getParent(), |
| (ChildListPropertyDescriptor)fSelectedNodes[0].getLocationInParent()); |
| List<ASTNode> nodes= new ArrayList<>(Arrays.asList(fSelectedNodes)); |
| if (!nodesInRange.isEmpty()) { |
| nodes.addAll(nodesInRange); |
| } |
| int index= fAutoClosableNodes.size(); |
| if (index < nodes.size()) { |
| ASTNode toMove= source.createMoveTarget(nodes.get(index), nodes.get(nodes.size() - 1), |
| index == 0 ? replacementNode : null, null); |
| statements.insertLast(toMove, null); |
| } |
| if (index > 0) { |
| source.replace(fAutoClosableNodes.get(0), replacementNode, null); |
| for (int i= 1; i < index; ++i) { |
| source.remove(fAutoClosableNodes.get(i), null); |
| } |
| } |
| } |
| |
| } |
| |
| public static IMethodBinding findAutocloseMethod(ITypeBinding type) { |
| while (type != null) { |
| IMethodBinding[] methods= type.getDeclaredMethods(); |
| for (IMethodBinding method : methods) { |
| if (method.getName().equals("close") && method.getParameterTypes().length == 0) { //$NON-NLS-1$ |
| return method; |
| } |
| } |
| type= type.getSuperclass(); |
| } |
| return null; |
| } |
| |
| private int findEndPosition(ASTNode node) { |
| int end= node.getStartPosition() + node.getLength(); |
| Map<SimpleName, IVariableBinding> nodeSimpleNameBindings= fAnalyzer.getVariableStatementBinding(node); |
| List<SimpleName> nodeNames= new ArrayList<>(nodeSimpleNameBindings.keySet()); |
| if (nodeNames.isEmpty()) { |
| return -1; |
| } |
| SimpleName nodeSimpleName= nodeNames.get(0); |
| SimpleName[] coveredNodeBindings= LinkedNodeFinder.findByNode(node.getRoot(), nodeSimpleName); |
| if (coveredNodeBindings.length == 0) { |
| return -1; |
| } |
| for (ASTNode astNode : coveredNodeBindings) { |
| end= Math.max(end, (astNode.getStartPosition() + astNode.getLength())); |
| } |
| return end; |
| } |
| |
| // find all nodes (statements) that are within the start/end positions |
| public static List<ASTNode> findNodesInRange(ASTNode astNode, final int start, final int end) { |
| List<ASTNode> nodesInRange= new ArrayList<>(); |
| astNode.accept(new ASTVisitor() { |
| int pre= start; |
| |
| @Override |
| public void preVisit(ASTNode preNode) { |
| pre= preNode.getStartPosition(); |
| super.preVisit(preNode); |
| } |
| |
| @Override |
| public void postVisit(ASTNode postNode) { |
| int post= postNode.getStartPosition() + postNode.getLength(); |
| if (pre >= start && post <= end) { |
| Statement statement= ASTResolving.findParentStatement(postNode); |
| loop: while (statement != null) { |
| if (statement.getParent() instanceof Statement) { |
| Statement pStatement= (Statement) statement.getParent(); |
| switch (pStatement.getNodeType()) { |
| case ASTNode.BLOCK: |
| if (pStatement.getParent().getNodeType() != ASTNode.METHOD_DECLARATION && |
| pStatement.getParent().getNodeType() != ASTNode.TRY_STATEMENT) { |
| statement= pStatement; |
| continue; |
| } else { |
| break loop; |
| } |
| case ASTNode.METHOD_DECLARATION: |
| break loop; |
| default: |
| break; |
| } |
| statement= pStatement; |
| } else { |
| break; |
| } |
| } |
| if (statement != null && !nodesInRange.contains(statement)) { |
| nodesInRange.add(statement); |
| } |
| } |
| super.postVisit(postNode); |
| } |
| }); |
| return nodesInRange; |
| } |
| |
| private List<ASTNode> getSpecialVariableDeclarationStatements() { |
| List<ASTNode> result= new ArrayList<>(3); |
| for (VariableDeclaration local : fAnalyzer.getAffectedLocals()) { |
| ASTNode parent= local.getParent(); |
| if (parent instanceof VariableDeclarationStatement && !result.contains(parent)) |
| result.add(parent); |
| } |
| return result; |
| |
| } |
| |
| private Statement getCatchBody(String type, String name, String lineSeparator) throws CoreException { |
| String s= StubUtility.getCatchBodyContent(fCUnit, type, name, fSelectedNodes[0], lineSeparator); |
| if (s == null) { |
| return null; |
| } else { |
| return (Statement)fRewriter.createStringPlaceholder(s, ASTNode.RETURN_STATEMENT); |
| } |
| } |
| } |