content: /*******************************************************************************
   * Copyright (c) 2000, 2008 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.refactoring.structure;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Set;
  
  import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.ASTVisitor;
  import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  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.QualifiedName;
  import org.eclipse.wst.jsdt.core.dom.QualifiedType;
  import org.eclipse.wst.jsdt.core.dom.SimpleName;
  import org.eclipse.wst.jsdt.core.dom.Type;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite;
  import org.eclipse.wst.jsdt.internal.corext.codemanipulation.ImportReferencesCollector;
  import org.eclipse.wst.jsdt.internal.corext.dom.Bindings;
  
  public class ImportRemover {
  
  	private static class StaticImportData {
  
  		private boolean fField;
  
  		private String fMember;
  
  		private String fQualifier;
  
  		private StaticImportData(String qualifier, String member, boolean field) {
  			fQualifier= qualifier;
  			fMember= member;
  			fField= field;
  		}
  	}
  
  	private Set/* <String> */fAddedImports= new HashSet();
  
  	private Set/* <StaticImportData> */fAddedStaticImports= new HashSet();
  
  	private final IJavaScriptProject fProject;
  
  	private List/* <ASTNode> */fRemovedNodes= new ArrayList();
  
  	private final JavaScriptUnit fRoot;
  
  	public ImportRemover(IJavaScriptProject project, JavaScriptUnit root) {
  		fProject= project;
  		fRoot= root;
  	}
  
  	private void divideTypeRefs(List/* <SimpleName> */importNames, List/* <SimpleName> */staticNames, List/* <SimpleName> */removedRefs, List/* <SimpleName> */unremovedRefs) {
  		int[] removedStartsEnds= new int[2 * fRemovedNodes.size()];
  		for (int index= 0; index < fRemovedNodes.size(); index++) {
  			ASTNode node= (ASTNode) fRemovedNodes.get(index);
  			int start= node.getStartPosition();
  			removedStartsEnds[2 * index]= start;
  			removedStartsEnds[2 * index + 1]= start + node.getLength();
  		}
  		for (Iterator iterator= importNames.iterator(); iterator.hasNext();) {
  			SimpleName name= (SimpleName) iterator.next();
  			if (isInRemoved(name, removedStartsEnds))
  				removedRefs.add(name);
  			else
  				unremovedRefs.add(name);
  		}
  		for (Iterator iterator= staticNames.iterator(); iterator.hasNext();) {
  			SimpleName name= (SimpleName) iterator.next();
  			if (isInRemoved(name, removedStartsEnds))
  				removedRefs.add(name);
  			else
  				unremovedRefs.add(name);
  		}
  	}
  
  	public IBinding[] getImportsToRemove() {
  		ArrayList/* <SimpleName> */importNames= new ArrayList();
  		ArrayList/* <SimpleName> */staticNames= new ArrayList();
  		fRoot.accept(new ImportReferencesCollector(fProject, null, importNames, staticNames));
  		List/* <SimpleName> */removedRefs= new ArrayList();
  		List/* <SimpleName> */unremovedRefs= new ArrayList();
  		divideTypeRefs(importNames, staticNames, removedRefs, unremovedRefs);
  		if (removedRefs.size() == 0)
  			return new IBinding[0];
  
  		HashMap/* <String, IBinding> */potentialRemoves= getPotentialRemoves(removedRefs);
  		for (Iterator iterator= unremovedRefs.iterator(); iterator.hasNext();) {
  			SimpleName name= (SimpleName) iterator.next();
  			potentialRemoves.remove(name.getIdentifier());
  		}
  
  		Collection importsToRemove= potentialRemoves.values();
  		return (IBinding[]) importsToRemove.toArray(new IBinding[importsToRemove.size()]);
  	}
  
  	private HashMap getPotentialRemoves(List removedRefs) {
  		HashMap/* <String, IBinding> */potentialRemoves= new HashMap();
  		for (Iterator iterator= removedRefs.iterator(); iterator.hasNext();) {
  			SimpleName name= (SimpleName) iterator.next();
  			if (fAddedImports.contains(name.getIdentifier()) || hasAddedStaticImport(name))
  				continue;
  			IBinding binding= name.resolveBinding();
  			if (binding != null)
  				potentialRemoves.put(name.getIdentifier(), binding);
  		}
  		return potentialRemoves;
  	}
  
  	private boolean hasAddedStaticImport(SimpleName name) {
  		IBinding binding= name.resolveBinding();
  		if (binding instanceof IVariableBinding) {
  			IVariableBinding variable= (IVariableBinding) binding;
  			return hasAddedStaticImport(variable.getDeclaringClass().getQualifiedName(), variable.getName(), true);
  		} else if (binding instanceof IFunctionBinding) {
  			IFunctionBinding method= (IFunctionBinding) binding;
  			return hasAddedStaticImport(method.getDeclaringClass().getQualifiedName(), method.getName(), false);
  		}
  		return false;
  	}
  
  	private boolean hasAddedStaticImport(String qualifier, String member, boolean field) {
  		StaticImportData data= null;
  		for (final Iterator iterator= fAddedStaticImports.iterator(); iterator.hasNext();) {
  			data= (StaticImportData) iterator.next();
  			if (data.fQualifier.equals(qualifier) && data.fMember.equals(member) && data.fField == field)
  				return true;
  		}
  		return false;
  	}
  
  	public boolean hasRemovedNodes() {
  		return fRemovedNodes.size() != 0;
  	}
  
  	private boolean isInRemoved(SimpleName ref, int[] removedStartsEnds) {
  		int start= ref.getStartPosition();
  		int end= start + ref.getLength();
  		for (int index= 0; index < removedStartsEnds.length; index+= 2) {
  			if (start >= removedStartsEnds[index] && end <= removedStartsEnds[index + 1])
  				return true;
  		}
  		return false;
  	}
  
  	public void registerAddedImport(String typeName) {
  		int dot= typeName.lastIndexOf('.');
  		if (dot == -1)
  			fAddedImports.add(typeName);
  		else
  			fAddedImports.add(typeName.substring(dot + 1));
  	}
  
  	public void registerAddedImports(Type newTypeNode) {
  		newTypeNode.accept(new ASTVisitor(true) {
  
  			private void addName(SimpleName name) {
  				fAddedImports.add(name.getIdentifier());
  			}
  
  			public boolean visit(QualifiedName node) {
  				addName(node.getName());
  				return false;
  			}
  
  			public boolean visit(QualifiedType node) {
  				addName(node.getName());
  				return false;
  			}
  
  			public boolean visit(SimpleName node) {
  				addName(node);
  				return false;
  			}
  		});
  	}
  
  	public void registerAddedStaticImport(String qualifier, String member, boolean field) {
  		fAddedStaticImports.add(new StaticImportData(qualifier, member, field));
  	}
  
  	public void registerAddedStaticImport(IBinding binding) {
  		if (binding instanceof IVariableBinding) {
  			ITypeBinding declaringType= ((IVariableBinding) binding).getDeclaringClass();
  			fAddedStaticImports.add(new StaticImportData(Bindings.getRawQualifiedName(declaringType), binding.getName(), true));
  			
  		} else if (binding instanceof IFunctionBinding) {
  			ITypeBinding declaringType= ((IFunctionBinding) binding).getDeclaringClass();
  			fAddedStaticImports.add(new StaticImportData(Bindings.getRawQualifiedName(declaringType), binding.getName(), false));
  			
  		} else {
  			throw new IllegalArgumentException(binding.toString());
  		}
  	}
  
  	public void registerRemovedNode(ASTNode removed) {
  		fRemovedNodes.add(removed);
  	}
  
  	
  	public void applyRemoves(ImportRewrite importRewrite) {
  		IBinding[] bindings= getImportsToRemove();
  		for (int i= 0; i < bindings.length; i++) {
  			if (bindings[i] instanceof ITypeBinding) {
  				ITypeBinding typeBinding= (ITypeBinding) bindings[i];
  				importRewrite.removeImport(typeBinding.getTypeDeclaration().getQualifiedName());
  			} else if (bindings[i] instanceof IFunctionBinding) {
  				IFunctionBinding binding= (IFunctionBinding) bindings[i];
  				importRewrite.removeStaticImport(binding.getDeclaringClass().getQualifiedName() + '.' + binding.getName());
  			} else if (bindings[i] instanceof IVariableBinding) {
  				IVariableBinding binding= (IVariableBinding) bindings[i];
  				importRewrite.removeStaticImport(binding.getDeclaringClass().getQualifiedName() + '.' + binding.getName());
  			}
  		}
  	}
  }
  