blob: f7bfd73c7b5abd27d6b23de66482924e53c7678c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2021 Fabrice TIERCELIN and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Fabrice TIERCELIN - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.fix;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
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.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
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.FieldAccess;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeMethodReference;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.OrderedInfixExpression;
import org.eclipse.jdt.internal.corext.fix.CleanUpConstants;
import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix;
import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix.CompilationUnitRewriteOperation;
import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
import org.eclipse.jdt.internal.corext.util.ControlWorkflowMatcher;
import org.eclipse.jdt.internal.corext.util.ControlWorkflowMatcherRunnable;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.NodeMatcher;
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 plain comparator instance by a lambda expression passed to a <code>Comparator.comparing()</code> method:
* <ul>
* <li>The <code>Comparator</code> type must be inferred by the destination of the comparator,</li>
* <li>The algorithm of the comparator must be standard and based on one field or method,</li>
* <li>The cleanup can handle the null values.</li>
* </ul>
*/
public class ComparingOnCriteriaCleanUp extends AbstractMultiFix {
private static final class ObjectNotNullMatcher extends NodeMatcher<Expression> {
private final SimpleName name;
private ObjectNotNullMatcher(final SimpleName name) {
this.name= name;
}
@Override
public Boolean isMatching(final Expression node) {
InfixExpression condition= ASTNodes.as(node, InfixExpression.class);
if (condition != null
&& ASTNodes.hasOperator(condition, InfixExpression.Operator.EQUALS, InfixExpression.Operator.NOT_EQUALS)) {
OrderedInfixExpression<SimpleName, NullLiteral> orderedInfix= ASTNodes.orderedInfix(condition, SimpleName.class, NullLiteral.class);
if (orderedInfix != null
&& ASTNodes.isSameVariable(orderedInfix.getFirstOperand(), name)
&& ASTNodes.isPassive(orderedInfix.getFirstOperand())) {
return ASTNodes.hasOperator(condition, InfixExpression.Operator.NOT_EQUALS);
}
}
return null;
}
}
public ComparingOnCriteriaCleanUp() {
this(Collections.emptyMap());
}
public ComparingOnCriteriaCleanUp(final Map<String, String> options) {
super(options);
}
@Override
public CleanUpRequirements getRequirements() {
boolean requireAST= isEnabled(CleanUpConstants.COMPARING_ON_CRITERIA);
return new CleanUpRequirements(requireAST, false, false, null);
}
@Override
public String[] getStepDescriptions() {
if (isEnabled(CleanUpConstants.COMPARING_ON_CRITERIA)) {
return new String[] { MultiFixMessages.ComparingOnCriteriaCleanUp_description };
}
return new String[0];
}
@Override
public String getPreview() {
if (isEnabled(CleanUpConstants.COMPARING_ON_CRITERIA)) {
return "Comparator<Date> comparator = Comparator.nullsFirst(Comparator.comparing(Date::toString));\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; //$NON-NLS-1$
}
return "" //$NON-NLS-1$
+ "Comparator<Date> comparator = new Comparator<Date>() {\n" //$NON-NLS-1$
+ " @Override\n" //$NON-NLS-1$
+ " public int compare(Date o1, Date o2) {\n" //$NON-NLS-1$
+ " if (o2 != null) {\n" //$NON-NLS-1$
+ " if (o1 != null) {\n" //$NON-NLS-1$
+ " return o1.toString().compareTo(o2.toString());\n" //$NON-NLS-1$
+ " }\n" //$NON-NLS-1$
+ " return -1;\n" //$NON-NLS-1$
+ " } else if (o1 != null) {\n" //$NON-NLS-1$
+ " return 1;\n" //$NON-NLS-1$
+ " } else {\n" //$NON-NLS-1$
+ " return 0;\n" //$NON-NLS-1$
+ " }\n" //$NON-NLS-1$
+ " }\n" //$NON-NLS-1$
+ "};\n"; //$NON-NLS-1$
}
private static boolean equalNotNull(final Object obj1, final Object obj2) {
return obj1 != null && obj1.equals(obj2);
}
@Override
protected ICleanUpFix createFix(final CompilationUnit unit) throws CoreException {
if (!isEnabled(CleanUpConstants.COMPARING_ON_CRITERIA) || !JavaModelUtil.is1d8OrHigher(unit.getJavaElement().getJavaProject())) {
return null;
}
final List<CompilationUnitRewriteOperation> rewriteOperations= new ArrayList<>();
unit.accept(new ASTVisitor() {
@Override
public boolean visit(final LambdaExpression visited) {
ITypeBinding targetType= ASTNodes.getTargetType(visited);
if (ASTNodes.hasType(targetType, Comparator.class.getCanonicalName())
&& targetType.getTypeArguments() != null
&& targetType.getTypeArguments().length == 1
&& visited.parameters() != null
&& visited.parameters().size() == 2) {
VariableDeclaration object1= (VariableDeclaration) visited.parameters().get(0);
VariableDeclaration object2= (VariableDeclaration) visited.parameters().get(1);
if (visited.getBody() instanceof Statement) {
return maybeRefactorBody(visited, targetType.getTypeArguments()[0], object1, object2, ASTNodes.asList((Statement) visited.getBody()));
}
if (visited.getBody() instanceof Expression) {
SimpleName name1= object1.getName();
SimpleName name2= object2.getName();
return maybeRefactorExpression(visited, targetType.getTypeArguments()[0], name1, name2, (Expression) visited.getBody());
}
}
return true;
}
@Override
public boolean visit(final ClassInstanceCreation visited) {
AnonymousClassDeclaration anonymousClassDecl= visited.getAnonymousClassDeclaration();
Type type= visited.getType();
if (type != null
&& type.resolveBinding() != null
&& type.resolveBinding().getTypeArguments() != null
&& type.resolveBinding().getTypeArguments().length == 1
&& ASTNodes.hasType(type.resolveBinding(), Comparator.class.getCanonicalName())
&& visited.arguments().isEmpty()
&& anonymousClassDecl != null
&& anonymousClassDecl.bodyDeclarations() != null
&& anonymousClassDecl.bodyDeclarations().size() == 1) {
List<BodyDeclaration> bodies= anonymousClassDecl.bodyDeclarations();
ITypeBinding typeArgument= type.resolveBinding().getTypeArguments()[0];
if (bodies != null
&& bodies.size() == 1
&& typeArgument != null) {
BodyDeclaration body= bodies.get(0);
if (body instanceof MethodDeclaration) {
return maybeRefactorMethod(visited, typeArgument, (MethodDeclaration) body);
}
}
}
return true;
}
private boolean maybeRefactorMethod(final ClassInstanceCreation visited, final ITypeBinding typeArgument,
final MethodDeclaration methodDecl) {
Block methodBody= methodDecl.getBody();
if (ASTNodes.usesGivenSignature(methodDecl, Comparator.class.getCanonicalName(), "compare", typeArgument.getQualifiedName(), //$NON-NLS-1$
typeArgument.getQualifiedName())) {
VariableDeclaration object1= (VariableDeclaration) methodDecl.parameters().get(0);
VariableDeclaration object2= (VariableDeclaration) methodDecl.parameters().get(1);
List<Statement> statements= methodBody.statements();
return maybeRefactorBody(visited, typeArgument, object1, object2, statements);
}
return true;
}
private boolean maybeRefactorBody(final Expression visited, final ITypeBinding typeArgument,
final VariableDeclaration object1, final VariableDeclaration object2, final List<Statement> statements) {
SimpleName name1= object1.getName();
SimpleName name2= object2.getName();
if (!maybeRefactorCompareToMethod(visited, typeArgument, statements, name1, name2)) {
return false;
}
AtomicReference<Expression> criteria= new AtomicReference<>();
AtomicBoolean isForward= new AtomicBoolean(true);
NodeMatcher<Expression> compareToMatcher= new NodeMatcher<Expression>() {
@Override
public Boolean isMatching(final Expression node) {
if (isReturnedExpressionToRefactor(node, criteria, isForward, name1, name2)) {
return Boolean.TRUE;
}
return null;
}
};
NodeMatcher<Expression> zeroMatcher= new NodeMatcher<Expression>() {
@Override
public Boolean isMatching(final Expression node) {
if (Long.valueOf(0L).equals(ASTNodes.getIntegerLiteral(node))) {
return Boolean.TRUE;
}
return null;
}
};
NodeMatcher<Expression> positiveMatcher= new NodeMatcher<Expression>() {
@Override
public Boolean isMatching(final Expression node) {
Long value= ASTNodes.getIntegerLiteral(node);
if (value != null && value > 0L) {
return Boolean.TRUE;
}
return null;
}
};
NodeMatcher<Expression> negativeMatcher= new NodeMatcher<Expression>() {
@Override
public Boolean isMatching(final Expression node) {
Long value= ASTNodes.getIntegerLiteral(node);
if (value != null && value < 0L) {
return Boolean.TRUE;
}
return null;
}
};
ControlWorkflowMatcherRunnable runnableMatcher= ControlWorkflowMatcher.createControlWorkflowMatcher().addWorkflow(new ObjectNotNullMatcher(name1)).condition(new ObjectNotNullMatcher(name2)).returnedValue(compareToMatcher)
.addWorkflow(new ObjectNotNullMatcher(name1).negate()).condition(new ObjectNotNullMatcher(name2).negate()).returnedValue(zeroMatcher)
.addWorkflow(new ObjectNotNullMatcher(name1).negate()).condition(new ObjectNotNullMatcher(name2)).returnedValue(negativeMatcher)
.addWorkflow(new ObjectNotNullMatcher(name1)).condition(new ObjectNotNullMatcher(name2).negate()).returnedValue(positiveMatcher);
if (runnableMatcher.isMatching(statements)) {
rewriteOperations.add(new ComparingOnCriteriaOperation(visited, typeArgument, name1, criteria, isForward, Boolean.TRUE));
return false;
}
runnableMatcher= ControlWorkflowMatcher.createControlWorkflowMatcher().addWorkflow(new ObjectNotNullMatcher(name1)).condition(new ObjectNotNullMatcher(name2)).returnedValue(compareToMatcher)
.addWorkflow(new ObjectNotNullMatcher(name1).negate()).condition(new ObjectNotNullMatcher(name2).negate()).returnedValue(zeroMatcher)
.addWorkflow(new ObjectNotNullMatcher(name1)).condition(new ObjectNotNullMatcher(name2).negate()).returnedValue(negativeMatcher)
.addWorkflow(new ObjectNotNullMatcher(name1).negate()).condition(new ObjectNotNullMatcher(name2)).returnedValue(positiveMatcher);
if (runnableMatcher.isMatching(statements)) {
rewriteOperations.add(new ComparingOnCriteriaOperation(visited, typeArgument, name1, criteria, isForward, Boolean.FALSE));
return false;
}
return true;
}
private boolean maybeRefactorCompareToMethod(final Expression visited, final ITypeBinding typeArgument,
final List<Statement> statements, final SimpleName name1,
final SimpleName name2) {
if (statements != null && statements.size() == 1) {
ReturnStatement returnStatement= ASTNodes.as(statements.get(0), ReturnStatement.class);
if (returnStatement != null) {
return maybeRefactorExpression(visited, typeArgument, name1, name2, returnStatement.getExpression());
}
}
return true;
}
private boolean maybeRefactorExpression(final Expression visited, final ITypeBinding typeArgument,
final SimpleName name1, final SimpleName name2,
final Expression expression) {
AtomicReference<Expression> criteria= new AtomicReference<>();
AtomicBoolean isForward= new AtomicBoolean(true);
if (isReturnedExpressionToRefactor(expression, criteria, isForward, name1, name2)) {
rewriteOperations.add(new ComparingOnCriteriaOperation(visited, typeArgument, name1, criteria, isForward, null));
return false;
}
return true;
}
private boolean isComparable(ITypeBinding classBinding) {
if ("java.lang.Comparable".equals(classBinding.getErasure().getQualifiedName())) { //$NON-NLS-1$
return true;
}
ITypeBinding superClass= classBinding.getSuperclass();
if (superClass != null && isComparable(superClass)) {
return true;
}
for (ITypeBinding binding : classBinding.getInterfaces()) {
if (isComparable(binding)) {
return true;
}
}
return false;
}
private boolean isReturnedExpressionToRefactor(final Expression returnExpression, final AtomicReference<Expression> criteria,
final AtomicBoolean isForward, final SimpleName name1,
final SimpleName name2) {
PrefixExpression negativeExpression= ASTNodes.as(returnExpression, PrefixExpression.class);
if (negativeExpression != null && ASTNodes.hasOperator(negativeExpression, PrefixExpression.Operator.MINUS)) {
isForward.lazySet(!isForward.get());
return isReturnedExpressionToRefactor(negativeExpression.getOperand(), criteria, isForward, name1, name2);
}
MethodInvocation compareToMethod= ASTNodes.as(returnExpression, MethodInvocation.class);
if (compareToMethod != null && compareToMethod.getExpression() != null) {
ITypeBinding comparisonType= compareToMethod.getExpression().resolveTypeBinding();
if (comparisonType != null && isComparable(comparisonType)) {
if (compareToMethod.getExpression() != null
&& ASTNodes.usesGivenSignature(compareToMethod, comparisonType.getQualifiedName(), "compareTo", comparisonType.getQualifiedName())) { //$NON-NLS-1$
return isRefactorComparisonToRefactor(criteria, isForward, name1, name2, compareToMethod.getExpression(), (Expression) compareToMethod.arguments().get(0));
}
String primitiveType= Bindings.getUnboxedTypeName(comparisonType.getQualifiedName());
if (primitiveType != null
&& ASTNodes.usesGivenSignature(compareToMethod, comparisonType.getQualifiedName(), "compare", primitiveType, primitiveType)) { //$NON-NLS-1$
return isRefactorComparisonToRefactor(criteria, isForward, name1, name2, (Expression) compareToMethod.arguments().get(0), (Expression) compareToMethod.arguments().get(1));
}
}
}
return false;
}
private boolean isRefactorComparisonToRefactor(final AtomicReference<Expression> criteria,
final AtomicBoolean isForward, final SimpleName name1, final SimpleName name2, final Expression expr1,
final Expression expr2) {
MethodInvocation method1= ASTNodes.as(expr1, MethodInvocation.class);
MethodInvocation method2= ASTNodes.as(expr2, MethodInvocation.class);
QualifiedName field1= ASTNodes.as(expr1, QualifiedName.class);
QualifiedName field2= ASTNodes.as(expr2, QualifiedName.class);
if (method1 != null
&& method1.arguments().isEmpty()
&& method2 != null
&& method2.arguments().isEmpty()) {
String methodName1= method1.getName().getIdentifier();
String methodName2= method2.getName().getIdentifier();
SimpleName objectExpr1= ASTNodes.as(method1.getExpression(), SimpleName.class);
SimpleName objectExpr2= ASTNodes.as(method2.getExpression(), SimpleName.class);
if (equalNotNull(methodName1, methodName2)
&& objectExpr1 != null
&& objectExpr2 != null) {
if (ASTNodes.isSameVariable(objectExpr1, name1)
&& ASTNodes.isSameVariable(objectExpr2, name2)) {
criteria.set(method1);
return true;
}
if (ASTNodes.isSameVariable(objectExpr1, name2)
&& ASTNodes.isSameVariable(objectExpr2, name1)) {
criteria.set(method1);
isForward.lazySet(!isForward.get());
return true;
}
}
} else if (field1 != null && field2 != null) {
SimpleName fieldName1= field1.getName();
SimpleName fieldName2= field2.getName();
SimpleName objectExpr1= ASTNodes.as(field1.getQualifier(), SimpleName.class);
SimpleName objectExpr2= ASTNodes.as(field2.getQualifier(), SimpleName.class);
if (ASTNodes.isSameVariable(fieldName1, fieldName2)
&& objectExpr1 != null
&& objectExpr2 != null) {
if (ASTNodes.isSameVariable(objectExpr1, name1)
&& ASTNodes.isSameVariable(objectExpr2, name2)) {
criteria.set(field1);
return true;
}
if (ASTNodes.isSameVariable(objectExpr1, name2)
&& ASTNodes.isSameVariable(objectExpr2, name1)) {
criteria.set(field1);
isForward.lazySet(!isForward.get());
return true;
}
}
}
return false;
}
});
if (rewriteOperations.isEmpty()) {
return null;
}
return new CompilationUnitRewriteOperationsFix(MultiFixMessages.ComparingOnCriteriaCleanUp_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 ComparingOnCriteriaOperation extends CompilationUnitRewriteOperation {
private final Expression visited;
private final ITypeBinding typeArgument;
private final SimpleName name1;
private final AtomicReference<Expression> criteria;
private final AtomicBoolean isForward;
private final Boolean isNullFirst;
public ComparingOnCriteriaOperation(final Expression visited, final ITypeBinding typeArgument,
final SimpleName name1, final AtomicReference<Expression> criteria, final AtomicBoolean isForward, final Boolean isNullFirst) {
this.visited= visited;
this.typeArgument= typeArgument;
this.name1= name1;
this.criteria= criteria;
this.isForward= isForward;
this.isNullFirst= isNullFirst;
}
@Override
public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
ASTRewrite rewrite= cuRewrite.getASTRewrite();
AST ast= cuRewrite.getRoot().getAST();
ImportRewrite importRewrite= cuRewrite.getImportRewrite();
TextEditGroup group= createTextEditGroup(MultiFixMessages.ComparingOnCriteriaCleanUp_description, cuRewrite);
String comparatorNameText= importRewrite.addImport(Comparator.class.getCanonicalName());
Expression lambda;
if (criteria.get() instanceof MethodInvocation) {
lambda= buildMethod(typeArgument, (MethodInvocation) criteria.get(), rewrite, ast, importRewrite);
} else {
lambda= buildField(visited, typeArgument, isForward.get(), isNullFirst, (QualifiedName) criteria.get(), name1, rewrite, ast, importRewrite);
}
MethodInvocation comparingMethod= ast.newMethodInvocation();
comparingMethod.setExpression(ASTNodeFactory.newName(ast, comparatorNameText));
comparingMethod.setName(ast.newSimpleName("comparing")); //$NON-NLS-1$
comparingMethod.arguments().add(lambda);
if (!isForward.get()) {
MethodInvocation newMethodInvocation= ast.newMethodInvocation();
newMethodInvocation.setExpression(comparingMethod);
newMethodInvocation.setName(ast.newSimpleName("reversed")); //$NON-NLS-1$
comparingMethod= newMethodInvocation;
}
if (isNullFirst != null) {
MethodInvocation newMethodInvocation= ast.newMethodInvocation();
newMethodInvocation.setExpression(ASTNodeFactory.newName(ast, comparatorNameText));
newMethodInvocation.setName(ast.newSimpleName(isNullFirst ? "nullsFirst" : "nullsLast")); //$NON-NLS-1$ //$NON-NLS-2$
newMethodInvocation.arguments().add(comparingMethod);
comparingMethod= newMethodInvocation;
}
ASTNodes.replaceButKeepComment(rewrite, visited, comparingMethod, group);
}
private TypeMethodReference buildMethod(final ITypeBinding type, final MethodInvocation method, final ASTRewrite rewrite, final AST ast, final ImportRewrite importRewrite) {
String comparedClassNameText= importRewrite.addImport(type);
TypeMethodReference typeMethodRef= ast.newTypeMethodReference();
typeMethodRef.setType(ast.newSimpleType(ASTNodeFactory.newName(ast, comparedClassNameText)));
typeMethodRef.setName(ASTNodes.createMoveTarget(rewrite, method.getName()));
return typeMethodRef;
}
private LambdaExpression buildField(final Expression node, final ITypeBinding type, final boolean straightOrder,
final Boolean isNullValuesFirst, final QualifiedName field, final SimpleName name, final ASTRewrite rewrite, final AST ast, final ImportRewrite importRewrite) {
LambdaExpression lambdaExpression= ast.newLambdaExpression();
ITypeBinding destinationType= ASTNodes.getTargetType(node);
boolean isTypeKnown= destinationType != null
&& ASTNodes.hasType(destinationType, Comparator.class.getCanonicalName())
&& destinationType.getTypeArguments() != null
&& destinationType.getTypeArguments().length == 1
&& equalNotNull(destinationType.getTypeArguments()[0], type);
if (isTypeKnown && straightOrder && isNullValuesFirst == null) {
VariableDeclarationFragment newVariableDeclarationFragment= ast.newVariableDeclarationFragment();
newVariableDeclarationFragment.setName((SimpleName) rewrite.createCopyTarget(name));
lambdaExpression.parameters().add(newVariableDeclarationFragment);
} else {
String comparedClassNameText= importRewrite.addImport(type);
SingleVariableDeclaration newSingleVariableDeclaration= ast.newSingleVariableDeclaration();
newSingleVariableDeclaration.setType(ast.newSimpleType(ASTNodeFactory.newName(ast, comparedClassNameText)));
newSingleVariableDeclaration.setName((SimpleName) rewrite.createCopyTarget(name));
lambdaExpression.parameters().add(newSingleVariableDeclaration);
}
FieldAccess newFieldAccess= ast.newFieldAccess();
newFieldAccess.setExpression((SimpleName) rewrite.createCopyTarget(name));
newFieldAccess.setName(ASTNodes.createMoveTarget(rewrite, field.getName()));
lambdaExpression.setBody(newFieldAccess);
lambdaExpression.setParentheses(false);
return lambdaExpression;
}
}
}