Bug 550394 - [cleanup & saveaction] Simplify lambda expressions
Change-Id: I6045a8772bb845875525a86dc8fa67eca506f9af
Signed-off-by: Fabrice-TIERCELIN <fabrice.tiercelin@yahoo.fr>
Depends-On: I2829d8ed4a876ae6a9b63cee2834bbd611318ab0
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 972bfb1..42efac6 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -104,6 +104,7 @@
public static String CodeFormatFix_description;
public static String LambdaExpressionsCleanUp_use_lambda_where_possible;
public static String LambdaExpressionsCleanUp_use_anonymous;
+ public static String LambdaExpressionAndMethodRefCleanUp_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 73f7320..9041e0e 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
@@ -84,6 +84,7 @@
ImportsCleanUp_OrganizeImports_Description=Organize imports
LambdaExpressionsCleanUp_use_lambda_where_possible=Use lambda where possible
LambdaExpressionsCleanUp_use_anonymous=Use anonymous class creations
+LambdaExpressionAndMethodRefCleanUp_description=Simplify lambda expression and method reference syntax
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/ASTNodeFactory.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java
index 718e45f..a0523b8 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -11,6 +11,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Red Hat Inc. - refactored to jdt.core.manipulation
+ * Fabrice TIERCELIN - Methods to identify a signature
*******************************************************************************/
package org.eclipse.jdt.internal.corext.dom;
@@ -38,6 +39,7 @@
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
@@ -55,7 +57,7 @@
/**
* JDT-UI-internal helper methods to create new {@link ASTNode}s.
* Complements <code>AST#new*(..)</code> and <code>ImportRewrite#add*(..)</code>.
- *
+ *
* see JDTUIHelperClasses
*/
public class ASTNodeFactory {
@@ -86,6 +88,41 @@
// no instance;
}
+ /**
+ * Parenthesizes the provided expression if its type requires it.
+ *
+ * @param ast The AST to create the resulting node with.
+ * @param expression the expression to conditionally return parenthesized
+ * @return the parenthesized expression of the provided expression to return or this expression
+ * itself
+ */
+ public static Expression parenthesizeIfNeeded(AST ast, Expression expression) {
+ switch (expression.getNodeType()) {
+ case ASTNode.ASSIGNMENT:
+ case ASTNode.CAST_EXPRESSION:
+ case ASTNode.CONDITIONAL_EXPRESSION:
+ case ASTNode.INFIX_EXPRESSION:
+ case ASTNode.INSTANCEOF_EXPRESSION:
+ return parenthesize(ast, expression);
+
+ default:
+ return expression;
+ }
+ }
+
+ /**
+ * Builds a new {@link ParenthesizedExpression} instance.
+ *
+ * @param ast The AST to create the resulting node with.
+ * @param expression the expression to wrap with parentheses
+ * @return a new parenthesized expression
+ */
+ public static ParenthesizedExpression parenthesize(AST ast, Expression expression) {
+ final ParenthesizedExpression pe= ast.newParenthesizedExpression();
+ pe.setExpression(expression);
+ return pe;
+ }
+
public static ASTNode newStatement(AST ast, String content) {
StringBuilder buffer= new StringBuilder(STATEMENT_HEADER);
buffer.append(content);
@@ -139,7 +176,7 @@
* Returns an {@link ArrayType} that adds one dimension to the given type node.
* If the given node is already an ArrayType, then a new {@link Dimension}
* without annotations is inserted at the first position.
- *
+ *
* @param type the type to be wrapped
* @return the array type
* @since 3.10
@@ -170,13 +207,13 @@
* Returns the new type node corresponding to the type of the given declaration
* including the extra dimensions. If the type is a {@link UnionType}, use the LUB type.
* If the <code>importRewrite</code> is <code>null</code>, the type may be fully-qualified.
- *
+ *
* @param ast The AST to create the resulting type with.
* @param declaration The variable declaration to get the type from
* @param importRewrite the import rewrite to use, or <code>null</code>
* @param context the import rewrite context, or <code>null</code>
* @return a new type node created with the given AST.
- *
+ *
* @since 3.7.1
*/
public static Type newType(AST ast, VariableDeclaration declaration, ImportRewrite importRewrite, ImportRewriteContext context) {
@@ -206,9 +243,9 @@
return type;
}
}
-
+
type= (Type) ASTNode.copySubtree(ast, type);
-
+
List<Dimension> extraDimensions= declaration.extraDimensions();
if (!extraDimensions.isEmpty()) {
ArrayType arrayType;
@@ -281,14 +318,14 @@
/**
* Returns the new type node representing the return type of <code>lambdaExpression</code>
* including the extra dimensions.
- *
+ *
* @param lambdaExpression the lambda expression
* @param ast the AST to create the return type with
* @param importRewrite the import rewrite to use, or <code>null</code>
* @param context the import rewrite context, or <code>null</code>
* @return a new type node created with the given AST representing the return type of
* <code>lambdaExpression</code>
- *
+ *
* @since 3.10
*/
public static Type newReturnType(LambdaExpression lambdaExpression, AST ast, ImportRewrite importRewrite, ImportRewriteContext context) {
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 5e4e0a6..689691a 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -418,6 +418,24 @@
return Modifier.isStatic(declaration.getModifiers());
}
+ /**
+ * True if the method is static, false if it is not or null if it is unknown.
+ *
+ * @param method The method
+ * @return True if the method is static, false if it is not or null if it is unknown.
+ */
+ public static Boolean isStatic(final MethodInvocation method) {
+ Expression calledType= method.getExpression();
+
+ if (method.resolveMethodBinding() != null) {
+ return (method.resolveMethodBinding().getModifiers() & Modifier.STATIC) != 0;
+ } else if ((calledType instanceof Name) && ((Name) calledType).resolveBinding().getKind() == IBinding.TYPE) {
+ return Boolean.TRUE;
+ }
+
+ return null;
+ }
+
public static List<BodyDeclaration> getBodyDeclarations(ASTNode node) {
if (node instanceof AbstractTypeDeclaration) {
return ((AbstractTypeDeclaration)node).bodyDeclarations();
@@ -1302,6 +1320,35 @@
return current;
}
+ /**
+ * Returns the same node after removing any parentheses around it.
+ *
+ * @param node the node around which parentheses must be removed
+ * @return the same node after removing any parentheses around it. If there are
+ * no parentheses around it then the exact same node is returned
+ */
+ public static ASTNode getUnparenthesedExpression(ASTNode node) {
+ if (node instanceof Expression) {
+ return getUnparenthesedExpression((Expression) node);
+ }
+ return node;
+ }
+
+ /**
+ * Returns the same expression after removing any parentheses around it.
+ *
+ * @param expression the expression around which parentheses must be removed
+ * @return the same expression after removing any parentheses around it If there
+ * are no parentheses around it then the exact same expression is
+ * returned
+ */
+ public static Expression getUnparenthesedExpression(Expression expression) {
+ if (expression != null && expression.getNodeType() == ASTNode.PARENTHESIZED_EXPRESSION) {
+ return getUnparenthesedExpression(((ParenthesizedExpression) expression).getExpression());
+ }
+ return expression;
+ }
+
/**
* Returns <code>true</code> iff <code>parent</code> is a true ancestor of <code>node</code>
* (i.e. returns <code>false</code> if <code>parent == node</code>).
@@ -1577,6 +1624,7 @@
return ((SimpleName) name).isDeclaration();
}
}
+
/**
* Returns whether the provided method invocation invokes a method with the
* provided method signature. The method signature is compared against the
@@ -1608,7 +1656,7 @@
* @return true if the provided method invocation matches the provided method signature, false
* otherwise
*/
- private static boolean usesGivenSignature(final IMethodBinding methodBinding, final String typeQualifiedName, final String methodName,
+ public static boolean usesGivenSignature(final IMethodBinding methodBinding, final String typeQualifiedName, final String methodName,
final String... parameterTypesQualifiedNames) {
// Let's do the fast checks first
if (methodBinding == null || !methodName.equals(methodBinding.getName())
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 8757d89..f2a17dc 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -679,6 +679,19 @@
public static final String USE_ANONYMOUS_CLASS_CREATION= "cleanup.use_anonymous_class_creation"; //$NON-NLS-1$
/**
+ * Removes useless parenthesis, return statements and brackets from lambda expressions and
+ * method references.
+ * <p>
+ * Possible values: {TRUE, FALSE}
+ * <p>
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
+ * @since 4.15
+ */
+ public static final String SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF= "cleanup.simplify_lambda_expression_and_method_ref"; //$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/CleanUpTest18.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest18.java
index ca50a29..769636b 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest18.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest18.java
@@ -10,6 +10,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Fabrice TIERCELIN - Lambda expression cleanup
*******************************************************************************/
package org.eclipse.jdt.ui.tests.quickfix;
@@ -57,6 +58,268 @@
}
@Test
+ public void testSimplifyLambdaExpression() throws Exception {
+ IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+ String sample= "" //
+ + "package test1;\n" //
+ + "\n" //
+ + "import static java.util.Calendar.getInstance;\n" //
+ + "import static java.util.Calendar.getAvailableLocales;\n" //
+ + "\n" //
+ + "import java.time.Instant;\n" //
+ + "import java.util.ArrayList;\n" //
+ + "import java.util.Calendar;\n" //
+ + "import java.util.Date;\n" //
+ + "import java.util.Locale;\n" //
+ + "import java.util.Vector;\n" //
+ + "import java.util.function.BiFunction;\n" //
+ + "import java.util.function.Function;\n" //
+ + "import java.util.function.Supplier;\n" //
+ + "\n" //
+ + "public class E extends Date {\n" //
+ + " public String changeableText = \"foo\";\n" //
+ + "\n" //
+ + " public Function<String, String> removeParentheses() {\n" //
+ + " return (someString) -> someString.trim().toLowerCase();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, String> doNotRemoveParenthesesWithSingleVariableDeclaration() {\n" //
+ + " return (String someString) -> someString.trim().toLowerCase();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public BiFunction<String, String, Integer> doNotRemoveParenthesesWithTwoParameters() {\n" //
+ + " return (someString, anotherString) -> someString.trim().compareTo(anotherString.trim());\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Supplier<Boolean> doNotRemoveParenthesesWithNoParameter() {\n" //
+ + " return () -> {System.out.println(\"foo\");return true;};\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, String> removeReturnAndBrackets() {\n" //
+ + " return someString -> {return someString.trim().toLowerCase();};\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, String> removeReturnAndBracketsWithParentheses() {\n" //
+ + " return someString -> {return someString.trim().toLowerCase() + \"bar\";};\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, String> doNotRemoveReturnWithSeveralStatements() {\n" //
+ + " return someString -> {String trimmed = someString.trim();\n" //
+ + " return trimmed.toLowerCase();};\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Supplier<ArrayList<String>> useCreationReference() {\n" //
+ + " return () -> new ArrayList<>();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, ArrayList<String>> useCreationReferenceWithParameter() {\n" //
+ + " return capacity -> new ArrayList<>(capacity);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, ArrayList<String>> useCreationReferenceWithParameterAndType() {\n" //
+ + " // TODO this can be refactored like useCreationReferenceWithParameter\n" //
+ + " return (Integer capacity) -> new ArrayList<>(capacity);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, ArrayList<String>> doNotRefactorWithExpressions() {\n" //
+ + " return capacity -> new ArrayList<>(capacity + 1);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public BiFunction<Integer, Integer, Vector<String>> useCreationReferenceWithParameters() {\n" //
+ + " return (initialCapacity, capacityIncrement) -> new Vector<>(initialCapacity, capacityIncrement);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public BiFunction<Integer, Integer, Vector<String>> doNotRefactorShuffledParams() {\n" //
+ + " return (initialCapacity, capacityIncrement) -> new Vector<>(capacityIncrement, initialCapacity);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Date, Long> useMethodReference() {\n" //
+ + " return date -> date.getTime();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public BiFunction<Date, Date, Integer> useMethodReferenceWithParameter() {\n" //
+ + " return (date, anotherDate) -> date.compareTo(anotherDate);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, Long> useTypeReference() {\n" //
+ + " return numberInText -> Long.getLong(numberInText);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public static Function<Instant, Date> useTypeReferenceOnClassMethod() {\n" //
+ + " return instant -> Date.from(instant);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public static Function<Locale, Calendar> useTypeReferenceOnImportedMethod() {\n" //
+ + " return locale -> Calendar.getInstance(locale);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public static Supplier<Locale[]> useTypeReferenceAsSupplier() {\n" //
+ + " return () -> Calendar.getAvailableLocales();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, Integer> useExpressionMethodReferenceOnLiteral() {\n" //
+ + " return textToSearch -> \"AutoRefactor\".indexOf(textToSearch);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, Integer> doNotUseExpressionMethodReferenceOnVariable() {\n" //
+ + " return textToSearch -> this.changeableText.indexOf(textToSearch);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Date, Integer> useThisMethodReference() {\n" //
+ + " return anotherDate -> this.compareTo(anotherDate);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Date, Integer> useThisMethodReferenceAddThis() {\n" //
+ + " return anotherDate -> this.compareTo(anotherDate);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Date, Integer> useSuperMethodReference() {\n" //
+ + " return anotherDate -> super.compareTo(anotherDate);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, String> doNotUseConflictingMethodReference() {\n" //
+ + " return numberToPrint -> numberToPrint.toString();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, String> doNotUseConflictingStaticMethodReference() {\n" //
+ + " return numberToPrint -> Integer.toString(numberToPrint);\n" //
+ + " }\n" //
+ + "}\n";
+ ICompilationUnit cu1= pack1.createCompilationUnit("E.java", sample, false, null);
+
+ enable(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF);
+
+ sample= "" //
+ + "package test1;\n" //
+ + "\n" //
+ + "import static java.util.Calendar.getInstance;\n" //
+ + "import static java.util.Calendar.getAvailableLocales;\n" //
+ + "\n" //
+ + "import java.time.Instant;\n" //
+ + "import java.util.ArrayList;\n" //
+ + "import java.util.Calendar;\n" //
+ + "import java.util.Date;\n" //
+ + "import java.util.Locale;\n" //
+ + "import java.util.Vector;\n" //
+ + "import java.util.function.BiFunction;\n" //
+ + "import java.util.function.Function;\n" //
+ + "import java.util.function.Supplier;\n" //
+ + "\n" //
+ + "public class E extends Date {\n" //
+ + " public String changeableText = \"foo\";\n" //
+ + "\n" //
+ + " public Function<String, String> removeParentheses() {\n" //
+ + " return someString -> someString.trim().toLowerCase();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, String> doNotRemoveParenthesesWithSingleVariableDeclaration() {\n" //
+ + " return (String someString) -> someString.trim().toLowerCase();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public BiFunction<String, String, Integer> doNotRemoveParenthesesWithTwoParameters() {\n" //
+ + " return (someString, anotherString) -> someString.trim().compareTo(anotherString.trim());\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Supplier<Boolean> doNotRemoveParenthesesWithNoParameter() {\n" //
+ + " return () -> {System.out.println(\"foo\");return true;};\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, String> removeReturnAndBrackets() {\n" //
+ + " return someString -> someString.trim().toLowerCase();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, String> removeReturnAndBracketsWithParentheses() {\n" //
+ + " return someString -> (someString.trim().toLowerCase() + \"bar\");\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, String> doNotRemoveReturnWithSeveralStatements() {\n" //
+ + " return someString -> {String trimmed = someString.trim();\n" //
+ + " return trimmed.toLowerCase();};\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Supplier<ArrayList<String>> useCreationReference() {\n" //
+ + " return ArrayList::new;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, ArrayList<String>> useCreationReferenceWithParameter() {\n" //
+ + " return ArrayList::new;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, ArrayList<String>> useCreationReferenceWithParameterAndType() {\n" //
+ + " // TODO this can be refactored like useCreationReferenceWithParameter\n" //
+ + " return (Integer capacity) -> new ArrayList<>(capacity);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, ArrayList<String>> doNotRefactorWithExpressions() {\n" //
+ + " return capacity -> new ArrayList<>(capacity + 1);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public BiFunction<Integer, Integer, Vector<String>> useCreationReferenceWithParameters() {\n" //
+ + " return Vector::new;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public BiFunction<Integer, Integer, Vector<String>> doNotRefactorShuffledParams() {\n" //
+ + " return (initialCapacity, capacityIncrement) -> new Vector<>(capacityIncrement, initialCapacity);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Date, Long> useMethodReference() {\n" //
+ + " return Date::getTime;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public BiFunction<Date, Date, Integer> useMethodReferenceWithParameter() {\n" //
+ + " return Date::compareTo;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, Long> useTypeReference() {\n" //
+ + " return Long::getLong;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public static Function<Instant, Date> useTypeReferenceOnClassMethod() {\n" //
+ + " return instant -> Date.from(instant);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public static Function<Locale, Calendar> useTypeReferenceOnImportedMethod() {\n" //
+ + " return Calendar::getInstance;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public static Supplier<Locale[]> useTypeReferenceAsSupplier() {\n" //
+ + " return Calendar::getAvailableLocales;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, Integer> useExpressionMethodReferenceOnLiteral() {\n" //
+ + " return \"AutoRefactor\"::indexOf;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<String, Integer> doNotUseExpressionMethodReferenceOnVariable() {\n" //
+ + " return textToSearch -> this.changeableText.indexOf(textToSearch);\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Date, Integer> useThisMethodReference() {\n" //
+ + " return this::compareTo;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Date, Integer> useThisMethodReferenceAddThis() {\n" //
+ + " return this::compareTo;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Date, Integer> useSuperMethodReference() {\n" //
+ + " return super::compareTo;\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, String> doNotUseConflictingMethodReference() {\n" //
+ + " return numberToPrint -> numberToPrint.toString();\n" //
+ + " }\n" //
+ + "\n" //
+ + " public Function<Integer, String> doNotUseConflictingStaticMethodReference() {\n" //
+ + " return numberToPrint -> Integer.toString(numberToPrint);\n" //
+ + " }\n" //
+ + "}\n";
+ String expected1= sample;
+
+ assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
+ }
+
+ @Test
public void testConvertToLambda01() throws Exception {
IPackageFragment pack1= fSourceFolder.createPackageFragment("test", false, null);
StringBuffer buf= new StringBuffer();
@@ -87,10 +350,10 @@
String expected1= buf.toString();
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-
+
disable(CleanUpConstants.USE_LAMBDA);
enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
}
@@ -136,10 +399,10 @@
String expected1= buf.toString();
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-
+
disable(CleanUpConstants.USE_LAMBDA);
enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
}
@@ -208,10 +471,10 @@
buf.append("}\n");
String original= buf.toString();
ICompilationUnit cu1= pack1.createCompilationUnit("E.java", original, false, null);
-
+
enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
enable(CleanUpConstants.USE_LAMBDA);
-
+
buf= new StringBuffer();
buf.append("package test;\n");
buf.append("import java.util.concurrent.Executors;\n");
@@ -221,12 +484,12 @@
buf.append(" }\n");
buf.append("}\n");
String expected1= buf.toString();
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-
+
disable(CleanUpConstants.USE_LAMBDA);
enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
}
@@ -373,10 +636,10 @@
String expected1= buf.toString();
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-
+
disable(CleanUpConstants.USE_LAMBDA);
enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
}
@@ -470,10 +733,10 @@
buf.append("}\n");
String original= buf.toString();
ICompilationUnit cu1= pack1.createCompilationUnit("E.java", original, false, null);
-
+
enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
enable(CleanUpConstants.USE_LAMBDA);
-
+
buf= new StringBuffer();
buf.append("package test;\n");
buf.append("\n");
@@ -519,12 +782,12 @@
buf.append(" }\n");
buf.append("}\n");
String expected1= buf.toString();
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-
+
disable(CleanUpConstants.USE_LAMBDA);
enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
}
@@ -564,10 +827,10 @@
buf.append("@FunctionalInterface interface ZV { void zoo(); }\n");
String original= buf.toString();
ICompilationUnit cu1= pack1.createCompilationUnit("E.java", original, false, null);
-
+
enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
enable(CleanUpConstants.USE_LAMBDA);
-
+
buf= new StringBuffer();
buf.append("package test;\n");
buf.append("public interface E {\n");
@@ -589,12 +852,12 @@
buf.append("@FunctionalInterface interface ZI { int zoo(); }\n");
buf.append("@FunctionalInterface interface ZV { void zoo(); }\n");
String expected1= buf.toString();
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-
+
disable(CleanUpConstants.USE_LAMBDA);
enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
}
@@ -767,10 +1030,10 @@
buf.append("}\n");
String original= buf.toString();
ICompilationUnit cu1= pack1.createCompilationUnit("E.java", original, false, null);
-
+
enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-
+
buf= new StringBuffer();
buf.append("package test;\n");
buf.append("import java.util.*;\n");
@@ -803,12 +1066,12 @@
buf.append(" };\n");
buf.append("}\n");
String expected1= buf.toString();
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-
+
disable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
enable(CleanUpConstants.USE_LAMBDA);
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
}
@@ -873,10 +1136,10 @@
buf.append("}\n");
String original= buf.toString();
ICompilationUnit cu1= pack1.createCompilationUnit("Test.java", original, false, null);
-
+
enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-
+
buf= new StringBuffer();
buf.append("package test;\n");
buf.append("\n");
@@ -892,12 +1155,12 @@
buf.append(" };\n");
buf.append("}\n");
String expected1= buf.toString();
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-
+
disable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
enable(CleanUpConstants.USE_LAMBDA);
-
+
assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
}
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 67bb1bf..dbb6daa 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2018, 2019 IBM Corporation and others.
+ * Copyright (c) 2018, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -68,6 +68,7 @@
options.setOption(CONVERT_FUNCTIONAL_INTERFACES, CleanUpOptions.FALSE);
options.setOption(USE_LAMBDA, CleanUpOptions.TRUE);
options.setOption(USE_ANONYMOUS_CLASS_CREATION, CleanUpOptions.FALSE);
+ options.setOption(SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF, CleanUpOptions.FALSE);
//Unused Code
options.setOption(REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.TRUE);
@@ -164,6 +165,7 @@
options.setOption(CONVERT_FUNCTIONAL_INTERFACES, CleanUpOptions.FALSE);
options.setOption(USE_LAMBDA, CleanUpOptions.TRUE);
options.setOption(USE_ANONYMOUS_CLASS_CREATION, CleanUpOptions.FALSE);
+ options.setOption(SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF, 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 f3cb463..785fc83 100644
--- a/org.eclipse.jdt.ui/plugin.xml
+++ b/org.eclipse.jdt.ui/plugin.xml
@@ -7040,9 +7040,14 @@
runAfter="org.eclipse.jdt.ui.cleanup.lambda">
</cleanUp>
<cleanUp
+ class="org.eclipse.jdt.internal.ui.fix.LambdaExpressionAndMethodRefCleanUp"
+ id="org.eclipse.jdt.ui.cleanup.lambda_and_method_ref"
+ runAfter="org.eclipse.jdt.ui.cleanup.expressions">
+ </cleanUp>
+ <cleanUp
class="org.eclipse.jdt.internal.ui.fix.NumberSuffixCleanUp"
id="org.eclipse.jdt.ui.cleanup.number_suffix"
- runAfter="org.eclipse.jdt.ui.cleanup.expressions">
+ runAfter="org.eclipse.jdt.ui.cleanup.lambda_and_method_ref">
</cleanUp>
<cleanUp
class="org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp"
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/LambdaExpressionAndMethodRefCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/LambdaExpressionAndMethodRefCleanUp.java
new file mode 100644
index 0000000..3fcdde4
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/LambdaExpressionAndMethodRefCleanUp.java
@@ -0,0 +1,475 @@
+/*******************************************************************************
+ * 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.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.CreationReference;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionMethodReference;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.LambdaExpression;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+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.StringLiteral;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.SuperMethodReference;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeMethodReference;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
+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 simplifies the lambda expression and the method reference syntax:
+ * <ul>
+ * <li>Parenthesis are not needed for a single untyped parameter,</li>
+ * <li>Return statement is not needed for a single expression,</li>
+ * <li>Brackets are not needed for a single statement,</li>
+ * <li>A lambda expression can be replaced by a creation or a method reference in some cases.</li>
+ * </ul>
+ */
+public class LambdaExpressionAndMethodRefCleanUp extends AbstractMultiFix {
+ public LambdaExpressionAndMethodRefCleanUp() {
+ this(Collections.emptyMap());
+ }
+
+ public LambdaExpressionAndMethodRefCleanUp(Map<String, String> options) {
+ super(options);
+ }
+
+ @Override
+ public CleanUpRequirements getRequirements() {
+ boolean requireAST= isEnabled(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF);
+ Map<String, String> requiredOptions= null;
+ return new CleanUpRequirements(requireAST, false, false, requiredOptions);
+ }
+
+ @Override
+ public String[] getStepDescriptions() {
+ if (isEnabled(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF)) {
+ return new String[] { MultiFixMessages.LambdaExpressionAndMethodRefCleanUp_description };
+ }
+ return new String[0];
+ }
+
+ @SuppressWarnings("nls")
+ @Override
+ public String getPreview() {
+ StringBuilder bld= new StringBuilder();
+ bld.append("\n");
+
+ if (isEnabled(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF)) {
+ bld.append("someString -> someString.trim().toLowerCase();\n");
+ bld.append("someString -> someString.trim().toLowerCase();\n");
+ bld.append("someString -> (someString.trim().toLowerCase() + \"bar\");\n");
+ bld.append("ArrayList::new;\n");
+ bld.append("Date::getTime;\n");
+ } else {
+ bld.append("(someString) -> someString.trim().toLowerCase();\n");
+ bld.append("someString -> {return someString.trim().toLowerCase();};\n");
+ bld.append("someString -> {return someString.trim().toLowerCase() + \"bar\";};\n");
+ bld.append("() -> new ArrayList<>();\n");
+ bld.append("date -> date.getTime();\n");
+ }
+
+ return bld.toString();
+ }
+
+ @Override
+ protected ICleanUpFix createFix(CompilationUnit unit) throws CoreException {
+ if (!isEnabled(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF) || !JavaModelUtil.is18OrHigher(unit.getJavaElement().getJavaProject())) {
+ return null;
+ }
+
+ final List<CompilationUnitRewriteOperation> rewriteOperations= new ArrayList<>();
+
+ unit.accept(new ASTVisitor() {
+ @Override
+ public boolean visit(final LambdaExpression node) {
+ if (node.hasParentheses() && node.parameters().size() == 1
+ && node.parameters().get(0) instanceof VariableDeclarationFragment) {
+ rewriteOperations.add(new RemoveParamParenthesesOperation(node));
+ return false;
+ } else if (node.getBody() instanceof Block) {
+ List<Statement> statements= ((Block) node.getBody()).statements();
+
+ if (statements.size() == 1 && statements.get(0) instanceof ReturnStatement) {
+ rewriteOperations.add(new RemoveReturnAndBracketsOperation(node, statements));
+ return false;
+ }
+ } else if (node.getBody() instanceof ClassInstanceCreation) {
+ ClassInstanceCreation ci= (ClassInstanceCreation) node.getBody();
+
+ List<Expression> arguments= ci.arguments();
+ if (node.parameters().size() == arguments.size() && areSameIdentifiers(node, arguments)) {
+ rewriteOperations.add(new ReplaceByCreationReferenceOperation(node, ci));
+ return false;
+ }
+ } else if (node.getBody() instanceof SuperMethodInvocation) {
+ SuperMethodInvocation smi= (SuperMethodInvocation) node.getBody();
+ List<Expression> arguments= smi.arguments();
+
+ if (node.parameters().size() == arguments.size() && areSameIdentifiers(node, arguments)) {
+ rewriteOperations.add(new ReplaceBySuperMethodReferenceOperation(node, smi));
+ return false;
+ }
+ } else if (node.getBody() instanceof MethodInvocation) {
+ MethodInvocation methodInvocation= (MethodInvocation) node.getBody();
+ Expression calledExpression= methodInvocation.getExpression();
+ List<Expression> arguments= methodInvocation.arguments();
+
+ if (node.parameters().size() == arguments.size()) {
+ if (!areSameIdentifiers(node, arguments)) {
+ return true;
+ }
+
+ IMethodBinding methodBinding= methodInvocation.resolveMethodBinding();
+
+ if (Boolean.TRUE.equals(ASTNodes.isStatic(methodInvocation))) {
+ if (methodBinding == null || methodBinding.getDeclaringClass() == null) {
+ return true;
+ }
+
+ ITypeBinding calledType= methodBinding.getDeclaringClass();
+
+ if (!arguments.isEmpty()) {
+ String[] remainingParams= new String[arguments.size() - 1];
+
+ for (int i= 0; i < arguments.size() - 1; i++) {
+ ITypeBinding resolveTypeBinding= arguments.get(i + 1).resolveTypeBinding();
+
+ if (resolveTypeBinding == null) {
+ return true;
+ }
+
+ remainingParams[i]= resolveTypeBinding.getQualifiedName();
+ }
+
+ for (IMethodBinding declaredMethodBinding : calledType.getDeclaredMethods()) {
+ if ((declaredMethodBinding.getModifiers() & Modifier.STATIC) == 0 && ASTNodes.usesGivenSignature(declaredMethodBinding,
+ calledType.getQualifiedName(), methodInvocation.getName().getIdentifier(), remainingParams)) {
+ return true;
+ }
+ }
+ }
+
+ rewriteOperations.add(new ReplaceByTypeReferenceOperation(node, methodInvocation, calledType));
+ return false;
+ }
+
+ if (calledExpression instanceof StringLiteral || calledExpression instanceof NumberLiteral
+ || calledExpression instanceof ThisExpression) {
+ rewriteOperations.add(new ReplaceByMethodReferenceOperation(node, methodInvocation));
+ return false;
+ } else if (calledExpression instanceof FieldAccess) {
+ FieldAccess fieldAccess= (FieldAccess) calledExpression;
+
+ if (fieldAccess.resolveFieldBinding() != null && fieldAccess.resolveFieldBinding().isEffectivelyFinal()) {
+ rewriteOperations.add(new ReplaceByMethodReferenceOperation(node, methodInvocation));
+ return false;
+ }
+ } else if (calledExpression instanceof SuperFieldAccess) {
+ SuperFieldAccess fieldAccess= (SuperFieldAccess) calledExpression;
+
+ if (fieldAccess.resolveFieldBinding() != null && fieldAccess.resolveFieldBinding().isEffectivelyFinal()) {
+ rewriteOperations.add(new ReplaceByMethodReferenceOperation(node, methodInvocation));
+ return false;
+ }
+ }
+ } else if (calledExpression instanceof SimpleName && node.parameters().size() == arguments.size() + 1) {
+ SimpleName calledObject= (SimpleName) calledExpression;
+
+ if (isSameIdentifier(node, 0, calledObject)) {
+ for (int i= 0; i < arguments.size(); i++) {
+ ASTNode expression= ASTNodes.getUnparenthesedExpression(arguments.get(i));
+
+ if (!(expression instanceof SimpleName) || !isSameIdentifier(node, i + 1, (SimpleName) expression)) {
+ return true;
+ }
+ }
+
+ ITypeBinding clazz= null;
+ if (calledExpression.resolveTypeBinding() != null) {
+ clazz= calledExpression.resolveTypeBinding();
+ } else if (methodInvocation.resolveMethodBinding() != null && methodInvocation.resolveMethodBinding().getDeclaringClass() != null) {
+ clazz= methodInvocation.resolveMethodBinding().getDeclaringClass();
+ } else {
+ return true;
+ }
+
+ String[] cumulativeParams= new String[arguments.size() + 1];
+ cumulativeParams[0]= clazz.getQualifiedName();
+
+ for (int i= 0; i < arguments.size(); i++) {
+ ITypeBinding resolveTypeBinding= arguments.get(i).resolveTypeBinding();
+
+ if (resolveTypeBinding == null) {
+ return true;
+ }
+
+ cumulativeParams[i + 1]= resolveTypeBinding.getQualifiedName();
+ }
+
+ for (IMethodBinding declaredMethodBinding : clazz.getDeclaredMethods()) {
+ if ((declaredMethodBinding.getModifiers() & Modifier.STATIC) > 0 && ASTNodes.usesGivenSignature(declaredMethodBinding,
+ clazz.getQualifiedName(), methodInvocation.getName().getIdentifier(), cumulativeParams)) {
+ return true;
+ }
+ }
+
+ rewriteOperations.add(new ReplaceByTypeReferenceOperation(node, methodInvocation, clazz));
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * In order to make parameters implicit, those ones should be the same in the same
+ * order.
+ *
+ * @param node the lambda expression
+ * @param arguments The arguments in the expression
+ * @return true if the parameters are obvious
+ */
+ private boolean areSameIdentifiers(LambdaExpression node, List<Expression> arguments) {
+ for (int i= 0; i < node.parameters().size(); i++) {
+ Expression expression= ASTNodes.getUnparenthesedExpression(arguments.get(i));
+
+ if (!(expression instanceof SimpleName) || !isSameIdentifier(node, i, (SimpleName) expression)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean isSameIdentifier(final LambdaExpression node, final int i, final SimpleName argument) {
+ Object param0= node.parameters().get(i);
+
+ if (param0 instanceof VariableDeclarationFragment) {
+ VariableDeclarationFragment vdf= (VariableDeclarationFragment) param0;
+ return vdf.getName().getIdentifier().equals(argument.getIdentifier());
+ }
+
+ return false;
+ }
+ });
+
+ if (rewriteOperations.isEmpty()) {
+ return null;
+ }
+
+ return new CompilationUnitRewriteOperationsFix(MultiFixMessages.LambdaExpressionAndMethodRefCleanUp_description, unit,
+ rewriteOperations.toArray(new CompilationUnitRewriteOperation[rewriteOperations.size()]));
+ }
+
+ @Override
+ public boolean canFix(ICompilationUnit compilationUnit, IProblemLocation problem) {
+ return false;
+ }
+
+ @Override
+ protected ICleanUpFix createFix(CompilationUnit unit, IProblemLocation[] problems) throws CoreException {
+ return null;
+ }
+
+ private static class RemoveParamParenthesesOperation extends CompilationUnitRewriteOperation {
+ private final LambdaExpression node;
+
+ public RemoveParamParenthesesOperation(final LambdaExpression node) {
+ this.node= node;
+ }
+
+ @Override
+ public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ AST ast= cuRewrite.getRoot().getAST();
+
+ LambdaExpression copyOfLambdaExpression= ast.newLambdaExpression();
+ ASTNode copyOfParameter= rewrite.createCopyTarget((ASTNode) node.parameters().get(0));
+ copyOfLambdaExpression.parameters().add(copyOfParameter);
+ copyOfLambdaExpression.setBody(rewrite.createCopyTarget(node.getBody()));
+ copyOfLambdaExpression.setParentheses(false);
+ rewrite.replace(node, copyOfLambdaExpression, null);
+ }
+ }
+
+ private static class RemoveReturnAndBracketsOperation extends CompilationUnitRewriteOperation {
+ private final LambdaExpression node;
+
+ private final List<Statement> statements;
+
+ public RemoveReturnAndBracketsOperation(final LambdaExpression node, final List<Statement> statements) {
+ this.node= node;
+ this.statements= statements;
+ }
+
+ @Override
+ public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ AST ast= cuRewrite.getRoot().getAST();
+
+ ReturnStatement returnStatement= (ReturnStatement) statements.get(0);
+ Expression copyOfExpression= (Expression) rewrite.createCopyTarget(returnStatement.getExpression());
+ rewrite.replace(node.getBody(), ASTNodeFactory.parenthesizeIfNeeded(ast, copyOfExpression), null);
+ }
+ }
+
+ private static class ReplaceByCreationReferenceOperation extends CompilationUnitRewriteOperation {
+ private final LambdaExpression node;
+
+ private final ClassInstanceCreation classInstanceCreation;
+
+ public ReplaceByCreationReferenceOperation(final LambdaExpression node, final ClassInstanceCreation classInstanceCreation) {
+ this.node= node;
+ this.classInstanceCreation= classInstanceCreation;
+ }
+
+ @Override
+ public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ AST ast= cuRewrite.getRoot().getAST();
+
+ CreationReference creationRef= ast.newCreationReference();
+ creationRef.setType(copyType(cuRewrite, ast, classInstanceCreation, classInstanceCreation.resolveTypeBinding()));
+ rewrite.replace(node, creationRef, null);
+ }
+ }
+
+ private static class ReplaceBySuperMethodReferenceOperation extends CompilationUnitRewriteOperation {
+ private final LambdaExpression node;
+
+ private final SuperMethodInvocation superMethodInvocation;
+
+ public ReplaceBySuperMethodReferenceOperation(final LambdaExpression node, final SuperMethodInvocation superMethodInvocation) {
+ this.node= node;
+ this.superMethodInvocation= superMethodInvocation;
+ }
+
+ @Override
+ public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ AST ast= cuRewrite.getRoot().getAST();
+
+ SuperMethodReference creationRef= ast.newSuperMethodReference();
+ creationRef.setName((SimpleName) rewrite.createCopyTarget(superMethodInvocation.getName()));
+ rewrite.replace(node, creationRef, null);
+ }
+ }
+
+ private static class ReplaceByTypeReferenceOperation extends CompilationUnitRewriteOperation {
+ private final LambdaExpression node;
+ private final MethodInvocation methodInvocation;
+
+ private final ITypeBinding type;
+
+ public ReplaceByTypeReferenceOperation(LambdaExpression node, MethodInvocation methodInvocation, ITypeBinding type) {
+ this.node= node;
+ this.methodInvocation= methodInvocation;
+ this.type= type;
+ }
+
+ @Override
+ public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ AST ast= cuRewrite.getRoot().getAST();
+
+ TypeMethodReference typeMethodRef= ast.newTypeMethodReference();
+
+ typeMethodRef.setType(copyType(cuRewrite, ast, methodInvocation, type));
+ typeMethodRef.setName((SimpleName) rewrite.createCopyTarget(methodInvocation.getName()));
+ rewrite.replace(node, typeMethodRef, null);
+ }
+ }
+
+ private static class ReplaceByMethodReferenceOperation extends CompilationUnitRewriteOperation {
+ private final LambdaExpression node;
+
+ private final MethodInvocation methodInvocation;
+
+ public ReplaceByMethodReferenceOperation(LambdaExpression node, MethodInvocation methodInvocation) {
+ this.node= node;
+ this.methodInvocation= methodInvocation;
+ }
+
+ @Override
+ public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ AST ast= cuRewrite.getRoot().getAST();
+ ExpressionMethodReference typeMethodRef= ast.newExpressionMethodReference();
+
+ if (methodInvocation.getExpression() != null) {
+ typeMethodRef.setExpression((Expression) rewrite.createCopyTarget(methodInvocation.getExpression()));
+ } else {
+ typeMethodRef.setExpression(ast.newThisExpression());
+ }
+
+ typeMethodRef.setName((SimpleName) rewrite.createCopyTarget(methodInvocation.getName()));
+ rewrite.replace(node, typeMethodRef, null);
+ }
+ }
+
+ private static Type copyType(final CompilationUnitRewrite cuRewrite, final AST ast, final ASTNode node, final ITypeBinding typeBinding) {
+ ImportRewrite importRewrite= cuRewrite.getImportRewrite();
+ ImportRewriteContext importContext= new ContextSensitiveImportRewriteContext(node, importRewrite);
+ ITypeBinding modifiedType;
+
+ if (typeBinding.getTypeParameters().length == 0) {
+ modifiedType= typeBinding.getErasure();
+ } else {
+ modifiedType= typeBinding;
+ }
+
+ return ASTNodeFactory.newCreationType(ast, modifiedType, importRewrite, importContext);
+ }
+}
\ No newline at end of file
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 7b6d156..e2bb676 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -76,6 +76,7 @@
public static String CodeStyleTabPage_CheckboxName_ConvertFunctionalInterfaces;
public static String CodeStyleTabPage_RadioName_UseLambdaWherePossible;
public static String CodeStyleTabPage_RadioName_UseAnonymous;
+ public static String CodeStyleTabPage_CheckboxName_SimplifyLambdaExpressionAndMethodRefSyntax;
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 58c0a9b..f9e3770 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
@@ -55,6 +55,7 @@
CodeStyleTabPage_GroupName_FunctionalInterfaces=Functional interface instances
CodeStyleTabPage_CheckboxName_ConvertFunctionalInterfaces=Con&vert functional interface instances
CodeStyleTabPage_RadioName_UseLambdaWherePossible=Use lambda where possible
+CodeStyleTabPage_CheckboxName_SimplifyLambdaExpressionAndMethodRefSyntax=Simplify &lambda expression and method reference syntax
CodeStyleTabPage_RadioName_UseAnonymous=Use anonymous class
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/CodeStyleTabPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CodeStyleTabPage.java
index f6ecb53..d49586b 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CodeStyleTabPage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CodeStyleTabPage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -24,6 +24,7 @@
import org.eclipse.jdt.internal.ui.fix.ControlStatementsCleanUp;
import org.eclipse.jdt.internal.ui.fix.ConvertLoopCleanUp;
import org.eclipse.jdt.internal.ui.fix.ExpressionsCleanUp;
+import org.eclipse.jdt.internal.ui.fix.LambdaExpressionAndMethodRefCleanUp;
import org.eclipse.jdt.internal.ui.fix.LambdaExpressionsCleanUp;
import org.eclipse.jdt.internal.ui.fix.NumberSuffixCleanUp;
import org.eclipse.jdt.internal.ui.fix.VariableDeclarationCleanUp;
@@ -39,16 +40,17 @@
new ExpressionsCleanUp(values),
new NumberSuffixCleanUp(values),
new VariableDeclarationCleanUp(values),
- new LambdaExpressionsCleanUp(values)
- };
- }
+ new LambdaExpressionsCleanUp(values),
+ new LambdaExpressionAndMethodRefCleanUp(values)
+ };
+ }
- @Override
+ @Override
protected void doCreatePreferences(Composite composite, int numColumns) {
Group controlGroup= createGroup(numColumns, composite, CleanUpMessages.CodeStyleTabPage_GroupName_ControlStatments);
- final CheckboxPreference useBlockPref= createCheckboxPref(controlGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseBlocks, CleanUpConstants.CONTROL_STATEMENTS_USE_BLOCKS, CleanUpModifyDialog.FALSE_TRUE);
- intent(controlGroup);
+ final CheckboxPreference useBlockPref= createCheckboxPref(controlGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseBlocks, CleanUpConstants.CONTROL_STATEMENTS_USE_BLOCKS, CleanUpModifyDialog.FALSE_TRUE);
+ intent(controlGroup);
final RadioPreference useBlockAlwaysPref= createRadioPref(controlGroup, numColumns - 1, CleanUpMessages.CodeStyleTabPage_RadioName_AlwaysUseBlocks, CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_ALWAYS, CleanUpModifyDialog.FALSE_TRUE);
intent(controlGroup);
final RadioPreference useBlockJDTStylePref= createRadioPref(controlGroup, numColumns - 1, CleanUpMessages.CodeStyleTabPage_RadioName_UseBlocksSpecial, CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_NO_FOR_RETURN_AND_THROW, CleanUpModifyDialog.FALSE_TRUE);
@@ -56,12 +58,12 @@
final RadioPreference useBlockNeverPref= createRadioPref(controlGroup, numColumns - 1, CleanUpMessages.CodeStyleTabPage_RadioName_NeverUseBlocks, CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_NEVER, CleanUpModifyDialog.FALSE_TRUE);
registerSlavePreference(useBlockPref, new RadioPreference[] {useBlockAlwaysPref, useBlockJDTStylePref, useBlockNeverPref});
- CheckboxPreference convertLoop= createCheckboxPref(controlGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_ConvertForLoopToEnhanced, CleanUpConstants.CONTROL_STATMENTS_CONVERT_FOR_LOOP_TO_ENHANCED, CleanUpModifyDialog.FALSE_TRUE);
- registerPreference(convertLoop);
+ CheckboxPreference convertLoop= createCheckboxPref(controlGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_ConvertForLoopToEnhanced, CleanUpConstants.CONTROL_STATMENTS_CONVERT_FOR_LOOP_TO_ENHANCED, CleanUpModifyDialog.FALSE_TRUE);
+ registerPreference(convertLoop);
- Group expressionsGroup= createGroup(numColumns, composite, CleanUpMessages.CodeStyleTabPage_GroupName_Expressions);
+ Group expressionsGroup= createGroup(numColumns, composite, CleanUpMessages.CodeStyleTabPage_GroupName_Expressions);
- final CheckboxPreference useParenthesesPref= createCheckboxPref(expressionsGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseParentheses, CleanUpConstants.EXPRESSIONS_USE_PARENTHESES, CleanUpModifyDialog.FALSE_TRUE);
+ final CheckboxPreference useParenthesesPref= createCheckboxPref(expressionsGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseParentheses, CleanUpConstants.EXPRESSIONS_USE_PARENTHESES, CleanUpModifyDialog.FALSE_TRUE);
intent(expressionsGroup);
final RadioPreference useParenthesesAlwaysPref= createRadioPref(expressionsGroup, 1, CleanUpMessages.CodeStyleTabPage_RadioName_AlwaysUseParantheses, CleanUpConstants.EXPRESSIONS_USE_PARENTHESES_ALWAYS, CleanUpModifyDialog.FALSE_TRUE);
final RadioPreference useParenthesesNeverPref= createRadioPref(expressionsGroup, 1, CleanUpMessages.CodeStyleTabPage_RadioName_NeverUseParantheses, CleanUpConstants.EXPRESSIONS_USE_PARENTHESES_NEVER, CleanUpModifyDialog.FALSE_TRUE);
@@ -75,7 +77,7 @@
Group variableGroup= createGroup(numColumns, composite, CleanUpMessages.CodeStyleTabPage_GroupName_VariableDeclarations);
- final CheckboxPreference useFinalPref= createCheckboxPref(variableGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseFinal, CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL, CleanUpModifyDialog.FALSE_TRUE);
+ final CheckboxPreference useFinalPref= createCheckboxPref(variableGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseFinal, CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL, CleanUpModifyDialog.FALSE_TRUE);
intent(variableGroup);
final CheckboxPreference useFinalFieldsPref= createCheckboxPref(variableGroup, 1, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseFinalForFields, CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL_PRIVATE_FIELDS, CleanUpModifyDialog.FALSE_TRUE);
final CheckboxPreference useFinalParametersPref= createCheckboxPref(variableGroup, 1, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseFinalForParameters, CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL_PARAMETERS, CleanUpModifyDialog.FALSE_TRUE);
@@ -89,5 +91,8 @@
RadioPreference useLambdaPref= createRadioPref(functionalInterfacesGroup, 1, CleanUpMessages.CodeStyleTabPage_RadioName_UseLambdaWherePossible, CleanUpConstants.USE_LAMBDA, CleanUpModifyDialog.FALSE_TRUE);
RadioPreference useAnonymousPref= createRadioPref(functionalInterfacesGroup, 1, CleanUpMessages.CodeStyleTabPage_RadioName_UseAnonymous, CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION, CleanUpModifyDialog.FALSE_TRUE);
registerSlavePreference(convertFunctionalInterfaces, new RadioPreference[] { useLambdaPref, useAnonymousPref });
+
+ CheckboxPreference simplifyLambdaExpressionAndMethodRef= createCheckboxPref(functionalInterfacesGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_SimplifyLambdaExpressionAndMethodRefSyntax, CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF, CleanUpModifyDialog.FALSE_TRUE);
+ registerPreference(simplifyLambdaExpressionAndMethodRef);
}
}