### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.ui Index: ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java,v retrieving revision 1.92.2.9 diff -u -r1.92.2.9 CorrectionMessages.java --- ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java 7 Jul 2011 02:43:38 -0000 1.92.2.9 +++ ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java 11 Jul 2011 14:03:58 -0000 @@ -257,9 +257,11 @@ public static String QuickAssistProcessor_unwrap_methodinvocation; public static String QuickAssistProcessor_unwrap_synchronizedstatement; public static String QuickAssistProcessor_splitdeclaration_description; + public static String QuickAssistProcessor_surroundresourcewithtry_description; public static String QuickAssistProcessor_joindeclaration_description; public static String QuickAssistProcessor_addfinallyblock_description; public static String QuickAssistProcessor_addelseblock_description; + public static String QuickAssistProcessor_addresourcetotry_description; public static String QuickAssistProcessor_replacethenwithblock_description; public static String QuickAssistProcessor_replaceelsewithblock_description; public static String QuickAssistProcessor_replacethenelsewithblock_description; Index: ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties,v retrieving revision 1.268.2.12 diff -u -r1.268.2.12 CorrectionMessages.properties --- ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties 7 Jul 2011 03:19:34 -0000 1.268.2.12 +++ ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties 11 Jul 2011 14:03:59 -0000 @@ -311,6 +311,7 @@ QuickAssistProcessor_unwrap_synchronizedstatement=Remove surrounding 'synchronized' statement QuickAssistProcessor_splitdeclaration_description=Split variable declaration +QuickAssistProcessor_surroundresourcewithtry_description=Surround resource ''{0}'' with try-with-resources QuickAssistProcessor_exceptiontothrows_description=Replace exception with throws QuickAssistProcessor_extract_to_local_all_description=Extract to local variable (replace all occurrences) QuickAssistProcessor_extract_to_local_description=Extract to local variable @@ -319,6 +320,7 @@ QuickAssistProcessor_joindeclaration_description=Join variable declaration QuickAssistProcessor_addfinallyblock_description=Add finally block QuickAssistProcessor_addelseblock_description=Add else block +QuickAssistProcessor_addresourcetotry_description=Add resource ''{0}'' to following try QuickAssistProcessor_replacethenwithblock_description=Change 'if' statement to block QuickAssistProcessor_replaceelsewithblock_description=Change 'else' statement to block Index: ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java,v retrieving revision 1.168.2.10 diff -u -r1.168.2.10 QuickAssistProcessor.java --- ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java 7 Jul 2011 12:51:46 -0000 1.168.2.10 +++ ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java 11 Jul 2011 14:04:02 -0000 @@ -55,6 +55,7 @@ import org.eclipse.jdt.core.compiler.IProblem; 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.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.ArrayCreation; import org.eclipse.jdt.core.dom.ArrayInitializer; @@ -87,6 +88,7 @@ import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.ParenthesizedExpression; import org.eclipse.jdt.core.dom.PrimitiveType; +import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SimpleType; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; @@ -116,6 +118,7 @@ import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.Bindings; +import org.eclipse.jdt.internal.corext.dom.GenericVisitor; import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder; import org.eclipse.jdt.internal.corext.dom.Selection; import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer; @@ -193,6 +196,7 @@ || getPickoutTypeFromMulticatchProposals(context, coveringNode, coveredNodes, null) || getConvertToMultiCatchProposals(context, coveringNode, null) || getUnrollMultiCatchProposals(context, coveringNode, null) + || getTryWithResourcesProposals(context, coveringNode, null) || getRenameLocalProposals(context, coveringNode, null, null) || getRenameRefactoringProposal(context, coveringNode, null, null) || getAssignToVariableProposals(context, coveringNode, null, null) @@ -240,6 +244,7 @@ getPickoutTypeFromMulticatchProposals(context, coveringNode, coveredNodes, resultingCollections); getConvertToMultiCatchProposals(context, coveringNode, resultingCollections); getUnrollMultiCatchProposals(context, coveringNode, resultingCollections); + getTryWithResourcesProposals(context, coveringNode, resultingCollections); getUnWrapProposals(context, coveringNode, resultingCollections); getJoinVariableProposals(context, coveringNode, resultingCollections); getSplitVariableProposals(context, coveringNode, resultingCollections); @@ -1554,6 +1559,257 @@ } } + private static boolean getTryWithResourcesProposals(IInvocationContext context, ASTNode covering, Collection resultingCollections) { + if (!JavaModelUtil.is17OrHigher(context.getCompilationUnit().getJavaProject())) + return false; + + //TODO: should the user be able to select more than one resource and enclose in one twr ? This should be doable + ASTNode node= covering; + while (!(node instanceof Statement) && node != null) { + node= node.getParent(); + } + + if (!(node instanceof VariableDeclarationStatement)) + return false; + + VariableDeclarationStatement variableDeclarationStatement= (VariableDeclarationStatement) node; + Type type= variableDeclarationStatement.getType(); + ITypeBinding typeBinding= type.resolveBinding(); + if (typeBinding == null) { + return false; + } + String autocloseable= "java.lang.AutoCloseable"; //$NON-NLS-1$ + ITypeBinding typeInHierarchy= Bindings.findTypeInHierarchy(typeBinding, autocloseable); + if (typeInHierarchy == null) + return false; + + MethodDeclaration methodDeclaration= ASTResolving.findParentMethodDeclaration(variableDeclarationStatement); + List fragments= variableDeclarationStatement.fragments(); + VariableDeclarationFragment variableDeclarationFragment= fragments.get(0); //TODO: More than one fragment ? + + VariableReferenceAnalyzer analyzer= new VariableReferenceAnalyzer(variableDeclarationFragment.resolveBinding(), covering); + methodDeclaration.accept(analyzer); + if (!analyzer.canProduceQuickAssist()) + return false; + + if (resultingCollections == null) { + return true; + } + + TryStatement tryStatement= analyzer.getTryStatement(); + List closeInvocations= analyzer.getCloseInvocations(); + if (tryStatement != null) { + AST ast= node.getAST(); + ASTRewrite rewrite= ASTRewrite.create(ast); + ListRewrite listRewrite= rewrite.getListRewrite(tryStatement, TryStatement.RESOURCES_PROPERTY); + + VariableDeclarationExpression newVariableDeclarationExpression= ast.newVariableDeclarationExpression((VariableDeclarationFragment) rewrite.createCopyTarget(variableDeclarationFragment)); + newVariableDeclarationExpression.setType((Type) rewrite.createCopyTarget(variableDeclarationStatement.getType())); + + listRewrite.insertLast(newVariableDeclarationExpression, null); + rewrite.remove(variableDeclarationStatement, null); + + if (closeInvocations != null) + removeCloseInvocations(closeInvocations, rewrite); + + Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); + String label= Messages.format(CorrectionMessages.QuickAssistProcessor_addresourcetotry_description, variableDeclarationFragment.getName().getIdentifier()); + ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, 6, image); + resultingCollections.add(proposal); + } else { + AST ast= node.getAST(); + ASTRewrite rewrite= ASTRewrite.create(ast); + + TryStatement newTryStatement= ast.newTryStatement(); + List nodes= analyzer.getReferences(); + + ASTNode start= ASTResolving.findParentStatement(nodes.get(0)); + ASTNode end= ASTResolving.findParentStatement(nodes.get(nodes.size() - 1)); + + ListRewrite tryResourcesListRewrite= rewrite.getListRewrite(newTryStatement, TryStatement.RESOURCES_PROPERTY); + VariableDeclarationExpression newVariableDeclarationExpression= ast.newVariableDeclarationExpression((VariableDeclarationFragment) rewrite.createCopyTarget(variableDeclarationFragment)); + newVariableDeclarationExpression.setType((Type) rewrite.createCopyTarget(variableDeclarationStatement.getType())); + tryResourcesListRewrite.insertLast(newVariableDeclarationExpression, null); + + ListRewrite tryStatementsListRewrite= rewrite.getListRewrite(newTryStatement.getBody(), Block.STATEMENTS_PROPERTY); + MethodDeclaration parentMethodDeclaration= ASTResolving.findParentMethodDeclaration(node); + ArrayList coveredNodes= getCoveredNodes(start.getStartPosition(), end.getStartPosition() + end.getLength(), parentMethodDeclaration); + + if (closeInvocations != null) + removeCloseInvocations(closeInvocations, rewrite); + + if (coveredNodes.size() >= 1) { + ListRewrite bodyStatementsListRewrite= rewrite.getListRewrite(coveredNodes.get(1).getParent(), (ChildListPropertyDescriptor) coveredNodes.get(1).getLocationInParent()); + ASTNode toMove= bodyStatementsListRewrite.createMoveTarget(coveredNodes.get(1), coveredNodes.get(coveredNodes.size() - 1), newTryStatement, null); + tryStatementsListRewrite.insertLast(toMove, null); + } + + rewrite.remove(variableDeclarationStatement, null); + + Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); + String label= Messages.format(CorrectionMessages.QuickAssistProcessor_surroundresourcewithtry_description, variableDeclarationFragment.getName().getIdentifier()); + ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, 6, image); + resultingCollections.add(proposal); + } + return true; + } + + private static void removeCloseInvocations(List closeInvocations, ASTRewrite rewrite) { + for (Iterator iterator= closeInvocations.iterator(); iterator.hasNext();) { + MethodInvocation closeInvocation= iterator.next(); + IfStatement parentif= null; + Statement parentStatement= ASTResolving.findParentStatement(closeInvocation); + + ASTNode parent= parentStatement.getParent(); + if (parent instanceof Block) { + parent= parent.getParent(); + } + if (parent instanceof IfStatement) { + parentif= (IfStatement) parent; + } + + if (parentif != null) { + Statement thenStatement= parentif.getThenStatement(); + Statement toRemove= parentif; + if (thenStatement instanceof Block) { + Block block= (Block) thenStatement; + if (block.statements().size() > 1) { + toRemove= parentStatement; + } + } + Statement elseStatement= parentif.getElseStatement(); + if (elseStatement != null && toRemove == parentif) { + ASTNode parentBlock= ASTResolving.findAncestor(parentif, ASTNode.BLOCK); + if (parentBlock instanceof Block) { + Block block2= (Block) parentBlock; + ASTNode copyTarget; + if (elseStatement instanceof Block) { + Block elseBlock= (Block) elseStatement; + List statements= elseBlock.statements(); + ASTNode[] array= statements.toArray(new ASTNode[statements.size()]); + copyTarget= rewrite.createGroupNode(array); + } else { + copyTarget= rewrite.createCopyTarget(elseStatement); + } + ListRewrite listRewrite= rewrite.getListRewrite(block2, Block.STATEMENTS_PROPERTY); + listRewrite.insertAfter(copyTarget, parentif, null); + } + } + rewrite.remove(toRemove, null); + } else { + rewrite.remove(parentStatement, null); + } + } + } + + private static ArrayList getCoveredNodes(final int selectionBegin, final int selectionEnd, ASTNode coveringNode) { + final ArrayList coveredNodes= new ArrayList(); + coveringNode.accept(new GenericVisitor() { + @Override + protected boolean visitNode(ASTNode node) { + int nodeStart= node.getStartPosition(); + int nodeEnd= nodeStart + node.getLength(); + // if node does not intersects with selection, don't visit children + if (nodeEnd < selectionBegin || selectionEnd < nodeStart) { + return false; + } + // if node is covered, we don't need to visit children + if (isCovered(node)) { + ASTNode parent= node.getParent(); + if (parent == null || !isCovered(parent)) { + coveredNodes.add(node); + return false; + } + } + // if node only partly intersects with selection, we try to find fully covered children + return true; + } + + private boolean isCovered(ASTNode node) { + int begin= node.getStartPosition(); + int end= begin + node.getLength(); + return (begin >= selectionBegin && end <= selectionEnd) || (begin <= selectionBegin && end <= selectionEnd) || (begin >= selectionBegin && end >= selectionEnd); + } + }); + return coveredNodes; + } + + static class VariableReferenceAnalyzer extends ASTVisitor { + private List fReferences= null; + private List fCloseInvocations= null; + private TryStatement fTryStatement= null; + private boolean fIsEnclosedInOneTryStatement= true; + private IVariableBinding fBinding; + private boolean fCanProduceQuickAssist= true; + + private final ASTNode fCoveringNode; + + public VariableReferenceAnalyzer(IVariableBinding binding, ASTNode coveringNode) { + fBinding= binding; + fCoveringNode= coveringNode; + } + + public List getReferences() { + return fReferences; + } + + public List getCloseInvocations() { + return fCloseInvocations; + } + + public TryStatement getTryStatement() { + return fTryStatement; + } + + public boolean canProduceQuickAssist() { + return fCanProduceQuickAssist; + } + + @Override + public boolean visit(SimpleName node) { + if (fReferences == null) { + fReferences= new ArrayList(); + } + if (Bindings.equals(fBinding, node.resolveBinding())) { + fReferences.add(node); + + StructuralPropertyDescriptor locationInParent= node.getLocationInParent(); + if (locationInParent instanceof ChildListPropertyDescriptor && locationInParent != InfixExpression.EXTENDED_OPERANDS_PROPERTY) { + // e.g. argument lists of MethodInvocation, ClassInstanceCreation ... + fCanProduceQuickAssist= false; + } + + if (locationInParent == ReturnStatement.EXPRESSION_PROPERTY || locationInParent == Assignment.LEFT_HAND_SIDE_PROPERTY) { + fCanProduceQuickAssist= false; + } + + ASTNode tryStatement= ASTResolving.findAncestor(node, ASTNode.TRY_STATEMENT); + if (node != fCoveringNode && tryStatement != null) { + if (fTryStatement == null && fIsEnclosedInOneTryStatement) { + fTryStatement= (TryStatement) tryStatement; + } else if (fTryStatement != tryStatement) { + fIsEnclosedInOneTryStatement= false; + fTryStatement= null; + } + } + + ASTNode parent= node.getParent(); + if (parent instanceof MethodInvocation && node.getLocationInParent() == MethodInvocation.EXPRESSION_PROPERTY) { + MethodInvocation methodInvocation= (MethodInvocation) parent; + IMethodBinding methodBinding= methodInvocation.resolveMethodBinding(); + if (methodBinding != null) { + if ("close".equals(methodBinding.getName())) { //$NON-NLS-1$ + if (fCloseInvocations == null) + fCloseInvocations= new ArrayList(); + fCloseInvocations.add(methodInvocation); + } + } + } + } + return false; + } + } + private static boolean getRenameLocalProposals(IInvocationContext context, ASTNode node, IProblemLocation[] locations, Collection resultingCollections) { if (!(node instanceof SimpleName)) { return false; #P org.eclipse.jdt.ui.tests Index: ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest17.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/Attic/AssistQuickFixTest17.java,v retrieving revision 1.1.2.10 diff -u -r1.1.2.10 AssistQuickFixTest17.java --- ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest17.java 7 Jul 2011 03:19:32 -0000 1.1.2.10 +++ ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest17.java 11 Jul 2011 14:04:06 -0000 @@ -928,4 +928,93 @@ assertProposalDoesNotExist(proposals, REMOVE_SURROUNDING_TRY_BLOCK); } + public void testUseTryWithResources1() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.io.FileReader;\n"); + buf.append("public class E {\n"); + buf.append(" void foo() throws Exception {\n"); + buf.append(" FileReader reader = new FileReader(\"file\");\n"); + buf.append(" try {\n"); + buf.append(" int ch;\n"); + buf.append(" while ((ch = reader.read()) != -1) {\n"); + buf.append(" System.out.println(ch);\n"); + buf.append(" }\n"); + buf.append(" } finally {\n"); + buf.append(" reader.close();\n"); + buf.append(" }\n"); + buf.append(" }\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("E.java", buf.toString(), false, null); + + int offset= buf.toString().indexOf("reader = "); + AssistContext context= getCorrectionContext(cu, offset, 0); + assertNoErrors(context); + List proposals= collectAssists(context, false); + + assertCorrectLabels(proposals); + + buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.io.FileReader;\n"); + buf.append("public class E {\n"); + buf.append(" void foo() throws Exception {\n"); + buf.append(" try (FileReader reader = new FileReader(\"file\")) {\n"); + buf.append(" int ch;\n"); + buf.append(" while ((ch = reader.read()) != -1) {\n"); + buf.append(" System.out.println(ch);\n"); + buf.append(" }\n"); + buf.append(" } finally {\n"); + buf.append(" }\n"); + buf.append(" }\n"); + buf.append("}\n"); + String expected1= buf.toString(); + + assertExpectedExistInProposals(proposals, new String[] { expected1 }); + } + + public void testUseTryWithResources2() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.io.FileReader;\n"); + buf.append("public class E {\n"); + buf.append(" void foo() throws Exception {\n"); + buf.append(" FileReader reader = new FileReader(\"file\");\n"); + buf.append(" int ch;\n"); + buf.append(" while ((ch = reader.read()) != -1) {\n"); + buf.append(" System.out.println(ch);\n"); + buf.append(" }\n"); + buf.append(" if (reader != null) {\n"); + buf.append(" reader.close();\n"); + buf.append(" }\n"); + buf.append(" }\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("E.java", buf.toString(), false, null); + + int offset= buf.toString().indexOf("reader = "); + AssistContext context= getCorrectionContext(cu, offset, 0); + assertNoErrors(context); + List proposals= collectAssists(context, false); + + assertCorrectLabels(proposals); + + buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.io.FileReader;\n"); + buf.append("public class E {\n"); + buf.append(" void foo() throws Exception {\n"); + buf.append(" try (FileReader reader = new FileReader(\"file\")){int ch;\n"); + buf.append(" while ((ch = reader.read()) != -1) {\n"); + buf.append(" System.out.println(ch);\n"); + buf.append(" }} \n"); + buf.append(" }\n"); + buf.append("}\n"); + String expected1= buf.toString(); + + assertExpectedExistInProposals(proposals, new String[] { expected1 }); + } }