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;
-		
-	}*/
 }