diff options
| author | Fabrice Tiercelin | 2021-03-23 20:52:04 +0000 |
|---|---|---|
| committer | Fabrice Tiercelin | 2021-03-24 07:19:11 +0000 |
| commit | 670d3f49634b1190d8aec209b266b97c560f11ef (patch) | |
| tree | 43fb44eccad0a1ece0196ba3c94d680e761f45ea | |
| parent | 1d9b3cfc4ec364c790432ca8e52809dc975baad2 (diff) | |
| download | eclipse.jdt.ui-670d3f49634b1190d8aec209b266b97c560f11ef.tar.gz eclipse.jdt.ui-670d3f49634b1190d8aec209b266b97c560f11ef.tar.xz eclipse.jdt.ui-670d3f49634b1190d8aec209b266b97c560f11ef.zip | |
Bug 572234 - [AutoRefactor immigration #65/151] [cleanup & saveaction]
valueOf() rather than instantiation
Given:
Byte by = new Byte((byte) 4);
Boolean bo = new Boolean(true);
Character c = new Character('c');
Double d = new Double(1);
Float f1 = new Float(1f);
Float f2 = new Float(1d);
Long l = new Long(1);
Short s = new Short((short) 1);
Integer i = new Integer(1);
new Byte("0").byteValue();
byte by2 = new Byte((byte) 0);
When:
Clean up the code enabling "valueOf() rather than instantiation"
Then:
Byte by = Byte.valueOf((byte) 4);
Boolean bo = Boolean.valueOf(true);
Character c = Character.valueOf('c');
Double d = Double.valueOf(1);
Float f1 = Float.valueOf(1f);
Float f2 = Float.valueOf((float) 1d);
Long l = Long.valueOf(1);
Short s = Short.valueOf((short) 1);
Integer i = Integer.valueOf(1);
Byte.valueOf("0").byteValue();
byte by2 = (byte) 0;
Change-Id: I09d22630e415640929f4bbaf5ab291aa88cc884d
Signed-off-by: Fabrice Tiercelin <fabrice.tiercelin@yahoo.fr>
15 files changed, 673 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 1b9619744b..6b28763e2b 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 @@ -134,6 +134,11 @@ public class MultiFixMessages extends NLS { public static String StringBuilderCleanUp_description; public static String PlainReplacementCleanUp_description; public static String CodeStyleCleanUp_LazyLogical_description; + public static String ValueOfRatherThanInstantiationCleanup_description; + public static String ValueOfRatherThanInstantiationCleanup_description_float_with_valueof; + public static String ValueOfRatherThanInstantiationCleanup_description_float_with_float_value; + public static String ValueOfRatherThanInstantiationCleanup_description_single_argument; + public static String ValueOfRatherThanInstantiationCleanup_description_valueof; public static String PrimitiveComparisonCleanUp_description; public static String PrimitiveParsingCleanUp_description; public static String PrimitiveSerializationCleanUp_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 3d46b6986f..21d60d55f1 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 @@ -116,6 +116,11 @@ StaticInnerClassCleanUp_description=Make inner classes static where possible StringBuilderCleanUp_description=Replace String concatenation by StringBuilder PlainReplacementCleanUp_description=Use String.replace() instead of String.replaceAll() when possible CodeStyleCleanUp_LazyLogical_description=Use lazy logical operator (&& and ||) +ValueOfRatherThanInstantiationCleanup_description=valueOf() rather than instantiation +ValueOfRatherThanInstantiationCleanup_description_float_with_valueof=Float with valueOf() +ValueOfRatherThanInstantiationCleanup_description_float_with_float_value=Float with float value +ValueOfRatherThanInstantiationCleanup_description_single_argument=Directly return the single argument +ValueOfRatherThanInstantiationCleanup_description_valueof=valueOf() PrimitiveComparisonCleanUp_description=Primitive comparison PrimitiveParsingCleanUp_description=Primitive parsing PrimitiveSerializationCleanUp_description=Primitive serialization diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/ValueOfRatherThanInstantiationCleanUpCore.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/ValueOfRatherThanInstantiationCleanUpCore.java new file mode 100644 index 0000000000..aec18f276c --- /dev/null +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/ValueOfRatherThanInstantiationCleanUpCore.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2021 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.List; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.manipulation.CleanUpContextCore; +import org.eclipse.jdt.core.manipulation.CleanUpRequirementsCore; +import org.eclipse.jdt.core.manipulation.ICleanUpFixCore; + +import org.eclipse.jdt.internal.corext.fix.CleanUpConstants; +import org.eclipse.jdt.internal.corext.fix.ValueOfRatherThanInstantiationFixCore; + +public class ValueOfRatherThanInstantiationCleanUpCore extends AbstractCleanUpCore { + public ValueOfRatherThanInstantiationCleanUpCore(final Map<String, String> options) { + super(options); + } + + public ValueOfRatherThanInstantiationCleanUpCore() { + } + + @Override + public CleanUpRequirementsCore getRequirementsCore() { + return new CleanUpRequirementsCore(requireAST(), false, false, null); + } + + public boolean requireAST() { + return isEnabled(CleanUpConstants.VALUEOF_RATHER_THAN_INSTANTIATION); + } + + @Override + public ICleanUpFixCore createFixCore(final CleanUpContextCore context) throws CoreException { + CompilationUnit compilationUnit= context.getAST(); + + if (compilationUnit == null || !isEnabled(CleanUpConstants.VALUEOF_RATHER_THAN_INSTANTIATION)) { + return null; + } + + return ValueOfRatherThanInstantiationFixCore.createCleanUp(compilationUnit); + } + + @Override + public String[] getStepDescriptions() { + List<String> result= new ArrayList<>(); + + if (isEnabled(CleanUpConstants.VALUEOF_RATHER_THAN_INSTANTIATION)) { + result.add(MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description); + } + + return result.toArray(new String[0]); + } + + @Override + public String getPreview() { + if (isEnabled(CleanUpConstants.VALUEOF_RATHER_THAN_INSTANTIATION)) { + return "" //$NON-NLS-1$ + + "Object characterObject = Character.valueOf('a');\n" //$NON-NLS-1$ + + "Byte.valueOf(\"0\").byteValue();\n" //$NON-NLS-1$ + + "long l = 42;\n"; //$NON-NLS-1$ + } + + return "" //$NON-NLS-1$ + + "Object characterObject = Character.valueOf('a');\n" //$NON-NLS-1$ + + "new Byte(\"0\").byteValue();\n" //$NON-NLS-1$ + + "long l = new Long(42);\n"; //$NON-NLS-1$ + } +} 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 489bbbef99..c2f955678b 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 @@ -762,6 +762,18 @@ public class CleanUpConstants { public static final String USE_LAZY_LOGICAL_OPERATOR= "cleanup.lazy_logical_operator"; //$NON-NLS-1$ /** + * Replace unnecessary primitive wrappers instance creations by using static factory <code>valueOf()</code> method. + * <p> + * Possible values: {TRUE, FALSE} + * <p> + * + * @see CleanUpOptionsCore#TRUE + * @see CleanUpOptionsCore#FALSE + * @since 4.20 + */ + public static final String VALUEOF_RATHER_THAN_INSTANTIATION= "cleanup.valueof_rather_than_instantiation"; //$NON-NLS-1$ + + /** * Replaces the <code>compareTo()</code> method by a comparison on primitive. * <p> * Possible values: {TRUE, FALSE} diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java index f185a8d8bb..23888f2410 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java @@ -104,6 +104,8 @@ public final class FixMessages extends NLS { public static String StringFix_AddNonNls_description; public static String StringFix_RemoveNonNls_description; + public static String ValueOfRatherThanInstantiationFix_description; + public static String CodeStyleFix_ChangeAccessToStatic_description; public static String CodeStyleFix_QualifyWithThis_description; public static String CodeStyleFix_ChangeAccessToStaticUsingInstanceType_description; diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties index 2e72d273ab..d1e005f647 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties @@ -68,6 +68,8 @@ StringFix_AddRemoveNonNls_description=Add/Remove '$NON-NLS$' tag StringFix_AddNonNls_description=Add missing '$NON-NLS$' tag StringFix_RemoveNonNls_description=Remove unnecessary '$NON-NLS$' tag +ValueOfRatherThanInstantiationFix_description=valueOf() rather than instantiation + CodeStyleFix_ChangeAccessToStatic_description=Change access to static using ''{0}'' (declaring type) CodeStyleFix_ChangeAccessUsingDeclaring_description=Change access to static using declaring type CodeStyleFix_ChangeControlToBlock_description=Change control statement body to block diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ValueOfRatherThanInstantiationFixCore.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ValueOfRatherThanInstantiationFixCore.java new file mode 100644 index 0000000000..7cbd897c5e --- /dev/null +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ValueOfRatherThanInstantiationFixCore.java @@ -0,0 +1,244 @@ +/******************************************************************************* + * Copyright (c) 2021 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.corext.fix; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.text.edits.TextEditGroup; + +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.CastExpression; +import org.eclipse.jdt.core.dom.ClassInstanceCreation; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.PrimitiveType; +import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer; +import org.eclipse.jdt.core.manipulation.ICleanUpFixCore; + +import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; +import org.eclipse.jdt.internal.corext.dom.ASTNodes; +import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; +import org.eclipse.jdt.internal.corext.util.JavaModelUtil; + +import org.eclipse.jdt.internal.ui.fix.MultiFixMessages; + +public class ValueOfRatherThanInstantiationFixCore extends CompilationUnitRewriteOperationsFixCore { + public static final class ValueOfRatherThanInstantiationFinder extends ASTVisitor { + private List<CompilationUnitRewriteOperation> fResult; + + public ValueOfRatherThanInstantiationFinder(List<CompilationUnitRewriteOperation> ops) { + fResult= ops; + } + + @Override + public boolean visit(final ClassInstanceCreation visited) { + ITypeBinding typeBinding= visited.getType().resolveBinding(); + List<Expression> args= visited.arguments(); + + if (args.size() == 1) { + Expression arg0= args.get(0); + + if (ASTNodes.hasType(typeBinding, Float.class.getCanonicalName())) { + if (ASTNodes.hasType(arg0, double.class.getSimpleName())) { + fResult.add(new ValueOfRatherThanInstantiationFloatWithValueOfFixOperation(visited, typeBinding, arg0)); + return false; + } + + if (ASTNodes.hasType(arg0, Double.class.getCanonicalName())) { + fResult.add(new ValueOfRatherThanInstantiationFloatWithFloatValueFixOperation(visited, arg0)); + return false; + } + } + + if (ASTNodes.hasType(typeBinding, Boolean.class.getCanonicalName(), Integer.class.getCanonicalName(), Long.class.getCanonicalName(), Double.class.getCanonicalName(), + Short.class.getCanonicalName(), Float.class.getCanonicalName(), Byte.class.getCanonicalName(), Character.class.getCanonicalName())) { + ITypeBinding destinationTypeBinding= ASTNodes.getTargetType(visited); + + if (destinationTypeBinding != null + && destinationTypeBinding.isPrimitive()) { + fResult.add(new ValueOfRatherThanInstantiationWithTheSingleArgumentFixOperation(visited)); + return false; + } + + if (JavaModelUtil.is50OrHigher(((CompilationUnit) visited.getRoot()).getJavaElement().getJavaProject()) + || ASTNodes.hasType(typeBinding, String.class.getCanonicalName())) { + fResult.add(new ValueOfRatherThanInstantiationWithValueOfFixOperation(visited, typeBinding, arg0)); + return false; + } + } + } + + return true; + } + } + + public static class ValueOfRatherThanInstantiationFloatWithValueOfFixOperation extends CompilationUnitRewriteOperation { + private final ClassInstanceCreation visited; + private final ITypeBinding typeBinding; + private final Expression arg0; + + public ValueOfRatherThanInstantiationFloatWithValueOfFixOperation(final ClassInstanceCreation visited, final ITypeBinding typeBinding, final Expression arg0) { + this.visited= visited; + this.typeBinding= typeBinding; + this.arg0= arg0; + } + + @Override + public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException { + ASTRewrite rewrite= cuRewrite.getASTRewrite(); + AST ast= cuRewrite.getRoot().getAST(); + TextEditGroup group= createTextEditGroup(MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_float_with_valueof, cuRewrite); + rewrite.setTargetSourceRangeComputer(new TargetSourceRangeComputer() { + @Override + public SourceRange computeSourceRange(final ASTNode nodeWithComment) { + if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) { + return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength()); + } + + return super.computeSourceRange(nodeWithComment); + } + }); + + MethodInvocation valueOfMethod= ast.newMethodInvocation(); + valueOfMethod.setExpression(ASTNodeFactory.newName(ast, typeBinding.getName())); + valueOfMethod.setName(ast.newSimpleName("valueOf")); //$NON-NLS-1$ + CastExpression newCastExpression= ast.newCastExpression(); + newCastExpression.setType(ast.newPrimitiveType(PrimitiveType.FLOAT)); + newCastExpression.setExpression(ASTNodes.createMoveTarget(rewrite, ASTNodes.getUnparenthesedExpression(arg0))); + valueOfMethod.arguments().add(newCastExpression); + + ASTNodes.replaceButKeepComment(rewrite, visited, valueOfMethod, group); + } + } + + public static class ValueOfRatherThanInstantiationFloatWithFloatValueFixOperation extends CompilationUnitRewriteOperation { + private final ClassInstanceCreation visited; + private final Expression arg0; + + public ValueOfRatherThanInstantiationFloatWithFloatValueFixOperation(final ClassInstanceCreation visited, final Expression arg0) { + this.visited= visited; + this.arg0= arg0; + } + + @Override + public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException { + ASTRewrite rewrite= cuRewrite.getASTRewrite(); + AST ast= cuRewrite.getRoot().getAST(); + TextEditGroup group= createTextEditGroup(MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_float_with_float_value, cuRewrite); + rewrite.setTargetSourceRangeComputer(new TargetSourceRangeComputer() { + @Override + public SourceRange computeSourceRange(final ASTNode nodeWithComment) { + if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) { + return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength()); + } + + return super.computeSourceRange(nodeWithComment); + } + }); + + MethodInvocation floatValueMethod= ast.newMethodInvocation(); + floatValueMethod.setExpression(ASTNodes.createMoveTarget(rewrite, arg0)); + floatValueMethod.setName(ast.newSimpleName("floatValue")); //$NON-NLS-1$ + + ASTNodes.replaceButKeepComment(rewrite, visited, floatValueMethod, group); + } + } + + public static class ValueOfRatherThanInstantiationWithTheSingleArgumentFixOperation extends CompilationUnitRewriteOperation { + private final ClassInstanceCreation visited; + + public ValueOfRatherThanInstantiationWithTheSingleArgumentFixOperation(final ClassInstanceCreation visited) { + this.visited= visited; + } + + @Override + public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException { + ASTRewrite rewrite= cuRewrite.getASTRewrite(); + TextEditGroup group= createTextEditGroup(MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_single_argument, cuRewrite); + rewrite.setTargetSourceRangeComputer(new TargetSourceRangeComputer() { + @Override + public SourceRange computeSourceRange(final ASTNode nodeWithComment) { + if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) { + return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength()); + } + + return super.computeSourceRange(nodeWithComment); + } + }); + + ASTNodes.replaceButKeepComment(rewrite, visited, ASTNodes.createMoveTarget(rewrite, (Expression) visited.arguments().get(0)), group); + } + } + + public static class ValueOfRatherThanInstantiationWithValueOfFixOperation extends CompilationUnitRewriteOperation { + private final ClassInstanceCreation visited; + private final ITypeBinding typeBinding; + private final Expression arg0; + + public ValueOfRatherThanInstantiationWithValueOfFixOperation(final ClassInstanceCreation visited, final ITypeBinding typeBinding, final Expression arg0) { + this.visited= visited; + this.typeBinding= typeBinding; + this.arg0= arg0; + } + + @Override + public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException { + ASTRewrite rewrite= cuRewrite.getASTRewrite(); + AST ast= cuRewrite.getRoot().getAST(); + TextEditGroup group= createTextEditGroup(MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_valueof, cuRewrite); + rewrite.setTargetSourceRangeComputer(new TargetSourceRangeComputer() { + @Override + public SourceRange computeSourceRange(final ASTNode nodeWithComment) { + if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) { + return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength()); + } + + return super.computeSourceRange(nodeWithComment); + } + }); + + MethodInvocation valueOfMethod= ast.newMethodInvocation(); + valueOfMethod.setExpression(ASTNodeFactory.newName(ast, typeBinding.getName())); + valueOfMethod.setName(ast.newSimpleName("valueOf")); //$NON-NLS-1$ + valueOfMethod.arguments().add(ASTNodes.createMoveTarget(rewrite, ASTNodes.getUnparenthesedExpression(arg0))); + + ASTNodes.replaceButKeepComment(rewrite, visited, valueOfMethod, group); + } + } + + public static ICleanUpFixCore createCleanUp(final CompilationUnit compilationUnit) { + List<CompilationUnitRewriteOperation> operations= new ArrayList<>(); + ValueOfRatherThanInstantiationFinder finder= new ValueOfRatherThanInstantiationFinder(operations); + compilationUnit.accept(finder); + + if (operations.isEmpty()) { + return null; + } + + CompilationUnitRewriteOperation[] ops= operations.toArray(new CompilationUnitRewriteOperation[0]); + return new ValueOfRatherThanInstantiationFixCore(FixMessages.ValueOfRatherThanInstantiationFix_description, compilationUnit, ops); + } + + protected ValueOfRatherThanInstantiationFixCore(final String name, final CompilationUnit compilationUnit, final CompilationUnitRewriteOperation[] fixRewriteOperations) { + super(name, compilationUnit, fixRewriteOperations); + } +} diff --git a/org.eclipse.jdt.ui.tests/performance/org/eclipse/jdt/ui/tests/performance/views/CleanUpPerfTest.java b/org.eclipse.jdt.ui.tests/performance/org/eclipse/jdt/ui/tests/performance/views/CleanUpPerfTest.java index 9467fd9f00..c59aa0fe03 100644 --- a/org.eclipse.jdt.ui.tests/performance/org/eclipse/jdt/ui/tests/performance/views/CleanUpPerfTest.java +++ b/org.eclipse.jdt.ui.tests/performance/org/eclipse/jdt/ui/tests/performance/views/CleanUpPerfTest.java @@ -95,6 +95,7 @@ import org.eclipse.jdt.internal.ui.fix.SwitchExpressionsCleanUp; import org.eclipse.jdt.internal.ui.fix.UnloopedWhileCleanUp; import org.eclipse.jdt.internal.ui.fix.UnnecessaryCodeCleanUp; import org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp; +import org.eclipse.jdt.internal.ui.fix.ValueOfRatherThanInstantiationCleanUp; import org.eclipse.jdt.internal.ui.fix.VariableDeclarationCleanUp; import org.eclipse.jdt.internal.ui.preferences.cleanup.CleanUpProfileVersioner; import org.eclipse.jdt.internal.ui.preferences.formatter.ProfileManager; @@ -769,6 +770,22 @@ public class CleanUpPerfTest extends JdtPerformanceTestCaseCommon { } @Test + public void testValueOfRatherThanInstantiationCleanUp() throws Exception { + CleanUpRefactoring cleanUpRefactoring= new CleanUpRefactoring(); + addAllCUs(cleanUpRefactoring, MyTestSetup.fJProject1.getChildren()); + + Map<String, String> node= getNullSettings(); + + node.put(CleanUpConstants.VALUEOF_RATHER_THAN_INSTANTIATION, CleanUpOptions.TRUE); + + storeSettings(node); + + cleanUpRefactoring.addCleanUp(new ValueOfRatherThanInstantiationCleanUp()); + + doCleanUp(cleanUpRefactoring); + } + + @Test public void testPrimitiveComparisonCleanUp() throws Exception { CleanUpRefactoring cleanUpRefactoring= new CleanUpRefactoring(); addAllCUs(cleanUpRefactoring, MyTestSetup.fJProject1.getChildren()); diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest.java index 519e05106c..0589cf2313 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest.java @@ -4758,6 +4758,226 @@ public class CleanUpTest extends CleanUpTestCase { } @Test + public void testValueOfRatherThanInstantiation() throws Exception { + // Given + IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null); + String given= "" // + + "package test1;\n" // + + "\n" // + + "public class E {\n" // + + " public static void replaceWrapperConstructorsWithValueOf() {\n" // + + " // Replace all calls to wrapper constructors with calls to .valueOf() methods\n" // + + " byte byPrimitive = 4;\n" // + + " boolean boPrimitive = true;\n" // + + " char cPrimitive = 'c';\n" // + + " double dPrimitive = 1;\n" // + + " Double dObject = Double.valueOf(1d);\n" // + + " float fPrimitive = 1f;\n" // + + " long lPrimitive = 1;\n" // + + " short shPrimitive = 1;\n" // + + " int iPrimitive = 1;\n" // + + "\n" // + + " // Primitive literals\n" // + + " Byte by = new Byte((byte) 4);\n" // + + " Boolean bo = new Boolean(true);\n" // + + " Character c = new Character('c');\n" // + + " Double d = new Double(1);\n" // + + " Float f1 = new Float(1f);\n" // + + " Float f2 = new Float(1d);\n" // + + " Long l = new Long(1);\n" // + + " Short s = new Short((short) 1);\n" // + + " Integer i = new Integer(1);\n" // + + "\n" // + + " // Primitive variables\n" // + + " by = new Byte(byPrimitive);\n" // + + " bo = new Boolean(boPrimitive);\n" // + + " c = new Character(cPrimitive);\n" // + + " d = new Double(dPrimitive);\n" // + + " f1 = new Float(fPrimitive);\n" // + + " f2 = new Float(dPrimitive);\n" // + + " l = new Long(lPrimitive);\n" // + + " s = new Short(shPrimitive);\n" // + + " i = new Integer(iPrimitive);\n" // + + "\n" // + + " // Implicit object narrowing\n" // + + " Float f3 = new Float(dObject);\n" // + + " }\n" // + + "\n" // + + " public static void removeUnnecessaryObjectCreation() {\n" // + + " // Keep this comment\n" // + + " new Byte(\"0\").byteValue();\n" // + + " new Boolean(\"true\").booleanValue();\n" // + + " new Integer(\"42\").intValue();\n" // + + " new Short(\"42\").shortValue();\n" // + + " new Long(\"42\").longValue();\n" // + + " new Float(\"42.42\").floatValue();\n" // + + " new Double(\"42.42\").doubleValue();\n" // + + " }\n" // + + "\n" // + + " public static void removeUnnecessaryConstructorInvocationsInPrimitiveContext() {\n" // + + " // Keep this comment\n" // + + " byte by = new Byte((byte) 0);\n" // + + " boolean bo = new Boolean(true);\n" // + + " int i = new Integer(42);\n" // + + " long l = new Long(42);\n" // + + " short s = new Short((short) 42);\n" // + + " float f = new Float(42.42F);\n" // + + " double d = new Double(42.42);\n" // + + " }\n" // + + "\n" // + + " public static void removeUnnecessaryConstructorInvocationsInSwitch() {\n" // + + " byte by = (byte) 4;\n" // + + " char c = 'c';\n" // + + " short s = (short) 1;\n" // + + " int i = 1;\n" // + + "\n" // + + " // Keep this comment\n" // + + " switch (new Byte(by)) {\n" // + + " // Keep this comment too\n" // + + " default:\n" // + + " }\n" // + + " switch (new Character(c)) {\n" // + + " default:\n" // + + " }\n" // + + " switch (new Short(s)) {\n" // + + " default:\n" // + + " }\n" // + + " switch (new Integer(i)) {\n" // + + " default:\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public static String removeUnnecessaryConstructorInvocationsInArrayAccess(String[] strings, int i) {\n" // + + " return strings[new Integer(i)];\n" // + + " }\n" // + + "}\n"; + + String expected= "" // + + "package test1;\n" // + + "\n" // + + "public class E {\n" // + + " public static void replaceWrapperConstructorsWithValueOf() {\n" // + + " // Replace all calls to wrapper constructors with calls to .valueOf() methods\n" // + + " byte byPrimitive = 4;\n" // + + " boolean boPrimitive = true;\n" // + + " char cPrimitive = 'c';\n" // + + " double dPrimitive = 1;\n" // + + " Double dObject = Double.valueOf(1d);\n" // + + " float fPrimitive = 1f;\n" // + + " long lPrimitive = 1;\n" // + + " short shPrimitive = 1;\n" // + + " int iPrimitive = 1;\n" // + + "\n" // + + " // Primitive literals\n" // + + " Byte by = Byte.valueOf((byte) 4);\n" // + + " Boolean bo = Boolean.valueOf(true);\n" // + + " Character c = Character.valueOf('c');\n" // + + " Double d = Double.valueOf(1);\n" // + + " Float f1 = Float.valueOf(1f);\n" // + + " Float f2 = Float.valueOf((float) 1d);\n" // + + " Long l = Long.valueOf(1);\n" // + + " Short s = Short.valueOf((short) 1);\n" // + + " Integer i = Integer.valueOf(1);\n" // + + "\n" // + + " // Primitive variables\n" // + + " by = Byte.valueOf(byPrimitive);\n" // + + " bo = Boolean.valueOf(boPrimitive);\n" // + + " c = Character.valueOf(cPrimitive);\n" // + + " d = Double.valueOf(dPrimitive);\n" // + + " f1 = Float.valueOf(fPrimitive);\n" // + + " f2 = Float.valueOf((float) dPrimitive);\n" // + + " l = Long.valueOf(lPrimitive);\n" // + + " s = Short.valueOf(shPrimitive);\n" // + + " i = Integer.valueOf(iPrimitive);\n" // + + "\n" // + + " // Implicit object narrowing\n" // + + " Float f3 = dObject.floatValue();\n" // + + " }\n" // + + "\n" // + + " public static void removeUnnecessaryObjectCreation() {\n" // + + " // Keep this comment\n" // + + " Byte.valueOf(\"0\").byteValue();\n" // + + " Boolean.valueOf(\"true\").booleanValue();\n" // + + " Integer.valueOf(\"42\").intValue();\n" // + + " Short.valueOf(\"42\").shortValue();\n" // + + " Long.valueOf(\"42\").longValue();\n" // + + " Float.valueOf(\"42.42\").floatValue();\n" // + + " Double.valueOf(\"42.42\").doubleValue();\n" // + + " }\n" // + + "\n" // + + " public static void removeUnnecessaryConstructorInvocationsInPrimitiveContext() {\n" // + + " // Keep this comment\n" // + + " byte by = (byte) 0;\n" // + + " boolean bo = true;\n" // + + " int i = 42;\n" // + + " long l = 42;\n" // + + " short s = (short) 42;\n" // + + " float f = 42.42F;\n" // + + " double d = 42.42;\n" // + + " }\n" // + + "\n" // + + " public static void removeUnnecessaryConstructorInvocationsInSwitch() {\n" // + + " byte by = (byte) 4;\n" // + + " char c = 'c';\n" // + + " short s = (short) 1;\n" // + + " int i = 1;\n" // + + "\n" // + + " // Keep this comment\n" // + + " switch (by) {\n" // + + " // Keep this comment too\n" // + + " default:\n" // + + " }\n" // + + " switch (c) {\n" // + + " default:\n" // + + " }\n" // + + " switch (s) {\n" // + + " default:\n" // + + " }\n" // + + " switch (i) {\n" // + + " default:\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public static String removeUnnecessaryConstructorInvocationsInArrayAccess(String[] strings, int i) {\n" // + + " return strings[i];\n" // + + " }\n" // + + "}\n"; + + // When + ICompilationUnit cu= pack.createCompilationUnit("E.java", given, false, null); + enable(CleanUpConstants.VALUEOF_RATHER_THAN_INSTANTIATION); + + // Then + assertNotEquals("The class must be changed", given, expected); + assertGroupCategoryUsed(new ICompilationUnit[] { cu },new HashSet<>(Arrays.asList( + MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_float_with_valueof, + MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_float_with_float_value, + MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_single_argument, + MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_valueof))); + assertRefactoringResultAsExpected(new ICompilationUnit[] { cu }, new String[] { expected }); + } + + @Test + public void testDoNotUseValueOfRatherThanInstantiation() throws Exception { + IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null); + String sample= "" // + + "package test1;\n" // + + "\n" // + + "import java.math.BigInteger;\n" // + + "\n" // + + "public class E {\n" // + + " public static void doNotRefactorBigInteger() {\n" // + + " BigInteger bi = new BigInteger(\"42\");\n" // + + " }\n" // + + "}\n"; + ICompilationUnit cu= pack.createCompilationUnit("E.java", sample, false, null); + + enable(CleanUpConstants.VALUEOF_RATHER_THAN_INSTANTIATION); + + assertRefactoringHasNoChange(new ICompilationUnit[] { cu }); + } + + @Test public void testPrimitiveComparison() throws Exception { // Given IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null); 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 70a8ccc10d..1f1edfe72d 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 @@ -81,6 +81,7 @@ public class CleanUpConstantsOptions extends CleanUpConstants { options.setOption(STRINGBUILDER, CleanUpOptions.FALSE); options.setOption(PLAIN_REPLACEMENT, CleanUpOptions.FALSE); options.setOption(USE_LAZY_LOGICAL_OPERATOR, CleanUpOptions.FALSE); + options.setOption(VALUEOF_RATHER_THAN_INSTANTIATION, CleanUpOptions.FALSE); options.setOption(PRIMITIVE_COMPARISON, CleanUpOptions.FALSE); options.setOption(PRIMITIVE_PARSING, CleanUpOptions.FALSE); options.setOption(PRIMITIVE_SERIALIZATION, CleanUpOptions.FALSE); @@ -241,6 +242,7 @@ public class CleanUpConstantsOptions extends CleanUpConstants { options.setOption(STRINGBUILDER, CleanUpOptions.FALSE); options.setOption(PLAIN_REPLACEMENT, CleanUpOptions.FALSE); options.setOption(USE_LAZY_LOGICAL_OPERATOR, CleanUpOptions.FALSE); + options.setOption(VALUEOF_RATHER_THAN_INSTANTIATION, CleanUpOptions.FALSE); options.setOption(PRIMITIVE_COMPARISON, CleanUpOptions.FALSE); options.setOption(PRIMITIVE_PARSING, CleanUpOptions.FALSE); options.setOption(PRIMITIVE_SERIALIZATION, CleanUpOptions.FALSE); diff --git a/org.eclipse.jdt.ui/plugin.xml b/org.eclipse.jdt.ui/plugin.xml index fd52ea3e2d..9d440bbe40 100644 --- a/org.eclipse.jdt.ui/plugin.xml +++ b/org.eclipse.jdt.ui/plugin.xml @@ -7153,9 +7153,14 @@ runAfter="org.eclipse.jdt.ui.cleanup.plain_replacement"> </cleanUp> <cleanUp + class="org.eclipse.jdt.internal.ui.fix.ValueOfRatherThanInstantiationCleanUp" + id="org.eclipse.jdt.ui.cleanup.valueof_rather_than_instantiation" + runAfter="org.eclipse.jdt.ui.cleanup.lazy_logical"> + </cleanUp> + <cleanUp class="org.eclipse.jdt.internal.ui.fix.PrimitiveComparisonCleanUp" id="org.eclipse.jdt.ui.cleanup.primitive_comparison" - runAfter="org.eclipse.jdt.ui.cleanup.lazy_logical"> + runAfter="org.eclipse.jdt.ui.cleanup.valueof_rather_than_instantiation"> </cleanUp> <cleanUp class="org.eclipse.jdt.internal.ui.fix.PrimitiveParsingCleanUp" diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/ValueOfRatherThanInstantiationCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/ValueOfRatherThanInstantiationCleanUp.java new file mode 100644 index 0000000000..d37e451635 --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/ValueOfRatherThanInstantiationCleanUp.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2021 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.Map; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jdt.core.manipulation.ICleanUpFixCore; + +import org.eclipse.jdt.ui.cleanup.CleanUpContext; +import org.eclipse.jdt.ui.cleanup.CleanUpOptions; +import org.eclipse.jdt.ui.cleanup.CleanUpRequirements; +import org.eclipse.jdt.ui.cleanup.ICleanUpFix; + +/** + * A fix that replaces unnecessary primitive wrappers instance creations by using static factory <code>valueOf()</code> method: + * <ul> + * <li>It dramatically improves the space performance.</li> + * </ul> + */ +public class ValueOfRatherThanInstantiationCleanUp extends AbstractCleanUp { + private ValueOfRatherThanInstantiationCleanUpCore coreCleanUp= new ValueOfRatherThanInstantiationCleanUpCore(); + + public ValueOfRatherThanInstantiationCleanUp(final Map<String, String> options) { + setOptions(options); + } + + public ValueOfRatherThanInstantiationCleanUp() { + } + + @Override + public void setOptions(final CleanUpOptions options) { + coreCleanUp.setOptions(options); + } + + @Override + public CleanUpRequirements getRequirements() { + return new CleanUpRequirements(coreCleanUp.getRequirementsCore()); + } + + @Override + public ICleanUpFix createFix(final CleanUpContext context) throws CoreException { + ICleanUpFixCore fixCore= coreCleanUp.createFixCore(context); + return fixCore != null ? new CleanUpFixWrapper(fixCore) : null; + } + + @Override + public String[] getStepDescriptions() { + return coreCleanUp.getStepDescriptions(); + } + + @Override + public String getPreview() { + return coreCleanUp.getPreview(); + } +} 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 f82aa997d7..f68501b5ed 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 @@ -88,6 +88,7 @@ public class CleanUpMessages extends NLS { public static String OptimizationTabPage_CheckboxName_StringBuilder; public static String OptimizationTabPage_CheckboxName_PlainReplacement; public static String OptimizationTabPage_CheckboxName_UseLazyLogicalOperator; + public static String OptimizationTabPage_CheckboxName_ValueOfRatherThanInstantiation; public static String OptimizationTabPage_CheckboxName_PrimitiveComparison; public static String OptimizationTabPage_CheckboxName_PrimitiveParsing; public static String OptimizationTabPage_CheckboxName_PrimitiveSerialization; 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 069a7f493d..86a95b80a1 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 @@ -67,6 +67,7 @@ OptimizationTabPage_CheckboxName_StaticInnerClass=Make inner classes static wher OptimizationTabPage_CheckboxName_StringBuilder=Replace &String concatenation by StringBuilder OptimizationTabPage_CheckboxName_PlainReplacement=Use String.replace() instead of String.replaceAll() when possible OptimizationTabPage_CheckboxName_UseLazyLogicalOperator=Use la&zy logical operator +OptimizationTabPage_CheckboxName_ValueOfRatherThanInstantiation=valueOf() rather than instantiation OptimizationTabPage_CheckboxName_PrimitiveComparison=Primitive comparison OptimizationTabPage_CheckboxName_PrimitiveParsing=Primitive parsing OptimizationTabPage_CheckboxName_PrimitiveSerialization=&Primitive serialization diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/OptimizationTabPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/OptimizationTabPage.java index 0abce493ad..a33122f1e1 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/OptimizationTabPage.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/OptimizationTabPage.java @@ -33,6 +33,7 @@ import org.eclipse.jdt.internal.ui.fix.PrimitiveSerializationCleanUp; import org.eclipse.jdt.internal.ui.fix.SingleUsedFieldCleanUp; import org.eclipse.jdt.internal.ui.fix.StaticInnerClassCleanUp; import org.eclipse.jdt.internal.ui.fix.StringBuilderCleanUp; +import org.eclipse.jdt.internal.ui.fix.ValueOfRatherThanInstantiationCleanUp; public final class OptimizationTabPage extends AbstractCleanUpTabPage { public static final String ID= "org.eclipse.jdt.ui.cleanup.tabpage.optimization"; //$NON-NLS-1$ @@ -46,6 +47,7 @@ public final class OptimizationTabPage extends AbstractCleanUpTabPage { new StringBuilderCleanUp(values), new PlainReplacementCleanUp(values), new LazyLogicalCleanUp(values), + new ValueOfRatherThanInstantiationCleanUp(values), new PrimitiveComparisonCleanUp(values), new PrimitiveParsingCleanUp(values), new PrimitiveSerializationCleanUp(values), @@ -78,6 +80,9 @@ public final class OptimizationTabPage extends AbstractCleanUpTabPage { CleanUpConstants.USE_LAZY_LOGICAL_OPERATOR, CleanUpModifyDialog.FALSE_TRUE); registerPreference(useLazyLogicalPref); + final CheckboxPreference valueOfRatherThanInstantiationPref= createCheckboxPref(optimizationGroup, numColumns, CleanUpMessages.OptimizationTabPage_CheckboxName_ValueOfRatherThanInstantiation, CleanUpConstants.VALUEOF_RATHER_THAN_INSTANTIATION, CleanUpModifyDialog.FALSE_TRUE); + registerPreference(valueOfRatherThanInstantiationPref); + final CheckboxPreference primitiveComparisonPref= createCheckboxPref(optimizationGroup, numColumns, CleanUpMessages.OptimizationTabPage_CheckboxName_PrimitiveComparison, CleanUpConstants.PRIMITIVE_COMPARISON, CleanUpModifyDialog.FALSE_TRUE); registerPreference(primitiveComparisonPref); |
