Bug 543347 - Create a LambdaExpressionsFixCore class for jdt.ls
- create new LambdaExpressionsFixCore class in jdt.core.manipulation
based on LambdaExpressionsFix
- create IAnonymousClassCreationOperation interface in
LambdaExpressionsFixCore so that the majority of logic
in CreateAnonymousClassCreationOperation can be shared between
LambdaExpressionsFixCore and LambdaExpressionsFix
- replace LambdaExpressionsFix CreateLambdaOperation and
CreateAnonymousClassCreation static classes with ones in
LambdaExpressionsFixCore
- add new constructor to CompilationUnitRewriteOperationsFix that
takes CompilationUnitRewriteOperation from
CompilationUnitRewriteOperationsFixCore array as last argument
- have jdt.ui LambdaExpressionsFix use other LambdaExpressionsFixCore
static classes
Change-Id: I828683dda5ee576398f94cb5f865602478792a05
Signed-off-by: Jeff Johnston <jjohnstn@redhat.com>
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFixCore.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFixCore.java
new file mode 100644
index 0000000..f62f296
--- /dev/null
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFixCore.java
@@ -0,0 +1,762 @@
+/*******************************************************************************
+ * Copyright (c) 2013, 2019 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Copied from package org.eclipse.jdt.internal.corext.fix.LambdaExpressionsFix
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Jerome Cambon <jerome.cambon@oracle.com> - [1.8][clean up][quick assist] Convert lambda to anonymous must qualify references to 'this'/'super' - https://bugs.eclipse.org/430573
+ * Stephan Herrmann - Contribution for Bug 463360 - [override method][null] generating method override should not create redundant null annotations
+ * Red Hat Inc. - modified to create core class in jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+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.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+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.CastExpression;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.IAnnotationBinding;
+import org.eclipse.jdt.core.dom.IBinding;
+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.MarkerAnnotation;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.Type;
+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.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
+
+import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
+import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2Core;
+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.HierarchicalASTVisitor;
+import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.structure.ImportRemover;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.internal.corext.util.JdtFlags;
+
+import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
+
+public class LambdaExpressionsFixCore extends CompilationUnitRewriteOperationsFixCore {
+
+ public static final class FunctionalAnonymousClassesFinder extends ASTVisitor {
+
+ private final ArrayList<ClassInstanceCreation> fNodes= new ArrayList<>();
+
+ public static ArrayList<ClassInstanceCreation> perform(ASTNode node) {
+ FunctionalAnonymousClassesFinder finder= new FunctionalAnonymousClassesFinder();
+ node.accept(finder);
+ return finder.fNodes;
+ }
+
+ @Override
+ public boolean visit(ClassInstanceCreation node) {
+ if (isFunctionalAnonymous(node) && !fConversionRemovesAnnotations) {
+ fNodes.add(node);
+ }
+ return true;
+ }
+ }
+
+ public static final class LambdaExpressionsFinder extends ASTVisitor {
+
+ private final ArrayList<LambdaExpression> fNodes= new ArrayList<>();
+
+ public static ArrayList<LambdaExpression> perform(ASTNode node) {
+ LambdaExpressionsFinder finder= new LambdaExpressionsFinder();
+ node.accept(finder);
+ return finder.fNodes;
+ }
+
+ @Override
+ public boolean visit(LambdaExpression node) {
+ ITypeBinding typeBinding= node.resolveTypeBinding();
+ if (typeBinding != null && typeBinding.getFunctionalInterfaceMethod() != null) {
+ fNodes.add(node);
+ }
+ return true;
+ }
+ }
+
+ public static class AbortSearchException extends RuntimeException {
+ private static final long serialVersionUID= 1L;
+ }
+
+ public static final class SuperThisReferenceFinder extends HierarchicalASTVisitor {
+
+ private ITypeBinding fFunctionalInterface;
+ private MethodDeclaration fMethodDeclaration;
+
+ static boolean hasReference(MethodDeclaration node) {
+ try {
+ SuperThisReferenceFinder finder= new SuperThisReferenceFinder();
+ ClassInstanceCreation cic= (ClassInstanceCreation) node.getParent().getParent();
+ finder.fFunctionalInterface= cic.getType().resolveBinding();
+ finder.fMethodDeclaration= node;
+ node.accept(finder);
+ } catch (AbortSearchException e) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(AnonymousClassDeclaration node) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(BodyDeclaration node) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(MethodDeclaration node) {
+ return node == fMethodDeclaration;
+ }
+
+ @Override
+ public boolean visit(ThisExpression node) {
+ if (node.getQualifier() == null) {
+ throw new AbortSearchException();
+ }
+ return true; // references to outer scope are harmless
+ }
+
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ if (node.getQualifier() == null) {
+ throw new AbortSearchException();
+ } else {
+ IBinding qualifierType= node.getQualifier().resolveBinding();
+ if (qualifierType instanceof ITypeBinding && ((ITypeBinding) qualifierType).isInterface()) {
+ throw new AbortSearchException(); // JLS8: new overloaded meaning of 'interface'.super.'method'(..)
+ }
+ }
+ return true; // references to outer scopes are harmless
+ }
+
+ @Override
+ public boolean visit(SuperFieldAccess node) {
+ if (node.getQualifier() == null) {
+ throw new AbortSearchException();
+ }
+ return true; // references to outer scope are harmless
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node) {
+ IMethodBinding binding= node.resolveMethodBinding();
+ if (binding != null && !JdtFlags.isStatic(binding) && node.getExpression() == null && Bindings.isSuperType(binding.getDeclaringClass(), fFunctionalInterface, false)) {
+ throw new AbortSearchException();
+ }
+ return true;
+ }
+ }
+
+ public static final class SuperThisQualifier extends HierarchicalASTVisitor {
+
+ private ITypeBinding fQualifierTypeBinding;
+ private ImportRewrite fImportRewrite;
+ private ASTRewrite fASTRewrite;
+ private TextEditGroup fGroup;
+
+ public static void perform(LambdaExpression lambdaExpression, ITypeBinding parentTypeBinding, CompilationUnitRewrite cuRewrite, TextEditGroup group) {
+ SuperThisQualifier qualifier= new SuperThisQualifier();
+ qualifier.fQualifierTypeBinding= parentTypeBinding;
+ qualifier.fImportRewrite= cuRewrite.getImportRewrite();
+ qualifier.fASTRewrite= cuRewrite.getASTRewrite();
+ qualifier.fGroup= group;
+ lambdaExpression.accept(qualifier);
+ }
+
+ public Name getQualifierTypeName() {
+ String typeName= fImportRewrite.addImport(fQualifierTypeBinding);
+ return fASTRewrite.getAST().newName(typeName);
+ }
+
+ @Override
+ public boolean visit(AnonymousClassDeclaration node) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(BodyDeclaration node) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(SuperFieldAccess node) {
+ if (node.getQualifier() == null) {
+ fASTRewrite.set(node, SuperFieldAccess.QUALIFIER_PROPERTY, getQualifierTypeName(), fGroup);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ if (node.getQualifier() == null) {
+ fASTRewrite.set(node, SuperMethodInvocation.QUALIFIER_PROPERTY, getQualifierTypeName(), fGroup);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visit(ThisExpression node) {
+ if (node.getQualifier() == null) {
+ fASTRewrite.set(node, ThisExpression.QUALIFIER_PROPERTY, getQualifierTypeName(), fGroup);
+ }
+ return true;
+ }
+ }
+
+ public static final class AnnotationsFinder extends ASTVisitor {
+ public static boolean hasAnnotations(SingleVariableDeclaration methodParameter) {
+ try {
+ AnnotationsFinder finder= new AnnotationsFinder();
+ methodParameter.accept(finder);
+ } catch (AbortSearchException e) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(MarkerAnnotation node) {
+ throw new AbortSearchException();
+ }
+
+ @Override
+ public boolean visit(NormalAnnotation node) {
+ throw new AbortSearchException();
+ }
+
+ @Override
+ public boolean visit(SingleMemberAnnotation node) {
+ throw new AbortSearchException();
+ }
+ }
+
+ public static class CreateLambdaOperation extends CompilationUnitRewriteOperation {
+
+ private final List<ClassInstanceCreation> fExpressions;
+
+ public CreateLambdaOperation(List<ClassInstanceCreation> expressions) {
+ fExpressions= expressions;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ ImportRemover importRemover= cuRewrite.getImportRemover();
+ AST ast= rewrite.getAST();
+
+ HashMap<ClassInstanceCreation, HashSet<String>> cicToNewNames= new HashMap<>();
+ for (int i= 0; i < fExpressions.size(); i++) {
+ ClassInstanceCreation classInstanceCreation= fExpressions.get(i);
+ TextEditGroup group= createTextEditGroup(FixMessages.LambdaExpressionsFix_convert_to_lambda_expression, cuRewrite);
+
+ AnonymousClassDeclaration anonymTypeDecl= classInstanceCreation.getAnonymousClassDeclaration();
+ List<BodyDeclaration> bodyDeclarations= anonymTypeDecl.bodyDeclarations();
+
+ Object object= bodyDeclarations.get(0);
+ if (!(object instanceof MethodDeclaration)) {
+ continue;
+ }
+ MethodDeclaration methodDeclaration= (MethodDeclaration) object;
+ HashSet<String> excludedNames= new HashSet<>();
+ if (i != 0) {
+ for (ClassInstanceCreation convertedCic : fExpressions.subList(0, i)) {
+ if (ASTNodes.isParent(convertedCic, classInstanceCreation)) {
+ excludedNames.addAll(cicToNewNames.get(convertedCic));
+ }
+ }
+ }
+ HashSet<String> newNames= makeNamesUnique(excludedNames, methodDeclaration, rewrite, group);
+ cicToNewNames.put(classInstanceCreation, new HashSet<>(newNames));
+ List<SingleVariableDeclaration> methodParameters= methodDeclaration.parameters();
+
+ // use short form with inferred parameter types and without parentheses if possible
+ boolean createExplicitlyTypedParameters= false;
+ for (SingleVariableDeclaration methodParameter : methodParameters) {
+ if (AnnotationsFinder.hasAnnotations(methodParameter)) {
+ createExplicitlyTypedParameters= true;
+ break;
+ }
+ }
+ LambdaExpression lambdaExpression= ast.newLambdaExpression();
+ List<VariableDeclaration> lambdaParameters= lambdaExpression.parameters();
+ lambdaExpression.setParentheses(createExplicitlyTypedParameters || methodParameters.size() != 1);
+ for (SingleVariableDeclaration methodParameter : methodParameters) {
+ if (createExplicitlyTypedParameters) {
+ lambdaParameters.add((SingleVariableDeclaration) rewrite.createCopyTarget(methodParameter));
+ importRemover.registerRetainedNode(methodParameter);
+ } else {
+ VariableDeclarationFragment lambdaParameter= ast.newVariableDeclarationFragment();
+ lambdaParameter.setName((SimpleName) rewrite.createCopyTarget(methodParameter.getName()));
+ lambdaParameters.add(lambdaParameter);
+ }
+ }
+
+ Block body= methodDeclaration.getBody();
+ List<Statement> statements= body.statements();
+ ASTNode lambdaBody= body;
+ if (statements.size() == 1) {
+ // use short form with just an expression body if possible
+ Statement statement= statements.get(0);
+ if (statement instanceof ExpressionStatement) {
+ lambdaBody= ((ExpressionStatement) statement).getExpression();
+ } else if (statement instanceof ReturnStatement) {
+ Expression returnExpression= ((ReturnStatement) statement).getExpression();
+ if (returnExpression != null) {
+ lambdaBody= returnExpression;
+ }
+ }
+ }
+ //TODO: Bug 421479: [1.8][clean up][quick assist] convert anonymous to lambda must consider lost scope of interface
+ // lambdaBody.accept(new InterfaceAccessQualifier(rewrite, classInstanceCreation.getType().resolveBinding())); //TODO: maybe need a separate ASTRewrite and string placeholder
+
+ lambdaExpression.setBody(ASTNodes.getCopyOrReplacement(rewrite, lambdaBody, group));
+ Expression replacement= lambdaExpression;
+ ITypeBinding targetTypeBinding= ASTNodes.getTargetType(classInstanceCreation);
+ if (ASTNodes.isTargetAmbiguous(classInstanceCreation, ASTNodes.isExplicitlyTypedLambda(lambdaExpression)) || targetTypeBinding.getFunctionalInterfaceMethod() == null) {
+ CastExpression cast= ast.newCastExpression();
+ cast.setExpression(lambdaExpression);
+ ImportRewrite importRewrite= cuRewrite.getImportRewrite();
+ ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(classInstanceCreation, importRewrite);
+ Type castType= importRewrite.addImport(classInstanceCreation.getType().resolveBinding(), ast, importRewriteContext, TypeLocation.CAST);
+ cast.setType(castType);
+ importRemover.registerAddedImports(castType);
+ replacement= cast;
+ }
+ rewrite.replace(classInstanceCreation, replacement, group);
+
+ importRemover.registerRemovedNode(classInstanceCreation);
+ importRemover.registerRetainedNode(lambdaBody);
+ }
+ }
+
+ private HashSet<String> makeNamesUnique(HashSet<String> excludedNames, MethodDeclaration methodDeclaration, ASTRewrite rewrite, TextEditGroup group) {
+ HashSet<String> newNames= new HashSet<>();
+ excludedNames.addAll(ASTNodes.getVisibleLocalVariablesInScope(methodDeclaration));
+ List<SimpleName> simpleNamesInMethod= getNamesInMethod(methodDeclaration);
+ List<String> namesInMethod= new ArrayList<>();
+ for (SimpleName name : simpleNamesInMethod) {
+ namesInMethod.add(name.getIdentifier());
+ }
+
+ for (int i= 0; i < simpleNamesInMethod.size(); i++) {
+ SimpleName name= simpleNamesInMethod.get(i);
+ String identifier= namesInMethod.get(i);
+ HashSet<String> allNamesToExclude= getNamesToExclude(excludedNames, namesInMethod, i);
+ if (allNamesToExclude.contains(identifier)) {
+ String newIdentifier= createName(identifier, allNamesToExclude);
+ excludedNames.add(newIdentifier);
+ newNames.add(newIdentifier);
+ SimpleName[] references= LinkedNodeFinder.findByNode(name.getRoot(), name);
+ for (SimpleName ref : references) {
+ rewrite.set(ref, SimpleName.IDENTIFIER_PROPERTY, newIdentifier, group);
+ }
+ }
+ }
+
+ return newNames;
+ }
+
+ private HashSet<String> getNamesToExclude(HashSet<String> excludedNames, List<String> namesInMethod, int i) {
+ HashSet<String> allNamesToExclude= new HashSet<>(excludedNames);
+ allNamesToExclude.addAll(namesInMethod.subList(0, i));
+ allNamesToExclude.addAll(namesInMethod.subList(i + 1, namesInMethod.size()));
+ return allNamesToExclude;
+ }
+
+ private List<SimpleName> getNamesInMethod(MethodDeclaration methodDeclaration) {
+ class NamesCollector extends HierarchicalASTVisitor {
+ private int fTypeCounter;
+
+ private List<SimpleName> fNames= new ArrayList<>();
+
+ @Override
+ public boolean visit(AbstractTypeDeclaration node) {
+ if (fTypeCounter++ == 0) {
+ fNames.add(node.getName());
+ }
+ return true;
+ }
+
+ @Override
+ public void endVisit(AbstractTypeDeclaration node) {
+ fTypeCounter--;
+ }
+
+ @Override
+ public boolean visit(AnonymousClassDeclaration node) {
+ fTypeCounter++;
+ return true;
+ }
+
+ @Override
+ public void endVisit(AnonymousClassDeclaration node) {
+ fTypeCounter--;
+ }
+
+ @Override
+ public boolean visit(VariableDeclaration node) {
+ if (fTypeCounter == 0) {
+ fNames.add(node.getName());
+ }
+ return true;
+ }
+ }
+
+ NamesCollector namesCollector= new NamesCollector();
+ methodDeclaration.accept(namesCollector);
+ return namesCollector.fNames;
+ }
+
+ private String createName(String candidate, HashSet<String> excludedNames) {
+ int i= 1;
+ String result= candidate;
+ while (excludedNames.contains(result)) {
+ result= candidate + i++;
+ }
+ return result;
+ }
+ }
+
+ public static interface IAnonymousClassCreationOperation {
+
+ public MethodDeclaration getMethodDeclaration(ICompilationUnit cu, ASTRewrite rewrite, ImportRewrite rewrites,
+ ImportRewriteContext context, IMethodBinding binding, String[] parameterNames, ITypeBinding targetType,
+ boolean inInterface, ASTNode astNode) throws CoreException;
+
+ }
+
+ public static class CreateAnonymousClassCreationOperation extends CompilationUnitRewriteOperation implements IAnonymousClassCreationOperation {
+
+ private final List<LambdaExpression> fExpressions;
+
+ public CreateAnonymousClassCreationOperation(List<LambdaExpression> changedNodes) {
+ fExpressions= changedNodes;
+ }
+
+ @Override
+ public MethodDeclaration getMethodDeclaration(ICompilationUnit cu, ASTRewrite rewrite, ImportRewrite rewrites,
+ ImportRewriteContext context, IMethodBinding binding, String[] parameterNames, ITypeBinding targetType,
+ boolean inInterface, ASTNode astNode) throws CoreException {
+ final CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(cu.getJavaProject());
+ MethodDeclaration methodDeclaration= StubUtility2Core.createImplementationStubCore(cu, rewrite, rewrites, context, binding, parameterNames, targetType, settings, inInterface,
+ astNode,
+ false);
+ return methodDeclaration;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ rewriteAST(this, cuRewrite, model);
+ }
+
+ public void rewriteAST(IAnonymousClassCreationOperation op, CompilationUnitRewrite cuRewrite, @SuppressWarnings("unused") LinkedProposalModelCore model) throws CoreException {
+
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ AST ast= rewrite.getAST();
+
+ for (Iterator<LambdaExpression> iterator= fExpressions.iterator(); iterator.hasNext();) {
+ LambdaExpression lambdaExpression= iterator.next();
+ TextEditGroup group= createTextEditGroup(FixMessages.LambdaExpressionsFix_convert_to_anonymous_class_creation, cuRewrite);
+
+ ITypeBinding lambdaTypeBinding= lambdaExpression.resolveTypeBinding();
+ IMethodBinding methodBinding= lambdaTypeBinding.getFunctionalInterfaceMethod();
+ List<VariableDeclaration> parameters= lambdaExpression.parameters();
+ String[] parameterNames= new String[parameters.size()];
+ for (int i= 0; i < parameterNames.length; i++) {
+ parameterNames[i]= parameters.get(i).getName().getIdentifier();
+ }
+
+ ImportRewrite importRewrite= cuRewrite.getImportRewrite();
+ ImportRewriteContext importContext= new ContextSensitiveImportRewriteContext(lambdaExpression, importRewrite);
+
+ MethodDeclaration methodDeclaration= op.getMethodDeclaration(cuRewrite.getCu(), rewrite, importRewrite, importContext, methodBinding, parameterNames, lambdaTypeBinding, false,
+ lambdaExpression);
+
+ // Qualify reference to this or super
+ ASTNode parentType= ASTResolving.findParentType(lambdaExpression);
+ ITypeBinding parentTypeBinding= null;
+ if (parentType instanceof AbstractTypeDeclaration) {
+ parentTypeBinding= ((AbstractTypeDeclaration) parentType).resolveBinding();
+ } else if (parentType instanceof AnonymousClassDeclaration) {
+ parentTypeBinding= ((AnonymousClassDeclaration) parentType).resolveBinding();
+ }
+ if (parentTypeBinding != null) {
+ parentTypeBinding= Bindings.normalizeTypeBinding(parentTypeBinding);
+ if (parentTypeBinding != null) {
+ SuperThisQualifier.perform(lambdaExpression, parentTypeBinding.getTypeDeclaration(), cuRewrite, group);
+ }
+ }
+
+ Block block;
+ ASTNode lambdaBody= lambdaExpression.getBody();
+ if (lambdaBody instanceof Block) {
+ block= (Block) ASTNodes.getCopyOrReplacement(rewrite, lambdaBody, group);
+ } else {
+ block= ast.newBlock();
+ List<Statement> statements= block.statements();
+ ITypeBinding returnType= methodBinding.getReturnType();
+ Expression copyTarget= (Expression) ASTNodes.getCopyOrReplacement(rewrite, lambdaBody, group);
+ if (Bindings.isVoidType(returnType)) {
+ ExpressionStatement newExpressionStatement= ast.newExpressionStatement(copyTarget);
+ statements.add(newExpressionStatement);
+ } else {
+ ReturnStatement returnStatement= ast.newReturnStatement();
+ returnStatement.setExpression(copyTarget);
+ statements.add(returnStatement);
+ }
+ }
+ methodDeclaration.setBody(block);
+
+ AnonymousClassDeclaration anonymousClassDeclaration= ast.newAnonymousClassDeclaration();
+ List<BodyDeclaration> bodyDeclarations= anonymousClassDeclaration.bodyDeclarations();
+ bodyDeclarations.add(methodDeclaration);
+
+ Type creationType= ASTNodeFactory.newCreationType(ast, lambdaTypeBinding, importRewrite, importContext);
+
+ ClassInstanceCreation classInstanceCreation= ast.newClassInstanceCreation();
+ classInstanceCreation.setType(creationType);
+ classInstanceCreation.setAnonymousClassDeclaration(anonymousClassDeclaration);
+
+ ASTNode toReplace= lambdaExpression;
+ if (lambdaExpression.getLocationInParent() == CastExpression.EXPRESSION_PROPERTY && lambdaTypeBinding.isEqualTo(((CastExpression) lambdaExpression.getParent()).resolveTypeBinding())) {
+ // remove cast to same type as the anonymous will use
+ toReplace= lambdaExpression.getParent();
+ }
+ rewrite.replace(toReplace, classInstanceCreation, group);
+ }
+ }
+ }
+
+ private static boolean fConversionRemovesAnnotations;
+
+ public static LambdaExpressionsFixCore createConvertToLambdaFix(ClassInstanceCreation cic) {
+ CompilationUnit root= (CompilationUnit) cic.getRoot();
+ if (!JavaModelUtil.is18OrHigher(root.getJavaElement().getJavaProject())) {
+ return null;
+ }
+
+ if (!LambdaExpressionsFixCore.isFunctionalAnonymous(cic)) {
+ return null;
+ }
+
+ CreateLambdaOperation op= new CreateLambdaOperation(Collections.singletonList(cic));
+ String message;
+ if (fConversionRemovesAnnotations) {
+ message= FixMessages.LambdaExpressionsFix_convert_to_lambda_expression_removes_annotations;
+ } else {
+ message= FixMessages.LambdaExpressionsFix_convert_to_lambda_expression;
+ }
+ return new LambdaExpressionsFixCore(message, root, new CompilationUnitRewriteOperation[] { op });
+ }
+
+ public static IProposableFix createConvertToAnonymousClassCreationsFix(LambdaExpression lambda) {
+ // offer the quick assist at pre 1.8 levels as well to get rid of the compilation error (TODO: offer this as a quick fix in that case)
+
+ if (lambda.resolveTypeBinding() == null || lambda.resolveTypeBinding().getFunctionalInterfaceMethod() == null) {
+ return null;
+ }
+
+ CreateAnonymousClassCreationOperation op= new CreateAnonymousClassCreationOperation(Collections.singletonList(lambda));
+ CompilationUnit root= (CompilationUnit) lambda.getRoot();
+ return new LambdaExpressionsFixCore(FixMessages.LambdaExpressionsFix_convert_to_anonymous_class_creation, root, new CompilationUnitRewriteOperation[] { op });
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit, boolean useLambda, boolean useAnonymous) {
+ if (!JavaModelUtil.is18OrHigher(compilationUnit.getJavaElement().getJavaProject())) {
+ return null;
+ }
+
+ if (useLambda) {
+ ArrayList<ClassInstanceCreation> convertibleNodes= FunctionalAnonymousClassesFinder.perform(compilationUnit);
+ if (convertibleNodes.isEmpty()) {
+ return null;
+ }
+
+ Collections.reverse(convertibleNodes); // process nested anonymous classes first
+ CompilationUnitRewriteOperation op= new CreateLambdaOperation(convertibleNodes);
+ return new LambdaExpressionsFixCore(FixMessages.LambdaExpressionsFix_convert_to_lambda_expression, compilationUnit, new CompilationUnitRewriteOperation[] { op });
+
+ } else if (useAnonymous) {
+ ArrayList<LambdaExpression> convertibleNodes= LambdaExpressionsFinder.perform(compilationUnit);
+ if (convertibleNodes.isEmpty()) {
+ return null;
+ }
+
+ Collections.reverse(convertibleNodes); // process nested lambdas first
+ CompilationUnitRewriteOperation op= new CreateAnonymousClassCreationOperation(convertibleNodes);
+ return new LambdaExpressionsFixCore(FixMessages.LambdaExpressionsFix_convert_to_anonymous_class_creation, compilationUnit, new CompilationUnitRewriteOperation[] { op });
+
+ }
+ return null;
+ }
+
+ public LambdaExpressionsFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
+ super(name, compilationUnit, fixRewriteOperations);
+ }
+
+ public static boolean isFunctionalAnonymous(ClassInstanceCreation node) {
+ ITypeBinding typeBinding= node.resolveTypeBinding();
+ if (typeBinding == null) {
+ return false;
+ }
+ ITypeBinding[] interfaces= typeBinding.getInterfaces();
+ if (interfaces.length != 1) {
+ return false;
+ }
+ if (interfaces[0].getFunctionalInterfaceMethod() == null) {
+ return false;
+ }
+
+ AnonymousClassDeclaration anonymTypeDecl= node.getAnonymousClassDeclaration();
+ if (anonymTypeDecl == null || anonymTypeDecl.resolveBinding() == null) {
+ return false;
+ }
+
+ List<BodyDeclaration> bodyDeclarations= anonymTypeDecl.bodyDeclarations();
+ // cannot convert if there are fields or additional methods
+ if (bodyDeclarations.size() != 1) {
+ return false;
+ }
+ BodyDeclaration bodyDeclaration= bodyDeclarations.get(0);
+ if (!(bodyDeclaration instanceof MethodDeclaration)) {
+ return false;
+ }
+
+ MethodDeclaration methodDecl= (MethodDeclaration) bodyDeclaration;
+ IMethodBinding methodBinding= methodDecl.resolveBinding();
+
+ if (methodBinding == null) {
+ return false;
+ }
+ // generic lambda expressions are not allowed
+ if (methodBinding.isGenericMethod()) {
+ return false;
+ }
+
+ int modifiers= methodBinding.getModifiers();
+ if (Modifier.isSynchronized(modifiers) || Modifier.isStrictfp(modifiers)) {
+ return false;
+ }
+
+ // lambda cannot refer to 'this'/'super' literals
+ if (SuperThisReferenceFinder.hasReference(methodDecl)) {
+ return false;
+ }
+
+ if (ASTNodes.getTargetType(node) == null) {
+ return false;
+ }
+
+ // Check if annotations other than @Override and @Deprecated will be removed
+ checkAnnotationsRemoval(methodBinding);
+
+ return true;
+ }
+
+ public static void checkAnnotationsRemoval(IMethodBinding methodBinding) {
+ fConversionRemovesAnnotations= false;
+ IAnnotationBinding[] declarationAnnotations= methodBinding.getAnnotations();
+ for (IAnnotationBinding declarationAnnotation : declarationAnnotations) {
+ ITypeBinding annotationType= declarationAnnotation.getAnnotationType();
+ if (annotationType != null) {
+ String qualifiedName= annotationType.getQualifiedName();
+ if (!"java.lang.Override".equals(qualifiedName) && !"java.lang.Deprecated".equals(qualifiedName)) { //$NON-NLS-1$ //$NON-NLS-2$
+ fConversionRemovesAnnotations= true;
+ return;
+ }
+ }
+ }
+ }
+
+ /*private static boolean isInTargetTypeContext(ClassInstanceCreation node) {
+ ITypeBinding targetType= ASTNodes.getTargetType(node);
+ return targetType != null && targetType.getFunctionalInterfaceMethod() != null;
+
+
+ //TODO: probably incomplete, should reuse https://bugs.eclipse.org/bugs/show_bug.cgi?id=408966#c6
+ StructuralPropertyDescriptor locationInParent= node.getLocationInParent();
+
+ if (locationInParent == ReturnStatement.EXPRESSION_PROPERTY) {
+ MethodDeclaration methodDeclaration= ASTResolving.findParentMethodDeclaration(node);
+ if (methodDeclaration == null)
+ return false;
+ IMethodBinding methodBinding= methodDeclaration.resolveBinding();
+ if (methodBinding == null)
+ return false;
+ //TODO: could also cast to the CIC type instead of aborting...
+ return methodBinding.getReturnType().getFunctionalInterfaceMethod() != null;
+ }
+
+ //TODO: should also check whether variable is of a functional type
+ return locationInParent == SingleVariableDeclaration.INITIALIZER_PROPERTY
+ || locationInParent == VariableDeclarationFragment.INITIALIZER_PROPERTY
+ || locationInParent == Assignment.RIGHT_HAND_SIDE_PROPERTY
+ || locationInParent == ArrayInitializer.EXPRESSIONS_PROPERTY
+
+ || locationInParent == MethodInvocation.ARGUMENTS_PROPERTY
+ || locationInParent == SuperMethodInvocation.ARGUMENTS_PROPERTY
+ || locationInParent == ConstructorInvocation.ARGUMENTS_PROPERTY
+ || locationInParent == SuperConstructorInvocation.ARGUMENTS_PROPERTY
+ || locationInParent == ClassInstanceCreation.ARGUMENTS_PROPERTY
+ || locationInParent == EnumConstantDeclaration.ARGUMENTS_PROPERTY
+
+ || locationInParent == LambdaExpression.BODY_PROPERTY
+ || locationInParent == ConditionalExpression.THEN_EXPRESSION_PROPERTY
+ || locationInParent == ConditionalExpression.ELSE_EXPRESSION_PROPERTY
+ || locationInParent == CastExpression.EXPRESSION_PROPERTY;
+
+ }*/
+}
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CompilationUnitRewriteOperationsFix.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CompilationUnitRewriteOperationsFix.java
index e971acb..92b4aa9 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CompilationUnitRewriteOperationsFix.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CompilationUnitRewriteOperationsFix.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007, 2018 IBM Corporation and others.
+ * Copyright (c) 2007, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -22,7 +22,7 @@
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
public class CompilationUnitRewriteOperationsFix extends CompilationUnitRewriteOperationsFixCore implements ILinkedFix {
-
+
public abstract static class CompilationUnitRewriteOperation extends CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation {
public abstract void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel linkedModel) throws CoreException;
@@ -43,6 +43,11 @@
fLinkedProposalModel= new LinkedProposalModel();
}
+ public CompilationUnitRewriteOperationsFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] operations) {
+ super(name, compilationUnit, operations);
+ fLinkedProposalModel= new LinkedProposalModel();
+ }
+
@Override
public LinkedProposalModel getLinkedPositions() {
if (!fLinkedProposalModel.hasLinkedPositions())
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java
index 2b50773..2903542 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2018 IBM Corporation and others.
+ * Copyright (c) 2013, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -12,550 +12,25 @@
* IBM Corporation - initial API and implementation
* Jerome Cambon <jerome.cambon@oracle.com> - [1.8][clean up][quick assist] Convert lambda to anonymous must qualify references to 'this'/'super' - https://bugs.eclipse.org/430573
* Stephan Herrmann - Contribution for Bug 463360 - [override method][null] generating method override should not create redundant null annotations
+ * Red Hat Inc. - modified to use LambdaExpressionsFixCore static classes
*******************************************************************************/
package org.eclipse.jdt.internal.corext.fix;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import org.eclipse.core.runtime.CoreException;
-
-import org.eclipse.text.edits.TextEditGroup;
-
-import org.eclipse.jdt.core.dom.AST;
-import org.eclipse.jdt.core.dom.ASTNode;
-import org.eclipse.jdt.core.dom.ASTVisitor;
-import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
-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.CastExpression;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
-import org.eclipse.jdt.core.dom.Expression;
-import org.eclipse.jdt.core.dom.ExpressionStatement;
-import org.eclipse.jdt.core.dom.IAnnotationBinding;
-import org.eclipse.jdt.core.dom.IBinding;
-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.MarkerAnnotation;
-import org.eclipse.jdt.core.dom.MethodDeclaration;
-import org.eclipse.jdt.core.dom.MethodInvocation;
-import org.eclipse.jdt.core.dom.Modifier;
-import org.eclipse.jdt.core.dom.Name;
-import org.eclipse.jdt.core.dom.NormalAnnotation;
-import org.eclipse.jdt.core.dom.ReturnStatement;
-import org.eclipse.jdt.core.dom.SimpleName;
-import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
-import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
-import org.eclipse.jdt.core.dom.Statement;
-import org.eclipse.jdt.core.dom.SuperFieldAccess;
-import org.eclipse.jdt.core.dom.SuperMethodInvocation;
-import org.eclipse.jdt.core.dom.ThisExpression;
-import org.eclipse.jdt.core.dom.Type;
-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.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
-import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
-import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
-import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
-import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
-import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
-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.HierarchicalASTVisitor;
-import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
-import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
-import org.eclipse.jdt.internal.corext.refactoring.structure.ImportRemover;
+import org.eclipse.jdt.internal.corext.fix.LambdaExpressionsFixCore.FunctionalAnonymousClassesFinder;
+import org.eclipse.jdt.internal.corext.fix.LambdaExpressionsFixCore.LambdaExpressionsFinder;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
-import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
-import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
-
public class LambdaExpressionsFix extends CompilationUnitRewriteOperationsFix {
- private static final class FunctionalAnonymousClassesFinder extends ASTVisitor {
-
- private final ArrayList<ClassInstanceCreation> fNodes= new ArrayList<>();
-
- public static ArrayList<ClassInstanceCreation> perform(ASTNode node) {
- FunctionalAnonymousClassesFinder finder= new FunctionalAnonymousClassesFinder();
- node.accept(finder);
- return finder.fNodes;
- }
-
- @Override
- public boolean visit(ClassInstanceCreation node) {
- if (isFunctionalAnonymous(node) && !fConversionRemovesAnnotations) {
- fNodes.add(node);
- }
- return true;
- }
- }
-
- private static final class LambdaExpressionsFinder extends ASTVisitor {
-
- private final ArrayList<LambdaExpression> fNodes= new ArrayList<>();
-
- public static ArrayList<LambdaExpression> perform(ASTNode node) {
- LambdaExpressionsFinder finder= new LambdaExpressionsFinder();
- node.accept(finder);
- return finder.fNodes;
- }
-
- @Override
- public boolean visit(LambdaExpression node) {
- ITypeBinding typeBinding= node.resolveTypeBinding();
- if (typeBinding != null && typeBinding.getFunctionalInterfaceMethod() != null) {
- fNodes.add(node);
- }
- return true;
- }
- }
-
- private static class AbortSearchException extends RuntimeException {
- private static final long serialVersionUID= 1L;
- }
-
- private static final class SuperThisReferenceFinder extends HierarchicalASTVisitor {
-
- private ITypeBinding fFunctionalInterface;
- private MethodDeclaration fMethodDeclaration;
-
- static boolean hasReference(MethodDeclaration node) {
- try {
- SuperThisReferenceFinder finder= new SuperThisReferenceFinder();
- ClassInstanceCreation cic= (ClassInstanceCreation) node.getParent().getParent();
- finder.fFunctionalInterface= cic.getType().resolveBinding();
- finder.fMethodDeclaration= node;
- node.accept(finder);
- } catch (AbortSearchException e) {
- return true;
- }
- return false;
- }
-
- @Override
- public boolean visit(AnonymousClassDeclaration node) {
- return false;
- }
-
- @Override
- public boolean visit(BodyDeclaration node) {
- return false;
- }
-
- @Override
- public boolean visit(MethodDeclaration node) {
- return node == fMethodDeclaration;
- }
-
- @Override
- public boolean visit(ThisExpression node) {
- if (node.getQualifier() == null)
- throw new AbortSearchException();
- return true; // references to outer scope are harmless
- }
-
- @Override
- public boolean visit(SuperMethodInvocation node) {
- if (node.getQualifier() == null) {
- throw new AbortSearchException();
- } else {
- IBinding qualifierType= node.getQualifier().resolveBinding();
- if (qualifierType instanceof ITypeBinding && ((ITypeBinding) qualifierType).isInterface()) {
- throw new AbortSearchException(); // JLS8: new overloaded meaning of 'interface'.super.'method'(..)
- }
- }
- return true; // references to outer scopes are harmless
- }
-
- @Override
- public boolean visit(SuperFieldAccess node) {
- if (node.getQualifier() == null)
- throw new AbortSearchException();
- return true; // references to outer scope are harmless
- }
-
- @Override
- public boolean visit(MethodInvocation node) {
- IMethodBinding binding= node.resolveMethodBinding();
- if (binding != null && !JdtFlags.isStatic(binding) && node.getExpression() == null
- && Bindings.isSuperType(binding.getDeclaringClass(), fFunctionalInterface, false))
- throw new AbortSearchException();
- return true;
- }
- }
-
- private static final class SuperThisQualifier extends HierarchicalASTVisitor {
-
- private ITypeBinding fQualifierTypeBinding;
- private ImportRewrite fImportRewrite;
- private ASTRewrite fASTRewrite;
- private TextEditGroup fGroup;
-
- public static void perform(LambdaExpression lambdaExpression, ITypeBinding parentTypeBinding, CompilationUnitRewrite cuRewrite, TextEditGroup group) {
- SuperThisQualifier qualifier= new SuperThisQualifier();
- qualifier.fQualifierTypeBinding= parentTypeBinding;
- qualifier.fImportRewrite= cuRewrite.getImportRewrite();
- qualifier.fASTRewrite= cuRewrite.getASTRewrite();
- qualifier.fGroup= group;
- lambdaExpression.accept(qualifier);
- }
-
- public Name getQualifierTypeName() {
- String typeName= fImportRewrite.addImport(fQualifierTypeBinding);
- return fASTRewrite.getAST().newName(typeName);
- }
-
- @Override
- public boolean visit(AnonymousClassDeclaration node) {
- return false;
- }
-
- @Override
- public boolean visit(BodyDeclaration node) {
- return false;
- }
-
- @Override
- public boolean visit(SuperFieldAccess node) {
- if (node.getQualifier() == null) {
- fASTRewrite.set(node, SuperFieldAccess.QUALIFIER_PROPERTY, getQualifierTypeName(), fGroup);
- }
- return true;
- }
-
- @Override
- public boolean visit(SuperMethodInvocation node) {
- if (node.getQualifier() == null) {
- fASTRewrite.set(node, SuperMethodInvocation.QUALIFIER_PROPERTY, getQualifierTypeName(), fGroup);
- }
- return true;
- }
-
- @Override
- public boolean visit(ThisExpression node) {
- if (node.getQualifier() == null) {
- fASTRewrite.set(node, ThisExpression.QUALIFIER_PROPERTY, getQualifierTypeName(), fGroup);
- }
- return true;
- }
- }
-
- private static final class AnnotationsFinder extends ASTVisitor {
- static boolean hasAnnotations(SingleVariableDeclaration methodParameter) {
- try {
- AnnotationsFinder finder= new AnnotationsFinder();
- methodParameter.accept(finder);
- } catch (AbortSearchException e) {
- return true;
- }
- return false;
- }
-
- @Override
- public boolean visit(MarkerAnnotation node) {
- throw new AbortSearchException();
- }
-
- @Override
- public boolean visit(NormalAnnotation node) {
- throw new AbortSearchException();
- }
-
- @Override
- public boolean visit(SingleMemberAnnotation node) {
- throw new AbortSearchException();
- }
- }
-
- private static class CreateLambdaOperation extends CompilationUnitRewriteOperation {
-
- private final List<ClassInstanceCreation> fExpressions;
-
- public CreateLambdaOperation(List<ClassInstanceCreation> expressions) {
- fExpressions= expressions;
- }
-
- @Override
- public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
-
- ASTRewrite rewrite= cuRewrite.getASTRewrite();
- ImportRemover importRemover= cuRewrite.getImportRemover();
- AST ast= rewrite.getAST();
-
- HashMap<ClassInstanceCreation, HashSet<String>> cicToNewNames= new HashMap<>();
- for (int i= 0; i < fExpressions.size(); i++) {
- ClassInstanceCreation classInstanceCreation= fExpressions.get(i);
- TextEditGroup group= createTextEditGroup(FixMessages.LambdaExpressionsFix_convert_to_lambda_expression, cuRewrite);
-
- AnonymousClassDeclaration anonymTypeDecl= classInstanceCreation.getAnonymousClassDeclaration();
- List<BodyDeclaration> bodyDeclarations= anonymTypeDecl.bodyDeclarations();
-
- Object object= bodyDeclarations.get(0);
- if (!(object instanceof MethodDeclaration))
- continue;
- MethodDeclaration methodDeclaration= (MethodDeclaration) object;
- HashSet<String> excludedNames= new HashSet<>();
- if (i != 0) {
- for (ClassInstanceCreation convertedCic : fExpressions.subList(0, i)) {
- if (ASTNodes.isParent(convertedCic, classInstanceCreation)) {
- excludedNames.addAll(cicToNewNames.get(convertedCic));
- }
- }
- }
- HashSet<String> newNames= makeNamesUnique(excludedNames, methodDeclaration, rewrite, group);
- cicToNewNames.put(classInstanceCreation, new HashSet<>(newNames));
- List<SingleVariableDeclaration> methodParameters= methodDeclaration.parameters();
-
- // use short form with inferred parameter types and without parentheses if possible
- boolean createExplicitlyTypedParameters= false;
- for (SingleVariableDeclaration methodParameter : methodParameters) {
- if (AnnotationsFinder.hasAnnotations(methodParameter)) {
- createExplicitlyTypedParameters= true;
- break;
- }
- }
- LambdaExpression lambdaExpression= ast.newLambdaExpression();
- List<VariableDeclaration> lambdaParameters= lambdaExpression.parameters();
- lambdaExpression.setParentheses(createExplicitlyTypedParameters || methodParameters.size() != 1);
- for (SingleVariableDeclaration methodParameter : methodParameters) {
- if (createExplicitlyTypedParameters) {
- lambdaParameters.add((SingleVariableDeclaration) rewrite.createCopyTarget(methodParameter));
- importRemover.registerRetainedNode(methodParameter);
- } else {
- VariableDeclarationFragment lambdaParameter= ast.newVariableDeclarationFragment();
- lambdaParameter.setName((SimpleName) rewrite.createCopyTarget(methodParameter.getName()));
- lambdaParameters.add(lambdaParameter);
- }
- }
-
- Block body= methodDeclaration.getBody();
- List<Statement> statements= body.statements();
- ASTNode lambdaBody= body;
- if (statements.size() == 1) {
- // use short form with just an expression body if possible
- Statement statement= statements.get(0);
- if (statement instanceof ExpressionStatement) {
- lambdaBody= ((ExpressionStatement) statement).getExpression();
- } else if (statement instanceof ReturnStatement) {
- Expression returnExpression= ((ReturnStatement) statement).getExpression();
- if (returnExpression != null) {
- lambdaBody= returnExpression;
- }
- }
- }
- //TODO: Bug 421479: [1.8][clean up][quick assist] convert anonymous to lambda must consider lost scope of interface
-// lambdaBody.accept(new InterfaceAccessQualifier(rewrite, classInstanceCreation.getType().resolveBinding())); //TODO: maybe need a separate ASTRewrite and string placeholder
-
- lambdaExpression.setBody(ASTNodes.getCopyOrReplacement(rewrite, lambdaBody, group));
- Expression replacement= lambdaExpression;
- ITypeBinding targetTypeBinding= ASTNodes.getTargetType(classInstanceCreation);
- if (ASTNodes.isTargetAmbiguous(classInstanceCreation, ASTNodes.isExplicitlyTypedLambda(lambdaExpression)) || targetTypeBinding.getFunctionalInterfaceMethod() == null) {
- CastExpression cast= ast.newCastExpression();
- cast.setExpression(lambdaExpression);
- ImportRewrite importRewrite= cuRewrite.getImportRewrite();
- ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(classInstanceCreation, importRewrite);
- Type castType= importRewrite.addImport(classInstanceCreation.getType().resolveBinding(), ast, importRewriteContext, TypeLocation.CAST);
- cast.setType(castType);
- importRemover.registerAddedImports(castType);
- replacement= cast;
- }
- rewrite.replace(classInstanceCreation, replacement, group);
-
- importRemover.registerRemovedNode(classInstanceCreation);
- importRemover.registerRetainedNode(lambdaBody);
- }
- }
-
- private HashSet<String> makeNamesUnique(HashSet<String> excludedNames, MethodDeclaration methodDeclaration, ASTRewrite rewrite, TextEditGroup group) {
- HashSet<String> newNames= new HashSet<>();
- excludedNames.addAll(ASTNodes.getVisibleLocalVariablesInScope(methodDeclaration));
- List<SimpleName> simpleNamesInMethod= getNamesInMethod(methodDeclaration);
- List<String> namesInMethod= new ArrayList<>();
- for (SimpleName name : simpleNamesInMethod) {
- namesInMethod.add(name.getIdentifier());
- }
-
- for (int i= 0; i < simpleNamesInMethod.size(); i++) {
- SimpleName name= simpleNamesInMethod.get(i);
- String identifier= namesInMethod.get(i);
- HashSet<String> allNamesToExclude= getNamesToExclude(excludedNames, namesInMethod, i);
- if (allNamesToExclude.contains(identifier)) {
- String newIdentifier= createName(identifier, allNamesToExclude);
- excludedNames.add(newIdentifier);
- newNames.add(newIdentifier);
- SimpleName[] references= LinkedNodeFinder.findByNode(name.getRoot(), name);
- for (SimpleName ref : references) {
- rewrite.set(ref, SimpleName.IDENTIFIER_PROPERTY, newIdentifier, group);
- }
- }
- }
-
- return newNames;
- }
-
- private HashSet<String> getNamesToExclude(HashSet<String> excludedNames, List<String> namesInMethod, int i) {
- HashSet<String> allNamesToExclude= new HashSet<>(excludedNames);
- allNamesToExclude.addAll(namesInMethod.subList(0, i));
- allNamesToExclude.addAll(namesInMethod.subList(i + 1, namesInMethod.size()));
- return allNamesToExclude;
- }
-
- private List<SimpleName> getNamesInMethod(MethodDeclaration methodDeclaration) {
- class NamesCollector extends HierarchicalASTVisitor {
- private int fTypeCounter;
-
- private List<SimpleName> fNames= new ArrayList<>();
-
- @Override
- public boolean visit(AbstractTypeDeclaration node) {
- if (fTypeCounter++ == 0) {
- fNames.add(node.getName());
- }
- return true;
- }
-
- @Override
- public void endVisit(AbstractTypeDeclaration node) {
- fTypeCounter--;
- }
-
- @Override
- public boolean visit(AnonymousClassDeclaration node) {
- fTypeCounter++;
- return true;
- }
-
- @Override
- public void endVisit(AnonymousClassDeclaration node) {
- fTypeCounter--;
- }
-
- @Override
- public boolean visit(VariableDeclaration node) {
- if (fTypeCounter == 0)
- fNames.add(node.getName());
- return true;
- }
- }
-
- NamesCollector namesCollector= new NamesCollector();
- methodDeclaration.accept(namesCollector);
- return namesCollector.fNames;
- }
-
- private String createName(String candidate, HashSet<String> excludedNames) {
- int i= 1;
- String result= candidate;
- while (excludedNames.contains(result)) {
- result= candidate + i++;
- }
- return result;
- }
- }
-
- private static class CreateAnonymousClassCreationOperation extends CompilationUnitRewriteOperation {
-
- private final List<LambdaExpression> fExpressions;
-
- public CreateAnonymousClassCreationOperation(List<LambdaExpression> changedNodes) {
- fExpressions= changedNodes;
- }
-
- @Override
- public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
-
- ASTRewrite rewrite= cuRewrite.getASTRewrite();
- AST ast= rewrite.getAST();
-
- for (Iterator<LambdaExpression> iterator= fExpressions.iterator(); iterator.hasNext();) {
- LambdaExpression lambdaExpression= iterator.next();
- TextEditGroup group= createTextEditGroup(FixMessages.LambdaExpressionsFix_convert_to_anonymous_class_creation, cuRewrite);
-
- ITypeBinding lambdaTypeBinding= lambdaExpression.resolveTypeBinding();
- IMethodBinding methodBinding= lambdaTypeBinding.getFunctionalInterfaceMethod();
- List<VariableDeclaration> parameters= lambdaExpression.parameters();
- String[] parameterNames= new String[parameters.size()];
- for (int i= 0; i < parameterNames.length; i++) {
- parameterNames[i]= parameters.get(i).getName().getIdentifier();
- }
-
- final CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(cuRewrite.getCu().getJavaProject());
- ImportRewrite importRewrite= cuRewrite.getImportRewrite();
- ImportRewriteContext importContext= new ContextSensitiveImportRewriteContext(lambdaExpression, importRewrite);
-
- MethodDeclaration methodDeclaration= StubUtility2.createImplementationStub(cuRewrite.getCu(), rewrite, importRewrite, importContext,
- methodBinding, parameterNames, lambdaTypeBinding, settings, false, lambdaExpression);
-
- // Qualify reference to this or super
- ASTNode parentType= ASTResolving.findParentType(lambdaExpression);
- ITypeBinding parentTypeBinding= null;
- if (parentType instanceof AbstractTypeDeclaration) {
- parentTypeBinding= ((AbstractTypeDeclaration) parentType).resolveBinding();
- } else if (parentType instanceof AnonymousClassDeclaration) {
- parentTypeBinding= ((AnonymousClassDeclaration) parentType).resolveBinding();
- }
- if (parentTypeBinding != null) {
- parentTypeBinding= Bindings.normalizeTypeBinding(parentTypeBinding);
- if (parentTypeBinding != null) {
- SuperThisQualifier.perform(lambdaExpression, parentTypeBinding.getTypeDeclaration(), cuRewrite, group);
- }
- }
-
- Block block;
- ASTNode lambdaBody= lambdaExpression.getBody();
- if (lambdaBody instanceof Block) {
- block= (Block) ASTNodes.getCopyOrReplacement(rewrite, lambdaBody, group);
- } else {
- block= ast.newBlock();
- List<Statement> statements= block.statements();
- ITypeBinding returnType= methodBinding.getReturnType();
- Expression copyTarget= (Expression) ASTNodes.getCopyOrReplacement(rewrite, lambdaBody, group);
- if (Bindings.isVoidType(returnType)) {
- ExpressionStatement newExpressionStatement= ast.newExpressionStatement(copyTarget);
- statements.add(newExpressionStatement);
- } else {
- ReturnStatement returnStatement= ast.newReturnStatement();
- returnStatement.setExpression(copyTarget);
- statements.add(returnStatement);
- }
- }
- methodDeclaration.setBody(block);
-
- AnonymousClassDeclaration anonymousClassDeclaration= ast.newAnonymousClassDeclaration();
- List<BodyDeclaration> bodyDeclarations= anonymousClassDeclaration.bodyDeclarations();
- bodyDeclarations.add(methodDeclaration);
-
- Type creationType= ASTNodeFactory.newCreationType(ast, lambdaTypeBinding, importRewrite, importContext);
-
- ClassInstanceCreation classInstanceCreation= ast.newClassInstanceCreation();
- classInstanceCreation.setType(creationType);
- classInstanceCreation.setAnonymousClassDeclaration(anonymousClassDeclaration);
-
- ASTNode toReplace= lambdaExpression;
- if (lambdaExpression.getLocationInParent() == CastExpression.EXPRESSION_PROPERTY
- && lambdaTypeBinding.isEqualTo(((CastExpression) lambdaExpression.getParent()).resolveTypeBinding())) {
- // remove cast to same type as the anonymous will use
- toReplace= lambdaExpression.getParent();
- }
- rewrite.replace(toReplace, classInstanceCreation, group);
- }
- }
- }
-
private static boolean fConversionRemovesAnnotations;
public static LambdaExpressionsFix createConvertToLambdaFix(ClassInstanceCreation cic) {
@@ -563,17 +38,17 @@
if (!JavaModelUtil.is18OrHigher(root.getJavaElement().getJavaProject()))
return null;
- if (!LambdaExpressionsFix.isFunctionalAnonymous(cic))
+ if (!LambdaExpressionsFixCore.isFunctionalAnonymous(cic))
return null;
- CreateLambdaOperation op= new CreateLambdaOperation(Collections.singletonList(cic));
+ LambdaExpressionsFixCore.CreateLambdaOperation op= new LambdaExpressionsFixCore.CreateLambdaOperation(Collections.singletonList(cic));
String message;
if (fConversionRemovesAnnotations) {
message= FixMessages.LambdaExpressionsFix_convert_to_lambda_expression_removes_annotations;
} else {
message= FixMessages.LambdaExpressionsFix_convert_to_lambda_expression;
}
- return new LambdaExpressionsFix(message, root, new CompilationUnitRewriteOperation[] { op });
+ return new LambdaExpressionsFix(message, root, new CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] { op });
}
public static IProposableFix createConvertToAnonymousClassCreationsFix(LambdaExpression lambda) {
@@ -582,9 +57,9 @@
if (lambda.resolveTypeBinding() == null || lambda.resolveTypeBinding().getFunctionalInterfaceMethod() == null)
return null;
- CreateAnonymousClassCreationOperation op= new CreateAnonymousClassCreationOperation(Collections.singletonList(lambda));
+ LambdaExpressionsFixCore.CreateAnonymousClassCreationOperation op= new LambdaExpressionsFixCore.CreateAnonymousClassCreationOperation(Collections.singletonList(lambda));
CompilationUnit root= (CompilationUnit) lambda.getRoot();
- return new LambdaExpressionsFix(FixMessages.LambdaExpressionsFix_convert_to_anonymous_class_creation, root, new CompilationUnitRewriteOperation[] { op });
+ return new LambdaExpressionsFix(FixMessages.LambdaExpressionsFix_convert_to_anonymous_class_creation, root, new CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] { op });
}
public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, boolean useLambda, boolean useAnonymous) {
@@ -597,125 +72,24 @@
return null;
Collections.reverse(convertibleNodes); // process nested anonymous classes first
- CompilationUnitRewriteOperation op= new CreateLambdaOperation(convertibleNodes);
- return new LambdaExpressionsFix(FixMessages.LambdaExpressionsFix_convert_to_lambda_expression, compilationUnit, new CompilationUnitRewriteOperation[] { op });
-
+ CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation op= new LambdaExpressionsFixCore.CreateLambdaOperation(convertibleNodes);
+ return new LambdaExpressionsFix(FixMessages.LambdaExpressionsFix_convert_to_lambda_expression, compilationUnit, new CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] { op });
+
} else if (useAnonymous) {
ArrayList<LambdaExpression> convertibleNodes= LambdaExpressionsFinder.perform(compilationUnit);
if (convertibleNodes.isEmpty())
return null;
-
+
Collections.reverse(convertibleNodes); // process nested lambdas first
- CompilationUnitRewriteOperation op= new CreateAnonymousClassCreationOperation(convertibleNodes);
- return new LambdaExpressionsFix(FixMessages.LambdaExpressionsFix_convert_to_anonymous_class_creation, compilationUnit, new CompilationUnitRewriteOperation[] { op });
-
+ CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation op= new LambdaExpressionsFixCore.CreateAnonymousClassCreationOperation(convertibleNodes);
+ return new LambdaExpressionsFix(FixMessages.LambdaExpressionsFix_convert_to_anonymous_class_creation, compilationUnit, new CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] { op });
+
}
return null;
}
- protected LambdaExpressionsFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
+ protected LambdaExpressionsFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] fixRewriteOperations) {
super(name, compilationUnit, fixRewriteOperations);
}
- static boolean isFunctionalAnonymous(ClassInstanceCreation node) {
- ITypeBinding typeBinding= node.resolveTypeBinding();
- if (typeBinding == null)
- return false;
- ITypeBinding[] interfaces= typeBinding.getInterfaces();
- if (interfaces.length != 1)
- return false;
- if (interfaces[0].getFunctionalInterfaceMethod() == null)
- return false;
-
- AnonymousClassDeclaration anonymTypeDecl= node.getAnonymousClassDeclaration();
- if (anonymTypeDecl == null || anonymTypeDecl.resolveBinding() == null)
- return false;
-
- List<BodyDeclaration> bodyDeclarations= anonymTypeDecl.bodyDeclarations();
- // cannot convert if there are fields or additional methods
- if (bodyDeclarations.size() != 1)
- return false;
- BodyDeclaration bodyDeclaration= bodyDeclarations.get(0);
- if (!(bodyDeclaration instanceof MethodDeclaration))
- return false;
-
- MethodDeclaration methodDecl= (MethodDeclaration) bodyDeclaration;
- IMethodBinding methodBinding= methodDecl.resolveBinding();
-
- if (methodBinding == null)
- return false;
- // generic lambda expressions are not allowed
- if (methodBinding.isGenericMethod())
- return false;
-
- int modifiers= methodBinding.getModifiers();
- if (Modifier.isSynchronized(modifiers) || Modifier.isStrictfp(modifiers))
- return false;
-
- // lambda cannot refer to 'this'/'super' literals
- if (SuperThisReferenceFinder.hasReference(methodDecl))
- return false;
-
- if (ASTNodes.getTargetType(node) == null) // #isInTargetTypeContext
- return false;
-
- // Check if annotations other than @Override and @Deprecated will be removed
- checkAnnotationsRemoval(methodBinding);
-
- return true;
- }
-
- private static void checkAnnotationsRemoval(IMethodBinding methodBinding) {
- fConversionRemovesAnnotations= false;
- IAnnotationBinding[] declarationAnnotations= methodBinding.getAnnotations();
- for (IAnnotationBinding declarationAnnotation : declarationAnnotations) {
- ITypeBinding annotationType= declarationAnnotation.getAnnotationType();
- if (annotationType != null) {
- String qualifiedName= annotationType.getQualifiedName();
- if (!"java.lang.Override".equals(qualifiedName) && !"java.lang.Deprecated".equals(qualifiedName)) { //$NON-NLS-1$ //$NON-NLS-2$
- fConversionRemovesAnnotations= true;
- return;
- }
- }
- }
- }
-
- /*private static boolean isInTargetTypeContext(ClassInstanceCreation node) {
- ITypeBinding targetType= ASTNodes.getTargetType(node);
- return targetType != null && targetType.getFunctionalInterfaceMethod() != null;
-
-
- //TODO: probably incomplete, should reuse https://bugs.eclipse.org/bugs/show_bug.cgi?id=408966#c6
- StructuralPropertyDescriptor locationInParent= node.getLocationInParent();
-
- if (locationInParent == ReturnStatement.EXPRESSION_PROPERTY) {
- MethodDeclaration methodDeclaration= ASTResolving.findParentMethodDeclaration(node);
- if (methodDeclaration == null)
- return false;
- IMethodBinding methodBinding= methodDeclaration.resolveBinding();
- if (methodBinding == null)
- return false;
- //TODO: could also cast to the CIC type instead of aborting...
- return methodBinding.getReturnType().getFunctionalInterfaceMethod() != null;
- }
-
- //TODO: should also check whether variable is of a functional type
- return locationInParent == SingleVariableDeclaration.INITIALIZER_PROPERTY
- || locationInParent == VariableDeclarationFragment.INITIALIZER_PROPERTY
- || locationInParent == Assignment.RIGHT_HAND_SIDE_PROPERTY
- || locationInParent == ArrayInitializer.EXPRESSIONS_PROPERTY
-
- || locationInParent == MethodInvocation.ARGUMENTS_PROPERTY
- || locationInParent == SuperMethodInvocation.ARGUMENTS_PROPERTY
- || locationInParent == ConstructorInvocation.ARGUMENTS_PROPERTY
- || locationInParent == SuperConstructorInvocation.ARGUMENTS_PROPERTY
- || locationInParent == ClassInstanceCreation.ARGUMENTS_PROPERTY
- || locationInParent == EnumConstantDeclaration.ARGUMENTS_PROPERTY
-
- || locationInParent == LambdaExpression.BODY_PROPERTY
- || locationInParent == ConditionalExpression.THEN_EXPRESSION_PROPERTY
- || locationInParent == ConditionalExpression.ELSE_EXPRESSION_PROPERTY
- || locationInParent == CastExpression.EXPRESSION_PROPERTY;
-
- }*/
}