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
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.corext.fix;
  
  import java.util.ArrayList;
  import java.util.Hashtable;
  import java.util.Iterator;
  import java.util.List;
  import java.util.ListIterator;
  import java.util.Map;
  
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.text.edits.TextEditGroup;
  import org.eclipse.wst.jsdt.core.compiler.IProblem;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.ASTVisitor;
  import org.eclipse.wst.jsdt.core.dom.Assignment;
  import org.eclipse.wst.jsdt.core.dom.Block;
  import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
  import org.eclipse.wst.jsdt.core.dom.EnhancedForStatement;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.ExpressionStatement;
  import org.eclipse.wst.jsdt.core.dom.FieldAccess;
  import org.eclipse.wst.jsdt.core.dom.FieldDeclaration;
  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.ImportDeclaration;
  import org.eclipse.wst.jsdt.core.dom.JSdoc;
  import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  import org.eclipse.wst.jsdt.core.dom.PostfixExpression;
  import org.eclipse.wst.jsdt.core.dom.PrefixExpression;
  import org.eclipse.wst.jsdt.core.dom.QualifiedName;
  import org.eclipse.wst.jsdt.core.dom.SimpleName;
  import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration;
  import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation;
  import org.eclipse.wst.jsdt.core.dom.TagElement;
  import org.eclipse.wst.jsdt.core.dom.Type;
  import org.eclipse.wst.jsdt.core.dom.TypeDeclarationStatement;
  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.rewrite.ASTRewrite;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ListRewrite;
  import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
  import org.eclipse.wst.jsdt.internal.corext.dom.LinkedNodeFinder;
  import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
  import org.eclipse.wst.jsdt.internal.corext.util.Messages;
  import org.eclipse.wst.jsdt.internal.ui.fix.UnusedCodeCleanUp;
  import org.eclipse.wst.jsdt.internal.ui.text.correction.JavadocTagsSubProcessor;
  import org.eclipse.wst.jsdt.internal.ui.text.correction.ProblemLocation;
  import org.eclipse.wst.jsdt.ui.text.java.IProblemLocation;
  
  /**
   * Fix which removes unused code.
   */
  public class UnusedCodeFix extends AbstractFix {
  	
  	private static class SideEffectFinder extends ASTVisitor {
  
  		private final ArrayList fSideEffectNodes;
  
  		public SideEffectFinder(ArrayList res) {
  			fSideEffectNodes= res;
  		}
  
  		public boolean visit(Assignment node) {
  			fSideEffectNodes.add(node);
  			return false;
  		}
  
  		public boolean visit(PostfixExpression node) {
  			fSideEffectNodes.add(node);
  			return false;
  		}
  
  		public boolean visit(PrefixExpression node) {
  			Object operator= node.getOperator();
  			if (operator == PrefixExpression.Operator.INCREMENT || operator == PrefixExpression.Operator.DECREMENT) {
  				fSideEffectNodes.add(node);
  			}
  			return false;
  		}
  
  		public boolean visit(FunctionInvocation node) {
  			fSideEffectNodes.add(node);
  			return false;
  		}
  
  		public boolean visit(ClassInstanceCreation node) {
  			fSideEffectNodes.add(node);
  			return false;
  		}
  
  		public boolean visit(SuperMethodInvocation node) {
  			fSideEffectNodes.add(node);
  			return false;
  		}
  	}
  	
  	private static class RemoveImportOperation extends AbstractFixRewriteOperation {
  
  		private final ImportDeclaration fImportDeclaration;
  		
  		public RemoveImportOperation(ImportDeclaration importDeclaration) {
  			fImportDeclaration= importDeclaration;
  		}
  
  		/* (non-Javadoc)
  		 * @see org.eclipse.wst.jsdt.internal.corext.fix.AbstractFix.IFixRewriteOperation#rewriteAST(org.eclipse.wst.jsdt.internal.corext.refactoring.structure.CompilationUnitRewrite, java.util.List)
  		 */
  		public void rewriteAST(CompilationUnitRewrite cuRewrite, List textEditGroups) throws CoreException {
  			ImportDeclaration node= fImportDeclaration;
  			TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveImport_description);
  			cuRewrite.getASTRewrite().remove(node, group);
  			textEditGroups.add(group);
  		}
  		
  	}
  	
  	private static class RemoveUnusedMemberOperation extends AbstractFixRewriteOperation {
  
  		private final SimpleName[] fUnusedNames;
  		private boolean fForceRemove;
  		private int fRemovedAssignmentsCount;
  		private int fAlteredAssignmentsCount;
  		
  		public RemoveUnusedMemberOperation(SimpleName[] unusedNames, boolean forceRemoveInitializer) {
  			fUnusedNames= unusedNames;
  			fForceRemove=forceRemoveInitializer;
  		}
  
  		/* (non-Javadoc)
  		 * @see org.eclipse.wst.jsdt.internal.corext.fix.AbstractFix.IFixRewriteOperation#rewriteAST(org.eclipse.wst.jsdt.internal.corext.refactoring.structure.CompilationUnitRewrite, java.util.List)
  		 */
  		public void rewriteAST(CompilationUnitRewrite cuRewrite, List textEditGroups) throws CoreException {
  			for (int i= 0; i < fUnusedNames.length; i++) {
  				removeUnusedName(cuRewrite.getASTRewrite(), fUnusedNames[i], cuRewrite.getRoot(), textEditGroups);	
  			}
  		}
  		
  		private void removeUnusedName(ASTRewrite rewrite, SimpleName simpleName, JavaScriptUnit completeRoot, List groups) {
  			IBinding binding= simpleName.resolveBinding();
  			JavaScriptUnit root= (JavaScriptUnit) simpleName.getRoot();
  			String displayString= getDisplayString(binding);
  			TextEditGroup group= createTextEditGroup(displayString);
  			groups.add(group);
  			if (binding.getKind() == IBinding.METHOD) {
  				IFunctionBinding decl= ((IFunctionBinding) binding).getMethodDeclaration();
  				ASTNode declaration= root.findDeclaringNode(decl);
  				rewrite.remove(declaration, group);
  			} else if (binding.getKind() == IBinding.TYPE) {
  				ITypeBinding decl= ((ITypeBinding) binding).getTypeDeclaration();
  				ASTNode declaration= root.findDeclaringNode(decl);
  				if (declaration.getParent() instanceof TypeDeclarationStatement) {
  					declaration= declaration.getParent();
  				}
  				rewrite.remove(declaration, group);
  			} else if (binding.getKind() == IBinding.VARIABLE) {
  				SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
  				SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
  				for (int i= 0; i < references.length; i++) {
  					removeVariableReferences(rewrite, references[i], group);
  				}
  
  				IVariableBinding bindingDecl= ((IVariableBinding) nameNode.resolveBinding()).getVariableDeclaration();
  				ASTNode declaringNode= completeRoot.findDeclaringNode(bindingDecl);
  				if (declaringNode instanceof SingleVariableDeclaration) {
  					removeParamTag(rewrite, (SingleVariableDeclaration) declaringNode, group);
  				}
  			} else {
  				// unexpected
  			}
  		}
  		
  		private String getDisplayString(IBinding binding) {
  			switch (binding.getKind()) {
  				case IBinding.TYPE:
  					return FixMessages.UnusedCodeFix_RemoveUnusedType_description;
  				case IBinding.METHOD:
  					if (((IFunctionBinding) binding).isConstructor()) {
  						return FixMessages.UnusedCodeFix_RemoveUnusedConstructor_description;
  					} else {
  						return FixMessages.UnusedCodeFix_RemoveUnusedPrivateMethod_description;
  					}
  				case IBinding.VARIABLE:
  					if (((IVariableBinding) binding).isField()) {
  						return FixMessages.UnusedCodeFix_RemoveUnusedField_description;
  					} else {
  						return FixMessages.UnusedCodeFix_RemoveUnusedVariabl_description;
  					}
  				default:
  					return ""; //$NON-NLS-1$
  			}
  		}
  
  		private void removeParamTag(ASTRewrite rewrite, SingleVariableDeclaration varDecl, TextEditGroup group) {
  			if (varDecl.getParent() instanceof FunctionDeclaration) {
  				JSdoc javadoc= ((FunctionDeclaration) varDecl.getParent()).getJavadoc();
  				if (javadoc != null) {
  					TagElement tagElement= JavadocTagsSubProcessor.findParamTag(javadoc, varDecl.getName().getIdentifier());
  					if (tagElement != null) {
  						rewrite.remove(tagElement, group);
  					}
  				}
  			}
  		}
  		
  		/**
  		 * Remove the field or variable declaration including the initializer.
  		 * @param rewrite the AST rewriter to use
  		 * @param reference a reference to the variable to remove
  		 * @param group the text edit group to use
  		 */
  		private void removeVariableReferences(ASTRewrite rewrite, SimpleName reference, TextEditGroup group) {
  			ASTNode parent= reference.getParent();
  			while (parent instanceof QualifiedName) {
  				parent= parent.getParent();
  			}
  			if (parent instanceof FieldAccess) {
  				parent= parent.getParent();
  			}
  
  			int nameParentType= parent.getNodeType();
  			if (nameParentType == ASTNode.ASSIGNMENT) {
  				Assignment assignment= (Assignment) parent;
  				Expression rightHand= assignment.getRightHandSide();
  
  				ASTNode assignParent= assignment.getParent();
  				if (assignParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT && rightHand.getNodeType() != ASTNode.ASSIGNMENT) {
  					removeVariableWithInitializer(rewrite, rightHand, assignParent, group);
  				}	else {
  					rewrite.replace(assignment, rewrite.createCopyTarget(rightHand), group);
  				}
  			} else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
  				rewrite.remove(parent, group);
  			} else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
  				VariableDeclarationFragment frag= (VariableDeclarationFragment) parent;
  				ASTNode varDecl= frag.getParent();
  				List fragments;
  				if (varDecl instanceof VariableDeclarationExpression) {
  					fragments= ((VariableDeclarationExpression) varDecl).fragments();
  				} else if (varDecl instanceof FieldDeclaration) {
  					fragments= ((FieldDeclaration) varDecl).fragments();
  				} else {
  					fragments= ((VariableDeclarationStatement) varDecl).fragments();
  				}
  				Expression initializer = frag.getInitializer();
  				boolean sideEffectInitializer = initializer instanceof FunctionInvocation || initializer instanceof ClassInstanceCreation;
  				if (fragments.size() == fUnusedNames.length) {
  					if (fForceRemove) {
  						rewrite.remove(varDecl, group);
  						return;
  					}
  					if (parent.getParent() instanceof FieldDeclaration) {
  						rewrite.remove(varDecl, group);
  						return;
  					}
  					if (sideEffectInitializer){
  						Expression movedInit = (Expression) rewrite.createMoveTarget(initializer);
  						ExpressionStatement wrapped = rewrite.getAST().newExpressionStatement(movedInit);
  						rewrite.replace(varDecl, wrapped, group);
  					} else {
  						rewrite.remove(varDecl, group);
  					}
  				} else {
  					if (fForceRemove) {
  						rewrite.remove(frag, group);
  						return;
  					}
  					//multiple declarations in one line
  					ASTNode declaration = parent.getParent();
  					if (declaration instanceof FieldDeclaration) {
  						rewrite.remove(frag, group);
  						return;
  					}
  					if (declaration instanceof VariableDeclarationStatement) {
  						ASTNode lst = declaration.getParent();
  						if (lst instanceof Block)
  							splitUpDeclarations(rewrite, group, frag, lst, (VariableDeclarationStatement) declaration);
  						rewrite.remove(frag, group);
  						return;
  					}
  					if (declaration instanceof VariableDeclarationExpression) {
  						//keep constructors and method invocations
  						if (!sideEffectInitializer){
  							rewrite.remove(frag, group);
  						}
  					}
  				}
  			}
  		}
  
  		private void splitUpDeclarations(ASTRewrite rewrite, TextEditGroup group, VariableDeclarationFragment frag, ASTNode block, VariableDeclarationStatement originalStatement) {
  			Expression initializer = frag.getInitializer();
  			//keep constructors and method invocations
  			if (initializer instanceof FunctionInvocation || initializer instanceof ClassInstanceCreation){
  				Expression movedInitializer= (Expression) rewrite.createMoveTarget(initializer);
  				ListRewrite statementRewrite= rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
  				ExpressionStatement newInitializer= rewrite.getAST().newExpressionStatement( movedInitializer);
  				statementRewrite.insertAfter(newInitializer, originalStatement, group);
  
  				VariableDeclarationStatement newDeclaration= null;
  				List fragments= originalStatement.fragments();
  				int fragIndex= fragments.indexOf(frag);
  				ListIterator fragmentIterator= fragments.listIterator(fragIndex+1);
  				while (fragmentIterator.hasNext()) {
  					VariableDeclarationFragment currentFragment= (VariableDeclarationFragment) fragmentIterator.next();
  					VariableDeclarationFragment movedFragment= (VariableDeclarationFragment) rewrite.createMoveTarget(currentFragment);
  					if (newDeclaration == null) {
  						newDeclaration= rewrite.getAST().newVariableDeclarationStatement(movedFragment);
  						Type copiedType= (Type) rewrite.createCopyTarget(originalStatement.getType());
  						newDeclaration.setType(copiedType);
  					} else
  						newDeclaration.fragments().add(movedFragment);
  				}
  				if (newDeclaration != null){
  					statementRewrite.insertAfter(newDeclaration, newInitializer, group);
  				}
  				if (originalStatement.fragments().size() == newDeclaration.fragments().size() + 1){
  					rewrite.remove(originalStatement, group);
  				}
  			}
  		}
  
  		private void removeVariableWithInitializer(ASTRewrite rewrite, ASTNode initializerNode, ASTNode statementNode, TextEditGroup group) {
  			boolean performRemove= fForceRemove;
  			if (!performRemove) {
  				ArrayList sideEffectNodes= new ArrayList();
  				initializerNode.accept(new SideEffectFinder(sideEffectNodes));
  				performRemove= sideEffectNodes.isEmpty();
  			}
  			if (performRemove) {
  				if (ASTNodes.isControlStatementBody(statementNode.getLocationInParent())) {
  					rewrite.replace(statementNode, rewrite.getAST().newBlock(), group);
  				} else {
  					rewrite.remove(statementNode, group);
  				}
  				fRemovedAssignmentsCount++;
  			} else {
  				ASTNode initNode = rewrite.createMoveTarget(initializerNode);
  				ExpressionStatement statement = rewrite.getAST().newExpressionStatement((Expression) initNode);
  				rewrite.replace(statementNode, statement, null);
  				fAlteredAssignmentsCount++;
  			}
  		}
  
  		public String getAdditionalInfo() {
  			StringBuffer sb=new StringBuffer();
  			if (fRemovedAssignmentsCount>0){
  				sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview,String.valueOf(fRemovedAssignmentsCount)));
  			}
  			if (fAlteredAssignmentsCount>0){
  				sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview,String.valueOf(fAlteredAssignmentsCount)));
  			}
  			if (sb.length()>0) {
  				return sb.toString();
  			} else
  				return null;
  		}
  	}
  	
  	public static UnusedCodeFix createRemoveUnusedImportFix(JavaScriptUnit compilationUnit, IProblemLocation problem) {
  		int id= problem.getProblemId();
  		if (id == IProblem.DuplicateImport || id == IProblem.ConflictingImport ||
  		    id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound) {
  			
  			ImportDeclaration node= getImportDeclaration(problem, compilationUnit);
  			if (node != null) {
  				String label= FixMessages.UnusedCodeFix_RemoveImport_description;
  				RemoveImportOperation operation= new RemoveImportOperation(node);
  				Map options= new Hashtable();
  				options.put(CleanUpConstants.REMOVE_UNUSED_CODE_IMPORTS, CleanUpConstants.TRUE);
  				return new UnusedCodeFix(label, compilationUnit, new IFixRewriteOperation[] {operation}, options);
  			}
  		}
  		return null;
  	}
  	
  	public static UnusedCodeFix createUnusedMemberFix(JavaScriptUnit compilationUnit, IProblemLocation problem, boolean forceInitializerRemoval) {
  		int id= problem.getProblemId();
  		if (id == IProblem.UnusedPrivateMethod || id == IProblem.UnusedPrivateConstructor || id == IProblem.UnusedPrivateField ||
  		    id == IProblem.UnusedPrivateType || id == IProblem.LocalVariableIsNeverUsed || id == IProblem.ArgumentIsNeverUsed) {
  			
  			SimpleName name= getUnusedName(compilationUnit, problem);
  			if (name != null) {
  				IBinding binding= name.resolveBinding();
  				if (binding != null) {
  					if (isFormalParameterInEnhancedForStatement(name))
  						return null;
  						
  					String label= getDisplayString(name, binding, forceInitializerRemoval);
  					RemoveUnusedMemberOperation operation= new RemoveUnusedMemberOperation(new SimpleName[] {name}, forceInitializerRemoval);
  					return new UnusedCodeFix(label, compilationUnit, new IFixRewriteOperation[] {operation}, getCleanUpOptions(binding));
  				}
  			}
  		}
  		return null;
  	}
  	
  	public static IFix createCleanUp(JavaScriptUnit compilationUnit, 
  			boolean removeUnusedPrivateMethods, 
  			boolean removeUnusedPrivateConstructors, 
  			boolean removeUnusedPrivateFields, 
  			boolean removeUnusedPrivateTypes, 
  			boolean removeUnusedLocalVariables, 
  			boolean removeUnusedImports,
  			boolean removeUnusedCast) {
  
  		IProblem[] problems= compilationUnit.getProblems();
  		IProblemLocation[] locations= new IProblemLocation[problems.length];
  		for (int i= 0; i < problems.length; i++) {
  			locations[i]= new ProblemLocation(problems[i]);
  		}
  		
  		return createCleanUp(compilationUnit, locations, 
  				removeUnusedPrivateMethods, 
  				removeUnusedPrivateConstructors, 
  				removeUnusedPrivateFields, 
  				removeUnusedPrivateTypes, 
  				removeUnusedLocalVariables, 
  				removeUnusedImports,
  				removeUnusedCast);
  	}
  	
  	public static IFix createCleanUp(JavaScriptUnit compilationUnit, IProblemLocation[] problems, 
  			boolean removeUnusedPrivateMethods, 
  			boolean removeUnusedPrivateConstructors, 
  			boolean removeUnusedPrivateFields, 
  			boolean removeUnusedPrivateTypes, 
  			boolean removeUnusedLocalVariables, 
  			boolean removeUnusedImports,
  			boolean removeUnusedCast) {
  
  		List/*<IFixRewriteOperation>*/ result= new ArrayList();
  		Hashtable/*<ASTNode, List>*/ variableDeclarations= new Hashtable();
  		for (int i= 0; i < problems.length; i++) {
  			IProblemLocation problem= problems[i];
  			int id= problem.getProblemId();
  			
  			if (removeUnusedImports && (id == IProblem.DuplicateImport || id == IProblem.ConflictingImport ||
  				    id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound)) 
  			{
  				ImportDeclaration node= UnusedCodeFix.getImportDeclaration(problem, compilationUnit);
  				if (node != null) {
  					result.add(new RemoveImportOperation(node));
  				}
  			}
  
  			if ((removeUnusedPrivateMethods && id == IProblem.UnusedPrivateMethod) || (removeUnusedPrivateConstructors && id == IProblem.UnusedPrivateConstructor) ||
  			    (removeUnusedPrivateTypes && id == IProblem.UnusedPrivateType)) {
  				
  				SimpleName name= getUnusedName(compilationUnit, problem);
  				if (name != null) {
  					IBinding binding= name.resolveBinding();
  					if (binding != null) {
  						result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
  					}
  				}
  			}
  			
  			if ((removeUnusedLocalVariables && id == IProblem.LocalVariableIsNeverUsed) ||  (removeUnusedPrivateFields && id == IProblem.UnusedPrivateField)) {
  				SimpleName name= getUnusedName(compilationUnit, problem);
  				if (name != null) {
  					IBinding binding= name.resolveBinding();
  					if (binding != null && !isFormalParameterInEnhancedForStatement(name) && isSideEffectFree(name, compilationUnit)) {
  						VariableDeclarationFragment parent= (VariableDeclarationFragment)ASTNodes.getParent(name, VariableDeclarationFragment.class);
  						if (parent != null) {
  							ASTNode varDecl= parent.getParent();
  							if (!variableDeclarations.containsKey(varDecl)) {
  								variableDeclarations.put(varDecl, new ArrayList());
  							}
  							((List)variableDeclarations.get(varDecl)).add(name);
  						} else {
  							result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
  						}
  					}
  				}
  			}
  		}
  		for (Iterator iter= variableDeclarations.keySet().iterator(); iter.hasNext();) {
  			ASTNode node= (ASTNode)iter.next();
  			List names= (List)variableDeclarations.get(node);
  			result.add(new RemoveUnusedMemberOperation((SimpleName[])names.toArray(new SimpleName[names.size()]), false));
  		}
  		
  		if (result.size() == 0)
  			return null;
  		
  		return new UnusedCodeFix(FixMessages.UnusedCodeFix_change_name, compilationUnit, (IFixRewriteOperation[])result.toArray(new IFixRewriteOperation[result.size()]));
  	}
  	
  	private static boolean isFormalParameterInEnhancedForStatement(SimpleName name) {
  		return name.getParent() instanceof SingleVariableDeclaration && name.getParent().getLocationInParent() == EnhancedForStatement.PARAMETER_PROPERTY;
  	}
  	
  	private static boolean isSideEffectFree(SimpleName simpleName, JavaScriptUnit completeRoot) {
  		SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
  		SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
  		for (int i= 0; i < references.length; i++) {
  			if (hasSideEffect(references[i]))
  				return false;
  		}
  		return true;
  	}
  
  	private static boolean hasSideEffect(SimpleName reference) {
  		ASTNode parent= reference.getParent();
  		while (parent instanceof QualifiedName) {
  			parent= parent.getParent();
  		}
  		if (parent instanceof FieldAccess) {
  			parent= parent.getParent();
  		}
  
  		ASTNode node= null;
  		int nameParentType= parent.getNodeType();
  		if (nameParentType == ASTNode.ASSIGNMENT) {
  			Assignment assignment= (Assignment) parent;
  			node= assignment.getRightHandSide();
  		} else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
  			SingleVariableDeclaration decl= (SingleVariableDeclaration)parent;
  			node= decl.getInitializer();
  			if (node == null)
  				return false;
  		} else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
  			node= parent;
  		} else {
  			return false;
  		}		
  
  		ArrayList sideEffects= new ArrayList();
  		node.accept(new SideEffectFinder(sideEffects));
  		return sideEffects.size() > 0;
  	}
  
  	private static SimpleName getUnusedName(JavaScriptUnit compilationUnit, IProblemLocation problem) {
  		ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
  
  		if (selectedNode instanceof FunctionDeclaration) {
  			return ((FunctionDeclaration) selectedNode).getName();
  		} else if (selectedNode instanceof SimpleName) {
  			return (SimpleName) selectedNode;
  		}
  		
  		return null;
  	}
  	
  	private static String getDisplayString(SimpleName simpleName, IBinding binding, boolean forceRemoveInitializer) {
  		String name= simpleName.getIdentifier();
  		switch (binding.getKind()) {
  			case IBinding.TYPE:
  				return Messages.format(FixMessages.UnusedCodeFix_RemoveType_description, name);
  			case IBinding.METHOD:
  				if (((IFunctionBinding) binding).isConstructor()) {
  					return Messages.format(FixMessages.UnusedCodeFix_RemoveConstructor_description, name);
  				} else {
  					return Messages.format(FixMessages.UnusedCodeFix_RemoveMethod_description, name);
  				}
  			case IBinding.VARIABLE:
  				if (forceRemoveInitializer) {
  					return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocalWithInitializer_description, name);
  				} else {
  					return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_description, name);
  				}
  			default:
  				return ""; //$NON-NLS-1$
  		}
  	}
  	
  	private static Map getCleanUpOptions(IBinding binding) {
  		Map result= new Hashtable();
  		
  		result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_MEMBERS, CleanUpConstants.TRUE);		
  		switch (binding.getKind()) {
  			case IBinding.TYPE:
  				result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_TYPES, CleanUpConstants.TRUE);
  				break;
  			case IBinding.METHOD:
  				if (((IFunctionBinding) binding).isConstructor()) {
  					result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_CONSTRUCTORS, CleanUpConstants.TRUE);
  				} else {
  					result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_METHODS, CleanUpConstants.TRUE);
  				}
  				break;
  			case IBinding.VARIABLE:
  				result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_FELDS, CleanUpConstants.TRUE);
  				result.put(CleanUpConstants.REMOVE_UNUSED_CODE_LOCAL_VARIABLES, CleanUpConstants.TRUE);
  				break;
  		}
  
  		return result;
  	}
  	
  	private static ImportDeclaration getImportDeclaration(IProblemLocation problem, JavaScriptUnit compilationUnit) {
  		ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
  		if (selectedNode != null) {
  			ASTNode node= ASTNodes.getParent(selectedNode, ASTNode.IMPORT_DECLARATION);
  			if (node instanceof ImportDeclaration) {
  				return (ImportDeclaration)node;
  			}
  		}
  		return null;
  	}
  	
  	private final Map fCleanUpOptions;
  	
  	private UnusedCodeFix(String name, JavaScriptUnit compilationUnit, IFixRewriteOperation[] fixRewriteOperations) {
  		this(name, compilationUnit, fixRewriteOperations, null);
  	}
  	
  	private UnusedCodeFix(String name, JavaScriptUnit compilationUnit, IFixRewriteOperation[] fixRewriteOperations, Map options) {
  		super(name, compilationUnit, fixRewriteOperations);
  		if (options == null) {
  			fCleanUpOptions= new Hashtable();			
  		} else {
  			fCleanUpOptions= options;
  		}
  	}
  
  	public UnusedCodeCleanUp getCleanUp() {
  		return new UnusedCodeCleanUp(fCleanUpOptions);
  	}
  
  }
  