diff options
| author | Fabrice Tiercelin | 2020-11-15 09:14:51 +0000 |
|---|---|---|
| committer | Fabrice Tiercelin | 2020-11-15 12:20:08 +0000 |
| commit | b4d4824ed84c5dd3f8af41448ac1c2ced44cde76 (patch) | |
| tree | ec1b9878d56a1449ef3d2f6e4e0b51dd3e550bab | |
| parent | 9148815e2eb76c4f56db14553e556b208859210c (diff) | |
| download | eclipse.jdt.ui-b4d4824ed84c5dd3f8af41448ac1c2ced44cde76.tar.gz eclipse.jdt.ui-b4d4824ed84c5dd3f8af41448ac1c2ced44cde76.tar.xz eclipse.jdt.ui-b4d4824ed84c5dd3f8af41448ac1c2ced44cde76.zip | |
Bug 568088 - [AutoRefactor immigration #37/141] [cleanup & saveaction]
try-with-resource
Change-Id: If847e94774241882996aac7f5914018507ea20c8
Signed-off-by: Fabrice Tiercelin <fabrice.tiercelin@yahoo.fr>
14 files changed, 901 insertions, 1 deletions
diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.java index e6c29d328f..8ce5730457 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.java +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.java @@ -161,6 +161,8 @@ public class MultiFixMessages extends NLS { public static String CheckSignOfBitwiseOperation_description; + public static String TryWithResourceCleanup_description; + static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, MultiFixMessages.class); diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.properties b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.properties index 45ce8bfc3f..2737cadbaf 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.properties +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.properties @@ -142,3 +142,5 @@ AddAllCleanup_description=Add elements in collections without loop ObjectsEqualsCleanup_description=Use Objects.equals() in the equals method implementation CheckSignOfBitwiseOperation_description=Use != 0 instead of > 0 when comparing the result of a bitwise expression SwitchExpressionsCleanUp_ConvertToSwitchExpressions_description=Convert to switch expression where possible + +TryWithResourceCleanup_description=Use try-with-resource diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java index 2f60640f9e..1dabe292c9 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java @@ -2124,6 +2124,26 @@ public class ASTNodes { return siblings.get(siblings.size() - 1); } + /** + * Returns the previous statement in the source file if it exists. + * + * @param startNode the start node + * @return the previous statement in the source file if it exists, null + * otherwise + */ + public static Statement getPreviousStatement(final Statement startNode) { + Statement previousSibling= getPreviousSibling(startNode); + if (previousSibling != null) { + return previousSibling; + } + ASTNode parent= startNode.getParent(); + if (parent instanceof Statement) { + return getPreviousStatement((Statement) parent); + } + + return null; + } + private static List<Statement> getSiblings(final Statement startNode, final boolean isForward) { Statement statementAtLevel= statementAtLevel(startNode); @@ -2614,6 +2634,24 @@ public class ASTNodes { } /** + * Returns whether the provided nodes all represent the same variable. + * + * @param node0 the first node to compare + * @param otherNodes the other nodes to compare + * @return true if all the provided nodes represent the same variable, false + * otherwise + */ + public static boolean areSameVariables(final ASTNode node0, final ASTNode... otherNodes) { + for (ASTNode nodeN : otherNodes) { + if (!isSameVariable(node0, nodeN)) { + return false; + } + } + + return true; + } + + /** * Returns whether the two provided nodes represent the same variable. * * @param node1 the first node to compare diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java index 31e9757d93..4aa5489a23 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java @@ -1718,6 +1718,18 @@ public class CleanUpConstants { public static final String ADD_MISSING_METHODES= "cleanup.add_missing_methods"; //$NON-NLS-1$ /** + * Changes code to make use of Java 7 try-with-resources feature. In particular, it removes now useless finally clauses. + * <p> + * Possible values: {TRUE, FALSE} + * <p> + * + * @see CleanUpOptionsCore#TRUE + * @see CleanUpOptionsCore#FALSE + * @since 4.18 + */ + public static final String TRY_WITH_RESOURCE= "cleanup.try_with_resource"; //$NON-NLS-1$ + + /** * Should the Clean Up Wizard be shown when executing the Clean Up Action? <br> * <br> * Possible values: {<code><b>true</b></code>, <code><b>false</b></code>} <br> diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d7.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d7.java index 5630ee94e8..f69519d888 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d7.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d7.java @@ -13,6 +13,11 @@ *******************************************************************************/ package org.eclipse.jdt.ui.tests.quickfix; +import static org.junit.Assert.assertNotEquals; + +import java.util.Arrays; +import java.util.HashSet; + import org.junit.Rule; import org.junit.Test; @@ -446,6 +451,208 @@ public class CleanUpTest1d7 extends CleanUpTestCase { } @Test + public void testUseTryWithResource() throws Exception { + IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null); + String given= "" // + + "package test1;\n" // + + "\n" // + + "import java.io.FileInputStream;\n" // + + "\n" // + + "public class E {\n" // + + " public void refactorFullyInitializedResourceRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorFullyInitializedResourceDoNotRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorNullInitializedResourceRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " FileInputStream inputStream = null;\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " inputStream = new FileInputStream(\"out.txt\");\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " if (inputStream != null) {\n" // + + " inputStream.close();\n" // + + " }\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorNullInitializedResourceDoNotRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " FileInputStream inputStream = null;\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " inputStream = new FileInputStream(\"out.txt\");\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " if (inputStream != null) {\n" // + + " inputStream.close();\n" // + + " }\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "}\n"; + ICompilationUnit cu= pack.createCompilationUnit("E.java", given, false, null); + + enable(CleanUpConstants.TRY_WITH_RESOURCE); + + String expected= "" // + + "package test1;\n" // + + "\n" // + + "import java.io.FileInputStream;\n" // + + "\n" // + + "public class E {\n" // + + " public void refactorFullyInitializedResourceRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " // Keep this comment too\n" // + + " try (FileInputStream inputStream = new FileInputStream(\"out.txt\")) {\n" // + + " System.out.println(inputStream.read());\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorFullyInitializedResourceDoNotRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " // Keep this comment too\n" // + + " try (FileInputStream inputStream = new FileInputStream(\"out.txt\")) {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorNullInitializedResourceRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " // Keep this comment too\n" // + + " try (FileInputStream inputStream = new FileInputStream(\"out.txt\")) {\n" // + + " System.out.println(inputStream.read());\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorNullInitializedResourceDoNotRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " // Keep this comment too\n" // + + " try (FileInputStream inputStream = new FileInputStream(\"out.txt\")) {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "}\n"; + + assertNotEquals("The class must be changed", given, expected); + assertGroupCategoryUsed(new ICompilationUnit[] { cu }, new HashSet<>(Arrays.asList(MultiFixMessages.TryWithResourceCleanup_description))); + assertRefactoringResultAsExpected(new ICompilationUnit[] { cu }, new String[] { expected }); + } + + @Test + public void testDoNotUseTryWithResource() throws Exception { + IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null); + String sample= "" // + + "package test1;\n" // + + "\n" // + + "import java.io.FileInputStream;\n" // + + "\n" // + + "public class E {\n" // + + " public void doNotRefactorNonEffectivelyFinalResource() throws Exception {\n" // + + " try (FileInputStream inputStream = new FileInputStream(\"out.txt\")) {\n" // + + " System.out.println(inputStream.read());\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void doNotRefactorFurtherAssignmentsToResource() throws Exception {\n" // + + " FileInputStream inputStream = null;\n" // + + " try {\n" // + + " inputStream = new FileInputStream(\"out.txt\");\n" // + + " System.out.println(inputStream.read());\n" // + + " inputStream = new FileInputStream(\"out.txt\");\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public boolean doNotRefactorStillUsedCloseable() throws Exception {\n" // + + " FileInputStream inputStream = null;\n" // + + " try {\n" // + + " inputStream = new FileInputStream(\"out.txt\");\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " }\n" // + + "\n" // + + " return inputStream != null;\n" // + + " }\n" // + + "\n" // + + " public void doNotRefactorUnrelated() throws Exception {\n" // + + " FileInputStream aStream = new FileInputStream(\"out.txt\");\n" // + + " Object o = null;\n" // + + " try {\n" // + + " o = aStream.read();\n" // + + " } finally {\n" // + + " aStream.close();\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void doNotRefactorUnclosedStream(int i) throws Exception {\n" // + + " FileInputStream inputStream = null;\n" // + + " try {\n" // + + " inputStream = new FileInputStream(\"out.txt\");\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " if (inputStream != null) {\n" // + + " i = inputStream.available();\n" // + + " }\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void doNotMoveVariableFromOtherScope(boolean isValid) throws Exception {\n" // + + " final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" // + + " if (isValid) {\n" // + + " try {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " }\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void doNotMoveReusedVariable() throws Exception {\n" // + + " final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" // + + " try {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " }\n" // + + "\n" // + + " inputStream.getFD();\n" // + + " }\n" // + + "}\n"; + ICompilationUnit cu= pack.createCompilationUnit("E.java", sample, false, null); + + enable(CleanUpConstants.TRY_WITH_RESOURCE); + + assertRefactoringHasNoChange(new ICompilationUnit[] { cu }); + } + + @Test public void testObjectsEqualsWithImportConflict() throws Exception { IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); String sample= "" // diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d9.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d9.java new file mode 100644 index 0000000000..a6dc3ae5bd --- /dev/null +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d9.java @@ -0,0 +1,249 @@ +/******************************************************************************* + * Copyright (c) 2016, 2020 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ui.tests.quickfix; + +import static org.junit.Assert.assertNotEquals; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Rule; +import org.junit.Test; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; + +import org.eclipse.jdt.internal.corext.fix.CleanUpConstants; + +import org.eclipse.jdt.ui.tests.core.rules.Java9ProjectTestSetup; +import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup; + +import org.eclipse.jdt.internal.ui.fix.MultiFixMessages; + +/** + * Tests the cleanup features related to Java 9. + */ +public class CleanUpTest1d9 extends CleanUpTestCase { + @Rule + public ProjectTestSetup projectSetup= new Java9ProjectTestSetup(); + + @Override + protected IJavaProject getProject() { + return projectSetup.getProject(); + } + + @Override + protected IClasspathEntry[] getDefaultClasspath() throws CoreException { + return projectSetup.getDefaultClasspath(); + } + + @Test + public void testUseTryWithResource() throws Exception { + IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null); + String given= "" // + + "package test1;\n" // + + "\n" // + + "import java.io.FileInputStream;\n" // + + "\n" // + + "public class E {\n" // + + " public void refactorFullyInitializedResourceRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"data.txt\");\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorFullyInitializedResourceDoNotRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public boolean removeClosureOnStillUsedCloseable() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"input.txt\");\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + "\n" // + + " return inputStream != null;\n" // + + " }\n" // + + "\n" // + + " public void refactorFullyInitializedResourceOnlyRemoveFinallyIf() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " if (inputStream != null) {\n" // + + " inputStream.close();\n" // + + " }\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorNullInitializedResourceRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " FileInputStream inputStream = null;\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " inputStream = new FileInputStream(\"output.txt\");\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " if (inputStream != null) {\n" // + + " inputStream.close();\n" // + + " }\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorNullInitializedResourceDoNotRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " FileInputStream inputStream = null;\n" // + + " // Keep this comment too\n" // + + " try {\n" // + + " inputStream = new FileInputStream(\"file.txt\");\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " if (null != inputStream) {\n" // + + " inputStream.close();\n" // + + " }\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "}\n"; + ICompilationUnit cu= pack.createCompilationUnit("E.java", given, false, null); + + enable(CleanUpConstants.TRY_WITH_RESOURCE); + + String expected= "" // + + "package test1;\n" // + + "\n" // + + "import java.io.FileInputStream;\n" // + + "\n" // + + "public class E {\n" // + + " public void refactorFullyInitializedResourceRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"data.txt\");\n" // + + " // Keep this comment too\n" // + + " try (inputStream) {\n" // + + " System.out.println(inputStream.read());\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorFullyInitializedResourceDoNotRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" // + + " // Keep this comment too\n" // + + " try (inputStream) {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public boolean removeClosureOnStillUsedCloseable() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"input.txt\");\n" // + + " // Keep this comment too\n" // + + " try (inputStream) {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + "\n" // + + " return inputStream != null;\n" // + + " }\n" // + + "\n" // + + " public void refactorFullyInitializedResourceOnlyRemoveFinallyIf() throws Exception {\n" // + + " // Keep this comment\n" // + + " final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" // + + " // Keep this comment too\n" // + + " try (inputStream) {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorNullInitializedResourceRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " // Keep this comment too\n" // + + " try (FileInputStream inputStream = new FileInputStream(\"output.txt\")) {\n" // + + " System.out.println(inputStream.read());\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public void refactorNullInitializedResourceDoNotRemoveFinally() throws Exception {\n" // + + " // Keep this comment\n" // + + " // Keep this comment too\n" // + + " try (FileInputStream inputStream = new FileInputStream(\"file.txt\")) {\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " System.out.println(\"Done\");\n" // + + " }\n" // + + " }\n" // + + "}\n"; + + assertNotEquals("The class must be changed", given, expected); + assertGroupCategoryUsed(new ICompilationUnit[] { cu }, new HashSet<>(Arrays.asList(MultiFixMessages.TryWithResourceCleanup_description))); + assertRefactoringResultAsExpected(new ICompilationUnit[] { cu }, new String[] { expected }); + } + + @Test + public void testDoNotUseTryWithResource() throws Exception { + IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null); + String sample= "" // + + "package test1;\n" // + + "\n" // + + "import java.io.FileInputStream;\n" // + + "\n" // + + "public class E {\n" // + + " public boolean doNotRefactorStillUsedCloseable() throws Exception {\n" // + + " FileInputStream inputStream = null;\n" // + + " try {\n" // + + " inputStream = new FileInputStream(\"out.txt\");\n" // + + " System.out.println(inputStream.read());\n" // + + " } finally {\n" // + + " inputStream.close();\n" // + + " }\n" // + + "\n" // + + " return inputStream != null;\n" // + + " }\n" // + + "}\n"; + ICompilationUnit cu= pack.createCompilationUnit("E.java", sample, false, null); + + enable(CleanUpConstants.TRY_WITH_RESOURCE); + + assertRefactoringHasNoChange(new ICompilationUnit[] { cu }); + } +} diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTestCaseSuite.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTestCaseSuite.java index 5a5c0b521e..4f3893c22d 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTestCaseSuite.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTestCaseSuite.java @@ -25,6 +25,7 @@ import org.junit.runners.Suite; CleanUpTest1d6.class, CleanUpTest1d7.class, CleanUpTest1d8.class, + CleanUpTest1d9.class, CleanUpTest1d10.class, CleanUpAnnotationTest.class, SaveParticipantTest.class, diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstantsOptions.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstantsOptions.java index 5660eb2b5d..a7cab903a1 100644 --- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstantsOptions.java +++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstantsOptions.java @@ -160,6 +160,9 @@ public class CleanUpConstantsOptions extends CleanUpConstants { // Duplicate Code options.setOption(STRICTLY_EQUAL_OR_DIFFERENT, CleanUpOptions.FALSE); + + // Java Features + options.setOption(TRY_WITH_RESOURCE, CleanUpOptions.FALSE); } private static void setSaveParticipantSettings(CleanUpOptions options) { @@ -298,6 +301,9 @@ public class CleanUpConstantsOptions extends CleanUpConstants { // Duplicate Code options.setOption(STRICTLY_EQUAL_OR_DIFFERENT, CleanUpOptions.FALSE); + + // Java Features + options.setOption(TRY_WITH_RESOURCE, CleanUpOptions.FALSE); } public static void initDefaults(IPreferenceStore store) { diff --git a/org.eclipse.jdt.ui/plugin.xml b/org.eclipse.jdt.ui/plugin.xml index 6fa20e69ea..3d92d89704 100644 --- a/org.eclipse.jdt.ui/plugin.xml +++ b/org.eclipse.jdt.ui/plugin.xml @@ -7321,6 +7321,11 @@ id="org.eclipse.jdt.ui.cleanup.if_condition" runAfter="org.eclipse.jdt.ui.cleanup.redundant_falling_through_block_end"> </cleanUp> + <cleanUp + class="org.eclipse.jdt.internal.ui.fix.TryWithResourceCleanUp" + id="org.eclipse.jdt.ui.cleanup.try_with_resource" + runAfter="org.eclipse.jdt.ui.cleanup.if_condition"> + </cleanUp> </extension> <extension diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/TryWithResourceCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/TryWithResourceCleanUp.java new file mode 100644 index 0000000000..fc99d48a45 --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/TryWithResourceCleanUp.java @@ -0,0 +1,336 @@ +/******************************************************************************* + * Copyright (c) 2020 Fabrice TIERCELIN 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: + * Fabrice TIERCELIN - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.ui.fix; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.text.edits.TextEditGroup; + +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.CompilationUnit; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.IfStatement; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.Statement; +import org.eclipse.jdt.core.dom.TryStatement; +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.ListRewrite; +import org.eclipse.jdt.core.refactoring.CompilationUnitChange; + +import org.eclipse.jdt.internal.corext.dom.ASTNodes; +import org.eclipse.jdt.internal.corext.fix.CleanUpConstants; +import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix; +import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix.CompilationUnitRewriteOperation; +import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel; +import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; +import org.eclipse.jdt.internal.corext.util.JavaModelUtil; + +import org.eclipse.jdt.ui.cleanup.CleanUpRequirements; +import org.eclipse.jdt.ui.cleanup.ICleanUpFix; +import org.eclipse.jdt.ui.text.java.IProblemLocation; + +/** + * A fix that changes code to make use of Java 7 try-with-resources feature. In particular, it removes now useless finally clauses. + */ +public class TryWithResourceCleanUp extends AbstractMultiFix implements ICleanUpFix { + public TryWithResourceCleanUp() { + this(Collections.emptyMap()); + } + + public TryWithResourceCleanUp(final Map<String, String> options) { + super(options); + } + + @Override + public CleanUpRequirements getRequirements() { + boolean requireAST= isEnabled(CleanUpConstants.TRY_WITH_RESOURCE); + return new CleanUpRequirements(requireAST, false, false, null); + } + + @Override + public String[] getStepDescriptions() { + if (isEnabled(CleanUpConstants.TRY_WITH_RESOURCE)) { + return new String[] { MultiFixMessages.TryWithResourceCleanup_description }; + } + + return new String[0]; + } + + @Override + public String getPreview() { + if (isEnabled(CleanUpConstants.TRY_WITH_RESOURCE)) { + return "" //$NON-NLS-1$ + + "final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" //$NON-NLS-1$ + + "try (inputStream) {\n" //$NON-NLS-1$ + + " System.out.println(inputStream.read());\n" //$NON-NLS-1$ + + "}\n\n\n"; //$NON-NLS-1$ + } + + return "" //$NON-NLS-1$ + + "final FileInputStream inputStream = new FileInputStream(\"out.txt\");\n" //$NON-NLS-1$ + + "try {\n" //$NON-NLS-1$ + + " System.out.println(inputStream.read());\n" //$NON-NLS-1$ + + "} finally {\n" //$NON-NLS-1$ + + " inputStream.close();\n" //$NON-NLS-1$ + + "}\n"; //$NON-NLS-1$ + } + + @Override + protected ICleanUpFix createFix(final CompilationUnit unit) throws CoreException { + if (!isEnabled(CleanUpConstants.TRY_WITH_RESOURCE) || !JavaModelUtil.is1d7OrHigher(unit.getJavaElement().getJavaProject())) { + return null; + } + + final List<CompilationUnitRewriteOperation> rewriteOperations= new ArrayList<>(); + + unit.accept(new ASTVisitor() { + @Override + public boolean visit(final Block visited) { + DeclarationAndTryVisitor declarationAndTryVisitor= new DeclarationAndTryVisitor(visited); + visited.accept(declarationAndTryVisitor); + return declarationAndTryVisitor.result; + } + + final class DeclarationAndTryVisitor extends ASTVisitor { + private final Block startNode; + private boolean result= true; + + public DeclarationAndTryVisitor(final Block startNode) { + this.startNode= startNode; + } + + @Override + public boolean visit(final Block visited) { + return startNode == visited; + } + + @Override + public boolean visit(final TryStatement visited) { + if (!result) { + return true; + } + + VariableDeclarationStatement previousDeclarationStatement= ASTNodes.as(ASTNodes.getPreviousSibling(visited), + VariableDeclarationStatement.class); + List<Statement> finallyStatements= ASTNodes.asList(visited.getFinally()); + + if (previousDeclarationStatement == null + || finallyStatements.isEmpty()) { + return true; + } + + VariableDeclarationFragment previousDeclarationFragment= ASTNodes.getUniqueFragment(previousDeclarationStatement); + + if (previousDeclarationFragment == null + || previousDeclarationFragment.resolveBinding() == null) { + return true; + } + + Statement finallyFirstFStatement= finallyStatements.get(0); + List<ASTNode> nodesToRemove= new ArrayList<>(); + nodesToRemove.add(finallyStatements.size() == 1 ? visited.getFinally() : finallyFirstFStatement); + + boolean isCloseableUsedAfter= isCloseableUsedAfter(previousDeclarationFragment, visited); + Expression closedVariable= getClosedVariable(previousDeclarationFragment, finallyFirstFStatement); + + if ((isCloseableUsedAfter && !JavaModelUtil.is9OrHigher(((CompilationUnit) visited.getRoot()).getJavaElement().getJavaProject())) + || closedVariable == null + || !ASTNodes.areSameVariables(previousDeclarationFragment, closedVariable)) { + return true; + } + + return maybeUseTryWithResource( visited, previousDeclarationStatement, previousDeclarationFragment, isCloseableUsedAfter, nodesToRemove); + } + + private boolean maybeUseTryWithResource( + final TryStatement visited, + final VariableDeclarationStatement previousDeclarationStatement, + final VariableDeclarationFragment previousDeclarationFragment, + final boolean isCloseableUsedAfter, + final List<ASTNode> nodesToRemove) { + VarDefinitionsUsesVisitor visitor= new VarDefinitionsUsesVisitor(previousDeclarationFragment); + List<SimpleName> closeableAssignments= visitor.getWrites(); + List<Statement> tryStatements= ASTNodes.asList(visited.getBody()); + + if (!isCloseableUsedAfter + && !tryStatements.isEmpty()) { + Statement tryFirstStatement= tryStatements.get(0); + Assignment assignResource= ASTNodes.asExpression(tryFirstStatement, Assignment.class); + + if (assignResource != null + && ASTNodes.isSameVariable(previousDeclarationFragment, assignResource.getLeftHandSide())) { + if (!containsExactly(closeableAssignments, previousDeclarationFragment.getName(), assignResource.getLeftHandSide())) { + return true; + } + + nodesToRemove.add(tryFirstStatement); + rewriteOperations.add(new TryWithResourceOperation(visited, previousDeclarationStatement, previousDeclarationFragment, assignResource, nodesToRemove)); + + result= false; + return false; + } + } + + if (containsExactly(closeableAssignments, previousDeclarationFragment.getName())) { + rewriteOperations.add(new TryWithResourceOperation(visited, previousDeclarationStatement, previousDeclarationFragment, null, nodesToRemove)); + + result= false; + return false; + } + + return true; + } + + private boolean containsExactly(final List<SimpleName> closeableOccurrences, final Expression... simpleNames) { + return closeableOccurrences.size() == simpleNames.length && closeableOccurrences.containsAll(Arrays.asList(simpleNames)); + } + + private boolean isCloseableUsedAfter(final VariableDeclarationFragment previousDeclarationFragment, final TryStatement visited) { + IVariableBinding varBinding= previousDeclarationFragment.resolveBinding(); + List<Statement> nextStatements= ASTNodes.getNextSiblings(visited); + + for (Statement nextStatement : nextStatements) { + VarDefinitionsUsesVisitor visitor= new VarDefinitionsUsesVisitor(varBinding, nextStatement, true); + + if (!visitor.getReads().isEmpty() || !visitor.getWrites().isEmpty()) { + return true; + } + } + + return false; + } + + private Expression getClosedVariable(final VariableDeclarationFragment previousDeclarationFragment, final Statement finallyStatement) { + Statement firstStatement= finallyStatement; + IfStatement finallyIfStatement= ASTNodes.as(finallyStatement, IfStatement.class); + + if (finallyIfStatement != null + && ASTNodes.asList(finallyIfStatement.getThenStatement()).size() == 1 + && ASTNodes.asList(finallyIfStatement.getElseStatement()).isEmpty()) { + Expression closedVariable= ASTNodes.getNullCheckedExpression(finallyIfStatement.getExpression()); + + if (ASTNodes.areSameVariables(previousDeclarationFragment, closedVariable)) { + firstStatement= ASTNodes.asList(finallyIfStatement.getThenStatement()).get(0); + } + } + + MethodInvocation closeMethod= ASTNodes.asExpression(firstStatement, MethodInvocation.class); + + if (closeMethod != null + && ASTNodes.usesGivenSignature(closeMethod, Closeable.class.getCanonicalName(), "close")) { //$NON-NLS-1$ + return closeMethod.getExpression(); + } + + return null; + } + } + }); + + if (rewriteOperations.isEmpty()) { + return null; + } + + return new CompilationUnitRewriteOperationsFix(MultiFixMessages.TryWithResourceCleanup_description, unit, + rewriteOperations.toArray(new CompilationUnitRewriteOperation[0])); + } + + @Override + public CompilationUnitChange createChange(IProgressMonitor progressMonitor) throws CoreException { + return null; + } + + @Override + public boolean canFix(final ICompilationUnit compilationUnit, final IProblemLocation problem) { + return false; + } + + @Override + protected ICleanUpFix createFix(final CompilationUnit unit, final IProblemLocation[] problems) throws CoreException { + return null; + } + + private static class TryWithResourceOperation extends CompilationUnitRewriteOperation { + private final TryStatement visited; + private final VariableDeclarationStatement previousDeclStatement; + private final VariableDeclarationFragment previousDeclFragment; + private final Assignment assignResource; + private final List<ASTNode> nodesToRemove; + + public TryWithResourceOperation(final TryStatement visited, + final VariableDeclarationStatement previousDeclStatement, + final VariableDeclarationFragment previousDeclFragment, + final Assignment assignResource, + final List<ASTNode> nodesToRemove) { + this.visited= visited; + this.previousDeclStatement= previousDeclStatement; + this.previousDeclFragment= previousDeclFragment; + this.assignResource= assignResource; + this.nodesToRemove= nodesToRemove; + } + + @Override + public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException { + ASTRewrite rewrite= cuRewrite.getASTRewrite(); + AST ast= cuRewrite.getRoot().getAST(); + TextEditGroup group= createTextEditGroup(MultiFixMessages.TryWithResourceCleanup_description, cuRewrite); + + Expression newResource; + if (JavaModelUtil.is9OrHigher(((CompilationUnit) visited.getRoot()).getJavaElement().getJavaProject()) + && assignResource == null) { + // So we are in Java 9 or higher + newResource= (Expression) rewrite.createCopyTarget(previousDeclFragment.getName()); + } else { + VariableDeclarationFragment newFragment; + if (assignResource != null) { + newFragment= ast.newVariableDeclarationFragment(); + newFragment.setName(ASTNodes.createMoveTarget(rewrite, previousDeclFragment.getName())); + newFragment.setInitializer(ASTNodes.createMoveTarget(rewrite, assignResource.getRightHandSide())); + } else { + newFragment= ASTNodes.createMoveTarget(rewrite, previousDeclFragment); + } + + VariableDeclarationExpression newResourceDeclaration= ast.newVariableDeclarationExpression(newFragment); + newResourceDeclaration.setType(ASTNodes.createMoveTarget(rewrite, previousDeclStatement.getType())); + + newResource= newResourceDeclaration; + ASTNodes.removeButKeepComment(rewrite, previousDeclStatement, group); + } + + ListRewrite listRewrite= rewrite.getListRewrite(visited, TryStatement.RESOURCES2_PROPERTY); + listRewrite.insertFirst(newResource, group); + + for (ASTNode nodeToRemove : nodesToRemove) { + ASTNodes.removeButKeepComment(rewrite, nodeToRemove, group); + } + } + } +} diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/VarDefinitionsUsesVisitor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/VarDefinitionsUsesVisitor.java index 4f3bd3991f..2e476dad27 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/VarDefinitionsUsesVisitor.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/VarDefinitionsUsesVisitor.java @@ -24,6 +24,7 @@ import org.eclipse.jdt.core.dom.ChildPropertyDescriptor; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.internal.corext.dom.ASTNodes; @@ -37,6 +38,16 @@ public final class VarDefinitionsUsesVisitor extends ASTVisitor { private final List<SimpleName> reads= new ArrayList<>(); /** + * Builds from a {@link VariableDeclaration} and infers the variable binding and + * the scope from it. + * + * @param variableDeclaration the variable declaration, cannot be {@code null} + */ + public VarDefinitionsUsesVisitor(final VariableDeclaration variableDeclaration) { + this(variableDeclaration.resolveBinding(), getDeclaringScope(variableDeclaration), true); + } + + /** * Builds with the variable binding to look for and the scope where to look for * references. * @@ -56,6 +67,28 @@ public final class VarDefinitionsUsesVisitor extends ASTVisitor { scopeNode.accept(this); } + private static ASTNode getDeclaringScope(final VariableDeclaration variableDeclaration) { + ASTNode node= variableDeclaration.getParent(); + while (isVariableDeclaration(node)) { + node= node.getParent(); + } + + return node; + } + + private static boolean isVariableDeclaration(final ASTNode node) { + switch (node.getNodeType()) { + case ASTNode.SINGLE_VARIABLE_DECLARATION: + case ASTNode.VARIABLE_DECLARATION_EXPRESSION: + case ASTNode.VARIABLE_DECLARATION_FRAGMENT: + case ASTNode.VARIABLE_DECLARATION_STATEMENT: + return true; + + default: + return false; + } + } + @Override public boolean visit(final SimpleName node) { if (ASTNodes.isSameLocalVariable(variableBinding, node)) { diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.java index f62e815b1c..e5a631d560 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.java @@ -173,6 +173,8 @@ public class CleanUpMessages extends NLS { public static String DuplicateCodeTabPage_CheckboxName_RedundantFallingThroughBlockEnd; public static String DuplicateCodeTabPage_CheckboxName_RedundantIfCondition; + public static String JavaFeatureTabPage_CheckboxName_TryWithResource; + static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, CleanUpMessages.class); diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.properties index ce0c7d5abd..3a7fc56e71 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.properties +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.properties @@ -147,3 +147,5 @@ DuplicateCodeTabPage_CheckboxName_StrictlyEqualOrDifferent=Use '==' or '^' on bo DuplicateCodeTabPage_CheckboxName_MergeConditionalBlocks=Merge &conditions of if/else if/else that have the same blocks DuplicateCodeTabPage_CheckboxName_RedundantFallingThroughBlockEnd=Remove redundant end of block with &jump statement DuplicateCodeTabPage_CheckboxName_RedundantIfCondition=R&edundant if condition + +JavaFeatureTabPage_CheckboxName_TryWithResource=Use try-with-resource (1.7 or higher) diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/UnnecessaryCodeTabPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/UnnecessaryCodeTabPage.java index 5a842fc375..ab120d3b70 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/UnnecessaryCodeTabPage.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/UnnecessaryCodeTabPage.java @@ -41,6 +41,7 @@ import org.eclipse.jdt.internal.ui.fix.RedundantModifiersCleanUp; import org.eclipse.jdt.internal.ui.fix.RedundantSemicolonsCleanUp; import org.eclipse.jdt.internal.ui.fix.RedundantSuperCallCleanUp; import org.eclipse.jdt.internal.ui.fix.StringCleanUp; +import org.eclipse.jdt.internal.ui.fix.TryWithResourceCleanUp; import org.eclipse.jdt.internal.ui.fix.TypeParametersCleanUp; import org.eclipse.jdt.internal.ui.fix.UnboxingCleanUp; import org.eclipse.jdt.internal.ui.fix.UnnecessaryArrayCreationCleanUp; @@ -80,7 +81,8 @@ public final class UnnecessaryCodeTabPage extends AbstractCleanUpTabPage { new UnnecessaryArrayCreationCleanUp(values), new UselessReturnCleanUp(values), new UselessContinueCleanUp(values), - new ObjectsEqualsCleanUp(values) + new ObjectsEqualsCleanUp(values), + new TryWithResourceCleanUp(values) }; } @@ -180,5 +182,8 @@ public final class UnnecessaryCodeTabPage extends AbstractCleanUpTabPage { CheckboxPreference objectsEquals= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_ObjectsEquals, CleanUpConstants.USE_OBJECTS_EQUALS, CleanUpModifyDialog.FALSE_TRUE); registerPreference(objectsEquals); + + CheckboxPreference tryWithResource= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.JavaFeatureTabPage_CheckboxName_TryWithResource, CleanUpConstants.TRY_WITH_RESOURCE, CleanUpModifyDialog.FALSE_TRUE); + registerPreference(tryWithResource); } } |
