Bug 535690 - [11][quick assist] Convert 'var' to resolved type for
lambda parameters

Change-Id: I051f270ec457a48886a568dde157862e8ae9c837
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java
index 94c9d79..474b0fc 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java
@@ -289,6 +289,11 @@
 	public static String QuickAssistProcessor_splitdeclaration_description;
 	public static String QuickAssistProcessor_joindeclaration_description;
 	public static String QuickAssistProcessor_add_inferred_lambda_parameter_types;
+	public static String QuickAssistProcessor_replace_var_with_inferred_lambda_parameter_types;
+	public static String QuickAssistProcessor_add_var_lambda_parameter_types;
+	public static String QuickAssistProcessor_replace_inferred_with_var_lambda_parameter_types;
+	public static String QuickAssistProcessor_remove_inferred_lambda_parameter_types;
+	public static String QuickAssistProcessor_remove_var_lambda_parameter_types;
 	public static String QuickAssistProcessor_addfinallyblock_description;
 	public static String QuickAssistProcessor_addelseblock_description;
 	public static String QuickAssistProcessor_addParenthesesInLambda;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties
index c00ab77..d3671ba 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties
@@ -350,6 +350,11 @@
 QuickAssistProcessor_extract_to_constant_description=Extract to constant
 QuickAssistProcessor_joindeclaration_description=Join variable declaration
 QuickAssistProcessor_add_inferred_lambda_parameter_types=Add inferred lambda parameter types
+QuickAssistProcessor_replace_var_with_inferred_lambda_parameter_types= Replace var types with inferred lambda parameter types
+QuickAssistProcessor_add_var_lambda_parameter_types=Add var lambda parameter types
+QuickAssistProcessor_replace_inferred_with_var_lambda_parameter_types=Replace inferred types with var lambda parameter types
+QuickAssistProcessor_remove_inferred_lambda_parameter_types=Remove inferred lambda parameter types
+QuickAssistProcessor_remove_var_lambda_parameter_types=Remove var lambda parameter types
 QuickAssistProcessor_addfinallyblock_description=Add finally block
 QuickAssistProcessor_addelseblock_description=Add else block
 QuickAssistProcessor_addParenthesesInLambda=Add parentheses around lambda parameter
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java
index 19fe0fa..172e852 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java
@@ -161,6 +161,7 @@
 import org.eclipse.jdt.internal.corext.refactoring.code.ExtractTempRefactoring;
 import org.eclipse.jdt.internal.corext.refactoring.code.InlineTempRefactoring;
 import org.eclipse.jdt.internal.corext.refactoring.code.PromoteTempToFieldRefactoring;
+import org.eclipse.jdt.internal.corext.refactoring.structure.ImportRemover;
 import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer;
 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
 import org.eclipse.jdt.internal.corext.util.Messages;
@@ -183,6 +184,7 @@
 import org.eclipse.jdt.internal.ui.fix.TypeParametersCleanUp;
 import org.eclipse.jdt.internal.ui.fix.VariableDeclarationCleanUp;
 import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
+import org.eclipse.jdt.internal.ui.text.correction.proposals.ASTRewriteRemoveImportsCorrectionProposal;
 import org.eclipse.jdt.internal.ui.text.correction.proposals.AssignToVariableAssistProposal;
 import org.eclipse.jdt.internal.ui.text.correction.proposals.FixCorrectionProposal;
 import org.eclipse.jdt.internal.ui.text.correction.proposals.GenerateForLoopAssistProposal;
@@ -256,6 +258,8 @@
 				|| getChangeLambdaBodyToBlockProposal(context, coveringNode, null)
 				|| getChangeLambdaBodyToExpressionProposal(context, coveringNode, null)
 				|| getAddInferredLambdaParameterTypes(context, coveringNode, null)
+				|| getAddVarLambdaParameterTypes(context, coveringNode, null)
+				|| getRemoveVarOrInferredLambdaParameterTypes(context, coveringNode, null)
 				|| getConvertMethodReferenceToLambdaProposal(context, coveringNode, null)
 				|| getConvertLambdaToMethodReferenceProposal(context, coveringNode, null)
 				|| getFixParenthesesInLambdaExpression(context, coveringNode, null)
@@ -311,6 +315,8 @@
 				getChangeLambdaBodyToBlockProposal(context, coveringNode, resultingCollections);
 				getChangeLambdaBodyToExpressionProposal(context, coveringNode, resultingCollections);
 				getAddInferredLambdaParameterTypes(context, coveringNode, resultingCollections);
+				getAddVarLambdaParameterTypes(context, coveringNode, resultingCollections);
+				getRemoveVarOrInferredLambdaParameterTypes(context, coveringNode, resultingCollections);
 				getConvertMethodReferenceToLambdaProposal(context, coveringNode, resultingCollections);
 				getConvertLambdaToMethodReferenceProposal(context, coveringNode, resultingCollections);
 				getFixParenthesesInLambdaExpression(context, coveringNode, resultingCollections);
@@ -1367,11 +1373,35 @@
 
 	public static boolean getAddInferredLambdaParameterTypes(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) {
 		LambdaExpression lambda;
+		boolean isvarTypeSingleVariableDeclaration= false;
 		if (covering instanceof LambdaExpression) {
 			lambda= (LambdaExpression) covering;
 		} else if (covering.getLocationInParent() == VariableDeclarationFragment.NAME_PROPERTY &&
 				((VariableDeclarationFragment) covering.getParent()).getLocationInParent() == LambdaExpression.PARAMETERS_PROPERTY) {
 			lambda= (LambdaExpression) covering.getParent().getParent();
+		} else if (covering.getLocationInParent() == SingleVariableDeclaration.NAME_PROPERTY &&
+				((SingleVariableDeclaration) covering.getParent()).getLocationInParent() == LambdaExpression.PARAMETERS_PROPERTY) {
+			CompilationUnit astRoot= context.getASTRoot();
+			IJavaElement root= astRoot.getJavaElement();
+			ASTNode parent= covering.getParent();
+			if (parent == null || root == null) {
+				return false;
+			}
+			IJavaProject javaProject= root.getJavaProject();
+			if (javaProject == null) {
+				return false;
+			}
+			if (!JavaModelUtil.is11OrHigher(javaProject)) {
+				return false;
+			} else {
+				SingleVariableDeclaration svDecl= (SingleVariableDeclaration) parent;
+				if (svDecl.getType() != null && svDecl.getType().isVar()) {
+					lambda= (LambdaExpression) covering.getParent().getParent();
+					isvarTypeSingleVariableDeclaration= true;
+				} else {
+					return false;
+				}
+			}
 		} else {
 			return false;
 		}
@@ -1381,7 +1411,7 @@
 		if (noOfLambdaParams == 0)
 			return false;
 
-		if (lambdaParameters.get(0) instanceof SingleVariableDeclaration)
+		if (lambdaParameters.get(0) instanceof SingleVariableDeclaration && !isvarTypeSingleVariableDeclaration)
 			return false;
 
 		IMethodBinding methodBinding= lambda.resolveMethodBinding();
@@ -1409,6 +1439,9 @@
 
 		// add proposal
 		String label= CorrectionMessages.QuickAssistProcessor_add_inferred_lambda_parameter_types;
+		if (isvarTypeSingleVariableDeclaration) {
+			label= CorrectionMessages.QuickAssistProcessor_replace_var_with_inferred_lambda_parameter_types;
+		}
 		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
 		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.ADD_INFERRED_LAMBDA_PARAMETER_TYPES, image);
 		proposal.setImportRewrite(importRewrite);
@@ -1416,6 +1449,191 @@
 		return true;
 	}
 
+	public static boolean getAddVarLambdaParameterTypes(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) {
+		CompilationUnit astRoot= context.getASTRoot();
+		IJavaElement root= astRoot.getJavaElement();
+		ASTNode parent= covering.getParent();
+		if (parent == null || root == null) {
+			return false;
+		}
+		IJavaProject javaProject= root.getJavaProject();
+		if (javaProject == null) {
+			return false;
+		}
+		if (!JavaModelUtil.is11OrHigher(javaProject)) {
+			return false;
+		}
+
+		LambdaExpression lambda;
+		boolean isvarTypeSingleVariableDeclaration= false;
+		if (covering instanceof LambdaExpression) {
+			lambda= (LambdaExpression) covering;
+		} else if (covering.getLocationInParent() == VariableDeclarationFragment.NAME_PROPERTY &&
+				((VariableDeclarationFragment) parent).getLocationInParent() == LambdaExpression.PARAMETERS_PROPERTY) {
+			lambda= (LambdaExpression) covering.getParent().getParent();
+		} else {
+			if (covering.getLocationInParent() == SingleVariableDeclaration.NAME_PROPERTY &&
+					((SingleVariableDeclaration) parent).getLocationInParent() == LambdaExpression.PARAMETERS_PROPERTY) {
+				SingleVariableDeclaration svDecl= (SingleVariableDeclaration) parent;
+				if (svDecl.getType() != null && !svDecl.getType().isVar()) {
+					lambda= (LambdaExpression) covering.getParent().getParent();
+					isvarTypeSingleVariableDeclaration= true;
+				} else {
+					return false;
+				}
+			} else {
+				return false;
+			}
+		}
+
+		List<VariableDeclaration> lambdaParameters= lambda.parameters();
+		int noOfLambdaParams= lambdaParameters.size();
+		if (noOfLambdaParams == 0)
+			return false;
+
+		if (lambdaParameters.get(0) instanceof SingleVariableDeclaration && !isvarTypeSingleVariableDeclaration)
+			return false;
+
+		IMethodBinding methodBinding= lambda.resolveMethodBinding();
+		if (methodBinding == null)
+			return false;
+
+		if (resultingCollections == null)
+			return true;
+
+		String VAR_TYPE= "var"; //$NON-NLS-1$
+		AST ast= lambda.getAST();
+		ASTRewrite rewrite= ASTRewrite.create(ast);
+
+		ImportRemover remover= new ImportRemover(context.getCompilationUnit().getJavaProject(), context.getASTRoot());
+
+		ImportRewrite importRewrite= StubUtility.createImportRewrite(context.getASTRoot(), true);
+
+		rewrite.set(lambda, LambdaExpression.PARENTHESES_PROPERTY, Boolean.valueOf(true), null);
+		for (int i= 0; i < noOfLambdaParams; i++) {
+			VariableDeclaration param= lambdaParameters.get(i);
+			Type oldType= null;
+			if (param instanceof SingleVariableDeclaration) {
+				SingleVariableDeclaration curParent= (SingleVariableDeclaration) param;
+				oldType= curParent.getType();
+				if (oldType != null) {
+					rewrite.replace(oldType, ast.newSimpleType(ast.newName(VAR_TYPE)), null);
+					remover.registerRemovedNode(oldType);
+				}
+			}
+			if (oldType == null) {
+				SingleVariableDeclaration newParam= ast.newSingleVariableDeclaration();
+				newParam.setName(ast.newSimpleName(param.getName().getIdentifier()));
+				newParam.setType(ast.newSimpleType(ast.newName(VAR_TYPE)));
+				rewrite.replace(param, newParam, null);
+			}
+		}
+
+		// add proposal
+		ASTRewriteCorrectionProposal proposal;
+		String label= null;
+		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
+		if (isvarTypeSingleVariableDeclaration) {
+			label= CorrectionMessages.QuickAssistProcessor_replace_inferred_with_var_lambda_parameter_types;
+			ASTRewriteRemoveImportsCorrectionProposal newProposal= new ASTRewriteRemoveImportsCorrectionProposal(label, context.getCompilationUnit(), rewrite,
+					IProposalRelevance.ADD_INFERRED_LAMBDA_PARAMETER_TYPES, image);
+			newProposal.setImportRemover(remover);
+			proposal= newProposal;
+
+		} else {
+			label= CorrectionMessages.QuickAssistProcessor_add_var_lambda_parameter_types;
+			proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.ADD_INFERRED_LAMBDA_PARAMETER_TYPES, image);
+		}
+		proposal.setImportRewrite(importRewrite);
+		resultingCollections.add(proposal);
+		return true;
+	}
+
+	public static boolean getRemoveVarOrInferredLambdaParameterTypes(IInvocationContext context, ASTNode covering, Collection<ICommandAccess> resultingCollections) {
+		CompilationUnit astRoot= context.getASTRoot();
+		IJavaElement root= astRoot.getJavaElement();
+		ASTNode parent= covering.getParent();
+		if (parent == null || root == null) {
+			return false;
+		}
+		IJavaProject javaProject= root.getJavaProject();
+		if (javaProject == null) {
+			return false;
+		}
+		boolean checkForVarTypes= false;
+		if (JavaModelUtil.is11OrHigher(javaProject)) {
+			checkForVarTypes= true;
+		}
+
+		LambdaExpression lambda;
+		if (covering instanceof LambdaExpression) {
+			lambda= (LambdaExpression) covering;
+		} else if (covering.getLocationInParent() == SingleVariableDeclaration.NAME_PROPERTY &&
+				((SingleVariableDeclaration) parent).getLocationInParent() == LambdaExpression.PARAMETERS_PROPERTY) {
+			lambda= (LambdaExpression) covering.getParent().getParent();
+		} else {
+			return false;
+		}
+
+		List<VariableDeclaration> lambdaParameters= lambda.parameters();
+		int noOfLambdaParams= lambdaParameters.size();
+		if (noOfLambdaParams == 0)
+			return false;
+
+		if (!(lambdaParameters.get(0) instanceof SingleVariableDeclaration))
+			return false;
+
+		IMethodBinding methodBinding= lambda.resolveMethodBinding();
+		if (methodBinding == null)
+			return false;
+
+		if (resultingCollections == null)
+			return true;
+
+		AST ast= lambda.getAST();
+		ASTRewrite rewrite= ASTRewrite.create(ast);
+
+		ImportRemover remover= new ImportRemover(context.getCompilationUnit().getJavaProject(), context.getASTRoot());
+
+		ImportRewrite importRewrite= StubUtility.createImportRewrite(context.getASTRoot(), true);
+
+		rewrite.set(lambda, LambdaExpression.PARENTHESES_PROPERTY, Boolean.valueOf(true), null);
+		boolean removeImports= false;
+		for (int i= 0; i < noOfLambdaParams; i++) {
+			VariableDeclaration param= lambdaParameters.get(i);
+			Type oldType= null;
+			if (param instanceof SingleVariableDeclaration) {
+				SingleVariableDeclaration curParent= (SingleVariableDeclaration) param;
+				oldType= curParent.getType();
+				if (oldType != null && (!checkForVarTypes || (checkForVarTypes && !oldType.isVar()))) {
+					remover.registerRemovedNode(oldType);
+					removeImports= true;
+				}
+				VariableDeclarationFragment newParam= ast.newVariableDeclarationFragment();
+				newParam.setName(ast.newSimpleName(param.getName().getIdentifier()));
+				rewrite.replace(param, newParam, null);
+			}
+		}
+
+		// add proposal
+		ASTRewriteCorrectionProposal proposal;
+		String label= null;
+		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
+		if (removeImports) {
+			label= CorrectionMessages.QuickAssistProcessor_remove_inferred_lambda_parameter_types;
+			ASTRewriteRemoveImportsCorrectionProposal newProposal= new ASTRewriteRemoveImportsCorrectionProposal(label, context.getCompilationUnit(), rewrite,
+					IProposalRelevance.ADD_INFERRED_LAMBDA_PARAMETER_TYPES, image);
+			newProposal.setImportRemover(remover);
+			proposal= newProposal;
+		} else {
+			label= CorrectionMessages.QuickAssistProcessor_remove_var_lambda_parameter_types;
+			proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.ADD_INFERRED_LAMBDA_PARAMETER_TYPES, image);
+		}
+		proposal.setImportRewrite(importRewrite);
+		resultingCollections.add(proposal);
+		return true;
+	}
+
 	public static boolean getInferDiamondArgumentsProposal(IInvocationContext context, ASTNode node, IProblemLocation[] locations, Collection<ICommandAccess> resultingCollections) {
 		// don't add if already added as quick fix
 		if (containsMatchingProblem(locations, IProblem.DiamondNotBelow17))
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/ASTRewriteRemoveImportsCorrectionProposal.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/ASTRewriteRemoveImportsCorrectionProposal.java
new file mode 100644
index 0000000..9f7f37b
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/ASTRewriteRemoveImportsCorrectionProposal.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2018 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
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.ui.text.correction.proposals;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+
+import org.eclipse.jdt.internal.corext.refactoring.structure.ImportRemover;
+
+import org.eclipse.jdt.ui.text.java.correction.ASTRewriteCorrectionProposal;
+
+public class ASTRewriteRemoveImportsCorrectionProposal extends ASTRewriteCorrectionProposal{
+
+	private ImportRemover fImportRemover;
+	
+	public ASTRewriteRemoveImportsCorrectionProposal(String name, ICompilationUnit cu, ASTRewrite rewrite, int relevance) {
+		super(name, cu, rewrite, relevance);
+	}
+
+	public ASTRewriteRemoveImportsCorrectionProposal(String name, ICompilationUnit cu, ASTRewrite rewrite, int relevance, Image image) {
+		super(name, cu, rewrite, relevance, image);		
+	}
+	
+	public void setImportRemover(ImportRemover remover) {
+		fImportRemover= remover;
+	}
+	
+	@Override
+	protected ASTRewrite getRewrite() throws CoreException {
+		ASTRewrite rewrite= super.getRewrite();
+		ImportRewrite importRewrite= getImportRewrite();
+		if (fImportRemover != null && importRewrite != null) {
+			fImportRemover.applyRemoves(importRewrite);
+		}
+		return rewrite;
+	}
+	
+}