content: /*******************************************************************************
   * Copyright (c) 2000, 2013 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.Collection;
  import java.util.HashSet;
  import java.util.List;
  
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.core.dom.AST;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration;
  import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration;
  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.ClassInstanceCreation;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.FieldAccess;
  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.ImportDeclaration;
  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.QualifiedName;
  import org.eclipse.wst.jsdt.core.dom.SimpleName;
  import org.eclipse.wst.jsdt.core.dom.Statement;
  import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation;
  import org.eclipse.wst.jsdt.core.dom.SwitchCase;
  import org.eclipse.wst.jsdt.core.dom.SwitchStatement;
  import org.eclipse.wst.jsdt.core.dom.Type;
  import org.eclipse.wst.jsdt.core.dom.TypeDeclarationStatement;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclaration;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationExpression;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationStatement;
  import org.eclipse.wst.jsdt.internal.core.SearchableEnvironment;
  import org.eclipse.wst.jsdt.internal.ui.text.correction.ASTResolving;
  
  /**
  *
  * 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 ScopeAnalyzer {
  	
  	private static final IBinding[] NO_BINDING= new IBinding[0];
  
  	/**
  	 * Flag to specify that method should be reported.
  	 */
  	public static final int METHODS= 1;
  	
  	/**
  	 * Flag to specify that variables should be reported.
  	 */	
  	public static final int VARIABLES= 2;
  	
  	/**
  	 * Flag to specify that types should be reported.
  	 */		
  	public static final int TYPES= 4;
  	
  	/**
  	 * Flag to specify that only visible elements should be added.
  	 */		
  	public static final int CHECK_VISIBILITY= 16;
  	
  	private static interface IBindingRequestor {
  		boolean acceptBinding(IBinding binding);
  	}
  	
  	private static class DefaultBindingRequestor implements IBindingRequestor {
  		
  		private final List fResult;
  		private final HashSet fNamesAdded;
  		private final int fFlags;
  		private final ITypeBinding fParentTypeBinding;
  		
  		public DefaultBindingRequestor(ITypeBinding parentTypeBinding, int flags) {
  			fParentTypeBinding= parentTypeBinding;
  			fFlags= flags;
  			fResult= new ArrayList();
  			fNamesAdded= new HashSet();
  		}
  		
  		public DefaultBindingRequestor() {
  			this(null, 0);
  		}
  
  		/**
  		 * {@inheritDoc}
  		 */
  		public boolean acceptBinding(IBinding binding) {
  			if (binding == null)
  				return false;
  			
  			String signature= getSignature(binding);
  			if (signature != null && fNamesAdded.add(signature)) { // avoid duplicated results from inheritance
  				fResult.add(binding);
  			}
  			return false;
  		}
  
  		public List getResult() {
  			if (hasFlag(CHECK_VISIBILITY, fFlags)) {
  				for (int i= fResult.size() - 1; i >= 0; i--) {
  					IBinding binding= (IBinding) fResult.get(i);
  					if (!isVisible(binding, fParentTypeBinding)) {
  						fResult.remove(i);
  					}
  				}
  			}
  			return fResult;
  		}
  		
  	}
  
  	private HashSet fTypesVisited;
  	
  	private JavaScriptUnit fRoot;
  	
  	public ScopeAnalyzer(JavaScriptUnit root) {
  		fTypesVisited= new HashSet();
  		fRoot= root;
  	}
  	
  	private void clearLists() {
  		fTypesVisited.clear();
  	}
  	
  	private static String getSignature(IBinding binding) {
  		if (binding != null) {
  			switch (binding.getKind()) {
  				case IBinding.METHOD:
  					StringBuffer buf= new StringBuffer();
  					buf.append('M');
  					buf.append(binding.getName()).append('(');
  					ITypeBinding[] parameters= ((IFunctionBinding) binding).getParameterTypes();
  					for (int i= 0; i < parameters.length; i++) {
  						if (i > 0) {
  							buf.append(',');
  						}
  						ITypeBinding paramType= parameters[i].getErasure();
  						buf.append(paramType.getQualifiedName());
  					}
  					buf.append(')');
  					return buf.toString();
  				case IBinding.VARIABLE:
  					return 'V' + binding.getName();
  				case IBinding.TYPE:
  					return 'T' + binding.getName();			
  			}
  		}
  		return null;
  	}
  	
  	static final boolean hasFlag(int property, int flags) {
  		return (flags & property) != 0;
  	}
  	
  	/**
  	 * Collects all elements available in a type and its hierarchy
  	 * @param binding The type binding
  	 * @param flags Flags defining the elements to report
  	 * @param requestor the requestor to which all results are reported
  	 * @return return <code>true</code> if the requestor has reported the binding as found and no further results are required
  	 */
  	private boolean addInherited(ITypeBinding binding, int flags, IBindingRequestor requestor) {
  		if (!fTypesVisited.add(binding)) {
  			return false;
  		}
  		if (hasFlag(VARIABLES, flags)) {
  			IVariableBinding[] variableBindings= binding.getDeclaredFields();
  			for (int i= 0; i < variableBindings.length; i++) {
  				if (requestor.acceptBinding(variableBindings[i]))
  					return true;
  			}
  		}
  		
  		if (hasFlag(METHODS, flags)) {
  			IFunctionBinding[] methodBindings= binding.getDeclaredMethods();
  			for (int i= 0; i < methodBindings.length; i++) {
  				IFunctionBinding curr= methodBindings[i];
  				if (!curr.isConstructor()) {
  					if (requestor.acceptBinding(curr))
  						return true;
  				}
  			}
  		}
  
  		if (hasFlag(TYPES, flags)) {
  			ITypeBinding[] typeBindings= binding.getDeclaredTypes();
  			for (int i= 0; i < typeBindings.length; i++) {
  				ITypeBinding curr= typeBindings[i];
  				if (requestor.acceptBinding(curr))
  					return true;			
  			}
  		}		
  		
  		
  		ITypeBinding superClass= binding.getSuperclass();
  		if (superClass != null) {
  			if (addInherited(superClass, flags, requestor)) // recursive
  				return true;
  		} else if (binding.isArray()) {
  			if (addInherited(fRoot.getAST().resolveWellKnownType("java.lang.Object"), flags, requestor)) //$NON-NLS-1$
  				return true;
  		}
  		
  		return false;
  	}
  		
  	
  	/**
  	 * Collects all elements available in a type: its hierarchy and its outer scopes.
  	 * @param binding The type binding
  	 * @param flags Flags defining the elements to report
  	 * @param requestor the requestor to which all results are reported
  	 * @return return <code>true</code> if the requestor has reported the binding as found and no further results are required
  	 */
  	private boolean addTypeDeclarations(ITypeBinding binding, int flags, IBindingRequestor requestor) {
  		if (hasFlag(TYPES, flags) && !binding.isAnonymous()) {
  			if (requestor.acceptBinding(binding))
  				return true;
  		}
  		
  		addInherited(binding, flags, requestor); // add inherited 
  		
  		if (binding.isLocal()) {
  			addOuterDeclarationsForLocalType(binding, flags, requestor);
  		} else {
  			ITypeBinding declaringClass= binding.getDeclaringClass();
  			if (declaringClass != null) {
  				if (addTypeDeclarations(declaringClass, flags, requestor)) // Recursively add inherited
  					return true;
  			} else if (hasFlag(TYPES, flags)) {
  				if (fRoot.findDeclaringNode(binding) != null) {
  					List types= fRoot.types();
  					for (int i= 0; i < types.size(); i++) {
  						if (requestor.acceptBinding(((AbstractTypeDeclaration) types.get(i)).resolveBinding()))
  							return true;
  					}
  				}
  			}
  		}
  		return false;
  	}
  	
  	private boolean addOuterDeclarationsForLocalType(ITypeBinding localBinding, int flags, IBindingRequestor requestor) {
  		ASTNode node= fRoot.findDeclaringNode(localBinding);
  		if (node == null) {
  			return false;
  		}
  		
  		if (node instanceof AbstractTypeDeclaration || node instanceof AnonymousClassDeclaration) {
  			if (addLocalDeclarations(node.getParent(), flags, requestor))
  				return true;
  			
  			ITypeBinding parentTypeBinding= Bindings.getBindingOfParentType(node.getParent());
  			if (parentTypeBinding != null) {
  				if (addTypeDeclarations(parentTypeBinding, flags, requestor))
  					return true;
  			}
  			
  		}else if (node instanceof JavaScriptUnit) {
  			addLocalDeclarations(node, flags, requestor);
  			ITypeBinding parentTypeBinding= Bindings.getBindingOfParentType(node.getParent());
  			if (parentTypeBinding != null) {
  				if (addTypeDeclarations(parentTypeBinding, flags, requestor))
  					return true;
  			}
  			
  			IJavaScriptElement element = ((JavaScriptUnit) node).getJavaElement();
  			try {
  				SearchableEnvironment env = element.newSearchableNameEnvironment(new IJavaScriptUnit[]{});
  				if(env!=null) {
  					//SearchRequestor searchReq = new SearchRequestor(localBinding,);
  					//env.findTypes(new char[] {}, true, false,flags, );
  				}
  				} catch (JavaScriptModelException ex) {
  				// TODO Auto-generated catch block
  				ex.printStackTrace();
  			}
  				return true;
  		}
  		return false;
  	}
  	
  	private static ITypeBinding getBinding(Expression node) {
  		if (node != null) {
  			return node.resolveTypeBinding();
  		}
  		return null;
  	}
  		
  	private static ITypeBinding getQualifier(SimpleName selector) {
  		ASTNode parent= selector.getParent();
  		switch (parent.getNodeType()) {
  			case ASTNode.FUNCTION_INVOCATION:
  				FunctionInvocation decl= (FunctionInvocation) parent;
  				if (selector == decl.getName()) {
  					return getBinding(decl.getExpression());
  				}
  				return null;
  			case ASTNode.QUALIFIED_NAME:
  				QualifiedName qualifiedName= (QualifiedName) parent;
  				if (selector == qualifiedName.getName()) {
  					return getBinding(qualifiedName.getQualifier());
  				}
  				return null;
  			case ASTNode.FIELD_ACCESS:
  				FieldAccess fieldAccess= (FieldAccess) parent;
  				if (selector == fieldAccess.getName()) {
  					return getBinding(fieldAccess.getExpression());
  				}
  				return null;			
  			case ASTNode.SUPER_FIELD_ACCESS: {
  				ITypeBinding curr= Bindings.getBindingOfParentType(parent);
  				return curr.getSuperclass();
  			}
  			case ASTNode.SUPER_METHOD_INVOCATION: {
  				SuperMethodInvocation superInv= (SuperMethodInvocation) parent;
  				if (selector == superInv.getName()) {
  					ITypeBinding curr= Bindings.getBindingOfParentType(parent);
  					return curr.getSuperclass();
  				}
  				return null;
  			}
  			default:
  				if (parent instanceof Type) {
  					// bug 67644: in 'a.new X()', all member types of A are visible as location of X. 
  					ASTNode normalizedNode= ASTNodes.getNormalizedNode(parent);
  					if (normalizedNode.getLocationInParent() == ClassInstanceCreation.TYPE_PROPERTY) {
  						ClassInstanceCreation creation= (ClassInstanceCreation) normalizedNode.getParent();
  						return getBinding(creation.getExpression());
  					}
  				}
  				return null;
  		}
  	}
  	
  	public IBinding[] getDeclarationsInScope(SimpleName selector, int flags) {
  		try {
  			// special case for switch on enum
  			if (selector.getLocationInParent() == SwitchCase.EXPRESSION_PROPERTY) {
  				((SwitchStatement) selector.getParent().getParent()).getExpression().resolveTypeBinding();
  			}
  			
  			ITypeBinding parentTypeBinding= Bindings.getBindingOfParentType(selector);
  			if (parentTypeBinding != null) {			
  				ITypeBinding binding= getQualifier(selector);
  				DefaultBindingRequestor requestor= new DefaultBindingRequestor(parentTypeBinding, flags);
  				if (binding == null) {
  					addLocalDeclarations(selector, flags, requestor);
  					addTypeDeclarations(parentTypeBinding, flags, requestor);
  				} else {
  					addInherited(binding, flags, requestor);
  				}
  
  				List result= requestor.getResult();
  				return (IBinding[]) result.toArray(new IBinding[result.size()]);
  			}
  			return NO_BINDING;
  		} finally {
  			clearLists();			
  		}
  	}
  	
  	private static class SearchRequestor implements IBindingRequestor {
  		
  		private final int fFlags;
  		private final ITypeBinding fParentTypeBinding;
  		private final IBinding fToSearch;
  		private boolean fFound;
  		private boolean fIsVisible;
  		
  		public SearchRequestor(IBinding toSearch, ITypeBinding parentTypeBinding, int flag) {
  			fFlags= flag;
  			fToSearch= toSearch;
  			fParentTypeBinding= parentTypeBinding;
  			fFound= false;
  			fIsVisible= true;
  		}
  		
  		public boolean acceptBinding(IBinding binding) {
  			if (fFound)
  				return true;
  			
  			if (binding == null)
  				return false;
  			
  			if (fToSearch.getKind() != binding.getKind()) {
  				return false;
  			}
  			
  			boolean checkVisibility= hasFlag(CHECK_VISIBILITY, fFlags);
  			if (binding == fToSearch) {
  				fFound= true;
  			} else {
  				IBinding bindingDeclaration= Bindings.getDeclaration(binding);
  				if (bindingDeclaration == fToSearch) {
  					fFound= true;
  				} else if (bindingDeclaration.getName().equals(fToSearch.getName())) {
  					String signature= getSignature(bindingDeclaration);
  					if (signature != null && signature.equals(getSignature(fToSearch))) {
  						if (checkVisibility) {
  							fIsVisible= false;
  						}
  						return true; // found element that hides the binding to find
  					}
  				}
  			}
  
  			if (fFound && checkVisibility) {
  				fIsVisible= ScopeAnalyzer.isVisible(binding, fParentTypeBinding);
  			}
  			return fFound;
  		}
  		
  		public boolean found() {
  			return fFound;
  		}
  
  		public boolean isVisible() {
  			return fIsVisible;
  		}
  	}
  	
  	public boolean isDeclaredInScope(IBinding declaration, SimpleName selector, int flags) {
  		try {
  			// special case for switch on enum
  			if (selector.getLocationInParent() == SwitchCase.EXPRESSION_PROPERTY) {
  				((SwitchStatement) selector.getParent().getParent()).getExpression().resolveTypeBinding();
  			}
  			
  			ITypeBinding parentTypeBinding= Bindings.getBindingOfParentTypeContext(selector);
  			if (parentTypeBinding != null) {			
  				ITypeBinding binding= getQualifier(selector);
  				SearchRequestor requestor= new SearchRequestor(declaration, parentTypeBinding, flags);
  				if (binding == null) {
  					addLocalDeclarations(selector, flags, requestor);
  					if (requestor.found())
  						return requestor.isVisible();
  					addTypeDeclarations(parentTypeBinding, flags, requestor);
  					if (requestor.found())
  						return requestor.isVisible();
  				} else {
  					addInherited(binding, flags, requestor);
  					if (requestor.found())
  						return requestor.isVisible();
  				}
  			}
  			return false;
  		} finally {
  			clearLists();
  		}
  	}
  
  	public IBinding[] getDeclarationsInScope(int offset, int flags) {
  		NodeFinder finder= new NodeFinder(offset, 0);
  		fRoot.accept(finder);
  		ASTNode node= finder.getCoveringNode();
  		if (node == null) {
  			return NO_BINDING;
  		}
  
  		if (node instanceof SimpleName) {
  			return getDeclarationsInScope((SimpleName) node, flags);
  		}
  		
  		try {
  			ITypeBinding binding= Bindings.getBindingOfParentType(node);
  			DefaultBindingRequestor requestor= new DefaultBindingRequestor(binding, flags);
  			addLocalDeclarations(node, offset, flags, requestor);			
  			if (binding != null) {
  				addTypeDeclarations(binding, flags, requestor);
  			}
  			List result= requestor.getResult();
  			return (IBinding[]) result.toArray(new IBinding[result.size()]);
  		} finally {
  			clearLists();			
  		}
  	}
  	
  	private static ITypeBinding getDeclaringType(IBinding binding) {
  		switch (binding.getKind()) {
  			case IBinding.VARIABLE:
  				return ((IVariableBinding) binding).getDeclaringClass();
  			case IBinding.METHOD:
  				return ((IFunctionBinding) binding).getDeclaringClass();
  			case IBinding.TYPE:
  				ITypeBinding typeBinding= (ITypeBinding) binding;
  				if (typeBinding.getDeclaringClass() != null) {
  					return typeBinding;
  				}
  				return typeBinding;
  		}
  		return null;
  	}
  	
  	/**
  	 * Evaluates if the declaration is visible in a certain context. 
  	 * @param binding The binding of the declaration to examine
  	 * @param context The context to test in
  	 * @return Returns 
  	 */
  	public static boolean isVisible(IBinding binding, ITypeBinding context) {
  		if (binding.getKind() == IBinding.VARIABLE && !((IVariableBinding) binding).isField()) {
  			return true; // all local variables found are visible
  		}
  		ITypeBinding declaring= getDeclaringType(binding);
  		if (declaring == null) {
  			return false;
  		}
  	
  		int modifiers= binding.getModifiers();
  		if (Modifier.isPublic(modifiers)) {
  			return true;
  		} else if (Modifier.isProtected(modifiers) || !Modifier.isPrivate(modifiers)) {
  			if (declaring.getPackage() == context.getPackage()) {
  				return true;
  			}
  			return isTypeInScope(declaring, context, Modifier.isProtected(modifiers));
  		}
  		// private visibility
  		return isTypeInScope(declaring, context, false);
  	}
  	
  	private static boolean isTypeInScope(ITypeBinding declaring, ITypeBinding context, boolean includeHierarchy) {
  		ITypeBinding curr= context;
  		while (curr != null && curr != declaring) {
  			if (includeHierarchy && Bindings.isSuperType(declaring, curr)) {
  				return true;
  			}
  			curr= curr.getDeclaringClass();
  		}
  		return curr == declaring;
  	}
  	
  
  	public IBinding[] getDeclarationsAfter(int offset, int flags) {
  		try {		
  			NodeFinder finder= new NodeFinder(offset, 0);
  			fRoot.accept(finder);
  			ASTNode node= finder.getCoveringNode();
  			if (node == null) {
  				return NO_BINDING;
  			}
  			
  			ASTNode declaration= ASTResolving.findParentStatement(node);
  			while (declaration instanceof Statement && declaration.getNodeType() != ASTNode.BLOCK) {
  				declaration= declaration.getParent();
  			}
  
  			if (declaration instanceof Block) {
  				DefaultBindingRequestor requestor= new DefaultBindingRequestor();
  				DeclarationsAfterVisitor visitor= new DeclarationsAfterVisitor(node.getStartPosition(), flags, requestor);
  				declaration.accept(visitor);
  				List result= requestor.getResult();
  				return (IBinding[])result.toArray(new IBinding[result.size()]);
  			}
  			return NO_BINDING;
  		} finally {
  			clearLists();			
  		}
  	}
  	
  	
  	private class ScopeAnalyzerVisitor extends HierarchicalASTVisitor {
  		
  		private int fPosition;
  		private int fFlags;
  		private final IBindingRequestor fRequestor;
  		private boolean fBreak;
  		
  		public ScopeAnalyzerVisitor(int position, int flags, IBindingRequestor requestor) {
  			fPosition= position;
  			fFlags= flags;
  			fRequestor= requestor;
  			fBreak= false;
  		}
  		
  		private boolean isInside(ASTNode node) {
  			int start= node.getStartPosition();
  			int end= start + node.getLength();
  					
  			return start <= fPosition && fPosition < end;
  		}
  		
  		public boolean visit(FunctionDeclaration node) {
  			if (isInside(node)) {
  				Block body= node.getBody();
  				if (body != null) {
  					body.accept(this);
  				}
  				visitBackwards(node.parameters());
  			}
  			return false;
  		}
  				
  		public boolean visit(SwitchCase node) {
  			// switch on enum allows to use enum constants without qualification
  			if (hasFlag(VARIABLES, fFlags) && !node.isDefault() && isInside(node.getExpression())) {
  				SwitchStatement switchStatement= (SwitchStatement) node.getParent();
  				switchStatement.getExpression().resolveTypeBinding();
  			}
  			return false;
  		}
  		
  		public boolean visit(Initializer node) {
  			return !fBreak && isInside(node);
  		}		
  		
  		public boolean visit(Statement node) {
  			return !fBreak && isInside(node);
  		}
  		
  		public boolean visit(ASTNode node) {
  			return false;
  		}
  		
  		public boolean visit(Block node) {
  			if (isInside(node)) {
  				visitBackwards(node.statements());
  			}
  			return false;
  		}		
  		
  		public boolean visit(VariableDeclaration node) {
  			if (hasFlag(VARIABLES, fFlags) && node.getStartPosition() < fPosition) {
  				fBreak= fRequestor.acceptBinding(node.resolveBinding());					
  			}
  			return !fBreak;
  		}
  		
  		public boolean visit(VariableDeclarationStatement node) {
  			visitBackwards(node.fragments());
  			return false;
  		}		
  		
  		public boolean visit(VariableDeclarationExpression node) {
  			visitBackwards(node.fragments());
  			return false;
  		}
  	
  		public boolean visit(CatchClause node) {
  			if (isInside(node)) {
  				node.getBody().accept(this);
  				node.getException().accept(this);
  			}
  			return false;			
  		}
  		
  		public boolean visit(ForStatement node) {
  			if (isInside(node)) {
  				node.getBody().accept(this);
  				visitBackwards(node.initializers());
  			}
  			return false;
  		}	
  	
  		public boolean visit(ForInStatement node) {
  			if (isInside(node)) {
  				node.getBody().accept(this);
  				node.getIterationVariable().accept(this);
  			}
  			return false;
  		}	
  
  		public boolean visit(TypeDeclarationStatement node) {
  			if (hasFlag(TYPES, fFlags) && node.getStartPosition() + node.getLength() < fPosition) {
  				if (node.getAST().apiLevel() == AST.JLS2) {
  					fBreak= fRequestor.acceptBinding(node.getTypeDeclaration().resolveBinding());
  				} else {
  					fBreak= fRequestor.acceptBinding(node.getDeclaration().getName().resolveBinding());
  				}
  				return false;
  			}
  			return !fBreak && isInside(node);
  		}
  		
  		private void visitBackwards(List list) {
  			if (fBreak)
  				return;
  			
  			for (int i= list.size() - 1; i >= 0; i--) {
  				ASTNode curr= (ASTNode) list.get(i);
  				if (curr.getStartPosition() <  fPosition) {
  					curr.accept(this);
  				}
  			}			
  		}
  	}
  	
  	private class DeclarationsAfterVisitor extends HierarchicalASTVisitor {
  		private final int fPosition;
  		private final int fFlags;
  		private final IBindingRequestor fRequestor;
  		private boolean fBreak;
  		
  		public DeclarationsAfterVisitor(int position, int flags, IBindingRequestor requestor) {
  			fPosition= position;
  			fFlags= flags;
  			fRequestor= requestor;
  			fBreak= false;
  		}
  		
  		public boolean visit(ASTNode node) {
  			return !fBreak;
  		}
  		
  		public boolean visit(VariableDeclaration node) {
  			if (hasFlag(VARIABLES, fFlags) && fPosition < node.getStartPosition()) {
  				fBreak= fRequestor.acceptBinding(node.resolveBinding());		
  			}
  			return false;
  		}
  		
  		public boolean visit(AnonymousClassDeclaration node) {
  			return false;
  		}
  
  		public boolean visit(TypeDeclarationStatement node) {
  			if (hasFlag(TYPES, fFlags) && fPosition < node.getStartPosition()) {
  				fBreak= fRequestor.acceptBinding(node.resolveBinding());
  			}
  			return false;
  		}
  	}
  	
  	private boolean addLocalDeclarations(ASTNode node, int flags, IBindingRequestor requestor) {
  		return addLocalDeclarations(node, node.getStartPosition(), flags, requestor);
  	}
  	
  	
  	private boolean addLocalDeclarations(ASTNode node, int offset, int flags, IBindingRequestor requestor) {
  		if (hasFlag(VARIABLES, flags) || hasFlag(TYPES, flags)) {
  			BodyDeclaration declaration= ASTResolving.findParentBodyDeclaration(node);
  			if (declaration instanceof FunctionDeclaration || declaration instanceof Initializer) {		
  				ScopeAnalyzerVisitor visitor= new ScopeAnalyzerVisitor(offset, flags, requestor);
  				declaration.accept(visitor);
  				return visitor.fBreak;
  			}
  		}
  		return false;
  	}
  	
  	public Collection getUsedVariableNames(int offset, int length) {
  		HashSet result= new HashSet();
  		IBinding[] bindingsBefore= getDeclarationsInScope(offset, VARIABLES);
  		for (int i= 0; i < bindingsBefore.length; i++) {
  			result.add(bindingsBefore[i].getName());
  		}
  		IBinding[] bindingsAfter= getDeclarationsAfter(offset + length, VARIABLES);
  		for (int i= 0; i < bindingsAfter.length; i++) {
  			result.add(bindingsAfter[i].getName());
  		}
  		List imports= fRoot.imports();
  		for (int i= 0; i < imports.size(); i++) {
  			ImportDeclaration decl= (ImportDeclaration) imports.get(i);
  			if (decl.isStatic() && !decl.isOnDemand()) {
  				result.add(ASTNodes.getSimpleNameIdentifier(decl.getName()));
  			}
  		}
  		return result;
  	}
  }
  