/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package net.sourceforge.phpdt.internal.compiler.lookup;

import net.sourceforge.phpdt.core.compiler.CharOperation;
import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.ImportReference;
import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
import net.sourceforge.phpdt.internal.compiler.impl.ReferenceContext;
import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
import net.sourceforge.phpdt.internal.compiler.util.ObjectVector;

public abstract class Scope implements BaseTypes, BindingIds,
		CompilerModifiers, ProblemReasons, TagBits, TypeConstants, TypeIds {

	public Scope parent;

	public int kind;

	public final static int BLOCK_SCOPE = 1;

	public final static int METHOD_SCOPE = 2;

	public final static int CLASS_SCOPE = 3;

	public final static int COMPILATION_UNIT_SCOPE = 4;

	protected Scope(int kind, Scope parent) {
		this.kind = kind;
		this.parent = parent;
	}

	public abstract ProblemReporter problemReporter();

	// Internal use only
	protected final boolean areParametersAssignable(TypeBinding[] parameters,
			TypeBinding[] arguments) {
		if (parameters == arguments)
			return true;

		int length = parameters.length;
		if (length != arguments.length)
			return false;

		for (int i = 0; i < length; i++)
			if (parameters[i] != arguments[i])
				if (!arguments[i].isCompatibleWith(parameters[i]))
					return false;
		return true;
	}

	/*
	 * Answer an int describing the relationship between the given types.
	 * 
	 * NotRelated EqualOrMoreSpecific : left is compatible with right
	 * MoreGeneric : right is compatible with left
	 */
	public static int compareTypes(TypeBinding left, TypeBinding right) {
		if (left.isCompatibleWith(right))
			return EqualOrMoreSpecific;
		if (right.isCompatibleWith(left))
			return MoreGeneric;
		return NotRelated;
	}

	/*
	 * Answer an int describing the relationship between the given type and
	 * unchecked exceptions.
	 * 
	 * NotRelated EqualOrMoreSpecific : type is known for sure to be an
	 * unchecked exception type MoreGeneric : type is a supertype of an actual
	 * unchecked exception type
	 */
	public int compareUncheckedException(ReferenceBinding type) {
		int comparison = compareTypes(type, getJavaLangRuntimeException());
		if (comparison != 0)
			return comparison;
		return compareTypes(type, getJavaLangError());
	}

	public final CompilationUnitScope compilationUnitScope() {
		Scope lastScope = null;
		Scope scope = this;
		do {
			lastScope = scope;
			scope = scope.parent;
		} while (scope != null);
		return (CompilationUnitScope) lastScope;
	}

	public ArrayBinding createArray(TypeBinding type, int dimension) {
		if (type.isValidBinding())
			return environment().createArrayType(type, dimension);
		else
			return new ArrayBinding(type, dimension);
	}

	public final ClassScope enclosingClassScope() {
		Scope scope = this;
		while ((scope = scope.parent) != null) {
			if (scope instanceof ClassScope)
				return (ClassScope) scope;
		}
		return null; // may answer null if no type around
	}

	public final MethodScope enclosingMethodScope() {
		Scope scope = this;
		while ((scope = scope.parent) != null) {
			if (scope instanceof MethodScope)
				return (MethodScope) scope;
		}
		return null; // may answer null if no method around
	}

	/*
	 * Answer the receiver's enclosing source type.
	 */
	public final SourceTypeBinding enclosingSourceType() {
		Scope scope = this;
		do {
			if (scope instanceof ClassScope)
				return ((ClassScope) scope).referenceContext.binding;
			scope = scope.parent;
		} while (scope != null);
		return null;
	}

	public final LookupEnvironment environment() {
		Scope scope, unitScope = this;
		while ((scope = unitScope.parent) != null)
			unitScope = scope;
		return ((CompilationUnitScope) unitScope).environment;
	}

	// Internal use only
	public ReferenceBinding findDirectMemberType(char[] typeName,
			ReferenceBinding enclosingType) {
		if ((enclosingType.tagBits & HasNoMemberTypes) != 0)
			return null; // know it has no member types (nor inherited member
							// types)

		SourceTypeBinding enclosingSourceType = enclosingSourceType();
		compilationUnitScope().recordReference(enclosingType.compoundName,
				typeName);
		ReferenceBinding memberType = enclosingType.getMemberType(typeName);
		if (memberType != null) {
			compilationUnitScope().recordTypeReference(memberType); // to record
																	// supertypes
			if (enclosingSourceType == null ? memberType
					.canBeSeenBy(getCurrentPackage()) : memberType.canBeSeenBy(
					enclosingType, enclosingSourceType))
				return memberType;
			else
				return new ProblemReferenceBinding(typeName, memberType,
						NotVisible);
		}
		return null;
	}

	// Internal use only
	public MethodBinding findExactMethod(ReferenceBinding receiverType,
			char[] selector, TypeBinding[] argumentTypes,
			InvocationSite invocationSite) {

		compilationUnitScope().recordTypeReference(receiverType);
		compilationUnitScope().recordTypeReferences(argumentTypes);
		MethodBinding exactMethod = receiverType.getExactMethod(selector,
				argumentTypes);
		if (exactMethod != null) {
			compilationUnitScope().recordTypeReferences(
					exactMethod.thrownExceptions);
			if (receiverType.isInterface()
					|| exactMethod.canBeSeenBy(receiverType, invocationSite,
							this))
				return exactMethod;
		}
		return null;
	}

	// Internal use only
	/*
	 * Answer the field binding that corresponds to fieldName. Start the lookup
	 * at the receiverType. InvocationSite implements isSuperAccess(); this is
	 * used to determine if the discovered field is visible. Only fields defined
	 * by the receiverType or its supertypes are answered; a field of an
	 * enclosing type will not be found using this API.
	 * 
	 * If no visible field is discovered, null is answered.
	 */
	public FieldBinding findField(TypeBinding receiverType, char[] fieldName,
			InvocationSite invocationSite) {
		if (receiverType.isBaseType())
			return null;
		if (receiverType.isArrayType()) {
			TypeBinding leafType = receiverType.leafComponentType();
			if (leafType instanceof ReferenceBinding) {
				if (!((ReferenceBinding) leafType).canBeSeenBy(this))
					return new ProblemFieldBinding((ReferenceBinding) leafType,
							fieldName, ReceiverTypeNotVisible);
			}
			if (CharOperation.equals(fieldName, LENGTH))
				return ArrayBinding.LengthField;
			return null;
		}

		compilationUnitScope().recordTypeReference(receiverType);

		ReferenceBinding currentType = (ReferenceBinding) receiverType;
		if (!currentType.canBeSeenBy(this))
			return new ProblemFieldBinding(currentType, fieldName,
					ReceiverTypeNotVisible);

		FieldBinding field = currentType.getField(fieldName);
		if (field != null) {
			if (field.canBeSeenBy(currentType, invocationSite, this))
				return field;
			else
				return new ProblemFieldBinding(field.declaringClass, fieldName,
						NotVisible);
		}
		// collect all superinterfaces of receiverType until the field is found
		// in a supertype
		ReferenceBinding[][] interfacesToVisit = null;
		int lastPosition = -1;
		FieldBinding visibleField = null;
		boolean keepLooking = true;
		boolean notVisible = false;
		// we could hold onto the not visible field for extra error reporting
		while (keepLooking) {
			ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
			if (itsInterfaces != NoSuperInterfaces) {
				if (interfacesToVisit == null)
					interfacesToVisit = new ReferenceBinding[5][];
				if (++lastPosition == interfacesToVisit.length)
					System
							.arraycopy(
									interfacesToVisit,
									0,
									interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
									0, lastPosition);
				interfacesToVisit[lastPosition] = itsInterfaces;
			}
			if ((currentType = currentType.superclass()) == null)
				break;

			if ((field = currentType.getField(fieldName)) != null) {
				keepLooking = false;
				if (field.canBeSeenBy(receiverType, invocationSite, this)) {
					if (visibleField == null)
						visibleField = field;
					else
						return new ProblemFieldBinding(
								visibleField.declaringClass, fieldName,
								Ambiguous);
				} else {
					notVisible = true;
				}
			}
		}

		// walk all visible interfaces to find ambiguous references
		if (interfacesToVisit != null) {
			ProblemFieldBinding ambiguous = null;
			done: for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++) {
					ReferenceBinding anInterface = interfaces[j];
					if ((anInterface.tagBits & InterfaceVisited) == 0) {
						// if interface as not already been visited
						anInterface.tagBits |= InterfaceVisited;
						if ((field = anInterface.getField(fieldName)) != null) {
							if (visibleField == null) {
								visibleField = field;
							} else {
								ambiguous = new ProblemFieldBinding(
										visibleField.declaringClass, fieldName,
										Ambiguous);
								break done;
							}
						} else {
							ReferenceBinding[] itsInterfaces = anInterface
									.superInterfaces();
							if (itsInterfaces != NoSuperInterfaces) {
								if (++lastPosition == interfacesToVisit.length)
									System
											.arraycopy(
													interfacesToVisit,
													0,
													interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
													0, lastPosition);
								interfacesToVisit[lastPosition] = itsInterfaces;
							}
						}
					}
				}
			}

			// bit reinitialization
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++)
					interfaces[j].tagBits &= ~InterfaceVisited;
			}
			if (ambiguous != null)
				return ambiguous;
		}

		if (visibleField != null)
			return visibleField;
		if (notVisible)
			return new ProblemFieldBinding(currentType, fieldName, NotVisible);
		return null;
	}

	// Internal use only
	public ReferenceBinding findMemberType(char[] typeName,
			ReferenceBinding enclosingType) {
		if ((enclosingType.tagBits & HasNoMemberTypes) != 0)
			return null; // know it has no member types (nor inherited member
							// types)

		SourceTypeBinding enclosingSourceType = enclosingSourceType();
		PackageBinding currentPackage = getCurrentPackage();
		compilationUnitScope().recordReference(enclosingType.compoundName,
				typeName);
		ReferenceBinding memberType = enclosingType.getMemberType(typeName);
		if (memberType != null) {
			compilationUnitScope().recordTypeReference(memberType); // to record
																	// supertypes
			if (enclosingSourceType == null ? memberType
					.canBeSeenBy(currentPackage) : memberType.canBeSeenBy(
					enclosingType, enclosingSourceType))
				return memberType;
			else
				return new ProblemReferenceBinding(typeName, memberType,
						NotVisible);
		}

		// collect all superinterfaces of receiverType until the memberType is
		// found in a supertype
		ReferenceBinding currentType = enclosingType;
		ReferenceBinding[][] interfacesToVisit = null;
		int lastPosition = -1;
		ReferenceBinding visibleMemberType = null;
		boolean keepLooking = true;
		ReferenceBinding notVisible = null;
		// we could hold onto the not visible field for extra error reporting
		while (keepLooking) {
			ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
			if (itsInterfaces != NoSuperInterfaces) {
				if (interfacesToVisit == null)
					interfacesToVisit = new ReferenceBinding[5][];
				if (++lastPosition == interfacesToVisit.length)
					System
							.arraycopy(
									interfacesToVisit,
									0,
									interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
									0, lastPosition);
				interfacesToVisit[lastPosition] = itsInterfaces;
			}
			if ((currentType = currentType.superclass()) == null)
				break;

			compilationUnitScope().recordReference(currentType.compoundName,
					typeName);
			if ((memberType = currentType.getMemberType(typeName)) != null) {
				compilationUnitScope().recordTypeReference(memberType); // to
																		// record
																		// supertypes
				keepLooking = false;
				if (enclosingSourceType == null ? memberType
						.canBeSeenBy(currentPackage) : memberType.canBeSeenBy(
						enclosingType, enclosingSourceType)) {
					if (visibleMemberType == null)
						visibleMemberType = memberType;
					else
						return new ProblemReferenceBinding(typeName, Ambiguous);
				} else {
					notVisible = memberType;
				}
			}
		}
		// walk all visible interfaces to find ambiguous references
		if (interfacesToVisit != null) {
			ProblemReferenceBinding ambiguous = null;
			done: for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++) {
					ReferenceBinding anInterface = interfaces[j];
					if ((anInterface.tagBits & InterfaceVisited) == 0) {
						// if interface as not already been visited
						anInterface.tagBits |= InterfaceVisited;
						compilationUnitScope().recordReference(
								anInterface.compoundName, typeName);
						if ((memberType = anInterface.getMemberType(typeName)) != null) {
							compilationUnitScope().recordTypeReference(
									memberType); // to record supertypes
							if (visibleMemberType == null) {
								visibleMemberType = memberType;
							} else {
								ambiguous = new ProblemReferenceBinding(
										typeName, Ambiguous);
								break done;
							}
						} else {
							ReferenceBinding[] itsInterfaces = anInterface
									.superInterfaces();
							if (itsInterfaces != NoSuperInterfaces) {
								if (++lastPosition == interfacesToVisit.length)
									System
											.arraycopy(
													interfacesToVisit,
													0,
													interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
													0, lastPosition);
								interfacesToVisit[lastPosition] = itsInterfaces;
							}
						}
					}
				}
			}

			// bit reinitialization
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++)
					interfaces[j].tagBits &= ~InterfaceVisited;
			}
			if (ambiguous != null)
				return ambiguous;
		}
		if (visibleMemberType != null)
			return visibleMemberType;
		if (notVisible != null)
			return new ProblemReferenceBinding(typeName, notVisible, NotVisible);
		return null;
	}

	// Internal use only
	public MethodBinding findMethod(ReferenceBinding receiverType,
			char[] selector, TypeBinding[] argumentTypes,
			InvocationSite invocationSite) {

		ReferenceBinding currentType = receiverType;
		MethodBinding matchingMethod = null;
		ObjectVector found = new ObjectVector();

		compilationUnitScope().recordTypeReference(receiverType);
		compilationUnitScope().recordTypeReferences(argumentTypes);

		if (currentType.isInterface()) {
			MethodBinding[] currentMethods = currentType.getMethods(selector);
			int currentLength = currentMethods.length;
			if (currentLength == 1) {
				matchingMethod = currentMethods[0];
			} else if (currentLength > 1) {
				found.addAll(currentMethods);
			}
			matchingMethod = findMethodInSuperInterfaces(currentType, selector,
					found, matchingMethod);
			currentType = getJavaLangObject();
		}

		// boolean isCompliant14 =
		// compilationUnitScope().environment.options.complianceLevel >=
		// CompilerOptions.JDK1_4;
		// superclass lookup
		ReferenceBinding classHierarchyStart = currentType;
		while (currentType != null) {
			MethodBinding[] currentMethods = currentType.getMethods(selector);
			int currentLength = currentMethods.length;

			/*
			 * if 1.4 compliant, must filter out redundant protected methods
			 * from superclasses
			 */
			// if (isCompliant14){
			// nextMethod: for (int i = 0; i < currentLength; i++){
			// MethodBinding currentMethod = currentMethods[i];
			// // protected method need to be checked only - default access is
			// already dealt with in #canBeSeen implementation
			// // when checking that p.C -> q.B -> p.A cannot see default access
			// members from A through B.
			// if ((currentMethod.modifiers & AccProtected) == 0) continue
			// nextMethod;
			// if (matchingMethod != null){
			// if (currentMethod.areParametersEqual(matchingMethod)){
			// currentLength--;
			// currentMethods[i] = null; // discard this match
			// continue nextMethod;
			// }
			// } else {
			// for (int j = 0, max = found.size; j < max; j++) {
			// if
			// (((MethodBinding)found.elementAt(j)).areParametersEqual(currentMethod)){
			// currentLength--;
			// currentMethods[i] = null;
			// continue nextMethod;
			// }
			// }
			// }
			// }
			// }
			if (currentLength == 1 && matchingMethod == null && found.size == 0) {
				matchingMethod = currentMethods[0];
			} else if (currentLength > 0) {
				if (matchingMethod != null) {
					found.add(matchingMethod);
					matchingMethod = null;
				}
				// append currentMethods, filtering out null entries
				int maxMethod = currentMethods.length;
				if (maxMethod == currentLength) { // no method was eliminated
													// for 1.4 compliance (see
													// above)
					found.addAll(currentMethods);
				} else {
					for (int i = 0, max = currentMethods.length; i < max; i++) {
						MethodBinding currentMethod = currentMethods[i];
						if (currentMethod != null)
							found.add(currentMethod);
					}
				}
			}
			currentType = currentType.superclass();
		}

		int foundSize = found.size;
		if (foundSize == 0) {
			if (matchingMethod != null
					&& areParametersAssignable(matchingMethod.parameters,
							argumentTypes)) {
				// (if no default abstract) must explicitly look for one
				// instead, which could be a better match
				if (!matchingMethod.canBeSeenBy(receiverType, invocationSite,
						this)) {
					// ignore matching method (to be consistent with multiple
					// matches, none visible (matching method is then null)
					MethodBinding interfaceMethod = findDefaultAbstractMethod(
							receiverType, selector, argumentTypes,
							invocationSite, classHierarchyStart, null, found);
					if (interfaceMethod != null)
						return interfaceMethod;
				}
				return matchingMethod;
			}
			return findDefaultAbstractMethod(receiverType, selector,
					argumentTypes, invocationSite, classHierarchyStart,
					matchingMethod, found);
		}

		MethodBinding[] candidates = new MethodBinding[foundSize];
		int candidatesCount = 0;
		// argument type compatibility check
		for (int i = 0; i < foundSize; i++) {
			MethodBinding methodBinding = (MethodBinding) found.elementAt(i);
			if (areParametersAssignable(methodBinding.parameters, argumentTypes))
				candidates[candidatesCount++] = methodBinding;
		}
		if (candidatesCount == 1) {
			compilationUnitScope().recordTypeReferences(
					candidates[0].thrownExceptions);
			return candidates[0]; // have not checked visibility
		}
		if (candidatesCount == 0) { // try to find a close match when the
									// parameter order is wrong or missing some
									// parameters
			MethodBinding interfaceMethod = findDefaultAbstractMethod(
					receiverType, selector, argumentTypes, invocationSite,
					classHierarchyStart, matchingMethod, found);
			if (interfaceMethod != null)
				return interfaceMethod;

			int argLength = argumentTypes.length;
			foundSize = found.size;
			nextMethod: for (int i = 0; i < foundSize; i++) {
				MethodBinding methodBinding = (MethodBinding) found
						.elementAt(i);
				TypeBinding[] params = methodBinding.parameters;
				int paramLength = params.length;
				nextArg: for (int a = 0; a < argLength; a++) {
					TypeBinding arg = argumentTypes[a];
					for (int p = 0; p < paramLength; p++)
						if (params[p] == arg)
							continue nextArg;
					continue nextMethod;
				}
				return methodBinding;
			}
			return (MethodBinding) found.elementAt(0); // no good match so just
														// use the first one
														// found
		}

		// visibility check
		int visiblesCount = 0;
		for (int i = 0; i < candidatesCount; i++) {
			MethodBinding methodBinding = candidates[i];
			if (methodBinding.canBeSeenBy(receiverType, invocationSite, this)) {
				if (visiblesCount != i) {
					candidates[i] = null;
					candidates[visiblesCount] = methodBinding;
				}
				visiblesCount++;
			}
		}
		if (visiblesCount == 1) {
			compilationUnitScope().recordTypeReferences(
					candidates[0].thrownExceptions);
			return candidates[0];
		}
		if (visiblesCount == 0) {
			MethodBinding interfaceMethod = findDefaultAbstractMethod(
					receiverType, selector, argumentTypes, invocationSite,
					classHierarchyStart, matchingMethod, found);
			if (interfaceMethod != null)
				return interfaceMethod;
			return new ProblemMethodBinding(candidates[0],
					candidates[0].selector, candidates[0].parameters,
					NotVisible);
		}
		if (candidates[0].declaringClass.isClass()) {
			return mostSpecificClassMethodBinding(candidates, visiblesCount);
		} else {
			return mostSpecificInterfaceMethodBinding(candidates, visiblesCount);
		}
	}

	// abstract method lookup lookup (since maybe missing default abstract
	// methods)
	public MethodBinding findDefaultAbstractMethod(
			ReferenceBinding receiverType, char[] selector,
			TypeBinding[] argumentTypes, InvocationSite invocationSite,
			ReferenceBinding classHierarchyStart, MethodBinding matchingMethod,
			ObjectVector found) {

		int startFoundSize = found.size;
		ReferenceBinding currentType = classHierarchyStart;
		while (currentType != null) {
			matchingMethod = findMethodInSuperInterfaces(currentType, selector,
					found, matchingMethod);
			currentType = currentType.superclass();
		}
		int foundSize = found.size;
		if (foundSize == startFoundSize)
			return matchingMethod; // maybe null

		MethodBinding[] candidates = new MethodBinding[foundSize
				- startFoundSize];
		int candidatesCount = 0;
		// argument type compatibility check
		for (int i = startFoundSize; i < foundSize; i++) {
			MethodBinding methodBinding = (MethodBinding) found.elementAt(i);
			if (areParametersAssignable(methodBinding.parameters, argumentTypes))
				candidates[candidatesCount++] = methodBinding;
		}
		if (candidatesCount == 1) {
			compilationUnitScope().recordTypeReferences(
					candidates[0].thrownExceptions);
			return candidates[0];
		}
		if (candidatesCount == 0) { // try to find a close match when the
									// parameter order is wrong or missing some
									// parameters
			int argLength = argumentTypes.length;
			nextMethod: for (int i = 0; i < foundSize; i++) {
				MethodBinding methodBinding = (MethodBinding) found
						.elementAt(i);
				TypeBinding[] params = methodBinding.parameters;
				int paramLength = params.length;
				nextArg: for (int a = 0; a < argLength; a++) {
					TypeBinding arg = argumentTypes[a];
					for (int p = 0; p < paramLength; p++)
						if (params[p] == arg)
							continue nextArg;
					continue nextMethod;
				}
				return methodBinding;
			}
			return (MethodBinding) found.elementAt(0); // no good match so just
														// use the first one
														// found
		}
		// no need to check for visibility - interface methods are public
		return mostSpecificInterfaceMethodBinding(candidates, candidatesCount);
	}

	public MethodBinding findMethodInSuperInterfaces(
			ReferenceBinding currentType, char[] selector, ObjectVector found,
			MethodBinding matchingMethod) {

		ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
		if (itsInterfaces != NoSuperInterfaces) {
			ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][];
			int lastPosition = -1;
			if (++lastPosition == interfacesToVisit.length)
				System
						.arraycopy(
								interfacesToVisit,
								0,
								interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
								0, lastPosition);
			interfacesToVisit[lastPosition] = itsInterfaces;

			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++) {
					currentType = interfaces[j];
					if ((currentType.tagBits & InterfaceVisited) == 0) {
						// if interface as not already been visited
						currentType.tagBits |= InterfaceVisited;

						MethodBinding[] currentMethods = currentType
								.getMethods(selector);
						int currentLength = currentMethods.length;
						if (currentLength == 1 && matchingMethod == null
								&& found.size == 0) {
							matchingMethod = currentMethods[0];
						} else if (currentLength > 0) {
							if (matchingMethod != null) {
								found.add(matchingMethod);
								matchingMethod = null;
							}
							found.addAll(currentMethods);
						}
						itsInterfaces = currentType.superInterfaces();
						if (itsInterfaces != NoSuperInterfaces) {
							if (++lastPosition == interfacesToVisit.length)
								System
										.arraycopy(
												interfacesToVisit,
												0,
												interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
												0, lastPosition);
							interfacesToVisit[lastPosition] = itsInterfaces;
						}
					}
				}
			}

			// bit reinitialization
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++)
					interfaces[j].tagBits &= ~InterfaceVisited;
			}
		}
		return matchingMethod;
	}

	// Internal use only
	public MethodBinding findMethodForArray(ArrayBinding receiverType,
			char[] selector, TypeBinding[] argumentTypes,
			InvocationSite invocationSite) {

		TypeBinding leafType = receiverType.leafComponentType();
		if (leafType instanceof ReferenceBinding) {
			if (!((ReferenceBinding) leafType).canBeSeenBy(this))
				return new ProblemMethodBinding(selector,
						MethodBinding.NoParameters,
						(ReferenceBinding) leafType, ReceiverTypeNotVisible);
		}

		ReferenceBinding object = getJavaLangObject();
		MethodBinding methodBinding = object.getExactMethod(selector,
				argumentTypes);
		if (methodBinding != null) {
			// handle the method clone() specially... cannot be protected or
			// throw exceptions
			if (argumentTypes == NoParameters
					&& CharOperation.equals(selector, CLONE))
				return new MethodBinding(
						(methodBinding.modifiers ^ AccProtected) | AccPublic,
						CLONE, methodBinding.returnType, argumentTypes, null,
						object);
			if (methodBinding.canBeSeenBy(receiverType, invocationSite, this))
				return methodBinding;
		}
		// answers closest approximation, may not check argumentTypes or
		// visibility
		methodBinding = findMethod(object, selector, argumentTypes,
				invocationSite);
		if (methodBinding == null)
			return new ProblemMethodBinding(selector, argumentTypes, NotFound);
		if (methodBinding.isValidBinding()) {
			if (!areParametersAssignable(methodBinding.parameters,
					argumentTypes))
				return new ProblemMethodBinding(methodBinding, selector,
						argumentTypes, NotFound);
			if (!methodBinding.canBeSeenBy(receiverType, invocationSite, this))
				return new ProblemMethodBinding(methodBinding, selector,
						methodBinding.parameters, NotVisible);
		}
		return methodBinding;
	}

	// Internal use only
	public ReferenceBinding findType(char[] typeName,
			PackageBinding declarationPackage, PackageBinding invocationPackage) {

		compilationUnitScope().recordReference(declarationPackage.compoundName,
				typeName);
		ReferenceBinding typeBinding = declarationPackage.getType(typeName);
		if (typeBinding == null)
			return null;

		if (typeBinding.isValidBinding()) {
			if (declarationPackage != invocationPackage
					&& !typeBinding.canBeSeenBy(invocationPackage))
				return new ProblemReferenceBinding(typeName, typeBinding,
						NotVisible);
		}
		return typeBinding;
	}

	public TypeBinding getBaseType(char[] name) {
		// list should be optimized (with most often used first)
		int length = name.length;
		if (length > 2 && length < 8) {
			switch (name[0]) {
			case 'i':
				if (length == 3 && name[1] == 'n' && name[2] == 't')
					return IntBinding;
				break;
			case 'v':
				if (length == 4 && name[1] == 'o' && name[2] == 'i'
						&& name[3] == 'd')
					return VoidBinding;
				break;
			case 'b':
				if (length == 7 && name[1] == 'o' && name[2] == 'o'
						&& name[3] == 'l' && name[4] == 'e' && name[5] == 'a'
						&& name[6] == 'n')
					return BooleanBinding;
				if (length == 4 && name[1] == 'y' && name[2] == 't'
						&& name[3] == 'e')
					return ByteBinding;
				break;
			case 'c':
				if (length == 4 && name[1] == 'h' && name[2] == 'a'
						&& name[3] == 'r')
					return CharBinding;
				break;
			case 'd':
				if (length == 6 && name[1] == 'o' && name[2] == 'u'
						&& name[3] == 'b' && name[4] == 'l' && name[5] == 'e')
					return DoubleBinding;
				break;
			case 'f':
				if (length == 5 && name[1] == 'l' && name[2] == 'o'
						&& name[3] == 'a' && name[4] == 't')
					return FloatBinding;
				break;
			case 'l':
				if (length == 4 && name[1] == 'o' && name[2] == 'n'
						&& name[3] == 'g')
					return LongBinding;
				break;
			case 's':
				if (length == 5 && name[1] == 'h' && name[2] == 'o'
						&& name[3] == 'r' && name[4] == 't')
					return ShortBinding;
			}
		}
		return null;
	}

	public final PackageBinding getCurrentPackage() {
		Scope scope, unitScope = this;
		while ((scope = unitScope.parent) != null)
			unitScope = scope;
		return ((CompilationUnitScope) unitScope).fPackage;
	}

	/**
	 * Returns the modifiers of the innermost enclosing declaration.
	 * 
	 * @return modifiers
	 */
	public int getDeclarationModifiers() {
		switch (this.kind) {
		case Scope.BLOCK_SCOPE:
		case Scope.METHOD_SCOPE:
			MethodScope methodScope = methodScope();
			if (!methodScope.isInsideInitializer()) {
				// check method modifiers to see if deprecated
				MethodBinding context = ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
				if (context != null) {
					return context.modifiers;
				}
			} else {
				SourceTypeBinding type = ((BlockScope) this).referenceType().binding;

				// inside field declaration ? check field modifier to see if
				// deprecated
				if (methodScope.initializedField != null) {
					return methodScope.initializedField.modifiers;
				}
				if (type != null) {
					return type.modifiers;
				}
			}
			break;
		case Scope.CLASS_SCOPE:
			ReferenceBinding context = ((ClassScope) this).referenceType().binding;
			if (context != null) {
				return context.modifiers;
			}
			break;
		}
		return -1;
	}

	public final ReferenceBinding getJavaIoSerializable() {
		compilationUnitScope().recordQualifiedReference(JAVA_IO_SERIALIZABLE);
		ReferenceBinding type = environment().getType(JAVA_IO_SERIALIZABLE);
		if (type != null)
			return type;

		problemReporter().isClassPathCorrect(JAVA_IO_SERIALIZABLE,
				referenceCompilationUnit());
		return null; // will not get here since the above error aborts the
						// compilation
	}

	public final ReferenceBinding getJavaLangClass() {
		compilationUnitScope().recordQualifiedReference(JAVA_LANG_CLASS);
		ReferenceBinding type = environment().getType(JAVA_LANG_CLASS);
		if (type != null)
			return type;

		problemReporter().isClassPathCorrect(JAVA_LANG_CLASS,
				referenceCompilationUnit());
		return null; // will not get here since the above error aborts the
						// compilation
	}

	public final ReferenceBinding getJavaLangCloneable() {
		compilationUnitScope().recordQualifiedReference(JAVA_LANG_CLONEABLE);
		ReferenceBinding type = environment().getType(JAVA_LANG_CLONEABLE);
		if (type != null)
			return type;

		problemReporter().isClassPathCorrect(JAVA_LANG_CLONEABLE,
				referenceCompilationUnit());
		return null; // will not get here since the above error aborts the
						// compilation
	}

	public final ReferenceBinding getJavaLangError() {
		compilationUnitScope().recordQualifiedReference(JAVA_LANG_ERROR);
		ReferenceBinding type = environment().getType(JAVA_LANG_ERROR);
		if (type != null)
			return type;

		problemReporter().isClassPathCorrect(JAVA_LANG_ERROR,
				referenceCompilationUnit());
		return null; // will not get here since the above error aborts the
						// compilation
	}

	public final ReferenceBinding getJavaLangAssertionError() {
		compilationUnitScope().recordQualifiedReference(
				JAVA_LANG_ASSERTIONERROR);
		ReferenceBinding type = environment().getType(JAVA_LANG_ASSERTIONERROR);
		if (type != null)
			return type;
		problemReporter().isClassPathCorrect(JAVA_LANG_ASSERTIONERROR,
				referenceCompilationUnit());
		return null; // will not get here since the above error aborts the
						// compilation
	}

	public final ReferenceBinding getJavaLangObject() {
		compilationUnitScope().recordQualifiedReference(JAVA_LANG_OBJECT);
		ReferenceBinding type = environment().getType(JAVA_LANG_OBJECT);
		if (type != null)
			return type;

		problemReporter().isClassPathCorrect(JAVA_LANG_OBJECT,
				referenceCompilationUnit());
		return null; // will not get here since the above error aborts the
						// compilation
	}

	public final ReferenceBinding getJavaLangRuntimeException() {
		compilationUnitScope().recordQualifiedReference(
				JAVA_LANG_RUNTIMEEXCEPTION);
		ReferenceBinding type = environment().getType(
				JAVA_LANG_RUNTIMEEXCEPTION);
		if (type != null)
			return type;

		problemReporter().isClassPathCorrect(JAVA_LANG_RUNTIMEEXCEPTION,
				referenceCompilationUnit());
		return null; // will not get here since the above error aborts the
						// compilation
	}

	public final ReferenceBinding getJavaLangString() {
		compilationUnitScope().recordQualifiedReference(JAVA_LANG_STRING);
		ReferenceBinding type = environment().getType(JAVA_LANG_STRING);
		if (type != null)
			return type;

		problemReporter().isClassPathCorrect(JAVA_LANG_STRING,
				referenceCompilationUnit());
		return null; // will not get here since the above error aborts the
						// compilation
	}

	public final ReferenceBinding getJavaLangThrowable() {
		compilationUnitScope().recordQualifiedReference(JAVA_LANG_THROWABLE);
		ReferenceBinding type = environment().getType(JAVA_LANG_THROWABLE);
		if (type != null)
			return type;

		// problemReporter().isClassPathCorrect(JAVA_LANG_THROWABLE,
		// referenceCompilationUnit());
		return null; // will not get here since the above error aborts the
						// compilation
	}

	/*
	 * Answer the type binding corresponding to the typeName argument, relative
	 * to the enclosingType.
	 */
	public final ReferenceBinding getMemberType(char[] typeName,
			ReferenceBinding enclosingType) {
		ReferenceBinding memberType = findMemberType(typeName, enclosingType);
		if (memberType != null)
			return memberType;
		return new ProblemReferenceBinding(typeName, NotFound);
	}

	/*
	 * Answer the type binding corresponding to the compoundName.
	 * 
	 * NOTE: If a problem binding is returned, senders should extract the
	 * compound name from the binding & not assume the problem applies to the
	 * entire compoundName.
	 */
	public final TypeBinding getType(char[][] compoundName) {
		int typeNameLength = compoundName.length;
		if (typeNameLength == 1) {
			// Would like to remove this test and require senders to specially
			// handle base types
			TypeBinding binding = getBaseType(compoundName[0]);
			if (binding != null)
				return binding;
		}

		compilationUnitScope().recordQualifiedReference(compoundName);
		Binding binding = getTypeOrPackage(compoundName[0],
				typeNameLength == 1 ? TYPE : TYPE | PACKAGE);
		if (binding == null)
			return new ProblemReferenceBinding(compoundName[0], NotFound);
		if (!binding.isValidBinding())
			return (ReferenceBinding) binding;

		int currentIndex = 1;
		boolean checkVisibility = false;
		if (binding instanceof PackageBinding) {
			PackageBinding packageBinding = (PackageBinding) binding;
			while (currentIndex < typeNameLength) {
				binding = packageBinding
						.getTypeOrPackage(compoundName[currentIndex++]); // does
																			// not
																			// check
																			// visibility
				if (binding == null)
					return new ProblemReferenceBinding(CharOperation.subarray(
							compoundName, 0, currentIndex), NotFound);
				if (!binding.isValidBinding())
					return new ProblemReferenceBinding(CharOperation.subarray(
							compoundName, 0, currentIndex), binding.problemId());
				if (!(binding instanceof PackageBinding))
					break;
				packageBinding = (PackageBinding) binding;
			}
			if (binding instanceof PackageBinding)
				return new ProblemReferenceBinding(CharOperation.subarray(
						compoundName, 0, currentIndex), NotFound);
			checkVisibility = true;
		}

		// binding is now a ReferenceBinding
		ReferenceBinding typeBinding = (ReferenceBinding) binding;
		compilationUnitScope().recordTypeReference(typeBinding); // to record
																	// supertypes
		if (checkVisibility) // handles the fall through case
			if (!typeBinding.canBeSeenBy(this))
				return new ProblemReferenceBinding(CharOperation.subarray(
						compoundName, 0, currentIndex), typeBinding, NotVisible);

		while (currentIndex < typeNameLength) {
			typeBinding = getMemberType(compoundName[currentIndex++],
					typeBinding);
			if (!typeBinding.isValidBinding())
				return new ProblemReferenceBinding(CharOperation.subarray(
						compoundName, 0, currentIndex), typeBinding.problemId());
		}
		return typeBinding;
	}

	/*
	 * Answer the type binding that corresponds the given name, starting the
	 * lookup in the receiver. The name provided is a simple source name (e.g.,
	 * "Object" , "Point", ...)
	 */
	// The return type of this method could be ReferenceBinding if we did not
	// answer base types.
	// NOTE: We could support looking for Base Types last in the search, however
	// any code using
	// this feature would be extraordinarily slow. Therefore we don't do this
	public final TypeBinding getType(char[] name) {
		// Would like to remove this test and require senders to specially
		// handle base types
		TypeBinding binding = getBaseType(name);
		if (binding != null)
			return binding;
		return (ReferenceBinding) getTypeOrPackage(name, TYPE);
	}

	// Added for code assist... NOT Public API
	public final Binding getTypeOrPackage(char[][] compoundName) {
		int nameLength = compoundName.length;
		if (nameLength == 1) {
			TypeBinding binding = getBaseType(compoundName[0]);
			if (binding != null)
				return binding;
		}
		Binding binding = getTypeOrPackage(compoundName[0], TYPE | PACKAGE);
		if (!binding.isValidBinding())
			return binding;

		int currentIndex = 1;
		boolean checkVisibility = false;
		if (binding instanceof PackageBinding) {
			PackageBinding packageBinding = (PackageBinding) binding;

			while (currentIndex < nameLength) {
				binding = packageBinding
						.getTypeOrPackage(compoundName[currentIndex++]);
				if (binding == null)
					return new ProblemReferenceBinding(CharOperation.subarray(
							compoundName, 0, currentIndex), NotFound);
				if (!binding.isValidBinding())
					return new ProblemReferenceBinding(CharOperation.subarray(
							compoundName, 0, currentIndex), binding.problemId());
				if (!(binding instanceof PackageBinding))
					break;
				packageBinding = (PackageBinding) binding;
			}
			if (binding instanceof PackageBinding)
				return binding;
			checkVisibility = true;
		}
		// binding is now a ReferenceBinding
		ReferenceBinding typeBinding = (ReferenceBinding) binding;
		if (checkVisibility) // handles the fall through case
			if (!typeBinding.canBeSeenBy(this))
				return new ProblemReferenceBinding(CharOperation.subarray(
						compoundName, 0, currentIndex), typeBinding, NotVisible);

		while (currentIndex < nameLength) {
			typeBinding = getMemberType(compoundName[currentIndex++],
					typeBinding);
			// checks visibility
			if (!typeBinding.isValidBinding())
				return new ProblemReferenceBinding(CharOperation.subarray(
						compoundName, 0, currentIndex), typeBinding.problemId());
		}
		return typeBinding;
	}

	/*
	 * Internal use only
	 */
	final Binding getTypeOrPackage(char[] name, int mask) {
		Scope scope = this;
		ReferenceBinding foundType = null;
		if ((mask & TYPE) == 0) {
			Scope next = scope;
			while ((next = scope.parent) != null)
				scope = next;
		} else {
			done: while (true) { // done when a COMPILATION_UNIT_SCOPE is
									// found
				switch (scope.kind) {
				case METHOD_SCOPE:
				case BLOCK_SCOPE:
					ReferenceBinding localType = ((BlockScope) scope)
							.findLocalType(name); // looks in this scope only
					if (localType != null) {
						if (foundType != null && foundType != localType)
							return new ProblemReferenceBinding(name,
									InheritedNameHidesEnclosingName);
						return localType;
					}
					break;
				case CLASS_SCOPE:
					SourceTypeBinding sourceType = ((ClassScope) scope).referenceContext.binding;
					// 6.5.5.1 - simple name favors member type over top-level
					// type in same unit
					ReferenceBinding memberType = findMemberType(name,
							sourceType);
					if (memberType != null) { // skip it if we did not find
												// anything
						if (memberType.problemId() == Ambiguous) {
							if (foundType == null
									|| foundType.problemId() == NotVisible)
								// supercedes any potential
								// InheritedNameHidesEnclosingName problem
								return memberType;
							else
								// make the user qualify the type, likely wants
								// the first inherited type
								return new ProblemReferenceBinding(name,
										InheritedNameHidesEnclosingName);
						}
						// if (memberType.isValidBinding()) {
						// if (sourceType == memberType.enclosingType()
						// || environment().options.complianceLevel >=
						// CompilerOptions.JDK1_4) {
						// // found a valid type in the 'immediate' scope (ie.
						// not inherited)
						// // OR in 1.4 mode (inherited shadows enclosing)
						// if (foundType == null)
						// return memberType;
						// if (foundType.isValidBinding())
						// // if a valid type was found, complain when another
						// is found in an 'immediate' enclosing type (ie. not
						// inherited)
						// if (foundType != memberType)
						// return new ProblemReferenceBinding(name,
						// InheritedNameHidesEnclosingName);
						// }
						// }
						if (foundType == null
								|| (foundType.problemId() == NotVisible && memberType
										.problemId() != NotVisible))
							// only remember the memberType if its the first one
							// found or the previous one was not visible &
							// memberType is...
							foundType = memberType;
					}
					if (CharOperation.equals(sourceType.sourceName, name)) {
						if (foundType != null && foundType != sourceType
								&& foundType.problemId() != NotVisible)
							return new ProblemReferenceBinding(name,
									InheritedNameHidesEnclosingName);
						return sourceType;
					}
					break;
				case COMPILATION_UNIT_SCOPE:
					break done;
				}
				scope = scope.parent;
			}
			if (foundType != null && foundType.problemId() != NotVisible)
				return foundType;
		}

		// at this point the scope is a compilation unit scope
		CompilationUnitScope unitScope = (CompilationUnitScope) scope;
		PackageBinding currentPackage = unitScope.fPackage;
		// ask for the imports + name
		if ((mask & TYPE) != 0) {
			// check single type imports.
			ImportBinding[] imports = unitScope.imports;
			if (imports != null) {
				// copy the list, since single type imports are removed if they
				// cannot be resolved
				for (int i = 0, length = imports.length; i < length; i++) {
					ImportBinding typeImport = imports[i];
					if (!typeImport.onDemand) {
						if (CharOperation
								.equals(
										typeImport.compoundName[typeImport.compoundName.length - 1],
										name)) {
							if (unitScope.resolveSingleTypeImport(typeImport) != null) {
								ImportReference importReference = typeImport.reference;
								if (importReference != null)
									importReference.used = true;
								return typeImport.resolvedImport; // already
																	// know its
																	// visible
							}
						}
					}
				}
			}
			// check if the name is in the current package, skip it if its a
			// sub-package
			unitScope.recordReference(currentPackage.compoundName, name);
			Binding binding = currentPackage.getTypeOrPackage(name);
			if (binding instanceof ReferenceBinding)
				return binding; // type is always visible to its own package

			// check on demand imports
			boolean foundInImport = false;
			ReferenceBinding type = null;
			if (imports != null) {
				for (int i = 0, length = imports.length; i < length; i++) {
					ImportBinding someImport = imports[i];
					if (someImport.onDemand) {
						Binding resolvedImport = someImport.resolvedImport;
						ReferenceBinding temp = resolvedImport instanceof PackageBinding ? findType(
								name, (PackageBinding) resolvedImport,
								currentPackage)
								: findDirectMemberType(name,
										(ReferenceBinding) resolvedImport);
						if (temp != null && temp.isValidBinding()) {
							// ImportReference importReference =
							// someImport.reference;
							// if (importReference != null) importReference.used
							// = true;
							if (foundInImport)
								// Answer error binding -- import on demand
								// conflict; name found in two import on demand
								// packages.
								return new ProblemReferenceBinding(name,
										Ambiguous);
							type = temp;
							foundInImport = true;
						}
					}
				}
			}
			if (type != null)
				return type;
		}

		unitScope.recordSimpleReference(name);
		if ((mask & PACKAGE) != 0) {
			PackageBinding packageBinding = unitScope.environment
					.getTopLevelPackage(name);
			if (packageBinding != null)
				return packageBinding;
		}

		// Answer error binding -- could not find name
		if (foundType != null)
			return foundType; // problem type from above
		return new ProblemReferenceBinding(name, NotFound);
	}

	/*
	 * Answer whether the type is defined in the same compilation unit as the
	 * receiver
	 */
	public final boolean isDefinedInSameUnit(ReferenceBinding type) {
		// find the outer most enclosing type
		ReferenceBinding enclosingType = type;
		while ((type = enclosingType.enclosingType()) != null)
			enclosingType = type;

		// find the compilation unit scope
		Scope scope, unitScope = this;
		while ((scope = unitScope.parent) != null)
			unitScope = scope;

		// test that the enclosingType is not part of the compilation unit
		SourceTypeBinding[] topLevelTypes = ((CompilationUnitScope) unitScope).topLevelTypes;
		for (int i = topLevelTypes.length; --i >= 0;)
			if (topLevelTypes[i] == enclosingType)
				return true;
		return false;
	}

	/*
	 * Answer true if the scope is nested inside a given field declaration.
	 * Note: it works as long as the scope.fieldDeclarationIndex is reflecting
	 * the field being traversed e.g. during name resolution.
	 */
	public final boolean isDefinedInField(FieldBinding field) {
		Scope scope = this;
		do {
			if (scope instanceof MethodScope) {
				MethodScope methodScope = (MethodScope) scope;
				ReferenceContext refContext = methodScope.referenceContext;
				if (refContext instanceof TypeDeclaration
						&& ((TypeDeclaration) refContext).binding == field.declaringClass
						&& methodScope.fieldDeclarationIndex == field.id) {
					return true;
				}
			}
			scope = scope.parent;
		} while (scope != null);
		return false;
	}

	/*
	 * Answer true if the scope is nested inside a given method declaration
	 */
	public final boolean isDefinedInMethod(MethodBinding method) {
		Scope scope = this;
		do {
			if (scope instanceof MethodScope) {
				ReferenceContext refContext = ((MethodScope) scope).referenceContext;
				if (refContext instanceof AbstractMethodDeclaration
						&& ((AbstractMethodDeclaration) refContext).binding == method) {
					return true;
				}
			}
			scope = scope.parent;
		} while (scope != null);
		return false;
	}

	/*
	 * Answer true if the scope is nested inside a given type declaration
	 */
	public final boolean isDefinedInType(ReferenceBinding type) {
		Scope scope = this;
		do {
			if (scope instanceof ClassScope)
				if (((ClassScope) scope).referenceContext.binding == type) {
					return true;
				}
			scope = scope.parent;
		} while (scope != null);
		return false;
	}

	public boolean isInsideDeprecatedCode() {
		switch (kind) {
		case Scope.BLOCK_SCOPE:
		case Scope.METHOD_SCOPE:
			MethodScope methodScope = methodScope();
			if (!methodScope.isInsideInitializer()) {
				// check method modifiers to see if deprecated
				MethodBinding context = ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
				if (context != null && context.isViewedAsDeprecated()) {
					return true;
				}
			} else {
				SourceTypeBinding type = ((BlockScope) this).referenceType().binding;

				// inside field declaration ? check field modifier to see if
				// deprecated
				if (methodScope.fieldDeclarationIndex != MethodScope.NotInFieldDecl) {
					for (int i = 0; i < type.fields.length; i++) {
						if (type.fields[i].id == methodScope.fieldDeclarationIndex) {
							// currently inside this field initialization
							if (type.fields[i].isViewedAsDeprecated()) {
								return true;
							}
							break;
						}
					}
				}
				if (type != null && type.isViewedAsDeprecated()) {
					return true;
				}
			}
			break;
		case Scope.CLASS_SCOPE:
			ReferenceBinding context = ((ClassScope) this).referenceType().binding;
			if (context != null && context.isViewedAsDeprecated()) {
				return true;
			}
			break;
		}
		return false;
	}

	public final boolean isJavaIoSerializable(TypeBinding tb) {
		return tb == getJavaIoSerializable();
	}

	public final boolean isJavaLangCloneable(TypeBinding tb) {
		return tb == getJavaLangCloneable();
	}

	public final boolean isJavaLangObject(TypeBinding type) {
		return type.id == T_JavaLangObject;
	}

	public final MethodScope methodScope() {
		Scope scope = this;
		do {
			if (scope instanceof MethodScope)
				return (MethodScope) scope;
			scope = scope.parent;
		} while (scope != null);
		return null;
	}

	// Internal use only
	/*
	 * All methods in visible are acceptable matches for the method in
	 * question... The methods defined by the receiver type appear before those
	 * defined by its superclass and so on. We want to find the one which
	 * matches best.
	 * 
	 * Since the receiver type is a class, we know each method's declaring class
	 * is either the receiver type or one of its superclasses. It is an error if
	 * the best match is defined by a superclass, when a lesser match is defined
	 * by the receiver type or a closer superclass.
	 */
	protected final MethodBinding mostSpecificClassMethodBinding(
			MethodBinding[] visible, int visibleSize) {

		MethodBinding method = null;
		MethodBinding previous = null;

		nextVisible: for (int i = 0; i < visibleSize; i++) {
			method = visible[i];

			if (previous != null
					&& method.declaringClass != previous.declaringClass)
				break; // cannot answer a method farther up the hierarchy than
						// the first method found
			previous = method;
			for (int j = 0; j < visibleSize; j++) {
				if (i == j)
					continue;
				MethodBinding next = visible[j];
				if (!areParametersAssignable(next.parameters, method.parameters))
					continue nextVisible;
			}
			compilationUnitScope()
					.recordTypeReferences(method.thrownExceptions);
			return method;
		}
		return new ProblemMethodBinding(visible[0].selector,
				visible[0].parameters, Ambiguous);
	}

	// Internal use only
	/*
	 * All methods in visible are acceptable matches for the method in
	 * question... Since the receiver type is an interface, we ignore the
	 * possibility that 2 inherited but unrelated superinterfaces may define the
	 * same method in acceptable but not identical ways... we just take the best
	 * match that we find since any class which implements the receiver
	 * interface MUST implement all signatures for the method... in which case
	 * the best match is correct.
	 * 
	 * NOTE: This is different than javac... in the following example, the
	 * message send of bar(X) in class Y is supposed to be ambiguous. But any
	 * class which implements the interface I MUST implement both signatures for
	 * bar. If this class was the receiver of the message send instead of the
	 * interface I, then no problem would be reported.
	 * 
	 * interface I1 { void bar(J j); } interface I2 { // void bar(J j); void
	 * bar(Object o); } interface I extends I1, I2 {} interface J {}
	 * 
	 * class X implements J {}
	 * 
	 * class Y extends X { public void foo(I i, X x) { i.bar(x); } }
	 */
	protected final MethodBinding mostSpecificInterfaceMethodBinding(
			MethodBinding[] visible, int visibleSize) {
		MethodBinding method = null;
		nextVisible: for (int i = 0; i < visibleSize; i++) {
			method = visible[i];
			for (int j = 0; j < visibleSize; j++) {
				if (i == j)
					continue;
				MethodBinding next = visible[j];
				if (!areParametersAssignable(next.parameters, method.parameters))
					continue nextVisible;
			}
			compilationUnitScope()
					.recordTypeReferences(method.thrownExceptions);
			return method;
		}
		return new ProblemMethodBinding(visible[0].selector,
				visible[0].parameters, Ambiguous);
	}

	public final ClassScope outerMostClassScope() {
		ClassScope lastClassScope = null;
		Scope scope = this;
		do {
			if (scope instanceof ClassScope)
				lastClassScope = (ClassScope) scope;
			scope = scope.parent;
		} while (scope != null);
		return lastClassScope; // may answer null if no class around
	}

	public final MethodScope outerMostMethodScope() {
		MethodScope lastMethodScope = null;
		Scope scope = this;
		do {
			if (scope instanceof MethodScope)
				lastMethodScope = (MethodScope) scope;
			scope = scope.parent;
		} while (scope != null);
		return lastMethodScope; // may answer null if no method around
	}

	public final CompilationUnitDeclaration referenceCompilationUnit() {
		Scope scope, unitScope = this;
		while ((scope = unitScope.parent) != null)
			unitScope = scope;
		return ((CompilationUnitScope) unitScope).referenceContext;
	}

	// start position in this scope - for ordering scopes vs. variables
	int startIndex() {
		return 0;
	}
}
