diff options
| author | Fabrice Tiercelin | 2020-10-08 04:25:54 +0000 |
|---|---|---|
| committer | Fabrice Tiercelin | 2020-10-18 13:02:22 +0000 |
| commit | 64e5a9cab23ee26c331d3fabad5e5c83e09b6f6f (patch) | |
| tree | 6a750475a117358699e986f9943020879b1e6d26 | |
| parent | 7b4291b1222ff497f5bec510115347d0b741128a (diff) | |
| download | eclipse.jdt.ui-64e5a9cab23ee26c331d3fabad5e5c83e09b6f6f.tar.gz eclipse.jdt.ui-64e5a9cab23ee26c331d3fabad5e5c83e09b6f6f.tar.xz eclipse.jdt.ui-64e5a9cab23ee26c331d3fabad5e5c83e09b6f6f.zip | |
Bug 567692 - [AutoRefactor immigration #31/138] [cleanup & saveaction]Y20201018-1200
Objects.hash()
Change-Id: Ia3bb7a33197ddd6d1f6a63689252634320a05e4f
Signed-off-by: Fabrice Tiercelin <fabrice.tiercelin@yahoo.fr>
11 files changed, 914 insertions, 2 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 792995ad47..4d7c037f59 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 @@ -122,6 +122,7 @@ public class MultiFixMessages extends NLS { public static String TypeParametersCleanUp_InsertInferredTypeArguments_description; public static String TypeParametersCleanUp_RemoveUnnecessaryTypeArguments_description; + public static String HashCleanup_description; public static String RedundantModifiersCleanup_description; public static String ArraysFillCleanUp_description; 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 7f64742fb8..9a3b9fb555 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 @@ -103,6 +103,7 @@ NullAnnotationsCleanUp_add_nonnullbydefault_annotation=Add missing @NonNullByDef NullAnnotationsCleanUp_remove_redundant_nullness_annotation=Remove redundant nullness annotation TypeParametersCleanUp_InsertInferredTypeArguments_description=Insert inferred type arguments TypeParametersCleanUp_RemoveUnnecessaryTypeArguments_description=Remove redundant type arguments +HashCleanup_description=Use Objects.hash() RedundantModifiersCleanup_description = Remove redundant modifiers ArraysFillCleanUp_description=Use Arrays.fill() when possible 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 27f9485d1e..5a35584f2e 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 @@ -2195,6 +2195,30 @@ public class ASTNodes { } /** + * Returns the first ancestor of the provided node which has the required type. + * + * @param <T> the required ancestor's type + * @param node the start node + * @param ancestorClass the required ancestor's type + * @return the first ancestor of the provided node which has the required type, + * {@code null} if no suitable ancestor can be found + */ + @SuppressWarnings("unchecked") + public static <T extends ASTNode> T getTypedAncestor(final ASTNode node, final Class<T> ancestorClass) { + if (node == null || node.getParent() == null) { + return null; + } + + ASTNode parent= node.getParent(); + + if (ancestorClass.isAssignableFrom(parent.getClass())) { + return (T) parent; + } + + return getTypedAncestor(parent, ancestorClass); + } + + /** * Returns the first ancestor of the provided node which has any of the required types. * * @param node the start node @@ -2932,6 +2956,26 @@ public class ASTNodes { } /** + * Returns whether the provided method declaration declares a method with the + * provided method signature. The method signature is compared against the + * erasure of the declared method. + * + * @param actualMethod the actual method declaration + * @param typeQualifiedName the expected qualified name of the type declaring + * the expected method + * @param methodName the expected method name + * @param parameterTypesQualifiedNames the expected qualified names of the parameter + * types + * @return true if the provided method declaration matches the provided method + * signature, false otherwise + */ + public static boolean usesGivenSignature(final MethodDeclaration actualMethod, final String typeQualifiedName, final String methodName, + final String... parameterTypesQualifiedNames) { + return actualMethod != null + && usesGivenSignature(actualMethod.resolveBinding(), typeQualifiedName, methodName, parameterTypesQualifiedNames); + } + + /** * Returns whether the provided method binding has the provided method signature. The method * signature is compared against the erasure of the invoked method. * 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 e7ee3d0935..f7adbc2abb 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 @@ -1104,6 +1104,18 @@ public class CleanUpConstants { public static final String REMOVE_REDUNDANT_TYPE_ARGUMENTS= "cleanup.remove_redundant_type_arguments"; //$NON-NLS-1$ /** + * Rewrites Eclipse-autogenerated hashcode method by Eclipse-autogenerated hashcode method for Java 7. + * <p> + * Possible values: {TRUE, FALSE} + * <p> + * + * @see CleanUpOptionsCore#TRUE + * @see CleanUpOptionsCore#FALSE + * @since 4.18 + */ + public static final String MODERNIZE_HASH= "cleanup.hash"; //$NON-NLS-1$ + + /** * Removes redundant modifiers.<br> * <br> * Possible values: {TRUE, FALSE}<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 6e8bf1d5c2..5630ee94e8 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 @@ -29,6 +29,8 @@ import org.eclipse.jdt.internal.corext.fix.FixMessages; import org.eclipse.jdt.ui.tests.core.rules.Java1d7ProjectTestSetup; import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup; +import org.eclipse.jdt.internal.ui.fix.MultiFixMessages; + public class CleanUpTest1d7 extends CleanUpTestCase { @Rule public ProjectTestSetup projectSetup= new Java1d7ProjectTestSetup(); @@ -82,6 +84,227 @@ public class CleanUpTest1d7 extends CleanUpTestCase { } @Test + public void testHash() throws Exception { + IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null); + String input= "" // + + "package test1;\n" // + + "\n" // + + "import java.util.Arrays;\n" // + + "import java.util.Map;\n" // + + "import java.util.Observable;\n" // + + "\n" // + + "public class E {\n" // + + " public class RefactoredClass {\n" // + + " private Map<Integer, String> innerTextById;\n" // + + " private Observable innerObservable;\n" // + + " private String innerText;\n" // + + " private String[] innerTexts;\n" // + + " private int[] innerIntegers;\n" // + + " private char innerChar;\n" // + + " private byte innerByte;\n" // + + " private boolean innerBoolean;\n" // + + " private int innerInt;\n" // + + " private long innerLong;\n" // + + " private double innerDouble;\n" // + + " private short innerShort;\n" // + + " private float innerFloat;\n" // + + " private double innerOtherDouble;\n" // + + " private Boolean innerBooleanWrapper;\n" // + + "\n" // + + " @Override\n" // + + " public int hashCode() {\n" // + + " // Keep this comment\n" // + + " final int prime = 31;\n" // + + " int result = 1;\n" // + + " result = prime * result + getEnclosingInstance().hashCode();\n" // + + " result = prime * result + (RefactoredClass.this.innerBoolean ? 1231 : 1237);\n" // + + " result = prime * result + this.innerByte;\n" // + + " result = prime * result + innerChar;\n" // + + " long temp = Double.doubleToLongBits(innerDouble);\n" // + + " result = prime * result + (int) ((temp >>> 32) ^ temp);\n" // + + " result = prime * result + Float.floatToIntBits(innerFloat);\n" // + + " result = result * prime + innerInt;\n" // + + " result = prime * result + Arrays.hashCode(innerIntegers);\n" // + + " result = prime * result + (int) (innerLong ^ (this.innerLong >>> 32));\n" // + + " result = prime * result + ((innerObservable == null) ? 0 : innerObservable.hashCode());\n" // + + " temp = Double.doubleToLongBits(innerOtherDouble);\n" // + + " result = prime * result + (int) (temp ^ (temp >>> 32));\n" // + + " result = prime * result + innerShort;\n" // + + " result = prime * result + ((innerText == null) ? 0 : innerText.hashCode());\n" // + + " result = prime * result + ((innerTextById != null) ? this.innerTextById.hashCode() : 0);\n" // + + " result = prime * result + ((this.innerBooleanWrapper != null) ? innerBooleanWrapper.hashCode() : 0);\n" // + + " return prime * result + Arrays.hashCode(innerTexts);\n" // + + " }\n" // + + "\n" // + + " private E getEnclosingInstance() {\n" // + + " return E.this;\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " private Map<Integer, String> textById;\n" // + + " private Observable anObservable;\n" // + + " private String aText;\n" // + + " private String[] texts;\n" // + + " private int[] integers;\n" // + + " private char aChar;\n" // + + " private byte aByte;\n" // + + " private boolean aBoolean;\n" // + + " private int anInt;\n" // + + " private long aLong;\n" // + + " private double aDouble;\n" // + + " private short aShort;\n" // + + " private float aFloat;\n" // + + " private double anotherDouble;\n" // + + "\n" // + + " @Override\n" // + + " public int hashCode() {\n" // + + " // Keep this comment\n" // + + " final int prime = 31;\n" // + + " int result = 1;\n" // + + " result = prime * result + (E.this.aBoolean ? 1231 : 1237);\n" // + + " result = prime * result + aByte;\n" // + + " result = prime * result + aChar;\n" // + + " result = prime * result + Float.floatToIntBits(aFloat);\n" // + + " result = prime * result + (int) (aLong ^ (aLong >>> 32));\n" // + + " long temp;\n" // + + " temp = Double.doubleToLongBits(aDouble);\n" // + + " result = prime * result + (int) (temp ^ (temp >>> 32));\n" // + + " result = prime * result + aShort;\n" // + + " result = prime * result + ((null == aText) ? 0 : aText.hashCode());\n" // + + " result = prime * result + anInt;\n" // + + " result = prime * result + ((anObservable == null) ? 0 : anObservable.hashCode());\n" // + + " result = prime * result + Arrays.hashCode(integers);\n" // + + " result = prime * result + ((textById == null) ? 0 : textById.hashCode());\n" // + + " result = prime * result + Arrays.hashCode(texts);\n" // + + " temp = Double.doubleToLongBits(anotherDouble);\n" // + + " result = prime * result + (int) (temp ^ (temp >>> 32));\n" // + + " return result;\n" // + + " }\n" // + + "}\n"; + ICompilationUnit cu= pack.createCompilationUnit("E.java", input, false, null); + + enable(CleanUpConstants.MODERNIZE_HASH); + + String output= "" // + + "package test1;\n" // + + "\n" // + + "import java.util.Arrays;\n" // + + "import java.util.Map;\n" // + + "import java.util.Objects;\n" // + + "import java.util.Observable;\n" // + + "\n" // + + "public class E {\n" // + + " public class RefactoredClass {\n" // + + " private Map<Integer, String> innerTextById;\n" // + + " private Observable innerObservable;\n" // + + " private String innerText;\n" // + + " private String[] innerTexts;\n" // + + " private int[] innerIntegers;\n" // + + " private char innerChar;\n" // + + " private byte innerByte;\n" // + + " private boolean innerBoolean;\n" // + + " private int innerInt;\n" // + + " private long innerLong;\n" // + + " private double innerDouble;\n" // + + " private short innerShort;\n" // + + " private float innerFloat;\n" // + + " private double innerOtherDouble;\n" // + + " private Boolean innerBooleanWrapper;\n" // + + "\n" // + + " @Override\n" // + + " public int hashCode() {\n" // + + " // Keep this comment\n" // + + " return Objects.hash(getEnclosingInstance().hashCode(), innerBoolean, innerByte, innerChar, innerDouble,\n" // + + " innerFloat, innerInt, Arrays.hashCode(innerIntegers), innerLong, innerObservable, innerOtherDouble,\n" // + + " innerShort, innerText, innerTextById, innerBooleanWrapper, Arrays.hashCode(innerTexts));\n" // + + " }\n" // + + "\n" // + + " private E getEnclosingInstance() {\n" // + + " return E.this;\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " private Map<Integer, String> textById;\n" // + + " private Observable anObservable;\n" // + + " private String aText;\n" // + + " private String[] texts;\n" // + + " private int[] integers;\n" // + + " private char aChar;\n" // + + " private byte aByte;\n" // + + " private boolean aBoolean;\n" // + + " private int anInt;\n" // + + " private long aLong;\n" // + + " private double aDouble;\n" // + + " private short aShort;\n" // + + " private float aFloat;\n" // + + " private double anotherDouble;\n" // + + "\n" // + + " @Override\n" // + + " public int hashCode() {\n" // + + " // Keep this comment\n" // + + " return Objects.hash(aBoolean, aByte, aChar, aFloat, aLong,\n" // + + " aDouble, aShort, aText, anInt, anObservable, Arrays.hashCode(integers), textById,\n" // + + " Arrays.hashCode(texts), anotherDouble);\n" // + + " }\n" // + + "}\n"; + assertGroupCategoryUsed(new ICompilationUnit[] { cu }, new String[] { MultiFixMessages.HashCleanup_description }); + assertRefactoringResultAsExpected(new ICompilationUnit[] { cu }, new String[] { output }); + } + + @Test + public void testKeepHash() throws Exception { + IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null); + String sample= "" // + + "package test1;\n" // + + "\n" // + + "public class E {\n" // + + " public class DoNotRefactorNewClass {\n" // + + " private boolean innerBoolean;\n" // + + "\n" // + + " @Override\n" // + + " public int hashCode() {\n" // + + " final int prime = 31;\n" // + + " int result = 1;\n" // + + " result = prime * result + getEnclosingInstance().hashCode();\n" // + + " result = prime * result + (innerBoolean ? 1231 : 1237);\n" // + + " return result;\n" // + + " }\n" // + + "\n" // + + " private E getEnclosingInstance() {\n" // + + " return new E();\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public class DoNotRefactorCustomHash {\n" // + + " private boolean innerBoolean;\n" // + + "\n" // + + " @Override\n" // + + " public int hashCode() {\n" // + + " final int prime = 63;\n" // + + " int result = 1;\n" // + + " result = prime * result + (innerBoolean ? 1231 : 1237);\n" // + + " return result;\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " private boolean innerBoolean;\n" // + + "\n" // + + " @Override\n" // + + " public int hashCode() {\n" // + + " final int prime = 31;\n" // + + " int result = 1;\n" // + + " result += prime * result + (innerBoolean ? 1231 : 1237);\n" // + + " return result;\n" // + + " }\n" // + + "}\n"; + ICompilationUnit cu= pack.createCompilationUnit("E.java", sample, false, null); + + enable(CleanUpConstants.MODERNIZE_HASH); + + assertRefactoringHasNoChange(new ICompilationUnit[] { cu }); + } + + @Test public void testObjectsEquals() throws Exception { IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); String sample= "" // 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 2d6a47e360..3aa5594b80 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 @@ -94,6 +94,7 @@ public class CleanUpConstantsOptions extends CleanUpConstants { options.setOption(REMOVE_UNNECESSARY_NLS_TAGS, CleanUpOptions.TRUE); options.setOption(INSERT_INFERRED_TYPE_ARGUMENTS, CleanUpOptions.FALSE); options.setOption(REMOVE_REDUNDANT_TYPE_ARGUMENTS, CleanUpOptions.FALSE); + options.setOption(MODERNIZE_HASH, CleanUpOptions.FALSE); options.setOption(ARRAYS_FILL, CleanUpOptions.FALSE); options.setOption(USE_AUTOBOXING, CleanUpOptions.FALSE); options.setOption(USE_UNBOXING, CleanUpOptions.FALSE); @@ -214,6 +215,7 @@ public class CleanUpConstantsOptions extends CleanUpConstants { options.setOption(REMOVE_UNNECESSARY_NLS_TAGS, CleanUpOptions.FALSE); options.setOption(INSERT_INFERRED_TYPE_ARGUMENTS, CleanUpOptions.FALSE); options.setOption(REMOVE_REDUNDANT_TYPE_ARGUMENTS, CleanUpOptions.FALSE); + options.setOption(MODERNIZE_HASH, CleanUpOptions.FALSE); options.setOption(ARRAYS_FILL, CleanUpOptions.FALSE); options.setOption(USE_AUTOBOXING, CleanUpOptions.FALSE); options.setOption(USE_UNBOXING, CleanUpOptions.FALSE); diff --git a/org.eclipse.jdt.ui/plugin.xml b/org.eclipse.jdt.ui/plugin.xml index 0c7915aa3e..a917f7599a 100644 --- a/org.eclipse.jdt.ui/plugin.xml +++ b/org.eclipse.jdt.ui/plugin.xml @@ -7208,12 +7208,17 @@ <cleanUp class="org.eclipse.jdt.internal.ui.fix.TypeParametersCleanUp" id="org.eclipse.jdt.ui.cleanup.type_parameters" - runAfter="org.eclipse.jdt.ui.cleanup.strings"> + runAfter="org.eclipse.jdt.ui.cleanup.format"> + </cleanUp> + <cleanUp + class="org.eclipse.jdt.internal.ui.fix.HashCleanUp" + id="org.eclipse.jdt.ui.cleanup.hash" + runAfter="org.eclipse.jdt.ui.cleanup.type_parameters"> </cleanUp> <cleanUp class="org.eclipse.jdt.internal.ui.fix.ArraysFillCleanUp" id="org.eclipse.jdt.ui.cleanup.arrays_fill" - runAfter="org.eclipse.jdt.ui.cleanup.type_parameters"> + runAfter="org.eclipse.jdt.ui.cleanup.hash"> </cleanUp> <cleanUp class="org.eclipse.jdt.internal.ui.fix.AutoboxingCleanUp" diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/HashCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/HashCleanUp.java new file mode 100644 index 0000000000..6e26f0f521 --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/HashCleanUp.java @@ -0,0 +1,617 @@ +/******************************************************************************* + * 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.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +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.ASTVisitor; +import org.eclipse.jdt.core.dom.Assignment; +import org.eclipse.jdt.core.dom.Block; +import org.eclipse.jdt.core.dom.CastExpression; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.ConditionalExpression; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.ExpressionStatement; +import org.eclipse.jdt.core.dom.FieldAccess; +import org.eclipse.jdt.core.dom.InfixExpression; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.NullLiteral; +import org.eclipse.jdt.core.dom.ParenthesizedExpression; +import org.eclipse.jdt.core.dom.ReturnStatement; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.Statement; +import org.eclipse.jdt.core.dom.SuperFieldAccess; +import org.eclipse.jdt.core.dom.ThisExpression; +import org.eclipse.jdt.core.dom.TypeDeclaration; +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.refactoring.CompilationUnitChange; + +import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; +import org.eclipse.jdt.internal.corext.dom.ASTNodes; +import org.eclipse.jdt.internal.corext.dom.OrderedInfixExpression; +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 rewrites Eclipse-autogenerated hashcode method by Eclipse-autogenerated hashcode method for Java 7: + * <ul> + * <li>It relies on the <code>Objects.hashCode()</code> method.</li> + * </ul> + */ +public class HashCleanUp extends AbstractMultiFix implements ICleanUpFix { + private static final String HASH_CODE_METHOD= "hashCode"; //$NON-NLS-1$ + + private static final class CollectedData { + public List<Expression> fields= new ArrayList<>(); + public SimpleName primeId; + public SimpleName resultId; + public Iterator<Statement> stmtIterator; + public SimpleName tempVar; + public boolean tempValueUsed= true; + public boolean hasReturnStatement; + } + + public HashCleanUp() { + this(Collections.emptyMap()); + } + + public HashCleanUp(Map<String, String> options) { + super(options); + } + + @Override + public CleanUpRequirements getRequirements() { + boolean requireAST= isEnabled(CleanUpConstants.MODERNIZE_HASH); + return new CleanUpRequirements(requireAST, false, false, null); + } + + @Override + public String[] getStepDescriptions() { + if (isEnabled(CleanUpConstants.MODERNIZE_HASH)) { + return new String[] { MultiFixMessages.HashCleanup_description }; + } + + return new String[0]; + } + + @Override + public String getPreview() { + if (isEnabled(CleanUpConstants.MODERNIZE_HASH)) { + return "" //$NON-NLS-1$ + + "return Objects.hash(aShort);\n\n\n\n"; //$NON-NLS-1$ + } + + return "" //$NON-NLS-1$ + + "final int prime = 31;\n" //$NON-NLS-1$ + + "int result = 1;\n" //$NON-NLS-1$ + + "result = prime * result + aShort;\n" //$NON-NLS-1$ + + "return result;\n"; //$NON-NLS-1$ + } + + @Override + protected ICleanUpFix createFix(CompilationUnit unit) throws CoreException { + if (!isEnabled(CleanUpConstants.MODERNIZE_HASH) || !JavaModelUtil.is17OrHigher(unit.getJavaElement().getJavaProject())) { + return null; + } + + final List<CompilationUnitRewriteOperation> rewriteOperations= new ArrayList<>(); + + unit.accept(new ASTVisitor() { + @Override + public boolean visit(final MethodDeclaration node) { + Block body= node.getBody(); + + if (ASTNodes.usesGivenSignature(node, Object.class.getCanonicalName(), HASH_CODE_METHOD) && body != null) { + List<Statement> statements= body.statements(); + + if (statements.size() > 2) { + CollectedData data= new CollectedData(); + data.stmtIterator= statements.iterator(); + + data.primeId= isVariableValid(data, 31); + data.resultId= isVariableValid(data, 1); + + if (data.primeId != null + && data.resultId != null + && data.stmtIterator.hasNext()) { + while (!data.hasReturnStatement && data.stmtIterator.hasNext()) { + if (!isStmtValid(data)) { + return true; + } + } + + if (data.hasReturnStatement && !data.stmtIterator.hasNext()) { + rewriteOperations.add(new HashOperation(node, data)); + return false; + } + } + } + } + + return true; + } + + private SimpleName isVariableValid(final CollectedData data, final int initValue) { + Statement statement= data.stmtIterator.next(); + VariableDeclarationStatement varDecl= ASTNodes.as(statement, VariableDeclarationStatement.class); + VariableDeclarationFragment fragment= ASTNodes.getUniqueFragment(varDecl); + + if (fragment != null + && ASTNodes.hasType(varDecl.getType().resolveBinding(), int.class.getSimpleName()) + && Long.valueOf(initValue).equals(ASTNodes.getIntegerLiteral(fragment.getInitializer()))) { + return fragment.getName(); + } + + return null; + } + + private boolean isStmtValid(final CollectedData data) { + Statement statement= data.stmtIterator.next(); + ExpressionStatement exprStatement= ASTNodes.as(statement, ExpressionStatement.class); + + if (exprStatement != null) { + return isAssignmentValid(data, exprStatement); + } + + ReturnStatement returnStatement= ASTNodes.as(statement, ReturnStatement.class); + + if (returnStatement != null) { + data.hasReturnStatement= true; + Expression expression= returnStatement.getExpression(); + + return isGivenVariable(expression, data.resultId) || isHashValid(data, expression); + } + + VariableDeclarationStatement varStatement= ASTNodes.as(statement, VariableDeclarationStatement.class); + + if (varStatement != null && data.tempVar == null) { + VariableDeclarationFragment fragment= ASTNodes.getUniqueFragment(varStatement); + + if (ASTNodes.hasType(varStatement.getType().resolveBinding(), long.class.getSimpleName()) && fragment != null) { + data.tempVar= fragment.getName(); + Expression initializer= fragment.getInitializer(); + + if (fragment.getExtraDimensions() == 0) { + if (initializer != null) { + SimpleName fieldToFind= isDoubleToLongBitsMethod(data, initializer); + data.tempValueUsed= false; + + if (fieldToFind != null && data.stmtIterator.hasNext()) { + boolean assignmentValid= isStmtValid(data); + + if (assignmentValid) { + data.fields.add(ASTNodes.getUnparenthesedExpression(fieldToFind)); + return true; + } + } + } else if (data.stmtIterator.hasNext()) { + return isStmtValid(data); + } + } + } + } + + return false; + } + + private boolean isAssignmentValid(final CollectedData data, final ExpressionStatement statement) { + Assignment assignment= ASTNodes.as(statement.getExpression(), Assignment.class); + + if (assignment != null && ASTNodes.hasOperator(assignment, Assignment.Operator.ASSIGN)) { + Expression field= assignment.getLeftHandSide(); + Expression resultComputation= assignment.getRightHandSide(); + + if (isGivenVariable(field, data.resultId)) { + return isHashValid(data, resultComputation); + } + if (data.tempVar != null && isGivenVariable(field, data.tempVar)) { + SimpleName fieldToFind= isDoubleToLongBitsMethod(data, resultComputation); + + if (fieldToFind != null && data.stmtIterator.hasNext()) { + data.tempValueUsed= false; + boolean assignmentValid= isStmtValid(data); + + if (assignmentValid) { + data.fields.add(ASTNodes.getUnparenthesedExpression(fieldToFind)); + return true; + } + } + } + } + + return false; + } + + private SimpleName isDoubleToLongBitsMethod(final CollectedData data, final Expression initializer) { + SimpleName fieldToFind= null; + MethodInvocation doubleToLongBits= ASTNodes.as(initializer, MethodInvocation.class); + + if (doubleToLongBits != null && ASTNodes.usesGivenSignature(doubleToLongBits, Double.class.getCanonicalName(), "doubleToLongBits", double.class.getSimpleName())) { //$NON-NLS-1$ + SimpleName fieldName= ASTNodes.as((Expression) doubleToLongBits.arguments().get(0), SimpleName.class); + + if (fieldName != null + && !ASTNodes.isSameVariable(fieldName, data.primeId) + && !ASTNodes.isSameVariable(fieldName, data.resultId)) { + fieldToFind= fieldName; + } + } + + return fieldToFind; + } + + private boolean isHashValid(final CollectedData data, final Expression hashComputation) { + InfixExpression hashAddition= ASTNodes.as(hashComputation, InfixExpression.class); + + if (hashAddition != null) { + InfixExpression primeTimesResult= ASTNodes.as(hashAddition.getLeftOperand(), InfixExpression.class); + Expression newHash= hashAddition.getRightOperand(); + + if (!hashAddition.hasExtendedOperands() + && ASTNodes.hasOperator(hashAddition, InfixExpression.Operator.PLUS) + && primeTimesResult != null + && !primeTimesResult.hasExtendedOperands() + && ASTNodes.hasOperator(primeTimesResult, InfixExpression.Operator.TIMES) + && (isGivenVariable(primeTimesResult.getLeftOperand(), data.primeId) + && isGivenVariable(primeTimesResult.getRightOperand(), data.resultId) + || isGivenVariable(primeTimesResult.getLeftOperand(), data.resultId) + && isGivenVariable(primeTimesResult.getRightOperand(), data.primeId))) { + return isNewHashValid(data, newHash); + } + } + + return false; + } + + private boolean isNewHashValid(final CollectedData data, final Expression newHash) { + if (newHash instanceof ParenthesizedExpression) { + ParenthesizedExpression newHashWithoutBrackets= (ParenthesizedExpression) newHash; + + return isNewHashValid(data, newHashWithoutBrackets.getExpression()); + } + + if ((newHash instanceof Name || newHash instanceof FieldAccess || newHash instanceof SuperFieldAccess) && data.tempValueUsed) { + SimpleName fieldName= getField(newHash); + + if (fieldName != null + && !ASTNodes.isSameVariable(data.primeId, fieldName) + && !ASTNodes.isSameVariable(data.resultId, fieldName)) { + data.fields.add(ASTNodes.getUnparenthesedExpression(fieldName)); + return true; + } + } else if (newHash instanceof ConditionalExpression && data.tempValueUsed) { + ConditionalExpression condition= (ConditionalExpression) newHash; + return isObjectValid(data, condition) || isBooleanValid(data, condition); + } else if (newHash instanceof MethodInvocation && data.tempValueUsed) { + MethodInvocation specificMethod= (MethodInvocation) newHash; + TypeDeclaration innerClass= ASTNodes.getTypedAncestor(newHash, TypeDeclaration.class); + TypeDeclaration topLevelClass= ASTNodes.getTypedAncestor(innerClass, TypeDeclaration.class); + + if (ASTNodes.usesGivenSignature(specificMethod, Float.class.getCanonicalName(), "floatToIntBits", float.class.getSimpleName())) { //$NON-NLS-1$ + SimpleName fieldName= getField((Expression) specificMethod.arguments().get(0)); + + if (fieldName != null + && !ASTNodes.isSameVariable(fieldName, data.primeId) + && !ASTNodes.isSameVariable(fieldName, data.resultId)) { + data.fields.add(ASTNodes.getUnparenthesedExpression(fieldName)); + return true; + } + } else if (ASTNodes.usesGivenSignature(specificMethod, Arrays.class.getCanonicalName(), HASH_CODE_METHOD, boolean[].class.getCanonicalName()) + || ASTNodes.usesGivenSignature(specificMethod, Arrays.class.getCanonicalName(), HASH_CODE_METHOD, byte[].class.getCanonicalName()) + || ASTNodes.usesGivenSignature(specificMethod, Arrays.class.getCanonicalName(), HASH_CODE_METHOD, char[].class.getCanonicalName()) + || ASTNodes.usesGivenSignature(specificMethod, Arrays.class.getCanonicalName(), HASH_CODE_METHOD, double[].class.getCanonicalName()) + || ASTNodes.usesGivenSignature(specificMethod, Arrays.class.getCanonicalName(), HASH_CODE_METHOD, float[].class.getCanonicalName()) + || ASTNodes.usesGivenSignature(specificMethod, Arrays.class.getCanonicalName(), HASH_CODE_METHOD, int[].class.getCanonicalName()) + || ASTNodes.usesGivenSignature(specificMethod, Arrays.class.getCanonicalName(), HASH_CODE_METHOD, Object[].class.getCanonicalName()) + || ASTNodes.usesGivenSignature(specificMethod, Arrays.class.getCanonicalName(), HASH_CODE_METHOD, long[].class.getCanonicalName()) + || ASTNodes.usesGivenSignature(specificMethod, Arrays.class.getCanonicalName(), HASH_CODE_METHOD, short[].class.getCanonicalName())) { + SimpleName fieldName= getField((Expression) specificMethod.arguments().get(0)); + + if (fieldName != null + && !ASTNodes.isSameVariable(fieldName, data.primeId) + && !ASTNodes.isSameVariable(fieldName, data.resultId)) { + data.fields.add(ASTNodes.getUnparenthesedExpression(specificMethod)); + return true; + } + } else if (innerClass != null + && innerClass.resolveBinding() != null + && topLevelClass != null + && topLevelClass.resolveBinding() != null + && ASTNodes.usesGivenSignature(specificMethod, topLevelClass.resolveBinding().getQualifiedName(), HASH_CODE_METHOD)) { + return isEnclosingHashCode(data, specificMethod, innerClass, topLevelClass); + } + } else if (newHash instanceof CastExpression) { + return isGreatNumberValid(data, (CastExpression) newHash); + } + + return false; + } + + private boolean isEnclosingHashCode(final CollectedData data, final MethodInvocation specificMethod, + final TypeDeclaration innerClass, final TypeDeclaration topLevelClass) { + MethodInvocation getEnclosingInstanceMethod= ASTNodes.as(specificMethod.getExpression(), MethodInvocation.class); + + if (ASTNodes.usesGivenSignature(getEnclosingInstanceMethod, innerClass.resolveBinding().getQualifiedName(), "getEnclosingInstance")) { //$NON-NLS-1$ + MethodDeclaration getEnclosingInstanceDeclaration= null; + + for (MethodDeclaration innerMethod : innerClass.getMethods()) { + if ("getEnclosingInstance".equals(innerMethod.getName().getIdentifier()) //$NON-NLS-1$ + && (innerMethod.parameters() == null || innerMethod.parameters().isEmpty()) + && !innerMethod.isConstructor() + && innerMethod.resolveBinding() != null + && ASTNodes.hasType(innerMethod.resolveBinding().getReturnType(), topLevelClass.resolveBinding().getQualifiedName())) { + getEnclosingInstanceDeclaration= innerMethod; + break; + } + } + + if (getEnclosingInstanceDeclaration != null) { + ReturnStatement returnStatement= ASTNodes.as(getEnclosingInstanceDeclaration.getBody(), ReturnStatement.class); + + if (returnStatement != null) { + ThisExpression thisExpression= ASTNodes.as(returnStatement.getExpression(), + ThisExpression.class); + + if (thisExpression != null) { + SimpleName topLevelClassReference= ASTNodes.as(thisExpression.getQualifier(), + SimpleName.class); + + if (topLevelClassReference != null + && topLevelClass.getName().getIdentifier().equals(topLevelClassReference.getIdentifier())) { + data.fields.add(ASTNodes.getUnparenthesedExpression(specificMethod)); + return true; + } + } + } + } + } + + return false; + } + + private SimpleName getField(final Expression expression) { + SimpleName simpleName= ASTNodes.as(expression, SimpleName.class); + + if (simpleName != null) { + return simpleName; + } + + FieldAccess fieldName= ASTNodes.as(expression, FieldAccess.class); + + if (fieldName != null) { + ThisExpression thisExpression= ASTNodes.as(fieldName.getExpression(), ThisExpression.class); + + if (thisExpression != null) { + if (thisExpression.getQualifier() == null) { + return fieldName.getName(); + } + + if (thisExpression.getQualifier().isSimpleName()) { + SimpleName qualifier= (SimpleName) thisExpression.getQualifier(); + TypeDeclaration visitedClass= ASTNodes.getTypedAncestor(expression, TypeDeclaration.class); + + if (visitedClass != null + && ASTNodes.isSameVariable(visitedClass.getName(), qualifier)) { + return fieldName.getName(); + } + } + } + } + + SuperFieldAccess superFieldAccess= ASTNodes.as(expression, SuperFieldAccess.class); + + if (superFieldAccess != null) { + if (superFieldAccess.getQualifier() == null) { + return superFieldAccess.getName(); + } + + if (superFieldAccess.getQualifier().isSimpleName()) { + SimpleName qualifier= (SimpleName) superFieldAccess.getQualifier(); + TypeDeclaration visitedClass= ASTNodes.getTypedAncestor(expression, TypeDeclaration.class); + + if (visitedClass != null + && ASTNodes.isSameVariable(visitedClass.getName(), qualifier)) { + return superFieldAccess.getName(); + } + } + } + + return null; + } + + private boolean isGreatNumberValid(final CollectedData data, final CastExpression newHash) { + OrderedInfixExpression<Expression, InfixExpression> orderedBitwise= ASTNodes.orderedInfix(newHash.getExpression(), Expression.class, InfixExpression.class); + + if (ASTNodes.hasType(newHash, int.class.getSimpleName()) + && orderedBitwise != null + && ASTNodes.hasType(newHash.getExpression(), long.class.getSimpleName(), double.class.getSimpleName()) + && InfixExpression.Operator.XOR.equals(orderedBitwise.getOperator())) { + SimpleName field= getField(orderedBitwise.getFirstOperand()); + InfixExpression moveExpression= orderedBitwise.getSecondOperand(); + + if (field != null + && moveExpression != null + && !ASTNodes.isSameVariable(field, data.primeId) + && !ASTNodes.isSameVariable(field, data.resultId) + && ASTNodes.hasOperator(moveExpression, InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED)) { + SimpleName againFieldName= getField(moveExpression.getLeftOperand()); + Long hash= ASTNodes.getIntegerLiteral(moveExpression.getRightOperand()); + + if (Long.valueOf(32).equals(hash) + && againFieldName != null + && ASTNodes.isSameVariable(againFieldName, field)) { + if (data.tempValueUsed) { + data.fields.add(ASTNodes.getUnparenthesedExpression(againFieldName)); + return true; + } + + if (ASTNodes.isSameVariable(data.tempVar, field)) { + data.tempValueUsed= true; + return true; + } + } + } + } + + return false; + } + + private boolean isBooleanValid(final CollectedData data, final ConditionalExpression newHash) { + SimpleName booleanField= getField(newHash.getExpression()); + Long hashForTrue= ASTNodes.getIntegerLiteral(newHash.getThenExpression()); + Long hashForFalse= ASTNodes.getIntegerLiteral(newHash.getElseExpression()); + + if (booleanField != null + && hashForTrue != null + && hashForFalse != null + && ASTNodes.hasType(booleanField, boolean.class.getSimpleName()) + && !ASTNodes.isSameVariable(booleanField, data.primeId) + && !ASTNodes.isSameVariable(booleanField, data.resultId) && Long.valueOf(1231).equals(hashForTrue) + && Long.valueOf(1237).equals(hashForFalse)) { + data.fields.add(ASTNodes.getUnparenthesedExpression(booleanField)); + return true; + } + + return false; + } + + private boolean isObjectValid(final CollectedData data, final ConditionalExpression condition) { + OrderedInfixExpression<Expression, NullLiteral> orderedIsFieldNull= ASTNodes.orderedInfix(condition.getExpression(), Expression.class, NullLiteral.class); + + if (orderedIsFieldNull != null + && Arrays.asList(InfixExpression.Operator.EQUALS, InfixExpression.Operator.NOT_EQUALS).contains(orderedIsFieldNull.getOperator())) { + SimpleName field= getField(orderedIsFieldNull.getFirstOperand()); + + if (field != null) { + Long zero; + MethodInvocation hashOnField; + + if (InfixExpression.Operator.EQUALS.equals(orderedIsFieldNull.getOperator())) { + zero= ASTNodes.getIntegerLiteral(condition.getThenExpression()); + hashOnField= ASTNodes.as(condition.getElseExpression(), MethodInvocation.class); + } else { + hashOnField= ASTNodes.as(condition.getThenExpression(), MethodInvocation.class); + zero= ASTNodes.getIntegerLiteral(condition.getElseExpression()); + } + + if (Long.valueOf(0).equals(zero) + && hashOnField != null + && hashOnField.getExpression() != null + && HASH_CODE_METHOD.equals(hashOnField.getName().getIdentifier()) + && (hashOnField.arguments() == null || hashOnField.arguments().isEmpty())) { + SimpleName fieldToHash= getField(hashOnField.getExpression()); + + if (fieldToHash != null + && ASTNodes.isSameVariable(field, fieldToHash)) { + data.fields.add(ASTNodes.getUnparenthesedExpression(fieldToHash)); + return true; + } + } + } + } + + return false; + } + + private boolean isGivenVariable(final Expression expression, final SimpleName varId) { + SimpleName field= getField(expression); + return field != null && ASTNodes.isSameVariable(varId, field); + } + }); + + if (rewriteOperations.isEmpty()) { + return null; + } + + return new CompilationUnitRewriteOperationsFix(MultiFixMessages.HashCleanup_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 HashOperation extends CompilationUnitRewriteOperation { + private final MethodDeclaration node; + private final CollectedData data; + + public HashOperation(final MethodDeclaration node, final CollectedData data) { + this.node= node; + this.data= data; + } + + @Override + public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException { + ASTRewrite rewrite= cuRewrite.getASTRewrite(); + AST ast= cuRewrite.getRoot().getAST(); + ImportRewrite importRewrite= cuRewrite.getImportRewrite(); + TextEditGroup group= createTextEditGroup(MultiFixMessages.HashCleanup_description, cuRewrite); + + String objectsNameText= importRewrite.addImport(Objects.class.getCanonicalName()); + + List<Statement> statements= node.getBody().statements(); + Name objectsClassName= ASTNodeFactory.newName(ast, objectsNameText); + + MethodInvocation methodInvocation= ast.newMethodInvocation(); + methodInvocation.setExpression(objectsClassName); + methodInvocation.setName(ast.newSimpleName("hash")); //$NON-NLS-1$ + methodInvocation.arguments().addAll(ASTNodes.createMoveTarget(rewrite, data.fields)); + + ReturnStatement newReturnStatement= ast.newReturnStatement(); + newReturnStatement.setExpression(methodInvocation); + ASTNodes.replaceButKeepComment(rewrite, statements.get(0), + newReturnStatement, group); + + for (int i= 1; i < statements.size(); i++) { + rewrite.remove(statements.get(i), group); + } + } + } +} 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 ad6ee3dba0..7532d4fc68 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 @@ -125,6 +125,7 @@ public class CleanUpMessages extends NLS { public static String UnnecessaryCodeTabPage_CheckboxName_UnnecessaryCasts; public static String UnnecessaryCodeTabPage_CheckboxName_UnnecessaryNLSTags; public static String UnnecessaryCodeTabPage_CheckboxName_RedundantTypeArguments; + public static String UnnecessaryCodeTabPage_CheckboxName_Hash; public static String UnnecessaryCodeTabPage_CheckboxName_ArraysFill; public static String UnnecessaryCodeTabPage_CheckboxName_Autoboxing; public static String UnnecessaryCodeTabPage_CheckboxName_Unboxing; 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 fb1a9b88b3..a2d021d949 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 @@ -110,6 +110,7 @@ UnnecessaryCodeTabPage_GroupName_UnnecessaryCode=Unnecessary code UnnecessaryCodeTabPage_CheckboxName_UnnecessaryCasts=Remove unnecessar&y casts UnnecessaryCodeTabPage_CheckboxName_UnnecessaryNLSTags=Remove unnecessary '$NON-NLS$' ta&gs UnnecessaryCodeTabPage_CheckboxName_RedundantTypeArguments=Remove redundant type &arguments (1.7 or higher) +UnnecessaryCodeTabPage_CheckboxName_Hash=Use Objects.hash() (1.7 or higher) UnnecessaryCodeTabPage_CheckboxName_ArraysFill=Use Arrays.&fill() when possible UnnecessaryCodeTabPage_CheckboxName_Autoboxing=Use Autobo&xing (1.5 or higher) UnnecessaryCodeTabPage_CheckboxName_Unboxing=Use Un&boxing (1.5 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 3fbcdaac3c..a12ab14936 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 @@ -26,6 +26,7 @@ import org.eclipse.jdt.internal.ui.fix.ArraysFillCleanUp; import org.eclipse.jdt.internal.ui.fix.AutoboxingCleanUp; import org.eclipse.jdt.internal.ui.fix.CollectionCloningCleanUp; import org.eclipse.jdt.internal.ui.fix.EmbeddedIfCleanUp; +import org.eclipse.jdt.internal.ui.fix.HashCleanUp; import org.eclipse.jdt.internal.ui.fix.MapCloningCleanUp; import org.eclipse.jdt.internal.ui.fix.MapMethodCleanUp; import org.eclipse.jdt.internal.ui.fix.MergeConditionalBlocksCleanUp; @@ -57,6 +58,7 @@ public final class UnnecessaryCodeTabPage extends AbstractCleanUpTabPage { new UnnecessaryCodeCleanUp(values), new StringCleanUp(values), new TypeParametersCleanUp(values), + new HashCleanUp(values), new ArraysFillCleanUp(values), new AutoboxingCleanUp(values), new UnboxingCleanUp(values), @@ -109,6 +111,9 @@ public final class UnnecessaryCodeTabPage extends AbstractCleanUpTabPage { CheckboxPreference typeArgs= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_RedundantTypeArguments, CleanUpConstants.REMOVE_REDUNDANT_TYPE_ARGUMENTS, CleanUpModifyDialog.FALSE_TRUE); registerPreference(typeArgs); + CheckboxPreference hash= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_Hash, CleanUpConstants.MODERNIZE_HASH, CleanUpModifyDialog.FALSE_TRUE); + registerPreference(hash); + CheckboxPreference arraysFill= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_ArraysFill, CleanUpConstants.ARRAYS_FILL, CleanUpModifyDialog.FALSE_TRUE); registerPreference(arraysFill); |
