Bug 567560 - [AutoRefactor immigration #28/137] [cleanup & saveaction]
Prefer boolean literal

Change-Id: Ibb915658c978eb35d53ba07754bd501938245b16
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 cbcca24..830c360 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
@@ -113,6 +113,7 @@
 	public static String LambdaExpressionAndMethodRefCleanUp_description;
 	public static String PatternCleanup_description;
 	public static String NoStringCreationCleanUp_description;
+	public static String BooleanLiteralCleanup_description;
 
 	public static String NullAnnotationsCleanUp_add_nullable_annotation;
 	public static String NullAnnotationsCleanUp_add_nonnull_annotation;
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 3cde74b..20e31df 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
@@ -91,6 +91,8 @@
 LambdaExpressionAndMethodRefCleanUp_description=Simplify lambda expression and method reference syntax
 PatternCleanup_description=Precompile reused regular expressions
 NoStringCreationCleanUp_description=Remove unnecessary String creation
+BooleanLiteralCleanup_description=Use boolean literals instead of Boolean.TRUE/FALSE when used as a primitive
+
 SortMembersCleanUp_AllMembers_description=Sort all members
 SortMembersCleanUp_Excluding_description=Sort members excluding fields, enum constants, and initializers
 SortMembersCleanUp_RemoveMarkersWarning0=The file ''{0}'' in project ''{1}'' contains markers which may be removed by Sort Members
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 c5f0a8f..03064e4 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
@@ -2857,20 +2857,35 @@
 		}
 	}
 
-    /**
-     * Returns whether the provided method invocation invokes a method with the
-     * provided method signature. The method signature is compared against the
-     * erasure of the invoked method.
-     *
-     * @param node                         the method invocation to compare
-     * @param typeQualifiedName            the qualified name of the type declaring
-     *                                     the method
-     * @param methodName                   the method name
-     * @param parameterTypesQualifiedNames the qualified names of the parameter
-     *                                     types
-     * @return true if the provided method invocation matches the provided method
-     *         signature, false otherwise
-     */
+	/**
+	 * Returns whether the provided qualified name accesses a field with the
+	 * provided signature.
+	 *
+	 * @param node              the qualified name to compare
+	 * @param qualifiedTypeName the qualified name of the type declaring the field
+	 * @param fieldNames        the field names
+	 * @return true if the provided qualified name matches the provided field
+	 *         signature, false otherwise
+	 */
+	public static boolean isField(final QualifiedName node, final String qualifiedTypeName, final String... fieldNames) {
+		return instanceOf(node, qualifiedTypeName)
+				&& Arrays.asList(fieldNames).contains(node.getName().getIdentifier());
+	}
+
+	/**
+	 * Returns whether the provided method invocation invokes a method with the
+	 * provided method signature. The method signature is compared against the
+	 * erasure of the invoked method.
+	 *
+	 * @param node                         the method invocation to compare
+	 * @param typeQualifiedName            the qualified name of the type declaring
+	 *                                     the method
+	 * @param methodName                   the method name
+	 * @param parameterTypesQualifiedNames the qualified names of the parameter
+	 *                                     types
+	 * @return true if the provided method invocation matches the provided method
+	 *         signature, false otherwise
+	 */
 	public static boolean usesGivenSignature(final MethodInvocation node, final String typeQualifiedName, final String methodName,
 			final String... parameterTypesQualifiedNames) {
 		return node != null
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 6828a63..5bf6753 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
@@ -805,6 +805,18 @@
 	public static final String NO_STRING_CREATION= "cleanup.no_string_creation"; //$NON-NLS-1$
 
 	/**
+	 * Replaces Boolean.TRUE/Boolean.FALSE by true/false when used as primitive.
+	 * <p>
+	 * Possible values: {TRUE, FALSE}
+	 * <p>
+	 *
+	 * @see CleanUpOptionsCore#TRUE
+	 * @see CleanUpOptionsCore#FALSE
+	 * @since 4.18
+	 */
+	public static final String PREFER_BOOLEAN_LITERAL= "cleanup.boolean_literal"; //$NON-NLS-1$
+
+	/**
 	 * Adds type parameters to raw type references.
 	 * <p>
 	 * Example:
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d5.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d5.java
index 55ebd4f..35d7724 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d5.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest1d5.java
@@ -2806,6 +2806,67 @@
 	}
 
 	@Test
+	public void testBooleanLiteral() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+		String input= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public static boolean replaceUselessUnboxing() {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        boolean bo1 = Boolean.TRUE;\n" //
+				+ "        boolean bo2 = Boolean.FALSE;\n" //
+				+ "        bo1 = Boolean.TRUE;\n" //
+				+ "        if (Boolean.TRUE) {\n" //
+				+ "            bo2 = Boolean.FALSE;\n" //
+				+ "        }\n" //
+				+ "        return bo1 && bo2;\n" //
+				+ "    }\n" //
+				+ "}\n";
+		ICompilationUnit cu= pack1.createCompilationUnit("E.java", input, false, null);
+
+		enable(CleanUpConstants.PREFER_BOOLEAN_LITERAL);
+
+		String output= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public static boolean replaceUselessUnboxing() {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        boolean bo1 = true;\n" //
+				+ "        boolean bo2 = false;\n" //
+				+ "        bo1 = true;\n" //
+				+ "        if (true) {\n" //
+				+ "            bo2 = false;\n" //
+				+ "        }\n" //
+				+ "        return bo1 && bo2;\n" //
+				+ "    }\n" //
+				+ "}\n";
+		assertGroupCategoryUsed(new ICompilationUnit[] { cu }, new String[] { MultiFixMessages.BooleanLiteralCleanup_description });
+		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu }, new String[] { output });
+	}
+
+	@Test
+	public void testDoNotUseBooleanLiteral() throws Exception {
+		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
+		String sample= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public static boolean doNotCreateUselessAutoboxing() {\n" //
+				+ "        Boolean bo = Boolean.TRUE;\n" //
+				+ "        bo = Boolean.FALSE;\n" //
+				+ "        return bo;\n" //
+				+ "    }\n" //
+				+ "}\n";
+		ICompilationUnit cu1= pack.createCompilationUnit("E.java", sample, false, null);
+
+		enable(CleanUpConstants.PREFER_BOOLEAN_LITERAL);
+
+		assertRefactoringHasNoChange(new ICompilationUnit[] { cu1 });
+	}
+
+	@Test
 	public void testUnnecessaryArrayBug550129() throws Exception {
 		IPackageFragment pack1= fSourceFolder.createPackageFragment("test", 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 637204b..8836e43 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
@@ -78,6 +78,7 @@
 		options.setOption(SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF, CleanUpOptions.FALSE);
 		options.setOption(PRECOMPILE_REGEX, CleanUpOptions.FALSE);
 		options.setOption(NO_STRING_CREATION, CleanUpOptions.FALSE);
+		options.setOption(PREFER_BOOLEAN_LITERAL, CleanUpOptions.FALSE);
 
 		//Unused Code
 		options.setOption(REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.TRUE);
@@ -194,6 +195,7 @@
 		options.setOption(SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF, CleanUpOptions.FALSE);
 		options.setOption(PRECOMPILE_REGEX, CleanUpOptions.FALSE);
 		options.setOption(NO_STRING_CREATION, CleanUpOptions.FALSE);
+		options.setOption(PREFER_BOOLEAN_LITERAL, CleanUpOptions.FALSE);
 
 		//Unused Code
 		options.setOption(REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.FALSE);
diff --git a/org.eclipse.jdt.ui/plugin.xml b/org.eclipse.jdt.ui/plugin.xml
index fd0c8e6..4ce52a2 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.precompile_regex">
       </cleanUp>
       <cleanUp
+            class="org.eclipse.jdt.internal.ui.fix.BooleanLiteralCleanUp"
+            id="org.eclipse.jdt.ui.cleanup.boolean_literal"
+            runAfter="org.eclipse.jdt.ui.cleanup.no_string_creation">
+      </cleanUp>
+      <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp"
             id="org.eclipse.jdt.ui.cleanup.unused_code"
-            runAfter="org.eclipse.jdt.ui.cleanup.no_string_creation">
+            runAfter="org.eclipse.jdt.ui.cleanup.boolean_literal">
       </cleanUp>
       <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.Java50CleanUp"
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/BooleanLiteralCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/BooleanLiteralCleanUp.java
new file mode 100644
index 0000000..7bd17e9
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/BooleanLiteralCleanUp.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * 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.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.BooleanLiteral;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
+
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.fix.CleanUpConstants;
+import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix;
+import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix.CompilationUnitRewriteOperation;
+import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+
+import org.eclipse.jdt.ui.cleanup.CleanUpRequirements;
+import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+
+/**
+ * A fix that replaces Boolean.TRUE/Boolean.FALSE by true/false when used as primitive.
+ */
+public class BooleanLiteralCleanUp extends AbstractMultiFix implements ICleanUpFix {
+	public BooleanLiteralCleanUp() {
+		this(Collections.emptyMap());
+	}
+
+	public BooleanLiteralCleanUp(Map<String, String> options) {
+		super(options);
+	}
+
+	@Override
+	public CleanUpRequirements getRequirements() {
+		boolean requireAST= isEnabled(CleanUpConstants.PREFER_BOOLEAN_LITERAL);
+		return new CleanUpRequirements(requireAST, false, false, null);
+	}
+
+	@Override
+	public String[] getStepDescriptions() {
+		if (isEnabled(CleanUpConstants.PREFER_BOOLEAN_LITERAL)) {
+			return new String[] { MultiFixMessages.BooleanLiteralCleanup_description };
+		}
+
+		return new String[0];
+	}
+
+	@Override
+	public String getPreview() {
+		if (isEnabled(CleanUpConstants.PREFER_BOOLEAN_LITERAL)) {
+			return "boolean isActive = true;\n"; //$NON-NLS-1$
+		}
+
+		return "boolean isActive = Boolean.TRUE;\n"; //$NON-NLS-1$
+	}
+
+	@Override
+	protected ICleanUpFix createFix(CompilationUnit unit) throws CoreException {
+		if (!isEnabled(CleanUpConstants.PREFER_BOOLEAN_LITERAL) || !JavaModelUtil.is50OrHigher(unit.getJavaElement().getJavaProject())) {
+			return null;
+		}
+
+		final List<CompilationUnitRewriteOperation> rewriteOperations= new ArrayList<>();
+
+		unit.accept(new ASTVisitor() {
+			@Override
+			public boolean visit(final QualifiedName node) {
+				ITypeBinding typeBinding= ASTNodes.getTargetType(node);
+
+				if (typeBinding != null && typeBinding.isPrimitive()) {
+					if (ASTNodes.isField(node, Boolean.class.getCanonicalName(), "TRUE")) { //$NON-NLS-1$
+						rewriteOperations.add(new BooleanLiteralOperation(node, true));
+						return false;
+					}
+
+					if (ASTNodes.isField(node, Boolean.class.getCanonicalName(), "FALSE")) { //$NON-NLS-1$
+						rewriteOperations.add(new BooleanLiteralOperation(node, false));
+						return false;
+					}
+				}
+
+				return true;
+			}
+		});
+
+		if (rewriteOperations.isEmpty()) {
+			return null;
+		}
+
+		return new CompilationUnitRewriteOperationsFix(MultiFixMessages.BooleanLiteralCleanup_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 BooleanLiteralOperation extends CompilationUnitRewriteOperation {
+		private final QualifiedName node;
+		private final boolean value;
+
+		public BooleanLiteralOperation(final QualifiedName node, final boolean value) {
+			this.node= node;
+			this.value= value;
+		}
+
+		@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.BooleanLiteralCleanup_description, cuRewrite);
+
+			BooleanLiteral booleanLiteral= ast.newBooleanLiteral(value);
+			ASTNodes.replaceButKeepComment(rewrite, node, booleanLiteral, 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 c180a26..22f8ce9 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
@@ -89,6 +89,7 @@
 	public static String OptimizationTabPage_CheckboxName_UseLazyLogicalOperator;
 	public static String OptimizationTabPage_CheckboxName_PrecompileRegEx;
 	public static String OptimizationTabPage_CheckboxName_NoStringCreation;
+	public static String OptimizationTabPage_CheckboxName_BooleanLiteral;
 
 	public static String ContributedCleanUpTabPage_ErrorPage_message;
 
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 b4a372c..cd9df39 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_UseLazyLogicalOperator=Use la&zy logical operator
 OptimizationTabPage_CheckboxName_PrecompileRegEx=Precompile reused regular e&xpressions
 OptimizationTabPage_CheckboxName_NoStringCreation=Remo&ve redundant string creation
+OptimizationTabPage_CheckboxName_BooleanLiteral=Prefer &boolean literals
 
 ContributedCleanUpTabPage_ErrorPage_message=An error occurred while creating this page. See the error log for details
 
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 ace6331..f8478f6 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
@@ -21,6 +21,7 @@
 import org.eclipse.jdt.internal.corext.fix.CleanUpConstants;
 
 import org.eclipse.jdt.internal.ui.fix.AbstractCleanUp;
+import org.eclipse.jdt.internal.ui.fix.BooleanLiteralCleanUp;
 import org.eclipse.jdt.internal.ui.fix.LazyLogicalCleanUp;
 import org.eclipse.jdt.internal.ui.fix.NoStringCreationCleanUp;
 import org.eclipse.jdt.internal.ui.fix.PatternCleanUp;
@@ -33,7 +34,8 @@
 		return new AbstractCleanUp[] {
 				new LazyLogicalCleanUp(values),
 				new PatternCleanUp(values),
-				new NoStringCreationCleanUp(values)
+				new NoStringCreationCleanUp(values),
+				new BooleanLiteralCleanUp(values)
 		};
 	}
 
@@ -52,5 +54,7 @@
 		final CheckboxPreference noStringCreation= createCheckboxPref(optimizationGroup, numColumns, CleanUpMessages.OptimizationTabPage_CheckboxName_NoStringCreation, CleanUpConstants.NO_STRING_CREATION, CleanUpModifyDialog.FALSE_TRUE);
 		registerPreference(noStringCreation);
 
+		final CheckboxPreference booleanLiteral= createCheckboxPref(optimizationGroup, numColumns, CleanUpMessages.OptimizationTabPage_CheckboxName_BooleanLiteral, CleanUpConstants.PREFER_BOOLEAN_LITERAL, CleanUpModifyDialog.FALSE_TRUE);
+		registerPreference(booleanLiteral);
 	}
 }