content: /*******************************************************************************
   * Copyright (c) 2000, 2009 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
   *     Sebastian Davids <sdavids@gmx.de> - Bug 37432 getInvertEqualsProposal
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.ui.text.correction;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Hashtable;
  import java.util.List;
  import java.util.Map;
  
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.NullProgressMonitor;
  import org.eclipse.ltk.core.refactoring.Refactoring;
  import org.eclipse.ltk.core.refactoring.TextChange;
  import org.eclipse.swt.graphics.Image;
  import org.eclipse.wst.jsdt.core.Flags;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.compiler.IProblem;
  import org.eclipse.wst.jsdt.core.dom.AST;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration;
  import org.eclipse.wst.jsdt.core.dom.ArrayCreation;
  import org.eclipse.wst.jsdt.core.dom.ArrayInitializer;
  import org.eclipse.wst.jsdt.core.dom.ArrayType;
  import org.eclipse.wst.jsdt.core.dom.Assignment;
  import org.eclipse.wst.jsdt.core.dom.Block;
  import org.eclipse.wst.jsdt.core.dom.BodyDeclaration;
  import org.eclipse.wst.jsdt.core.dom.CatchClause;
  import org.eclipse.wst.jsdt.core.dom.ChildListPropertyDescriptor;
  import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
  import org.eclipse.wst.jsdt.core.dom.ConditionalExpression;
  import org.eclipse.wst.jsdt.core.dom.DoStatement;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.ExpressionStatement;
  import org.eclipse.wst.jsdt.core.dom.ForInStatement;
  import org.eclipse.wst.jsdt.core.dom.ForStatement;
  import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
  import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
  import org.eclipse.wst.jsdt.core.dom.IBinding;
  import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  import org.eclipse.wst.jsdt.core.dom.IVariableBinding;
  import org.eclipse.wst.jsdt.core.dom.IfStatement;
  import org.eclipse.wst.jsdt.core.dom.InfixExpression;
  import org.eclipse.wst.jsdt.core.dom.Initializer;
  import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  import org.eclipse.wst.jsdt.core.dom.Modifier;
  import org.eclipse.wst.jsdt.core.dom.Name;
  import org.eclipse.wst.jsdt.core.dom.ParenthesizedExpression;
  import org.eclipse.wst.jsdt.core.dom.PrimitiveType;
  import org.eclipse.wst.jsdt.core.dom.SimpleName;
  import org.eclipse.wst.jsdt.core.dom.SimpleType;
  import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration;
  import org.eclipse.wst.jsdt.core.dom.Statement;
  import org.eclipse.wst.jsdt.core.dom.StructuralPropertyDescriptor;
  import org.eclipse.wst.jsdt.core.dom.ThisExpression;
  import org.eclipse.wst.jsdt.core.dom.TryStatement;
  import org.eclipse.wst.jsdt.core.dom.Type;
  import org.eclipse.wst.jsdt.core.dom.TypeDeclaration;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclaration;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationExpression;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationStatement;
  import org.eclipse.wst.jsdt.core.dom.WhileStatement;
  import org.eclipse.wst.jsdt.core.dom.WithStatement;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ListRewrite;
  import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodeFactory;
  import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
  import org.eclipse.wst.jsdt.internal.corext.dom.Bindings;
  import org.eclipse.wst.jsdt.internal.corext.dom.LinkedNodeFinder;
  import org.eclipse.wst.jsdt.internal.corext.dom.Selection;
  import org.eclipse.wst.jsdt.internal.corext.dom.SelectionAnalyzer;
  import org.eclipse.wst.jsdt.internal.corext.fix.CleanUpConstants;
  import org.eclipse.wst.jsdt.internal.corext.fix.ControlStatementsFix;
  import org.eclipse.wst.jsdt.internal.corext.fix.ConvertLoopFix;
  import org.eclipse.wst.jsdt.internal.corext.fix.IFix;
  import org.eclipse.wst.jsdt.internal.corext.fix.LinkedProposalModel;
  import org.eclipse.wst.jsdt.internal.corext.fix.VariableDeclarationFix;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.code.ConvertAnonymousToNestedRefactoring;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.code.ExtractConstantRefactoring;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.code.ExtractTempRefactoring;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.code.InlineTempRefactoring;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.code.PromoteTempToFieldRefactoring;
  import org.eclipse.wst.jsdt.internal.corext.util.Messages;
  import org.eclipse.wst.jsdt.internal.ui.JavaPluginImages;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.fix.ControlStatementsCleanUp;
  import org.eclipse.wst.jsdt.internal.ui.fix.ConvertLoopCleanUp;
  import org.eclipse.wst.jsdt.internal.ui.fix.ICleanUp;
  import org.eclipse.wst.jsdt.internal.ui.fix.VariableDeclarationCleanUp;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.JavaElementImageProvider;
  import org.eclipse.wst.jsdt.ui.text.java.IInvocationContext;
  import org.eclipse.wst.jsdt.ui.text.java.IJavaCompletionProposal;
  import org.eclipse.wst.jsdt.ui.text.java.IProblemLocation;
  import org.eclipse.wst.jsdt.ui.text.java.IQuickAssistProcessor;
  
  /**
    */
  public class QuickAssistProcessor implements IQuickAssistProcessor {
  
  	public static final String SPLIT_JOIN_VARIABLE_DECLARATION_ID= "org.eclipse.wst.jsdt.ui.correction.splitJoinVariableDeclaration.assist"; //$NON-NLS-1$
  	public static final String CONVERT_FOR_LOOP_ID= "org.eclipse.wst.jsdt.ui.correction.convertForLoop.assist"; //$NON-NLS-1$
  	public static final String ASSIGN_TO_LOCAL_ID= "org.eclipse.wst.jsdt.ui.correction.assignToLocal.assist"; //$NON-NLS-1$
  	public static final String ASSIGN_TO_FIELD_ID= "org.eclipse.wst.jsdt.ui.correction.assignToField.assist"; //$NON-NLS-1$
  	public static final String ASSIGN_PARAM_TO_FIELD_ID= "org.eclipse.wst.jsdt.ui.correction.assignParamToField.assist"; //$NON-NLS-1$
  	public static final String ADD_BLOCK_ID= "org.eclipse.wst.jsdt.ui.correction.addBlock.assist"; //$NON-NLS-1$
  	public static final String EXTRACT_LOCAL_ID= "org.eclipse.wst.jsdt.ui.correction.extractLocal.assist"; //$NON-NLS-1$
  	public static final String EXTRACT_CONSTANT_ID= "org.eclipse.wst.jsdt.ui.correction.extractConstant.assist"; //$NON-NLS-1$
  	public static final String INLINE_LOCAL_ID= "org.eclipse.wst.jsdt.ui.correction.inlineLocal.assist"; //$NON-NLS-1$
  	public static final String CONVERT_LOCAL_TO_FIELD_ID= "org.eclipse.wst.jsdt.ui.correction.convertLocalToField.assist"; //$NON-NLS-1$
  	public static final String CONVERT_ANONYMOUS_TO_LOCAL_ID= "org.eclipse.wst.jsdt.ui.correction.convertAnonymousToLocal.assist"; //$NON-NLS-1$
  	
  	public QuickAssistProcessor() {
  		super();
  	}
  
  	public boolean hasAssists(IInvocationContext context) throws CoreException {
  		ASTNode coveringNode= context.getCoveringNode();
  		if (coveringNode != null) {
  			return getCatchClauseToThrowsProposals(context, coveringNode, null)
  				|| getRenameLocalProposals(context, coveringNode, null, false, null)
  				|| getAssignToVariableProposals(context, coveringNode, null)
  				|| getUnWrapProposals(context, coveringNode, null)
  				|| getAssignParamToFieldProposals(context, coveringNode, null)
  				|| getJoinVariableProposals(context, coveringNode, null)
  				|| getAddFinallyProposals(context, coveringNode, null)
  				|| getAddElseProposals(context, coveringNode, null)
  				|| getSplitVariableProposals(context, coveringNode, null)
  				|| getAddBlockProposals(context, coveringNode, null)
  				|| getArrayInitializerToArrayCreation(context, coveringNode, null)
  				|| getCreateInSuperClassProposals(context, coveringNode, null)
  				|| getInvertEqualsProposal(context, coveringNode, null)
  				|| getExtractLocalProposal(context, coveringNode, null)
  				|| getInlineLocalProposal(context, coveringNode, null)
  				|| getConvertLocalToFieldProposal(context, coveringNode, null)
  				|| getConvertAnonymousToNestedProposal(context, coveringNode, null)
  				|| getRemoveBlockProposals(context, coveringNode, null)
  				|| getMakeVariableDeclarationFinalProposals(context, coveringNode, null);
  		}
  		return false;
  	}
  
  	public IJavaCompletionProposal[] getAssists(IInvocationContext context, IProblemLocation[] locations) throws CoreException {
  		ASTNode coveringNode= context.getCoveringNode();
  		if (coveringNode != null) {
  			ArrayList resultingCollections= new ArrayList();
  			boolean noErrorsAtLocation= noErrorsAtLocation(locations);
  			
  			// quick assists that show up also if there is an error/warning
  			getRenameLocalProposals(context, coveringNode, locations, noErrorsAtLocation, resultingCollections);
  			getAssignToVariableProposals(context, coveringNode, resultingCollections);
  			getAssignParamToFieldProposals(context, coveringNode, resultingCollections);
  			
  			if (noErrorsAtLocation) {
  				getCatchClauseToThrowsProposals(context, coveringNode, resultingCollections);
  				getUnWrapProposals(context, coveringNode, resultingCollections);
  				getSplitVariableProposals(context, coveringNode, resultingCollections);
  				getJoinVariableProposals(context, coveringNode, resultingCollections);
  				getAddFinallyProposals(context, coveringNode, resultingCollections);
  				getAddElseProposals(context, coveringNode, resultingCollections);
  				getAddBlockProposals(context, coveringNode, resultingCollections);
  				getInvertEqualsProposal(context, coveringNode, resultingCollections);
  				getArrayInitializerToArrayCreation(context, coveringNode, resultingCollections);
  				getCreateInSuperClassProposals(context, coveringNode, resultingCollections);
  				getExtractLocalProposal(context, coveringNode, resultingCollections);
  				getInlineLocalProposal(context, coveringNode, resultingCollections);
  				getConvertLocalToFieldProposal(context, coveringNode, resultingCollections);				
  				getConvertAnonymousToNestedProposal(context, coveringNode, resultingCollections);
  				getRemoveBlockProposals(context, coveringNode, resultingCollections);
  				getMakeVariableDeclarationFinalProposals(context, coveringNode, resultingCollections);
  			}
  			return (IJavaCompletionProposal[]) resultingCollections.toArray(new IJavaCompletionProposal[resultingCollections.size()]);
  		}
  		return null;
  	}
  
  	private boolean noErrorsAtLocation(IProblemLocation[] locations) {
  		if (locations != null) {
  			for (int i= 0; i < locations.length; i++) {
  				if (locations[i].isError()) {
  					return false;
  				}
  			}
  		}
  		return true;
  	}
  	
  	private static boolean getExtractLocalProposal(IInvocationContext context, ASTNode covering, Collection proposals) throws CoreException {
  		ASTNode node= context.getCoveredNode();
  		
  		if (!(node instanceof Expression)) {
  			return false;
  		}
  		final Expression expression= (Expression) node;
  		
  		ITypeBinding binding= expression.resolveTypeBinding();
  		if (binding == null || Bindings.isVoidType(binding)) {
  			return false;
  		}
  		if (proposals == null) {
  			return true;
  		}
  		
  		final IJavaScriptUnit cu= context.getCompilationUnit();
  		final ExtractTempRefactoring extractTempRefactoring= new ExtractTempRefactoring(context.getASTRoot(), expression.getStartPosition(), expression.getLength());
  		if (extractTempRefactoring.checkInitialConditions(new NullProgressMonitor()).isOK()) {
  			String label= CorrectionMessages.QuickAssistProcessor_extract_to_local_description;
  			Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL);
  			CUCorrectionProposal proposal= new CUCorrectionProposal(label, cu, 5, image) {
  				protected TextChange createTextChange() throws CoreException {
  					extractTempRefactoring.setTempName(extractTempRefactoring.guessTempName()); // expensive
  					extractTempRefactoring.setLinkedProposalModel(getLinkedProposalModel());
  					return extractTempRefactoring.createTextChange(new NullProgressMonitor());
  				}
  			};
  			proposal.setCommandId(EXTRACT_LOCAL_ID);
  			proposals.add(proposal);
  		}
  		final ExtractConstantRefactoring extractConstRefactoring= new ExtractConstantRefactoring(context.getASTRoot(), expression.getStartPosition(), expression.getLength());
  		if (extractConstRefactoring.checkInitialConditions(new NullProgressMonitor()).isOK()) {
  			String label= CorrectionMessages.QuickAssistProcessor_extract_to_constant_description;
  			Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL);
  			CUCorrectionProposal proposal= new CUCorrectionProposal(label, cu, 4, image) {
  				protected TextChange createTextChange() throws CoreException {
  					extractConstRefactoring.setConstantName(extractConstRefactoring.guessConstantName()); // expensive
  					extractConstRefactoring.setLinkedProposalModel(getLinkedProposalModel());
  					return extractConstRefactoring.createTextChange(new NullProgressMonitor());
  				}
  			};
  			proposal.setCommandId(EXTRACT_CONSTANT_ID);
  			proposals.add(proposal);
  		}
  		return false;
  	}
  	
  
  	private static boolean getConvertAnonymousToNestedProposal(IInvocationContext context, final ASTNode node, Collection proposals) throws CoreException {
  		if (!(node instanceof Name))
  			return false;
  		
  		ASTNode normalized= ASTNodes.getNormalizedNode(node);
  		if (normalized.getLocationInParent() != ClassInstanceCreation.TYPE_PROPERTY)
  			return false;
  		
  		final AnonymousClassDeclaration anonymTypeDecl= ((ClassInstanceCreation) normalized.getParent()).getAnonymousClassDeclaration();
  		if (anonymTypeDecl == null || anonymTypeDecl.resolveBinding() == null) {
  			return false;
  		}
  		
  		if (proposals == null) {
  			return true;
  		}
  
  		final IJavaScriptUnit cu= context.getCompilationUnit();
  		final ConvertAnonymousToNestedRefactoring refactoring= new ConvertAnonymousToNestedRefactoring(anonymTypeDecl);
  		String extTypeName= ASTNodes.getSimpleNameIdentifier((Name) node);
  		
  		refactoring.setClassName(Messages.format(CorrectionMessages.QuickAssistProcessor_name_extension_from_interface, extTypeName));
  
  		if (refactoring.checkInitialConditions(new NullProgressMonitor()).isOK()) {
  			LinkedProposalModel linkedProposalModel= new LinkedProposalModel();
  			refactoring.setLinkedProposalModel(linkedProposalModel);
  			
  			String label= CorrectionMessages.QuickAssistProcessor_convert_anonym_to_nested;
  			Image image= JavaScriptPlugin.getImageDescriptorRegistry().get(JavaElementImageProvider.getTypeImageDescriptor(true, false, Flags.AccPrivate, false));
  			RefactoringCorrectionProposal proposal= new RefactoringCorrectionProposal(label, cu, refactoring, 5, image);
  			proposal.setLinkedProposalModel(linkedProposalModel);
  			proposal.setCommandId(CONVERT_ANONYMOUS_TO_LOCAL_ID);
  			proposals.add(proposal);
  		}
  		return false;
  	}
  
  	private static boolean getJoinVariableProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		ASTNode parent= node.getParent();
  		
  		VariableDeclarationFragment fragment= null;
  		boolean onFirstAccess= false;
  		if (node instanceof SimpleName && node.getLocationInParent() == Assignment.LEFT_HAND_SIDE_PROPERTY) {
  			onFirstAccess= true;
  			SimpleName name= (SimpleName) node;
  			IBinding binding= name.resolveBinding();
  			if (!(binding instanceof IVariableBinding)) {
  				return false;
  			}
  			ASTNode declaring= context.getASTRoot().findDeclaringNode(binding);
  			if (declaring instanceof VariableDeclarationFragment) {
  				fragment= (VariableDeclarationFragment) declaring;
  			} else {
  				return false;
  			}
  		} else if (parent instanceof VariableDeclarationFragment) {
  			fragment= (VariableDeclarationFragment) parent;
  		} else {
  			return false;
  		}
  
  		IVariableBinding binding= fragment.resolveBinding();
  		if (fragment.getInitializer() != null || binding == null || binding.isField()) {
  			return false;
  		}
  
  		if (!(fragment.getParent() instanceof VariableDeclarationStatement)) {
  			return false;
  		}
  		VariableDeclarationStatement statement= (VariableDeclarationStatement) fragment.getParent();
  
  		SimpleName[] names= LinkedNodeFinder.findByBinding(statement.getParent(), binding);
  		if (names.length <= 1 || names[0] != fragment.getName()) {
  			return false;
  		}
  		SimpleName firstAccess= names[1];
  		if (onFirstAccess) {
  			if (firstAccess != node) {
  				return false;
  			}
  		} else {
  			if (firstAccess.getLocationInParent() != Assignment.LEFT_HAND_SIDE_PROPERTY) {
  				return false;
  			}
  		}
  		Assignment assignment= (Assignment) firstAccess.getParent();
  		if (assignment.getLocationInParent() != ExpressionStatement.EXPRESSION_PROPERTY) {
  			return false;
  		}
  		ExpressionStatement assignParent= (ExpressionStatement) assignment.getParent();
  
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		AST ast= statement.getAST();
  		ASTRewrite rewrite= ASTRewrite.create(ast);
  
  		String label= CorrectionMessages.QuickAssistProcessor_joindeclaration_description;
  		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL);
  		LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, context.getCompilationUnit(), rewrite, 1, image);
  		proposal.setCommandId(SPLIT_JOIN_VARIABLE_DECLARATION_ID);
  		
  		Expression placeholder= (Expression) rewrite.createMoveTarget(assignment.getRightHandSide());
  		rewrite.set(fragment, VariableDeclarationFragment.INITIALIZER_PROPERTY, placeholder, null);
  
  		
  		if (onFirstAccess) {
  			// replace assignment with variable declaration
  			rewrite.replace(assignParent, rewrite.createMoveTarget(statement), null);
  		} else {
  			// different scopes -> remove assignments, set variable initializer
  			if (ASTNodes.isControlStatementBody(assignParent.getLocationInParent())) {
  				Block block= ast.newBlock();
  				rewrite.replace(assignParent, block, null);
  			} else {
  				rewrite.remove(assignParent, null);
  			}
  		}
  
  		proposal.setEndPosition(rewrite.track(fragment.getName()));
  		resultingCollections.add(proposal);
  		return true;
  
  	}
  
  	private static boolean getSplitVariableProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		VariableDeclarationFragment fragment;
  		if (node instanceof VariableDeclarationFragment) {
  			fragment= (VariableDeclarationFragment) node;
  		} else if (node.getLocationInParent() == VariableDeclarationFragment.NAME_PROPERTY) {
  			fragment= (VariableDeclarationFragment) node.getParent();
  		} else {
  			return false;
  		}
  
  		if (fragment.getInitializer() == null) {
  			return false;
  		}
  
  		Statement statement;
  		ASTNode fragParent= fragment.getParent();
  		if (fragParent instanceof VariableDeclarationStatement) {
  			statement= (VariableDeclarationStatement) fragParent;
  		} else if (fragParent instanceof VariableDeclarationExpression) {
  			statement= (Statement) fragParent.getParent();
  		} else {
  			return false;
  		}
  		// statement is ForStatement or VariableDeclarationStatement
  
  		ASTNode statementParent= statement.getParent();
  		StructuralPropertyDescriptor property= statement.getLocationInParent();
  		if (!property.isChildListProperty()) {
  			return false;
  		}
  
  		List list= (List) statementParent.getStructuralProperty(property);
  
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		AST ast= statement.getAST();
  		ASTRewrite rewrite= ASTRewrite.create(ast);
  
  		String label= CorrectionMessages.QuickAssistProcessor_splitdeclaration_description;
  		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL);
  		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, 1, image);
  		proposal.setCommandId(SPLIT_JOIN_VARIABLE_DECLARATION_ID);
  		
  		Statement newStatement;
  		int insertIndex= list.indexOf(statement);
  
  		Expression placeholder= (Expression) rewrite.createMoveTarget(fragment.getInitializer());
  		ITypeBinding binding= fragment.getInitializer().resolveTypeBinding();
  		if (placeholder instanceof ArrayInitializer && binding != null && binding.isArray()) {
  			ArrayCreation creation= ast.newArrayCreation();
  			creation.setInitializer((ArrayInitializer) placeholder);
  			final ITypeBinding componentType= binding.getElementType();
  			Type type= null;
  			if (componentType.isPrimitive())
  				type= ast.newPrimitiveType(PrimitiveType.toCode(componentType.getName()));
  			else
  				type= ast.newSimpleType(ast.newSimpleName(componentType.getName()));
  			creation.setType(ast.newArrayType(type, binding.getDimensions()));
  			placeholder= creation;
  		}
  		Assignment assignment= ast.newAssignment();
  		assignment.setRightHandSide(placeholder);
  		assignment.setLeftHandSide(ast.newSimpleName(fragment.getName().getIdentifier()));
  
  		if (statement instanceof VariableDeclarationStatement) {
  			newStatement= ast.newExpressionStatement(assignment);
  			insertIndex+= 1; // add after declaration
  		} else {
  			rewrite.replace(fragment.getParent(), assignment, null);
  			VariableDeclarationFragment newFrag= ast.newVariableDeclarationFragment();
  			newFrag.setName(ast.newSimpleName(fragment.getName().getIdentifier()));
  			newFrag.setExtraDimensions(fragment.getExtraDimensions());
  
  			VariableDeclarationExpression oldVarDecl= (VariableDeclarationExpression) fragParent;
  
  			VariableDeclarationStatement newVarDec= ast.newVariableDeclarationStatement(newFrag);
  			newVarDec.setType((Type) ASTNode.copySubtree(ast, oldVarDecl.getType()));
  			newVarDec.modifiers().addAll(ASTNodeFactory.newModifiers(ast, oldVarDecl.getModifiers()));
  			newStatement= newVarDec;
  		}
  
  		ListRewrite listRewriter= rewrite.getListRewrite(statementParent, (ChildListPropertyDescriptor) property);
  		listRewriter.insertAt(newStatement, insertIndex, null);
  
  		resultingCollections.add(proposal);
  		return true;
  	}
  
  	private static boolean getAssignToVariableProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		Statement statement= ASTResolving.findParentStatement(node);
  		if (!(statement instanceof ExpressionStatement)) {
  			return false;
  		}
  		ExpressionStatement expressionStatement= (ExpressionStatement) statement;
  
  		Expression expression= expressionStatement.getExpression();
  		if (expression.getNodeType() == ASTNode.ASSIGNMENT) {
  			return false; // too confusing and not helpful
  		}
  
  		ITypeBinding typeBinding= expression.resolveTypeBinding();
  		typeBinding= Bindings.normalizeTypeBinding(typeBinding);
  		if (typeBinding == null) {
  			return false;
  		}
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		IJavaScriptUnit cu= context.getCompilationUnit();
  
  		AssignToVariableAssistProposal localProposal= new AssignToVariableAssistProposal(cu, AssignToVariableAssistProposal.LOCAL, expressionStatement, typeBinding, 2);
  		localProposal.setCommandId(ASSIGN_TO_LOCAL_ID);
  		resultingCollections.add(localProposal);
  
  		ASTNode type= ASTResolving.findParentType(expression);
  		if (type != null) {
  			AssignToVariableAssistProposal fieldProposal= new AssignToVariableAssistProposal(cu, AssignToVariableAssistProposal.FIELD, expressionStatement, typeBinding, 1);
  			fieldProposal.setCommandId(ASSIGN_TO_FIELD_ID);
  			resultingCollections.add(fieldProposal);
  		}
  		return false;
  
  	}
  
  	private static boolean getAssignParamToFieldProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		node= ASTNodes.getNormalizedNode(node);
  		ASTNode parent= node.getParent();
  		if (!(parent instanceof SingleVariableDeclaration) || !(parent.getParent() instanceof FunctionDeclaration)) {
  			return false;
  		}
  		SingleVariableDeclaration paramDecl= (SingleVariableDeclaration) parent;
  		IVariableBinding binding= paramDecl.resolveBinding();
  
  		FunctionDeclaration methodDecl= (FunctionDeclaration) parent.getParent();
  		if (binding == null || methodDecl.getBody() == null) {
  			return false;
  		}
  		ITypeBinding typeBinding= binding.getType();
  		if (typeBinding == null) {
  			return false;
  		}
  		
  		if (resultingCollections == null) {
  			return true;
  		}
  		
  		ITypeBinding parentType= Bindings.getBindingOfParentType(node);
  		if (parentType != null) {
  			// assign to existing fields
  			JavaScriptUnit root= context.getASTRoot();
  			IVariableBinding[] declaredFields= parentType.getDeclaredFields();
  			boolean isStaticContext= ASTResolving.isInStaticContext(node);
  			for (int i= 0; i < declaredFields.length; i++) {
  				IVariableBinding curr= declaredFields[i];
  				if (isStaticContext == Modifier.isStatic(curr.getModifiers()) && typeBinding.isAssignmentCompatible(curr.getType())) {
  					ASTNode fieldDeclFrag= root.findDeclaringNode(curr);
  					if (fieldDeclFrag instanceof VariableDeclarationFragment) {
  						VariableDeclarationFragment fragment= (VariableDeclarationFragment) fieldDeclFrag;
  						if (fragment.getInitializer() == null) {
  							resultingCollections.add(new AssignToVariableAssistProposal(context.getCompilationUnit(), paramDecl, fragment, typeBinding, 1));
  						}
  					}
  				}
  			}
  		}
  
  		AssignToVariableAssistProposal fieldProposal= new AssignToVariableAssistProposal(context.getCompilationUnit(), paramDecl, null, typeBinding, 3);
  		fieldProposal.setCommandId(ASSIGN_PARAM_TO_FIELD_ID);
  		resultingCollections.add(fieldProposal);
  		return true;
  	}
  
  	private static boolean getAddFinallyProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		TryStatement tryStatement= ASTResolving.findParentTryStatement(node);
  		if (tryStatement == null || tryStatement.getFinally() != null) {
  			return false;
  		}
  		Statement statement= ASTResolving.findParentStatement(node);
  		if (tryStatement != statement && tryStatement.getBody() != statement) {
  			return false; // an node inside a catch or finally block
  		}
  
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		AST ast= tryStatement.getAST();
  		ASTRewrite rewrite= ASTRewrite.create(ast);
  		Block finallyBody= ast.newBlock();
  
  		rewrite.set(tryStatement, TryStatement.FINALLY_PROPERTY, finallyBody, null);
  
  		String label= CorrectionMessages.QuickAssistProcessor_addfinallyblock_description;
  		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_ADD);
  		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, 1, image);
  		resultingCollections.add(proposal);
  		return true;
  	}
  
  	private static boolean getAddElseProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		Statement statement= ASTResolving.findParentStatement(node);
  		if (!(statement instanceof IfStatement)) {
  			return false;
  		}
  		IfStatement ifStatement= (IfStatement) statement;
  		if (ifStatement.getElseStatement() != null) {
  			return false;
  		}
  
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		AST ast= statement.getAST();
  		ASTRewrite rewrite= ASTRewrite.create(ast);
  		Block body= ast.newBlock();
  
  		rewrite.set(ifStatement, IfStatement.ELSE_STATEMENT_PROPERTY, body, null);
  
  		String label= CorrectionMessages.QuickAssistProcessor_addelseblock_description;
  		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_ADD);
  		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, 1, image);
  		resultingCollections.add(proposal);
  		return true;
  	}
  
  	public static boolean getCatchClauseToThrowsProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		CatchClause catchClause= (CatchClause) ASTResolving.findAncestor(node, ASTNode.CATCH_CLAUSE);
  		if (catchClause == null) {
  			return false;
  		}
  
  		Statement statement= ASTResolving.findParentStatement(node);
  		if (statement != catchClause.getParent() && statement != catchClause.getBody()) {
  			return false; // selection is in a statement inside the body
  		}
  
  		Type type= catchClause.getException().getType();
  		if (!type.isSimpleType()) {
  			return false;
  		}
  
  		BodyDeclaration bodyDeclaration= ASTResolving.findParentBodyDeclaration(catchClause);
  		if (!(bodyDeclaration instanceof FunctionDeclaration) && !(bodyDeclaration instanceof Initializer)) {
  			return false;
  		}
  
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		AST ast= bodyDeclaration.getAST();
  
  		if (bodyDeclaration instanceof FunctionDeclaration) {
  			FunctionDeclaration methodDeclaration= (FunctionDeclaration) bodyDeclaration;
  
  			ASTRewrite rewrite= ASTRewrite.create(ast);
  
  			removeCatchBlock(rewrite, catchClause);
  
  			ITypeBinding binding= type.resolveBinding();
  			if (binding == null || isNotYetThrown(binding, methodDeclaration.thrownExceptions())) {
  				Name name= ((SimpleType) type).getName();
  				Name newName= (Name) ASTNode.copySubtree(ast, name);
  
  				ListRewrite listRewriter= rewrite.getListRewrite(methodDeclaration, FunctionDeclaration.THROWN_EXCEPTIONS_PROPERTY);
  				listRewriter.insertLast(newName, null);
  			}
  
  			String label= CorrectionMessages.QuickAssistProcessor_catchclausetothrows_description;
  			Image image= JavaPluginImages.get(JavaPluginImages.IMG_OBJS_EXCEPTION);
  			ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, 4, image);
  			resultingCollections.add(proposal);
  		}
  		{  // for initializers or method declarations
  			ASTRewrite rewrite= ASTRewrite.create(ast);
  
  			removeCatchBlock(rewrite, catchClause);
  			String label= CorrectionMessages.QuickAssistProcessor_removecatchclause_description;
  			Image image= JavaPluginImages.get(JavaPluginImages.IMG_OBJS_EXCEPTION);
  			ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, 5, image);
  			resultingCollections.add(proposal);
  		}
  
  		return true;
  	}
  
  	private static void removeCatchBlock(ASTRewrite rewrite, CatchClause catchClause) {
  		TryStatement tryStatement= (TryStatement) catchClause.getParent();
  		if (tryStatement.catchClauses().size() > 1 || tryStatement.getFinally() != null) {
  			rewrite.remove(catchClause, null);
  		} else {
  			Block block= tryStatement.getBody();
  			List statements= block.statements();
  			int nStatements= statements.size();
  			if (nStatements == 1) {
  				ASTNode first= (ASTNode) statements.get(0);
  				rewrite.replace(tryStatement, rewrite.createCopyTarget(first), null);
  			} else if (nStatements > 1) {
  				ListRewrite listRewrite= rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
  				ASTNode first= (ASTNode) statements.get(0);
  				ASTNode last= (ASTNode) statements.get(statements.size() - 1);
  				ASTNode newStatement= listRewrite.createCopyTarget(first, last);
  				if (ASTNodes.isControlStatementBody(tryStatement.getLocationInParent())) {
  					Block newBlock= rewrite.getAST().newBlock();
  					newBlock.statements().add(newStatement);
  					newStatement= newBlock;
  				}
  				rewrite.replace(tryStatement, newStatement, null);
  			} else {
  				rewrite.remove(tryStatement, null);
  			}
  		}
  	}
  
  	private static boolean isNotYetThrown(ITypeBinding binding, List thrownExcpetions) {
  		for (int i= 0; i < thrownExcpetions.size(); i++) {
  			Name name= (Name) thrownExcpetions.get(i);
  			ITypeBinding elem= (ITypeBinding) name.resolveBinding();
  			if (elem != null) {
  				if (Bindings.isSuperType(elem, binding)) { // existing exception is base class of new
  					return false;
  				}
  			}
  		}
  		return true;
  	}
  
  
  	private static boolean getRenameLocalProposals(IInvocationContext context, ASTNode node, IProblemLocation[] locations, boolean noErrorsAtLocation, Collection resultingCollections) {
  		if (!(node instanceof SimpleName)) {
  			return false;
  		}
  		SimpleName name= (SimpleName) node;
  		IBinding binding= name.resolveBinding();
  		if (binding != null && binding.getKind() == IBinding.PACKAGE) {
  			return false;
  		}
  
  		if (locations != null) {
  			for (int i= 0; i < locations.length; i++) {
  				switch (locations[i].getProblemId()) {
  					case IProblem.LocalVariableHidingLocalVariable:
  					case IProblem.LocalVariableHidingField:
  					case IProblem.FieldHidingLocalVariable:
  					case IProblem.FieldHidingField:
  					case IProblem.ArgumentHidingLocalVariable:
  					case IProblem.ArgumentHidingField:
  						return false;
  				}
  			}
  		}
  
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		LinkedNamesAssistProposal proposal= new LinkedNamesAssistProposal(context.getCompilationUnit(), name);
  		if (!noErrorsAtLocation) {
  			proposal.setRelevance(1);
  		}
  		
  		resultingCollections.add(proposal);
  		return true;
  	}
  
  	public static ASTNode getCopyOfInner(ASTRewrite rewrite, ASTNode statement, boolean toControlStatementBody) {
  		if (statement.getNodeType() == ASTNode.BLOCK) {
  			Block block= (Block) statement;
  			List innerStatements= block.statements();
  			int nStatements= innerStatements.size();
  			if (nStatements == 1) {
  				return rewrite.createCopyTarget(((ASTNode) innerStatements.get(0)));
  			} else if (nStatements > 1) {
  				if (toControlStatementBody) {
  					return rewrite.createCopyTarget(block);
  				}
  				ListRewrite listRewrite= rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
  				ASTNode first= (ASTNode) innerStatements.get(0);
  				ASTNode last= (ASTNode) innerStatements.get(nStatements - 1);
  				return listRewrite.createCopyTarget(first, last);
  			}
  			return null;
  		} else {
  			return rewrite.createCopyTarget(statement);
  		}
  	}
  
  
  	private static boolean getUnWrapProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		ASTNode outer= node;
  
  		Block block= null;
  		if (outer.getNodeType() == ASTNode.BLOCK) {
  			block= (Block) outer;
  			outer= block.getParent();
  		}
  
  		ASTNode body= null;
  		String label= null;
  		if (outer instanceof IfStatement) {
  			IfStatement ifStatement= (IfStatement) outer;
  			Statement elseBlock= ifStatement.getElseStatement();
  			if (elseBlock == null || ((elseBlock instanceof Block) && ((Block) elseBlock).statements().isEmpty())) {
  				body= ifStatement.getThenStatement();
  			}
  			label= CorrectionMessages.QuickAssistProcessor_unwrap_ifstatement;
  		} else if (outer instanceof WhileStatement) {
  			body=((WhileStatement) outer).getBody();
  			label= CorrectionMessages.QuickAssistProcessor_unwrap_whilestatement;
  		} else if (outer instanceof ForStatement) {
  			body=((ForStatement) outer).getBody();
  			label= CorrectionMessages.QuickAssistProcessor_unwrap_forstatement;
  		} else if (outer instanceof DoStatement) {
  			body=((DoStatement) outer).getBody();
  			label= CorrectionMessages.QuickAssistProcessor_unwrap_dostatement;
  		} else if (outer instanceof TryStatement) {
  			TryStatement tryStatement= (TryStatement) outer;
  			if (tryStatement.catchClauses().isEmpty()) {
  				body= tryStatement.getBody();
  			}
  			label= CorrectionMessages.QuickAssistProcessor_unwrap_trystatement;
  		} else if (outer instanceof AnonymousClassDeclaration) {
  			List decls= ((AnonymousClassDeclaration) outer).bodyDeclarations();
  			for (int i= 0; i < decls.size(); i++) {
  				ASTNode elem= (ASTNode) decls.get(i);
  				if (elem instanceof FunctionDeclaration) {
  					Block curr= ((FunctionDeclaration) elem).getBody();
  					if (curr != null && !curr.statements().isEmpty()) {
  						if (body != null) {
  							return false;
  						}
  						body= curr;
  					}
  				} else if (elem instanceof TypeDeclaration) {
  					return false;
  				}
  			}
  			label= CorrectionMessages.QuickAssistProcessor_unwrap_anonymous;
  			outer= ASTResolving.findParentStatement(outer);
  			if (outer == null) {
  				return false; // private Object o= new Object() { ... };
  			}
  		} else if (outer instanceof Block) {
  			//	-> a block in a block
  			body= block;
  			outer= block;
  			label= CorrectionMessages.QuickAssistProcessor_unwrap_block;
  		} else if (outer instanceof ParenthesizedExpression) {
  			//ParenthesizedExpression expression= (ParenthesizedExpression) outer;
  			//body= expression.getExpression();
  			//label= CorrectionMessages.getString("QuickAssistProcessor.unwrap.parenthesis");	 //$NON-NLS-1$
  		} else if (outer instanceof FunctionInvocation) {
  			FunctionInvocation invocation= (FunctionInvocation) outer;
  			if (invocation.arguments().size() == 1) {
  				body= (ASTNode) invocation.arguments().get(0);
  				if (invocation.getParent().getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
  					int kind= body.getNodeType();
  					if (kind != ASTNode.ASSIGNMENT && kind != ASTNode.PREFIX_EXPRESSION && kind != ASTNode.POSTFIX_EXPRESSION
  							&& kind != ASTNode.FUNCTION_INVOCATION && kind != ASTNode.SUPER_METHOD_INVOCATION) {
  						body= null;
  					}
  				}
  				label= CorrectionMessages.QuickAssistProcessor_unwrap_methodinvocation;
  			}
  		}
  		if (body == null) {
  			return false;
  		}
  		ASTRewrite rewrite= ASTRewrite.create(outer.getAST());
  		ASTNode inner= getCopyOfInner(rewrite, body, ASTNodes.isControlStatementBody(outer.getLocationInParent()));
  		if (inner == null) {
  			return false;
  		}
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		rewrite.replace(outer, inner, null);
  		Image image= JavaPluginImages.get(JavaPluginImages.IMG_OBJS_EXCEPTION);
  		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, 1, image);
  		resultingCollections.add(proposal);
  		return true;
  	}
  
  	private static boolean isControlStatementWithBlock(ASTNode node) {
  		switch (node.getNodeType()) {
  			case ASTNode.IF_STATEMENT:
  			case ASTNode.WHILE_STATEMENT:
  			case ASTNode.FOR_STATEMENT:
  			case ASTNode.FOR_IN_STATEMENT:
  			case ASTNode.DO_STATEMENT:
  				return true;
  			default:
  				return false;
  		}
  	}
  
  	private static boolean getRemoveBlockProposals(IInvocationContext context, ASTNode coveringNode, Collection resultingCollections) {
  		IFix[] fixes= ControlStatementsFix.createRemoveBlockFix(context.getASTRoot(), coveringNode);
  		if (fixes != null) {
  			if (resultingCollections == null) {
  				return true;
  			}
  			Map options= new Hashtable();
  			options.put(CleanUpConstants.CONTROL_STATEMENTS_USE_BLOCKS, CleanUpConstants.TRUE);
  			options.put(CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_NEVER, CleanUpConstants.TRUE);
  			ICleanUp cleanUp= new ControlStatementsCleanUp(options);
  			for (int i= 0; i < fixes.length; i++) {
  				IFix fix= fixes[i];
  				Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
  				FixCorrectionProposal proposal= new FixCorrectionProposal(fix, cleanUp, 0, image, context);
  				resultingCollections.add(proposal);
  			}
  			return true;
  		}
  		return false;
  	}
  
  	private static boolean getAddBlockProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		Statement statement= ASTResolving.findParentStatement(node);
  		if (statement == null) {
  			return false;
  		}
  
  		if (!isControlStatementWithBlock(statement)) {
  			if (!isControlStatementWithBlock(statement.getParent())) {
  				return false;
  			}
  			int statementStart= statement.getStartPosition();
  			int statementEnd= statementStart + statement.getLength();
  
  			int offset= context.getSelectionOffset();
  			int length= context.getSelectionLength();
  			if (length == 0) {
  				if (offset != statementEnd) { // cursor at end
  					return false;
  				}
  			} else {
  				if (offset > statementStart || offset + length < statementEnd) { // statement selected
  					return false;
  				}
  			}
  			statement= (Statement) statement.getParent();
  		}
  
  		StructuralPropertyDescriptor childProperty= null;
  		ASTNode child= null;
  		switch (statement.getNodeType()) {
  			case ASTNode.IF_STATEMENT:
  				int selectionStart= context.getSelectionOffset();
  				int selectionEnd= context.getSelectionOffset() + context.getSelectionLength();
  				ASTNode then= ((IfStatement) statement).getThenStatement();
  				if (selectionEnd <= then.getStartPosition() + then.getLength()) {
  					if (!(then instanceof Block)) {
  						childProperty= IfStatement.THEN_STATEMENT_PROPERTY;
  						child= then;
  					}
  				} else if (selectionStart >=  then.getStartPosition() + then.getLength()) {
  					ASTNode elseStatement= ((IfStatement) statement).getElseStatement();
  					if (!(elseStatement instanceof Block)) {
  						childProperty= IfStatement.ELSE_STATEMENT_PROPERTY;
  						child= elseStatement;
  					}
  				}
  				break;
  			case ASTNode.WHILE_STATEMENT:
  				ASTNode whileBody= ((WhileStatement) statement).getBody();
  				if (!(whileBody instanceof Block)) {
  					childProperty= WhileStatement.BODY_PROPERTY;
  					child= whileBody;
  				}
  				break;
  			case ASTNode.WITH_STATEMENT:
  				ASTNode withBody= ((WithStatement) statement).getBody();
  				if (!(withBody instanceof Block)) {
  					childProperty= WithStatement.BODY_PROPERTY;
  					child= withBody;
  				}
  				break;
  			case ASTNode.FOR_STATEMENT:
  				ASTNode forBody= ((ForStatement) statement).getBody();
  				if (!(forBody instanceof Block)) {
  					childProperty= ForStatement.BODY_PROPERTY;
  					child= forBody;
  				}
  				break;
  			case ASTNode.FOR_IN_STATEMENT:
  				ASTNode forInBody= ((ForInStatement) statement).getBody();
  				if (!(forInBody instanceof Block)) {
  					childProperty= ForInStatement.BODY_PROPERTY;
  					child= forInBody;
  				}
  				break;
  			case ASTNode.DO_STATEMENT:
  				ASTNode doBody= ((DoStatement) statement).getBody();
  				if (!(doBody instanceof Block)) {
  					childProperty= DoStatement.BODY_PROPERTY;
  					child= doBody;
  				}
  				break;
  			default:
  		}
  		if (child == null) {
  			return false;
  		}
  
  		if (resultingCollections == null) {
  			return true;
  		}
  		AST ast= statement.getAST();
  		{
  			ASTRewrite rewrite= ASTRewrite.create(ast);
  
  			ASTNode childPlaceholder= rewrite.createMoveTarget(child);
  			Block replacingBody= ast.newBlock();
  			replacingBody.statements().add(childPlaceholder);
  			rewrite.set(statement, childProperty, replacingBody, null);
  
  			String label;
  			if (childProperty == IfStatement.THEN_STATEMENT_PROPERTY) {
  				label = CorrectionMessages.QuickAssistProcessor_replacethenwithblock_description;
  			} else if (childProperty == IfStatement.ELSE_STATEMENT_PROPERTY) {
  				label = CorrectionMessages.QuickAssistProcessor_replaceelsewithblock_description;
  			} else {
  				label = CorrectionMessages.QuickAssistProcessor_replacebodywithblock_description;
  			}
  
  			Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
  			LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, context.getCompilationUnit(), rewrite, 10, image);
  			proposal.setCommandId(ADD_BLOCK_ID);
  			proposal.setEndPosition(rewrite.track(child));
  			resultingCollections.add(proposal);
  		}
  
  		if (statement.getNodeType() == ASTNode.IF_STATEMENT) {
  			ASTRewrite rewrite= ASTRewrite.create(ast);
  			
  			while (statement.getLocationInParent() == IfStatement.ELSE_STATEMENT_PROPERTY) {
  				statement= (Statement) statement.getParent();
  			}
  			
  			boolean missingBlockFound= false;
  			boolean foundElse= false;
  			
  			IfStatement ifStatement;
  			Statement thenStatment;
  			Statement elseStatment;
  			do {
  				ifStatement= (IfStatement) statement;
  				thenStatment= ifStatement.getThenStatement();
  				elseStatment= ifStatement.getElseStatement();
  
  				if (!(thenStatment instanceof Block)) {
  					ASTNode childPlaceholder1= rewrite.createMoveTarget(thenStatment);
  					Block replacingBody1= ast.newBlock();
  					replacingBody1.statements().add(childPlaceholder1);
  					rewrite.set(ifStatement, IfStatement.THEN_STATEMENT_PROPERTY, replacingBody1, null);
  					if (thenStatment != child) {
  						missingBlockFound= true;
  					}
  				}
  				if (elseStatment != null) {
  					foundElse= true;
  				}
  				statement= elseStatment;
  			} while (elseStatment instanceof IfStatement);
  			
  			if (elseStatment != null && !(elseStatment instanceof Block)) {
  				ASTNode childPlaceholder2= rewrite.createMoveTarget(elseStatment);
  				
  				Block replacingBody2= ast.newBlock();
  				replacingBody2.statements().add(childPlaceholder2);
  				rewrite.set(ifStatement, IfStatement.ELSE_STATEMENT_PROPERTY, replacingBody2, null);
  				if (elseStatment != child) {
  					missingBlockFound= true;
  				}
  			}
  
  			if (missingBlockFound && foundElse) {
  				String label = CorrectionMessages.QuickAssistProcessor_replacethenelsewithblock_description;
  				Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
  				ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, 10, image);
  				resultingCollections.add(proposal);
  			}
  		}
  		return true;
  	}
  
  	private static boolean getInvertEqualsProposal(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		ASTNode parent= node.getParent();
  		if (!(parent instanceof FunctionInvocation)) {
  			return false;
  		}
  		FunctionInvocation method= (FunctionInvocation) parent;
  		if (!"equals".equals(method.getName().getIdentifier())) { //$NON-NLS-1$
  			return false;
  		}
  		List arguments= method.arguments();
  		if (arguments.size() != 1) { //overloaded equals w/ more than 1 argument
  			return false;
  		}
  		Expression right= (Expression) arguments.get(0);
  		ITypeBinding binding = right.resolveTypeBinding();
  		if (binding != null && !(binding.isClass())) { //overloaded equals w/ non-class/interface argument or null
  			return false;
  		}
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		Expression left= method.getExpression();
  
  		AST ast= method.getAST();
  		ASTRewrite rewrite= ASTRewrite.create(ast);
  		if (left == null) { // equals(x) -> x.equals(this)
  			FunctionInvocation replacement= ast.newFunctionInvocation();
  			replacement.setName((SimpleName) rewrite.createCopyTarget(method.getName()));
  			replacement.arguments().add(ast.newThisExpression());
  			replacement.setExpression((Expression) rewrite.createCopyTarget(right));
  			rewrite.replace(method, replacement, null);
  		} else if (right instanceof ThisExpression) { // x.equals(this) -> equals(x)
  			FunctionInvocation replacement= ast.newFunctionInvocation();
  			replacement.setName((SimpleName) rewrite.createCopyTarget(method.getName()));
  			replacement.arguments().add(rewrite.createCopyTarget(left));
  			rewrite.replace(method, replacement, null);
  		} else {
  			ASTNode leftExpression= left;
  			while (leftExpression instanceof ParenthesizedExpression) {
  				leftExpression= ((ParenthesizedExpression) left).getExpression();
  			}
  			rewrite.replace(right, rewrite.createCopyTarget(leftExpression), null);
  
  			if ((right instanceof Assignment)
  				|| (right instanceof ConditionalExpression)
  				|| (right instanceof InfixExpression)) {
  				ParenthesizedExpression paren= ast.newParenthesizedExpression();
  				paren.setExpression((Expression) rewrite.createCopyTarget(right));
  				rewrite.replace(left, paren, null);
  			} else {
  				rewrite.replace(left, rewrite.createCopyTarget(right), null);
  			}
  		}
  
  		String label= CorrectionMessages.QuickAssistProcessor_invertequals_description;
  		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
  
  		LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, context.getCompilationUnit(), rewrite, 1, image);
  		resultingCollections.add(proposal);
  		return true;
  	}
  
  	private static boolean getArrayInitializerToArrayCreation(IInvocationContext context, ASTNode node, Collection resultingCollections) throws CoreException {
  		if (!(node instanceof ArrayInitializer)) {
  			return false;
  		}
  		ArrayInitializer initializer= (ArrayInitializer) node;
  
  		ASTNode parent= initializer.getParent();
  		while (parent instanceof ArrayInitializer) {
  			initializer= (ArrayInitializer) parent;
  			parent= parent.getParent();
  		}
  		ITypeBinding typeBinding= initializer.resolveTypeBinding();
  		if (!(parent instanceof VariableDeclaration) || typeBinding == null || !typeBinding.isArray()) {
  			return false;
  		}
  		if (resultingCollections == null) {
  			return true;
  		}
  
  		AST ast= node.getAST();
  		ASTRewrite rewrite= ASTRewrite.create(ast);
  
  		String label= CorrectionMessages.QuickAssistProcessor_typetoarrayInitializer_description;
  		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
  
  		LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, context.getCompilationUnit(), rewrite, 1, image);
  		
  		ImportRewrite imports= proposal.createImportRewrite(context.getASTRoot());
  		String typeName= imports.addImport(typeBinding);
  
  		ArrayCreation creation= ast.newArrayCreation();
  		creation.setInitializer((ArrayInitializer) rewrite.createMoveTarget(initializer));
  		creation.setType((ArrayType) ASTNodeFactory.newType(ast, typeName));
  
  		rewrite.replace(initializer, creation, null);
  		
  		resultingCollections.add(proposal);
  		return true;
  	}
  
  
  	public static boolean getCreateInSuperClassProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) throws CoreException {
  		if (!(node instanceof SimpleName) || !(node.getParent() instanceof FunctionDeclaration)) {
  			return false;
  		}
  		FunctionDeclaration decl= (FunctionDeclaration) node.getParent();
  		if (decl.getName() != node || decl.resolveBinding() == null || Modifier.isPrivate(decl.getModifiers())) {
  			return false;
  		}
  
  		IJavaScriptUnit cu= context.getCompilationUnit();
  		JavaScriptUnit astRoot= context.getASTRoot();
  
  		IFunctionBinding binding= decl.resolveBinding();
  		ITypeBinding[] paramTypes= binding.getParameterTypes();
  
  		ITypeBinding[] superTypes= Bindings.getAllSuperTypes(binding.getDeclaringClass());
  		if (resultingCollections == null) {
  			for (int i= 0; i < superTypes.length; i++) {
  				ITypeBinding curr= superTypes[i];
  				if (curr.isFromSource() && Bindings.findOverriddenMethodInType(curr, binding) == null) {
  					return true;
  				}
  			}
  			return false;
  		}
  		List params= decl.parameters();
  		String[] paramNames= new String[paramTypes.length];
  		for (int i = 0; i < params.size(); i++) {
  			SingleVariableDeclaration param= (SingleVariableDeclaration) params.get(i);
  			paramNames[i]= param.getName().getIdentifier();
  		}
  
  		for (int i= 0; i < superTypes.length; i++) {
  			ITypeBinding curr= superTypes[i];
  			if (curr.isFromSource()) {
  				IFunctionBinding method= Bindings.findOverriddenMethodInType(curr, binding);
  				if (method == null) {
  					ITypeBinding typeDecl= curr.getTypeDeclaration();
  					IJavaScriptUnit targetCU= ASTResolving.findCompilationUnitForBinding(cu, astRoot, typeDecl);
  					if (targetCU != null) {
  						String label= Messages.format(CorrectionMessages.QuickAssistProcessor_createmethodinsuper_description, new String[] { curr.getName(), binding.getName() });
  						resultingCollections.add(new NewDefiningMethodProposal(label, targetCU, astRoot, typeDecl, binding, paramNames, 6));
  					}
  				}
  			}
  		}
  		return true;
  	}
  
  	private static boolean getConvertIterableLoopProposal(IInvocationContext context, ASTNode node, Collection resultingCollections) throws CoreException {
  		ForStatement forStatement= getEnclosingForStatementHeader(node);
  		if (forStatement == null)
  			return false;
  
  		if (resultingCollections == null)
  			return true;
  		
  		IFix fix= ConvertLoopFix.createConvertIterableLoopToEnhancedFix(context.getASTRoot(), forStatement);
  		if (fix == null)
  			return false;
  		
  		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
  		Map options= new HashMap();
  		options.put(CleanUpConstants.CONTROL_STATMENTS_CONVERT_FOR_LOOP_TO_ENHANCED, CleanUpConstants.TRUE);
  		ICleanUp cleanUp= new ConvertLoopCleanUp(options);
  		FixCorrectionProposal proposal= new FixCorrectionProposal(fix, cleanUp, 1, image, context);
  		proposal.setCommandId(CONVERT_FOR_LOOP_ID);
  		
  		resultingCollections.add(proposal);
  		return true;
  	}
  
  	private static ForStatement getEnclosingForStatementHeader(ASTNode node) {
  		if (node instanceof ForStatement)
  			return (ForStatement) node;
  
  		while (node != null) {
  			ASTNode parent= node.getParent();
  			if (parent instanceof ForStatement) {
  				StructuralPropertyDescriptor locationInParent= node.getLocationInParent();
  				if (locationInParent == ForStatement.EXPRESSION_PROPERTY
  						|| locationInParent == ForStatement.INITIALIZERS_PROPERTY
  						|| locationInParent == ForStatement.UPDATERS_PROPERTY)
  					return (ForStatement) parent;
  				else
  					return null;
  			}
  			node= parent;
  		}
  		return null;
  	}
  	
  	private static boolean getMakeVariableDeclarationFinalProposals(IInvocationContext context, ASTNode node, Collection resultingCollections) {
  		SelectionAnalyzer analyzer= new SelectionAnalyzer(Selection.createFromStartLength(context.getSelectionOffset(), context.getSelectionLength()), false);
  		context.getASTRoot().accept(analyzer);
  		ASTNode[] selectedNodes= analyzer.getSelectedNodes();
  		if (selectedNodes.length == 0)
  			return false;
  		
  		IFix fix= VariableDeclarationFix.createChangeModifierToFinalFix(context.getASTRoot(), selectedNodes);
  		if (fix == null)
  			return false;
  		
  		if (resultingCollections == null)
  			return true;
  
  		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
  		Map options= new Hashtable();
  		options.put(CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL, CleanUpConstants.TRUE);
  		options.put(CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL_LOCAL_VARIABLES, CleanUpConstants.TRUE);
  		options.put(CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL_PARAMETERS, CleanUpConstants.TRUE);
  		options.put(CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL_PRIVATE_FIELDS, CleanUpConstants.TRUE);
  		VariableDeclarationCleanUp cleanUp= new VariableDeclarationCleanUp(options);
  		FixCorrectionProposal proposal= new FixCorrectionProposal(fix, cleanUp, 5, image, context);
  		resultingCollections.add(proposal);
  		return true;
  	}
  		
  	private static boolean getInlineLocalProposal(IInvocationContext context, final ASTNode node, Collection proposals) throws CoreException {
  		if (!(node instanceof SimpleName))
  			return false;
  		
  		SimpleName name= (SimpleName) node;
  		IBinding binding= name.resolveBinding();
  		if (!(binding instanceof IVariableBinding))
  			return false;
  		IVariableBinding varBinding= (IVariableBinding) binding;
  		if (varBinding.isField() || varBinding.isParameter())
  			return false;
  		ASTNode decl= context.getASTRoot().findDeclaringNode(varBinding);
  		if (!(decl instanceof VariableDeclarationFragment) || decl.getLocationInParent() != VariableDeclarationStatement.FRAGMENTS_PROPERTY)
  			return false;
  		
  		if (proposals == null) {
  			return true;
  		}
  			
  		InlineTempRefactoring refactoring= new InlineTempRefactoring((VariableDeclaration) decl);
  		if (refactoring.checkInitialConditions(new NullProgressMonitor()).isOK()) {
  			String label= CorrectionMessages.QuickAssistProcessor_inline_local_description;
  			Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);		
  			RefactoringCorrectionProposal proposal= new RefactoringCorrectionProposal(label, context.getCompilationUnit(), refactoring, 5, image);
  			proposal.setCommandId(INLINE_LOCAL_ID);
  			proposals.add(proposal);
  			
  		}
  		return true;
  	}
  	
  	private static boolean getConvertLocalToFieldProposal(IInvocationContext context, final ASTNode node, Collection proposals) throws CoreException {
  		if (!(node instanceof SimpleName))
  			return false;
  		
  		SimpleName name= (SimpleName) node;
  		IBinding binding= name.resolveBinding();
  		if (!(binding instanceof IVariableBinding) || name.getLocationInParent() != VariableDeclarationFragment.NAME_PROPERTY)
  			return false;
  		IVariableBinding varBinding= (IVariableBinding) binding;
  		if (varBinding.isField() || varBinding.isParameter())
  			return false;
  		VariableDeclarationFragment decl= (VariableDeclarationFragment) name.getParent();
  		if (decl.getLocationInParent() != VariableDeclarationStatement.FRAGMENTS_PROPERTY)
  			return false;
  		
  		if (proposals == null) {
  			return true;
  		}
  			
  		PromoteTempToFieldRefactoring refactoring= new PromoteTempToFieldRefactoring(decl);
  		if (refactoring.checkInitialConditions(new NullProgressMonitor()).isOK()) {
  			String label= CorrectionMessages.QuickAssistProcessor_convert_local_to_field_description;
  			Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
  			LinkedProposalModel linkedProposalModel= new LinkedProposalModel();
  			refactoring.setLinkedProposalModel(linkedProposalModel);
  			
  			RefactoringCorrectionProposal proposal= new RefactoringCorrectionProposal(label, context.getCompilationUnit(), refactoring, 5, image);
  			proposal.setLinkedProposalModel(linkedProposalModel);
  			proposal.setCommandId(CONVERT_LOCAL_TO_FIELD_ID);
  			proposals.add(proposal);
  		}
  		return true;
  	}
  	
  	private static class RefactoringCorrectionProposal extends CUCorrectionProposal {
  		private final Refactoring fRefactoring;
  
  		public RefactoringCorrectionProposal(String name, IJavaScriptUnit cu, Refactoring refactoring, int relevance, Image image) {
  			super(name, cu, null, relevance, image);
  			fRefactoring= refactoring;
  		}
  		
  		protected TextChange createTextChange() throws CoreException {
  			return (TextChange) fRefactoring.createChange(new NullProgressMonitor());
  		}
  	}
  }
  