Bug 568327 - [AutoRefactor immigration #39/142] [cleanup & saveaction]
Primitive serialization

Change-Id: I61f2809f32d0f6fb2a7076d7eeb9bd95113cfe05
Signed-off-by: Fabrice Tiercelin <fabrice.tiercelin@yahoo.fr>
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 038cb89..e38c1b7 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
@@ -72,7 +72,6 @@
 	public static String CodeStyleCleanUp_numberSuffix_description;
 	public static String CodeStyleCleanUp_QualifyNonStaticMethod_description;
 	public static String CodeStyleCleanUp_QualifyStaticMethod_description;
-	public static String CodeStyleCleanUp_LazyLogical_description;
 	public static String CodeStyleCleanUp_removeFieldThis_description;
 	public static String CodeStyleCleanUp_removeMethodThis_description;
 
@@ -121,6 +120,9 @@
 	public static String NullAnnotationsCleanUp_add_nonnullbydefault_annotation;
 	public static String NullAnnotationsCleanUp_remove_redundant_nullness_annotation;
 
+	public static String CodeStyleCleanUp_LazyLogical_description;
+	public static String PrimitiveSerializationCleanUp_description;
+
 	public static String TypeParametersCleanUp_InsertInferredTypeArguments_description;
 	public static String TypeParametersCleanUp_RemoveUnnecessaryTypeArguments_description;
 	public static String HashCleanup_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 637268a..7ee35d3 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
@@ -43,7 +43,6 @@
 CodeFormatCleanUp_correctIndentation_description=Correct indentation
 CodeFormatFix_correctIndentation_changeGroupLabel=Correct indentation
 CodeStyleMultiFix_QualifyAccessToStaticField=Qualify accesses to static fields with declaring class
-CodeStyleCleanUp_LazyLogical_description= Use lazy logical operator (&& and ||)
 CodeStyleCleanUp_removeFieldThis_description=Remove 'this' qualifier for non static field accesses
 CodeStyleCleanUp_removeMethodThis_description=Remove 'this' qualifier for non static method accesses
 CodeStyleMultiFix_ConvertSingleStatementInControlBodyToBlock_description=Convert control statement bodies to block
@@ -102,6 +101,10 @@
 NullAnnotationsCleanUp_add_nonnull_annotation=Add missing @NonNull annotation
 NullAnnotationsCleanUp_add_nonnullbydefault_annotation=Add missing @NonNullByDefault annotation
 NullAnnotationsCleanUp_remove_redundant_nullness_annotation=Remove redundant nullness annotation
+
+CodeStyleCleanUp_LazyLogical_description= Use lazy logical operator (&& and ||)
+PrimitiveSerializationCleanUp_description=Primitive serialization
+
 TypeParametersCleanUp_InsertInferredTypeArguments_description=Insert inferred type arguments
 TypeParametersCleanUp_RemoveUnnecessaryTypeArguments_description=Remove redundant type arguments
 HashCleanup_description=Use Objects.hash()
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 19e8600..fd8374f 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
@@ -736,6 +736,18 @@
 	public static final String USE_LAZY_LOGICAL_OPERATOR= "cleanup.lazy_logical_operator"; //$NON-NLS-1$
 
 	/**
+	 * Replaces a primitive boxing to serialize by a call to the static <code>toString()</code> method.
+	 * <p>
+	 * Possible values: {TRUE, FALSE}
+	 * <p>
+	 *
+	 * @see CleanUpOptionsCore#TRUE
+	 * @see CleanUpOptionsCore#FALSE
+	 * @since 4.18
+	 */
+	public static final String PRIMITIVE_SERIALIZATION= "cleanup.primitive_serialization"; //$NON-NLS-1$
+
+	/**
 	 * Controls the usage of 'final' modifier for variable declarations. For detailed settings
 	 * use:<br>
 	 * {@link #VARIABLE_DECLARATIONS_USE_FINAL_LOCAL_VARIABLES}<br>
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 67bacae..3c0f6ba 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
@@ -18,10 +18,13 @@
 package org.eclipse.jdt.ui.tests.quickfix;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Map;
 
@@ -5040,6 +5043,153 @@
 	}
 
 	@Test
+	public void testPrimitiveSerialization() throws Exception {
+		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
+		String input= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public String simplifyIntegerSerialization(int number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Integer.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyDoubleSerialization(double number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Double.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyFloatSerialization(float number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Float.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyShortSerialization(short number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Short.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyLongSerialization(long number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Long.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyCharacterSerialization(char number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Character.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyByteSerialization(byte number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Byte.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyBooleanSerialization(boolean number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Boolean.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String refactorIntegerInstantiation(int number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return new Integer(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String refactorIntegerCast(int number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return ((Integer) number).toString();\n" //
+				+ "    }\n" //
+				+ "}\n";
+		ICompilationUnit cu= pack.createCompilationUnit("E.java", input, false, null);
+
+		enable(CleanUpConstants.PRIMITIVE_SERIALIZATION);
+
+		String output= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public String simplifyIntegerSerialization(int number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Integer.toString(number);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyDoubleSerialization(double number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Double.toString(number);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyFloatSerialization(float number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Float.toString(number);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyShortSerialization(short number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Short.toString(number);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyLongSerialization(long number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Long.toString(number);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyCharacterSerialization(char number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Character.toString(number);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyByteSerialization(byte number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Byte.toString(number);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String simplifyBooleanSerialization(boolean number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Boolean.toString(number);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String refactorIntegerInstantiation(int number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Integer.toString(number);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String refactorIntegerCast(int number) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Integer.toString(number);\n" //
+				+ "    }\n" //
+				+ "}\n";
+
+		assertNotEquals("The class must be changed", input, output);
+		assertGroupCategoryUsed(new ICompilationUnit[] { cu }, new HashSet<>(Arrays.asList(MultiFixMessages.PrimitiveSerializationCleanUp_description)));
+		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu }, new String[] { output });
+	}
+
+	@Test
+	public void testDoNotUsePrimitiveSerialization() throws Exception {
+		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
+		String sample= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public String doNotRefactorWrapper(Integer number) {\n" //
+				+ "        return Integer.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String doNotRefactorString(String number) {\n" //
+				+ "        return Integer.valueOf(number).toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String doNotRefactorBadMethod(int number) {\n" //
+				+ "        return Integer.valueOf(number).toBinaryString(0);\n" //
+				+ "    }\n" //
+				+ "}\n";
+		ICompilationUnit cu= pack.createCompilationUnit("E.java", sample, false, null);
+
+		enable(CleanUpConstants.PRIMITIVE_SERIALIZATION);
+
+		assertRefactoringHasNoChange(new ICompilationUnit[] { cu });
+	}
+
+	@Test
 	public void testPushDownNegationReplaceDoubleNegation() 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 6d37231..2b1d4c5 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
@@ -59,7 +59,6 @@
 		options.setOption(EXPRESSIONS_USE_PARENTHESES, CleanUpOptions.FALSE);
 		options.setOption(EXPRESSIONS_USE_PARENTHESES_NEVER, CleanUpOptions.TRUE);
 		options.setOption(EXPRESSIONS_USE_PARENTHESES_ALWAYS, CleanUpOptions.FALSE);
-		options.setOption(USE_LAZY_LOGICAL_OPERATOR, CleanUpOptions.FALSE);
 		options.setOption(CHECK_SIGN_OF_BITWISE_OPERATION, CleanUpOptions.FALSE);
 		options.setOption(ATOMIC_OBJECT, CleanUpOptions.FALSE);
 
@@ -81,6 +80,10 @@
 		options.setOption(NO_STRING_CREATION, CleanUpOptions.FALSE);
 		options.setOption(PREFER_BOOLEAN_LITERAL, CleanUpOptions.FALSE);
 
+		// Optimization
+		options.setOption(USE_LAZY_LOGICAL_OPERATOR, CleanUpOptions.FALSE);
+		options.setOption(PRIMITIVE_SERIALIZATION, CleanUpOptions.FALSE);
+
 		//Unused Code
 		options.setOption(REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.TRUE);
 		options.setOption(REMOVE_UNUSED_CODE_PRIVATE_MEMBERS, CleanUpOptions.FALSE);
@@ -183,7 +186,6 @@
 		options.setOption(EXPRESSIONS_USE_PARENTHESES, CleanUpOptions.FALSE);
 		options.setOption(EXPRESSIONS_USE_PARENTHESES_NEVER, CleanUpOptions.TRUE);
 		options.setOption(EXPRESSIONS_USE_PARENTHESES_ALWAYS, CleanUpOptions.FALSE);
-		options.setOption(USE_LAZY_LOGICAL_OPERATOR, CleanUpOptions.FALSE);
 		options.setOption(CHECK_SIGN_OF_BITWISE_OPERATION, CleanUpOptions.FALSE);
 		options.setOption(ATOMIC_OBJECT, CleanUpOptions.FALSE);
 
@@ -205,6 +207,10 @@
 		options.setOption(NO_STRING_CREATION, CleanUpOptions.FALSE);
 		options.setOption(PREFER_BOOLEAN_LITERAL, CleanUpOptions.FALSE);
 
+		// Optimization
+		options.setOption(USE_LAZY_LOGICAL_OPERATOR, CleanUpOptions.FALSE);
+		options.setOption(PRIMITIVE_SERIALIZATION, CleanUpOptions.FALSE);
+
 		//Unused Code
 		options.setOption(REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.FALSE);
 		options.setOption(REMOVE_UNUSED_CODE_PRIVATE_MEMBERS, CleanUpOptions.FALSE);
diff --git a/org.eclipse.jdt.ui/plugin.xml b/org.eclipse.jdt.ui/plugin.xml
index 0686e42..c8cf88e 100644
--- a/org.eclipse.jdt.ui/plugin.xml
+++ b/org.eclipse.jdt.ui/plugin.xml
@@ -7116,9 +7116,14 @@
             runAfter="org.eclipse.jdt.ui.cleanup.number_suffix">
       </cleanUp>
       <cleanUp
+            class="org.eclipse.jdt.internal.ui.fix.PrimitiveSerializationCleanUp"
+            id="org.eclipse.jdt.ui.cleanup.primitive_serialization"
+            runAfter="org.eclipse.jdt.ui.cleanup.lazy_logical">
+      </cleanUp>
+      <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.PatternCleanUp"
             id="org.eclipse.jdt.ui.cleanup.precompile_regex"
-            runAfter="org.eclipse.jdt.ui.cleanup.lazy_logical">
+            runAfter="org.eclipse.jdt.ui.cleanup.primitive_serialization">
       </cleanUp>
       <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.NoStringCreationCleanUp"
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/PrimitiveSerializationCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/PrimitiveSerializationCleanUp.java
new file mode 100644
index 0000000..cb7a9f9
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/PrimitiveSerializationCleanUp.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * 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.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+
+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.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.MethodInvocation;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
+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.ui.cleanup.CleanUpRequirements;
+import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+
+/**
+ * A fix that replaces a primitive boxing to serialize by a call to the static <code>toString()</code> method.
+ *
+ * The conditions of triggering are:
+ * <ul>
+ *  <li>The structure of the code (a <code>toString()</code> call on a boxing)</li>
+ *  <li>The boxing can be a <code>valueOf()</code>, an instantiation or a cast,</li>
+ *  <li>The boxing should not be on a wrapper.</li>
+ * </ul>
+ */
+public class PrimitiveSerializationCleanUp extends AbstractMultiFix {
+	private static final Class<?>[] WRAPPER_CLASSES= { Integer.class, Boolean.class, Long.class, Double.class, Character.class, Float.class, Short.class, Byte.class };
+
+	public PrimitiveSerializationCleanUp() {
+		this(Collections.emptyMap());
+	}
+
+	public PrimitiveSerializationCleanUp(final Map<String, String> options) {
+		super(options);
+	}
+
+	@Override
+	public CleanUpRequirements getRequirements() {
+		boolean requireAST= isEnabled(CleanUpConstants.PRIMITIVE_SERIALIZATION);
+		return new CleanUpRequirements(requireAST, false, false, null);
+	}
+
+	@Override
+	public String[] getStepDescriptions() {
+		if (isEnabled(CleanUpConstants.PRIMITIVE_SERIALIZATION)) {
+			return new String[] { MultiFixMessages.PrimitiveSerializationCleanUp_description };
+		}
+
+		return new String[0];
+	}
+
+	@Override
+	public String getPreview() {
+		if (isEnabled(CleanUpConstants.PRIMITIVE_SERIALIZATION)) {
+			return "" //$NON-NLS-1$
+					+ "String text1 = Integer.toString(number);\n" //$NON-NLS-1$
+					+ "String text2 = Character.toString(letter);\n"; //$NON-NLS-1$
+		}
+
+		return "" //$NON-NLS-1$
+				+ "String text1 = Integer.valueOf(number).toString();\n" //$NON-NLS-1$
+				+ "String text2 = Character.valueOf(letter).toString();\n"; //$NON-NLS-1$
+	}
+
+	@Override
+	protected ICleanUpFix createFix(final CompilationUnit unit) throws CoreException {
+		if (!isEnabled(CleanUpConstants.PRIMITIVE_SERIALIZATION)) {
+			return null;
+		}
+
+		final List<CompilationUnitRewriteOperation> rewriteOperations= new ArrayList<>();
+
+		unit.accept(new ASTVisitor() {
+			@Override
+			public boolean visit(final MethodInvocation visited) {
+				if (visited.getExpression() != null
+						&& visited.arguments().isEmpty()) {
+					for (Class<?> wrapperClass : WRAPPER_CLASSES) {
+						String canonicalName= wrapperClass.getCanonicalName();
+
+						if (ASTNodes.usesGivenSignature(visited, canonicalName, "toString")) { //$NON-NLS-1$
+							String primitiveType= Bindings.getUnboxedTypeName(canonicalName);
+							MethodInvocation methodInvocation = ASTNodes.as(visited.getExpression(), MethodInvocation.class);
+
+							if (methodInvocation != null
+									&& ASTNodes.usesGivenSignature(methodInvocation, canonicalName, "valueOf", primitiveType)) { //$NON-NLS-1$
+								return maybeRefactor(visited, wrapperClass, primitiveType, (Expression) methodInvocation.arguments().get(0));
+							}
+
+							ClassInstanceCreation classInstanceCreation = ASTNodes.as(visited.getExpression(), ClassInstanceCreation.class);
+
+							if (classInstanceCreation != null
+									&& ASTNodes.hasType(classInstanceCreation.getType().resolveBinding(), canonicalName)) {
+								return maybeRefactor(visited, wrapperClass, primitiveType, (Expression) classInstanceCreation.arguments().get(0));
+							}
+
+							CastExpression castExpression = ASTNodes.as(visited.getExpression(), CastExpression.class);
+
+							if (castExpression != null
+									&& ASTNodes.hasType(castExpression.getType().resolveBinding(), canonicalName)) {
+								return maybeRefactor(visited, wrapperClass, primitiveType, castExpression.getExpression());
+							}
+
+							return true;
+						}
+					}
+				}
+
+				return true;
+			}
+
+			private boolean maybeRefactor(final MethodInvocation visited, final Class<?> wrapperClass, final String primitiveType, final Expression primitiveValue) {
+				if (ASTNodes.isPrimitive(primitiveValue, primitiveType)) {
+					rewriteOperations.add(new PrimitiveSerializationOperation(visited, primitiveValue, wrapperClass));
+					return false;
+				}
+
+				return true;
+			}
+		});
+
+		if (rewriteOperations.isEmpty()) {
+			return null;
+		}
+
+		return new CompilationUnitRewriteOperationsFix(MultiFixMessages.PrimitiveSerializationCleanUp_description, unit,
+				rewriteOperations.toArray(new CompilationUnitRewriteOperation[0]));
+	}
+
+	@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 PrimitiveSerializationOperation extends CompilationUnitRewriteOperation {
+		private final MethodInvocation visited;
+		private final Expression primitiveValue;
+		private final Class<?> wrapperClass;
+
+		public PrimitiveSerializationOperation(final MethodInvocation visited, final Expression primitiveValue, final Class<?> wrapperClass) {
+			this.visited= visited;
+			this.primitiveValue= primitiveValue;
+			this.wrapperClass= wrapperClass;
+		}
+
+		@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.PrimitiveSerializationCleanUp_description, cuRewrite);
+
+			MethodInvocation newMethodInvocation= ast.newMethodInvocation();
+			newMethodInvocation.setExpression(ast.newSimpleName(wrapperClass.getSimpleName()));
+			newMethodInvocation.setName(ast.newSimpleName("toString")); //$NON-NLS-1$
+			newMethodInvocation.arguments().add(ASTNodes.createMoveTarget(rewrite, ASTNodes.getUnparenthesedExpression(primitiveValue)));
+			ASTNodes.replaceButKeepComment(rewrite, visited, newMethodInvocation, 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 20c4e01..e75b07d 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
@@ -87,6 +87,7 @@
 
 	public static String OptimizationTabPage_GroupName_Optimization;
 
+	public static String OptimizationTabPage_CheckboxName_PrimitiveSerialization;
 	public static String OptimizationTabPage_CheckboxName_UseLazyLogicalOperator;
 	public static String OptimizationTabPage_CheckboxName_PrecompileRegEx;
 	public static String OptimizationTabPage_CheckboxName_NoStringCreation;
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 70ac06d..50b2708 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
@@ -66,6 +66,7 @@
 
 OptimizationTabPage_GroupName_Optimization=Optimization
 OptimizationTabPage_CheckboxName_UseLazyLogicalOperator=Use la&zy logical operator
+OptimizationTabPage_CheckboxName_PrimitiveSerialization=&Primitive serialization
 OptimizationTabPage_CheckboxName_PrecompileRegEx=Precompile reused regular e&xpressions
 OptimizationTabPage_CheckboxName_NoStringCreation=Remo&ve redundant string creation
 OptimizationTabPage_CheckboxName_BooleanLiteral=Prefer &boolean literals
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 f8478f6..01de826 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
@@ -25,6 +25,7 @@
 import org.eclipse.jdt.internal.ui.fix.LazyLogicalCleanUp;
 import org.eclipse.jdt.internal.ui.fix.NoStringCreationCleanUp;
 import org.eclipse.jdt.internal.ui.fix.PatternCleanUp;
+import org.eclipse.jdt.internal.ui.fix.PrimitiveSerializationCleanUp;
 
 public final class OptimizationTabPage extends AbstractCleanUpTabPage {
 	public static final String ID= "org.eclipse.jdt.ui.cleanup.tabpage.optimization"; //$NON-NLS-1$
@@ -33,6 +34,7 @@
 	protected AbstractCleanUp[] createPreviewCleanUps(Map<String, String> values) {
 		return new AbstractCleanUp[] {
 				new LazyLogicalCleanUp(values),
+				new PrimitiveSerializationCleanUp(values),
 				new PatternCleanUp(values),
 				new NoStringCreationCleanUp(values),
 				new BooleanLiteralCleanUp(values)
@@ -47,6 +49,9 @@
 				CleanUpConstants.USE_LAZY_LOGICAL_OPERATOR, CleanUpModifyDialog.FALSE_TRUE);
 		registerPreference(useLazyLogicalPref);
 
+		final CheckboxPreference primitiveSerializationPref= createCheckboxPref(optimizationGroup, numColumns, CleanUpMessages.OptimizationTabPage_CheckboxName_PrimitiveSerialization, CleanUpConstants.PRIMITIVE_SERIALIZATION, CleanUpModifyDialog.FALSE_TRUE);
+		registerPreference(primitiveSerializationPref);
+
 		final CheckboxPreference precompileRegExPref= createCheckboxPref(optimizationGroup, numColumns, CleanUpMessages.OptimizationTabPage_CheckboxName_PrecompileRegEx, CleanUpConstants.PRECOMPILE_REGEX,
 				CleanUpModifyDialog.FALSE_TRUE);
 		registerPreference(precompileRegExPref);