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.dom;
  
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  
  import org.eclipse.wst.jsdt.core.dom.AST;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.ASTVisitor;
  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.Expression;
  import org.eclipse.wst.jsdt.core.dom.ForInStatement;
  import org.eclipse.wst.jsdt.core.dom.ForStatement;
  import org.eclipse.wst.jsdt.core.dom.IBinding;
  import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
  import org.eclipse.wst.jsdt.core.dom.QualifiedName;
  import org.eclipse.wst.jsdt.core.dom.SimpleName;
  import org.eclipse.wst.jsdt.core.dom.TypeDeclarationStatement;
  
  /**
  *
  * Provisional API: This class/interface is part of an interim API that is still under development and expected to
  * change significantly before reaching stability. It is being made available at this early stage to solicit feedback
  * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
  * (repeatedly) as the API evolves.
  */
  public class CodeScopeBuilder extends ASTVisitor {
  
  	public static class Scope {
  		private Scope fParent;
  		private int fStart;
  		private int fLength;
  		private List fNames;
  		private List fChildren;
  		private int fCursorOffset;
  		Scope(Scope parent, int start, int length) {
  			fParent= parent;
  			fStart= start;
  			fLength= length;
  			if (fParent != null)	
  				fParent.addChild(this);
  		}
  		public void setCursor(int offset) {
  			fCursorOffset= offset;
  		}
  		private void addChild(Scope child) {
  			if (fChildren == null)
  				fChildren= new ArrayList(2);
  			fChildren.add(child);
  		}
  		private void addName(String name) {
  			if (fNames == null)
  				fNames= new ArrayList(2);
  			fNames.add(name);
  		}
  		public Scope findScope(int start, int length) {
  			if (fStart <= start && start + length <= fStart + fLength) {
  				if (fChildren == null)
  					return this;
  				for (Iterator iter= fChildren.iterator(); iter.hasNext();) {
  					Scope scope= ((Scope)iter.next()).findScope(start, length);
  					if (scope != null)
  						return scope;
  				}
  				return this;
  			}
  			return null;
  		}
  		public String createName(String candidate, boolean add) {
  			int i= 1;
  			String result= candidate;
  			while(isInUse(result)) {
  				result= candidate + i++;
  			}
  			if (add)
  				addName(result);
  			return result;
  		}
  		public boolean isInUse(String name) {
  			if (internalIsInUse(name))
  				return true;
  			if (fChildren != null) {
  				for (Iterator iter= fChildren.iterator(); iter.hasNext();) {
  					Scope child= (Scope) iter.next();
  					if (fCursorOffset < child.fStart && child.isInUseDown(name)) {
  						return true;
  					}
  				}
  			}
  			return false;
  		}
  		private boolean internalIsInUse(String name) {
  			if (fNames != null && fNames.contains(name))
  				return true;
  			if (fParent != null)
  				return fParent.internalIsInUse(name);
  			return false;
  			
  		}
  		private boolean isInUseDown(String name) {
  			if (fNames != null && fNames.contains(name))
  				return true;
  			if (fChildren == null)
  				return false;
  			for (Iterator iter= fChildren.iterator(); iter.hasNext();) {
  				Scope scope= (Scope) iter.next();
  				if (scope.isInUseDown(name))
  					return true;
  			}
  			return false;
  		}
  	}
  	
  	private IBinding fIgnoreBinding;
  	private Selection fIgnoreRange;
  	private Scope fScope;
  	private List fScopes;
  
  	public static Scope perform(BodyDeclaration node, IBinding ignore) {
  		CodeScopeBuilder collector= new CodeScopeBuilder(node, ignore);
  		node.accept(collector);
  		return collector.fScope;
  	}
  
  	public static Scope perform(BodyDeclaration node, Selection ignore) {
  		CodeScopeBuilder collector= new CodeScopeBuilder(node, ignore);
  		node.accept(collector);
  		return collector.fScope;
  	}
  
  	private CodeScopeBuilder(ASTNode node, IBinding ignore) {
  		fScope= new Scope(null, node.getStartPosition(), node.getLength());
  		fScopes= new ArrayList();
  		fIgnoreBinding= ignore;
  	}
  	
  	private CodeScopeBuilder(ASTNode node, Selection ignore) {
  		fScope= new Scope(null, node.getStartPosition(), node.getLength());
  		fScopes= new ArrayList();
  		fIgnoreRange= ignore;
  	}
  	
  	public boolean visit(CatchClause node) {
  		// open a new scope for the exception declaration.
  		fScopes.add(fScope);
  		fScope= new Scope(fScope, node.getStartPosition(), node.getLength());
  		return true;
  	}
  	
  	public void endVisit(CatchClause node) {
  		fScope= (Scope)fScopes.remove(fScopes.size() - 1);
  	}
  	
  	public boolean visit(SimpleName node) {
  		if (fIgnoreBinding != null && Bindings.equals(fIgnoreBinding, node.resolveBinding()))
  			return false;
  		if (fIgnoreRange != null && fIgnoreRange.covers(node))
  			return false;
  		fScope.addName(node.getIdentifier());
  		return false;
  	}
  	
  	public boolean visit(QualifiedName node) {
  		// only consider the left most identifier.
  		node.getQualifier().accept(this);
  		return false;
  	}
  	
  	public boolean visit(FunctionInvocation node) {
  		Expression receiver= node.getExpression();
  		if (receiver == null) {
  			SimpleName name= node.getName();
  			if (fIgnoreBinding == null || (name!=null && !Bindings.equals(fIgnoreBinding, name.resolveBinding())) )
  				node.getName().accept(this);
  		} else {
  			receiver.accept(this);
  		}
  		accept(node.arguments());
  		return false;
  	}
  	
  	public boolean visit(TypeDeclarationStatement node) {
  		if (node.getAST().apiLevel() == AST.JLS2) {
  			fScope.addName(node.getTypeDeclaration().getName().getIdentifier());
  		} else {
  			fScope.addName(node.getDeclaration().getName().getIdentifier());
  		}
  		return false;
  	}
  	
  	public boolean visit(Block node) {
  		fScopes.add(fScope);
  		fScope= new Scope(fScope, node.getStartPosition(), node.getLength());
  		return true;
  	}
  	
  	public void endVisit(Block node) {
  		fScope= (Scope)fScopes.remove(fScopes.size() - 1);
  	}
  	
  	public boolean visit(ForStatement node) {
  		fScopes.add(fScope);
  		fScope= new Scope(fScope, node.getStartPosition(), node.getLength());
  		return true;
  	}
  	
  	public void endVisit(ForStatement node) {
  		fScope= (Scope)fScopes.remove(fScopes.size() - 1);
  	}
  	
  	private void accept(List list) {
  		int size;
  		if (list == null || (size= list.size()) == 0)
  			return;
  		for (int i= 0; i < size; i++) {
  			((ASTNode)list.get(i)).accept(this);
  		}
  	}
  	public boolean visit(ForInStatement node) {
  		fScopes.add(fScope);
  		fScope= new Scope(fScope, node.getStartPosition(), node.getLength());
  		return true;
  	}
  	
  	public void endVisit(ForInStatement node) {
  		fScope= (Scope)fScopes.remove(fScopes.size() - 1);
  	}
  	
  }
  