content: /*******************************************************************************
   * Copyright (c) 2000, 2015 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
   *     Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
   *       bug "inline method - doesn't handle implicit cast" (see
   *       https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.corext.dom;
  
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.wst.jsdt.core.IFunction;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.IType;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.core.Signature;
  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.Assignment;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.FieldAccess;
  import org.eclipse.wst.jsdt.core.dom.IBinding;
  import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
  import org.eclipse.wst.jsdt.core.dom.IPackageBinding;
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  import org.eclipse.wst.jsdt.core.dom.IVariableBinding;
  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.StructuralPropertyDescriptor;
  import org.eclipse.wst.jsdt.core.dom.SuperFieldAccess;
  import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
  /**
  *
  * 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 Bindings {
  	
  	public static final String ARRAY_LENGTH_FIELD_BINDING_STRING= "(array type):length";//$NON-NLS-1$
  	private Bindings() {
  		// No instance
  	}
  
  	/**
  	 * Checks if the two bindings are equals. First an identity check is
  	 * made an then the key of the bindings are compared. 
  	 * @param b1 first binding treated as <code>this</code>. So it must
  	 *  not be <code>null</code>
  	 * @param b2 the second binding.
  	 * @return boolean
  	 */
  	public static boolean equals(IBinding b1, IBinding b2) {
  		return b1.isEqualTo(b2);
  	}
  
  	/**
  	 * Checks if the two arrays of bindings have the same length and
  	 * their elements are equal. Uses
  	 * <code>Bindings.equals(IBinding, IBinding)</code> to compare.
  	 * @param b1 the first array of bindings. Must not be <code>null</code>.
  	 * @param b2 the second array of bindings.
  	 * @return boolean
  	 */
  	public static boolean equals(IBinding[] b1, IBinding[] b2) {
  		Assert.isNotNull(b1);
  		if (b1 == b2)
  			return true;
  		if (b2 == null)
  			return false;		
  		if (b1.length != b2.length)
  			return false;
  		for (int i= 0; i < b1.length; i++) {
  			if (! Bindings.equals(b1[i], b2[i]))
  				return false;
  		}
  		return true;
  	}
  	
  	public static int hashCode(IBinding binding){
  		Assert.isNotNull(binding);
  		String key= binding.getKey();
  		if (key == null)
  			return binding.hashCode();
  		return key.hashCode();
  	}
  	
  	/**
  	 * Note: this method is for debugging and testing purposes only.
  	 * There are tests whose pre-computed test results rely on the returned String's format.
  	 * @see org.eclipse.wst.jsdt.internal.ui.viewsupport.BindingLabelProvider
  	 */
  	public static String asString(IBinding binding) {
  		if (binding instanceof IFunctionBinding)
  			return asString((IFunctionBinding)binding);
  		else if (binding instanceof ITypeBinding)
  			return ((ITypeBinding)binding).getQualifiedName();
  		else if (binding instanceof IVariableBinding)
  			return asString((IVariableBinding)binding);
  		return binding.toString();
  	}
  
  	private static String asString(IVariableBinding variableBinding) {
  		if (! variableBinding.isField())
  			return variableBinding.toString();
  		if (variableBinding.getDeclaringClass() == null) {
  			Assert.isTrue(variableBinding.getName().equals("length"));//$NON-NLS-1$
  			return ARRAY_LENGTH_FIELD_BINDING_STRING;
  		}
  		StringBuffer result= new StringBuffer();
  		result.append(variableBinding.getDeclaringClass().getName());
  		result.append(':');
  		result.append(variableBinding.getName());				
  		return result.toString();		
  	}
  
  	private static String asString(IFunctionBinding method) {
  		StringBuffer result= new StringBuffer();
  		result.append(method.getDeclaringClass().getName());
  		result.append(':');
  		result.append(method.getName());
  		result.append('(');
  		ITypeBinding[] parameters= method.getParameterTypes();
  		int lastComma= parameters.length - 1;
  		for (int i= 0; i < parameters.length; i++) {
  			ITypeBinding parameter= parameters[i];
  			result.append(parameter.getName());
  			if (i < lastComma)
  				result.append(", "); //$NON-NLS-1$
  		}
  		result.append(')');
  		return result.toString();
  	}
  	
  	public static String getTypeQualifiedName(ITypeBinding type) {
  		List result= new ArrayList(5);
  		createName(type, false, result);
  		
  		StringBuffer buffer= new StringBuffer();
  		for (int i= 0; i < result.size(); i++) {
  			if (i > 0) {
  				buffer.append('.');
  			}
  			buffer.append(((String) result.get(i)));
  		}
  		return buffer.toString();
  	}
  
  	/**
  	 * Returns the fully qualified name of the specified type binding.
  	 * <p>
  	 * If the binding resolves to a generic type, the fully qualified name of the raw type is returned.
  	 * 
  	 * @param type the type binding to get its fully qualified name
  	 * @return the fully qualified name
  	 */
  	public static String getFullyQualifiedName(ITypeBinding type) {
  		String name= type.getQualifiedName();
  		final int index= name.indexOf('<');
  		if (index > 0)
  			name= name.substring(0, index);
  		return name;
  	}
  
  	public static String getImportName(IBinding binding) {
  		ITypeBinding declaring= null;
  		switch (binding.getKind()) {
  			case IBinding.TYPE:
  				return getRawQualifiedName((ITypeBinding) binding);
  			case IBinding.PACKAGE:
  				return binding.getName() + ".*"; //$NON-NLS-1$
  			case IBinding.METHOD:
  				declaring= ((IFunctionBinding) binding).getDeclaringClass();
  				break;
  			case IBinding.VARIABLE:
  				declaring= ((IVariableBinding) binding).getDeclaringClass();
  				if (declaring == null) {
  					return binding.getName(); // array.length
  				}
  				
  				break;
  			default:
  				return binding.getName();
  		}
  		return JavaModelUtil.concatenateName(getRawQualifiedName(declaring), binding.getName());
  	}	
  	
  	
  	private static void createName(ITypeBinding type, boolean includePackage, List list) {
  		ITypeBinding baseType= type;
  		if (type.isArray()) {
  			baseType= type.getElementType();
  		}
  		if (!baseType.isPrimitive() && !baseType.isNullType()) {
  			ITypeBinding declaringType= baseType.getDeclaringClass();
  			if (declaringType != null) {
  				createName(declaringType, includePackage, list);
  			} else if (includePackage && !baseType.getPackage().isUnnamed()) {
  				String[] components= baseType.getPackage().getNameComponents();
  				for (int i= 0; i < components.length; i++) {
  					list.add(components[i]);
  				}
  			}
  		}
  		if (!baseType.isAnonymous()) {
  			list.add(type.getName());
  		} else {
  			list.add("$local$"); //$NON-NLS-1$
  		}		
  	}	
  	
  	
  	public static String[] getNameComponents(ITypeBinding type) {
  		List result= new ArrayList(5);
  		createName(type, false, result);
  		return (String[]) result.toArray(new String[result.size()]);
  	}
  	
  	public static String[] getAllNameComponents(ITypeBinding type) {
  		List result= new ArrayList(5);
  		createName(type, true, result);
  		return (String[]) result.toArray(new String[result.size()]);
  	}
  	
  	public static ITypeBinding getTopLevelType(ITypeBinding type) {
  		ITypeBinding parent= type.getDeclaringClass();
  		while (parent != null) {
  			type= parent;
  			parent= type.getDeclaringClass();
  		}
  		return type;
  	}
  	
  	/**
  	 * Checks whether the passed type binding is a runtime exception.
  	 * 
  	 * @param thrownException the type binding
  	 * 
  	 * @return <code>true</code> if the passed type binding is a runtime exception;
  	 * 	otherwise <code>false</code> is returned
  	 */
  	public static boolean isRuntimeException(ITypeBinding thrownException) {
  		if (thrownException == null || thrownException.isPrimitive() || thrownException.isArray())
  			return false;
  		return findTypeInHierarchy(thrownException, "java.lang.RuntimeException") != null; //$NON-NLS-1$
  	}
  	
  	/**
  	 * Finds the field specified by <code>fieldName<code> in
  	 * the given <code>type</code>. Returns <code>null</code> if no such field exits.
  	 * @param type the type to search the field in
  	 * @param fieldName the field name
  	 * @return the binding representing the field or <code>null</code>
  	 */
  	public static IVariableBinding findFieldInType(ITypeBinding type, String fieldName) {
  		if (type.isPrimitive())
  			return null;
  		IVariableBinding[] fields= type.getDeclaredFields();
  		for (int i= 0; i < fields.length; i++) {
  			IVariableBinding field= fields[i];
  			if (field.getName().equals(fieldName))
  				return field;
  		}
  		return null;
  	}
  	
  	/**
  	 * Finds the field specified by <code>fieldName</code> in
  	 * the type hierarchy denoted by the given type. Returns <code>null</code> if no such field
  	 * exists. If the field is defined in more than one super type only the first match is 
  	 * returned. First the super class is examined and than the implemented interfaces.
  	 * @param type The type to search the field in
  	 * @param fieldName The name of the field to find
  	 * @return the variable binding representing the field
  	 */
  	public static IVariableBinding findFieldInHierarchy(ITypeBinding type, String fieldName) {
  		IVariableBinding field= findFieldInType(type, fieldName);
  		if (field != null)
  			return field;
  		ITypeBinding superClass= type.getSuperclass();
  		if (superClass != null) {
  			field= findFieldInHierarchy(superClass, fieldName);
  			if (field != null)
  				return field;			
  		}
  		return null;
  	}
  		
  	/**
  	 * Finds the method specified by <code>methodName<code> and </code>parameters</code> in
  	 * the given <code>type</code>. Returns <code>null</code> if no such method exits.
  	 * @param type The type to search the method in
  	 * @param methodName The name of the method to find
  	 * @param parameters The parameter types of the method to find. If <code>null</code> is passed, only 
  	 *  the name is matched and parameters are ignored.
  	 * @return the method binding representing the method
  	 */
  	public static IFunctionBinding findMethodInType(ITypeBinding type, String methodName, ITypeBinding[] parameters) {
  		if (type.isPrimitive())
  			return null;
  		IFunctionBinding[] methods= type.getDeclaredMethods();
  		for (int i= 0; i < methods.length; i++) {
  			if (parameters == null) {
  				if (methodName.equals(methods[i].getName()))
  					return methods[i];
  			} else {
  				if (isEqualMethod(methods[i], methodName, parameters))
  					return methods[i];
  			}
  		}
  		return null;
  	}
  	
  	/**
  	 * Finds the method specified by <code>methodName</code> and </code>parameters</code> in
  	 * the type hierarchy denoted by the given type. Returns <code>null</code> if no such method
  	 * exists. If the method is defined in more than one super type only the first match is 
  	 * returned. First the super class is examined and than the implemented interfaces.
  	 * @param type The type to search the method in
  	 * @param methodName The name of the method to find
  	 * @param parameters The parameter types of the method to find. If <code>null</code> is passed, only the name is matched and parameters are ignored.
  	 * @return the method binding representing the method
  	 */
  	public static IFunctionBinding findMethodInHierarchy(ITypeBinding type, String methodName, ITypeBinding[] parameters) {
  		if (type==null)
  			return null;
  		IFunctionBinding method= findMethodInType(type, methodName, parameters);
  		if (method != null)
  			return method;
  		ITypeBinding superClass= type.getSuperclass();
  		if (superClass != null) {
  			method= findMethodInHierarchy(superClass, methodName, parameters);
  			if (method != null)
  				return method;			
  		}
  		return null;
  	}
  	
  	/**
  	 * Finds the method specified by <code>methodName<code> and </code>parameters</code> in
  	 * the given <code>type</code>. Returns <code>null</code> if no such method exits.
  	 * @param type The type to search the method in
  	 * @param methodName The name of the method to find
  	 * @param parameters The parameter types of the method to find. If <code>null</code> is passed, only the name is matched and parameters are ignored.
  	 * @return the method binding representing the method
  	 */
  	public static IFunctionBinding findMethodInType(ITypeBinding type, String methodName, String[] parameters) {
  		if (type.isPrimitive())
  			return null;
  		IFunctionBinding[] methods= type.getDeclaredMethods();
  		for (int i= 0; i < methods.length; i++) {
  			if (parameters == null) {
  				if (methodName.equals(methods[i].getName()))
  					return methods[i];
  			} else {
  				if (isEqualMethod(methods[i], methodName, parameters))
  					return methods[i];
  			}
  		}
  		return null;
  	}
  	
  	/**
  	 * Finds the method specified by <code>methodName</code> and </code>parameters</code> in
  	 * the type hierarchy denoted by the given type. Returns <code>null</code> if no such method
  	 * exists. If the method is defined in more than one super type only the first match is 
  	 * returned. First the super class is examined and than the implemented interfaces.
  	 * @param type the type to search the method in
  	 * @param methodName The name of the method to find
  	 * @param parameters The parameter types of the method to find. If <code>null</code> is passed, only the name is matched and parameters are ignored.
  	 * @return the method binding representing the method
  	 */
  	public static IFunctionBinding findMethodInHierarchy(ITypeBinding type, String methodName, String[] parameters) {
  		IFunctionBinding method= findMethodInType(type, methodName, parameters);
  		if (method != null)
  			return method;
  		ITypeBinding superClass= type.getSuperclass();
  		if (superClass != null) {
  			method= findMethodInHierarchy(superClass, methodName, parameters);
  			if (method != null)
  				return method;			
  		}
  		return null;
  	}
  	
  	/**
  	 * Finds the method in the given <code>type</code> that is overridden by the specified <code>method<code>.
  	 * Returns <code>null</code> if no such method exits.
  	 * @param type The type to search the method in
  	 * @param method The specified method that would override the result
  	 * @return the method binding of the method that is overridden by the specified <code>method<code>, or <code>null</code>
  	 */
  	public static IFunctionBinding findOverriddenMethodInType(ITypeBinding type, IFunctionBinding method) {
  		IFunctionBinding[] methods= type.getDeclaredMethods();
  		for (int i= 0; i < methods.length; i++) {
  			if (isSubsignature(method, methods[i]))
  				return methods[i];
  		}
  		return null;
  	}
  	
  	/**
  	 * Finds a method in the hierarchy of <code>type</code> that is overridden by </code>binding</code>.
  	 * Returns <code>null</code> if no such method exists. If the method is defined in more than one super type only the first match is 
  	 * returned. First the super class is examined and than the implemented interfaces.
  	 * @param type The type to search the method in
  	 * @param binding The method that overrides
  	 * @return the method binding overridden the method
  	 */
  	public static IFunctionBinding findOverriddenMethodInHierarchy(ITypeBinding type, IFunctionBinding binding) {
  		IFunctionBinding method= findOverriddenMethodInType(type, binding);
  		if (method != null)
  			return method;
  		ITypeBinding superClass= type.getSuperclass();
  		if (superClass != null) {
  			method= findOverriddenMethodInHierarchy(superClass, binding);
  			if (method != null)
  				return method;			
  		}
  		return null;
  	}
  	
  	
  	/**
  	 * Finds the method that is overridden by the given method. The search is bottom-up, so this
  	 * returns the nearest defining/declaring method.
  	 * @param overriding overriding method
  	 * @param testVisibility If true the result is tested on visibility. Null is returned if the method is not visible.
  	 * @return the method binding representing the method
  	 */
  	public static IFunctionBinding findOverriddenMethod(IFunctionBinding overriding, boolean testVisibility) {
  		int modifiers= overriding.getModifiers();
  		if (Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || overriding.isConstructor()) {
  			return null;
  		}
  		
  		ITypeBinding type= overriding.getDeclaringClass();
  		ITypeBinding superType = type != null ? type.getSuperclass() : null;
  		if (superType != null) {
  			IFunctionBinding res= findOverriddenMethodInHierarchy(superType, overriding);
  			if (res != null && !Modifier.isPrivate(res.getModifiers())) {
  				if (!testVisibility || isVisibleInHierarchy(res, overriding.getDeclaringClass().getPackage())) {
  					return res;
  				}
  			}
  		}
  		return null;
  	}
  	
  	
  	public static boolean isVisibleInHierarchy(IFunctionBinding member, IPackageBinding pack) {
  		int otherflags= member.getModifiers();
  		if (Modifier.isPublic(otherflags) || Modifier.isProtected(otherflags)) {
  			return true;
  		} else if (Modifier.isPrivate(otherflags)) {
  			return false;
  		}		
  
  		ITypeBinding declaringType= member.getDeclaringClass();
  		return declaringType != null && pack == declaringType.getPackage();
  	}
  		
  	/**
  	 * Returns all super types (classes and interfaces) for the given type.
  	 * @param type The type to get the supertypes of.
  	 * @return all super types (excluding <code>type</code>)
  	 */
  	public static ITypeBinding[] getAllSuperTypes(ITypeBinding type) {
  		Set result= new HashSet();
  		collectSuperTypes(type, result);
  		result.remove(type);
  		return (ITypeBinding[]) result.toArray(new ITypeBinding[result.size()]);
  	}
  	
  	private static void collectSuperTypes(ITypeBinding curr, Set collection) {
  		if (collection.add(curr)) {
  			ITypeBinding superClass= curr.getSuperclass();
  			if (superClass != null) {
  				collectSuperTypes(superClass, collection);
  			}
  		}
  	}
  
  	/**
  	 * Method to visit a type hierarchy defined by a given type.
  	 * 
  	 * @param type the type which hierarchy is to be visited
  	 * @param visitor the visitor
  	 * @return <code>false</code> if the visiting got interrupted
  	 */
  	public static boolean visitHierarchy(ITypeBinding type, TypeBindingVisitor visitor) {
  		boolean result= visitSuperclasses(type, visitor);
  		return result;
  	}
  
  	/**
  	 * Method to visit a super class hierarchy defined by a given type.
  	 * 
  	 * @param type the type which super class hierarchy is to be visited
  	 * @param visitor the visitor
  	 * @return <code>false</code> if the visiting got interrupted
  	 */
  	public static boolean visitSuperclasses(ITypeBinding type, TypeBindingVisitor visitor) {
  		while ((type= type.getSuperclass()) != null) {
  			if (!visitor.visit(type)) {
  				return false;
  			}
  		}
  		return true;
  	}
  
  	/**
  	 * Tests whether the two methods are erasure-equivalent.
  	 * @deprecated use {@link #isSubsignature(IFunctionBinding, IFunctionBinding)}
  	 */
  	//TODO: rename to isErasureEquivalentMethod and change to two IFunctionBinding parameters
  	public static boolean isEqualMethod(IFunctionBinding method, String methodName, ITypeBinding[] parameters) {
  		if (!method.getName().equals(methodName))
  			return false;
  			
  		ITypeBinding[] methodParameters= method.getParameterTypes();
  		if (methodParameters.length != parameters.length)
  			return false;
  		for (int i= 0; i < parameters.length; i++) {
  			if (!equals(methodParameters[i].getErasure(), parameters[i].getErasure()))
  				return false;
  		}
  		//Can't use this fix, since some clients assume that this method tests erasure equivalence:
  //		if (method.getTypeParameters().length == 0) {
  //			//a method without type parameters cannot be overridden by one that declares type parameters -> can be exact here
  //			for (int i= 0; i < parameters.length; i++) {
  //				if ( ! (equals(methodParameters[i], parameters[i])
  //						|| equals(methodParameters[i].getErasure(), parameters[i]))) // subsignature
  //					return false;
  //			}
  //		} else {
  //			//this will find all overridden methods, but may generate false positives in some cases:
  //			for (int i= 0; i < parameters.length; i++) {
  //				if (!equals(methodParameters[i].getErasure(), parameters[i].getErasure()))
  //					return false;
  //			}
  //		}
  		return true;
  	}
  
  	/**
  	 * @param overriding overriding method (m1)
  	 * @param overridden overridden method (m2)
  	 * @return <code>true</code> iff the method <code>m1</code> is a subsignature of the method <code>m2</code>.
  	 * 		This is one of the requirements for m1 to override m2.
  	 * 		Accessibility and return types are not taken into account.
  	 * 		Note that subsignature is <em>not</em> symmetric!
  	 */
  	public static boolean isSubsignature(IFunctionBinding overriding, IFunctionBinding overridden) {
  		//TODO: use IFunctionBinding#isSubsignature(..) once it is tested and fixed (only erasure of m1's parameter types, considering type variable counts, doing type variable substitution		
  		if (!overriding.getName().equals(overridden.getName()))
  			return false;
  			
  		ITypeBinding[] m1Params= overriding.getParameterTypes();
  		ITypeBinding[] m2Params= overridden.getParameterTypes();
  		if (m1Params.length != m2Params.length)
  			return false;
  	
  		// m1TypeParams.length == m2TypeParams.length == 0  
  		if (equals(m1Params, m2Params))
  			return true;
  		for (int i= 0; i < m1Params.length; i++) {
  			ITypeBinding m1Param= m1Params[i];
  			if (! (equals(m1Param, m2Params[i].getErasure()))) // can erase m2
  				return false;
  		}
  		return true;
  		
  	}
  
  	/**
  	 * @param method
  	 * @param methodName
  	 * @param parameters
  	 * @return <code>true</code> iff the method
  	 * 		m1 (with name <code>methodName</code> and method parameters <code>parameters</code>)
  	 * 		is a subsignature of the method <code>m2</code>. Accessibility and return types are not taken into account.
  	 */
  	public static boolean isEqualMethod(IFunctionBinding method, String methodName, String[] parameters) {
  		if (!method.getName().equals(methodName))
  			return false;
  
  		ITypeBinding[] methodParameters= method.getParameterTypes();
  		if (methodParameters.length != parameters.length)
  			return false;
  		String first, second;
  		int index;
  		for (int i= 0; i < parameters.length; i++) {
  			first= parameters[i];
  			index= first.indexOf('<');
  			if (index > 0)
  				first= first.substring(0, index);
  			second= methodParameters[i].getErasure().getQualifiedName();
  			index= second.indexOf('<');
  			if (index > 0)
  				second= second.substring(0, index);
  			if (!first.equals(second))
  				return false;
  		}
  		return true;
  	}
  
  	/**
  	 * Finds a type binding for a given fully qualified type in the hierarchy of a type.
  	 * Returns <code>null</code> if no type binding is found.
  	 * @param hierarchyType the binding representing the hierarchy
  	 * @param fullyQualifiedTypeName the fully qualified name to search for
  	 * @return the type binding
  	 */
  	public static ITypeBinding findTypeInHierarchy(ITypeBinding hierarchyType, String fullyQualifiedTypeName) {
  		if (hierarchyType.isArray() || hierarchyType.isPrimitive()) {
  			return null;
  		}
  		if (fullyQualifiedTypeName.equals(hierarchyType.getQualifiedName())) {
  			return hierarchyType;
  		}
  		ITypeBinding superClass= hierarchyType.getSuperclass();
  		if (superClass != null) {
  			ITypeBinding res= findTypeInHierarchy(superClass, fullyQualifiedTypeName);
  			if (res != null) {
  				return res;
  			}
  		}
  		return null;
  	}
  	
  	/**
  	 * Returns the binding of the variable written in an Assignment.
  	 * @param assignment The assignment 
  	 * @return The binding or <code>null</code> if no bindings are available.
  	 */
  	public static IVariableBinding getAssignedVariable(Assignment assignment) {
  		Expression leftHand = assignment.getLeftHandSide();
  		switch (leftHand.getNodeType()) {
  			case ASTNode.SIMPLE_NAME:
  				return (IVariableBinding) ((SimpleName) leftHand).resolveBinding();
  			case ASTNode.QUALIFIED_NAME:
  				return (IVariableBinding) ((QualifiedName) leftHand).getName().resolveBinding();				
  			case ASTNode.FIELD_ACCESS:
  				return ((FieldAccess) leftHand).resolveFieldBinding();
  			case ASTNode.SUPER_FIELD_ACCESS:
  				return ((SuperFieldAccess) leftHand).resolveFieldBinding();
  			default:
  				return null;
  		}
  	}
  	
  	/**
  	 * Returns <code>true</code> if the given type is a super type of a candidate.
  	 * <code>true</code> is returned if the two type bindings are identical (TODO)
  	 * @param possibleSuperType the type to inspect
  	 * @param type the type whose super types are looked at
  	 * @return <code>true</code> iff <code>possibleSuperType</code> is
  	 * 		a super type of <code>type</code> or is equal to it
  	 */
  	public static boolean isSuperType(ITypeBinding possibleSuperType, ITypeBinding type) {
  		if (type.isArray() || type.isPrimitive()) {
  			return false;
  		}
  		if (Bindings.equals(type, possibleSuperType)) {
  			return true;
  		}
  		ITypeBinding superClass= type.getSuperclass();
  		if (superClass != null) {
  			if (isSuperType(possibleSuperType, superClass)) {
  				return true;
  			}
  		}
  		return false;
  	}
  
  	/**
  	 * Finds the compilation unit where the type of the given <code>ITypeBinding</code> is defined,
  	 * using the class path defined by the given Java project. Returns <code>null</code>
  	 * if no compilation unit is found (e.g. type binding is from a binary type)
  	 * @param typeBinding the type binding to search for
  	 * @param project the project used as a scope
  	 * @return the compilation unit containing the type
  	 * @throws JavaScriptModelException if an errors occurs in the Java model
  	 */
  	public static IJavaScriptUnit findCompilationUnit(ITypeBinding typeBinding, IJavaScriptProject project) throws JavaScriptModelException {
  		IJavaScriptElement type= typeBinding.getJavaElement();
  		if (type instanceof IType)
  			return ((IType) type).getJavaScriptUnit();
  		else
  			return null;
  	}
  
  	/**
  	 * Finds a method for the given <code>IFunctionBinding</code>. Returns
  	 * <code>null</code> if the type doesn't contain a corresponding method.
  	 * @param method the method to find
  	 * @param type the type to look in
  	 * @return the corresponding IFunction or <code>null</code>
  	 * @throws JavaScriptModelException if an error occurs in the Java model
  	 * @deprecated Use {@link #findMethodInHierarchy(ITypeBinding, String, String[])} or {@link JavaModelUtil}
  	 */
  	public static IFunction findMethod(IFunctionBinding method, IType type) throws JavaScriptModelException {
  		method= method.getMethodDeclaration();
  		
  		IFunction[] candidates= type.getFunctions();
  		for (int i= 0; i < candidates.length; i++) {
  			IFunction candidate= candidates[i];
  			if (candidate.getElementName().equals(method.getName()) && sameParameters(method, candidate)) {
  				return candidate;
  			}
  		}
  		return null;
  	}			
  
  	//---- Helper methods to convert a method ---------------------------------------------
  	
  	private static boolean sameParameters(IFunctionBinding method, IFunction candidate) throws JavaScriptModelException {
  		ITypeBinding[] methodParamters= method.getParameterTypes();
  		String[] candidateParameters= candidate.getParameterTypes();
  		if (methodParamters.length != candidateParameters.length)
  			return false;
  		IType scope= candidate.getDeclaringType();
  		for (int i= 0; i < methodParamters.length; i++) {
  			ITypeBinding methodParameter= methodParamters[i];
  			String candidateParameter= candidateParameters[i];
  			if (!sameParameter(methodParameter, candidateParameter, scope))
  				return false;
  		}
  		return true;
  	}
  
  	private static boolean sameParameter(ITypeBinding type, String candidate, IType scope) throws JavaScriptModelException {
  		if (type.getDimensions() != Signature.getArrayCount(candidate))
  			return false;
  			
  		// Normalizes types
  		if (type.isArray())
  			type= type.getElementType();
  		candidate= Signature.getElementType(candidate);
  		
  		if ((Signature.getTypeSignatureKind(candidate) == Signature.BASE_TYPE_SIGNATURE) != type.isPrimitive()) {
  			return false;
  		}
  			
  		if (type.isPrimitive()) {
  			return type.getName().equals(Signature.toString(candidate));
  		} else {
  			type= type.getErasure();
  			
  			if (candidate.charAt(Signature.getArrayCount(candidate)) == Signature.C_RESOLVED) {
  				return Signature.toString(candidate).equals(Bindings.getFullyQualifiedName(type));
  			} else {
  				String[][] qualifiedCandidates= scope.resolveType(Signature.toString(candidate));
  				if (qualifiedCandidates == null || qualifiedCandidates.length == 0)
  					return false;
  				String packageName= type.getPackage().isUnnamed() ? "" : type.getPackage().getName(); //$NON-NLS-1$
  				String typeName= getTypeQualifiedName(type);
  				for (int i= 0; i < qualifiedCandidates.length; i++) {
  					String[] qualifiedCandidate= qualifiedCandidates[i];
  					if (	qualifiedCandidate[0].equals(packageName) &&
  							qualifiedCandidate[1].equals(typeName))
  						return true;
  				}
  			}
  		}
  		return false;
  	}
  
  	/**
  	 * Normalizes a type binding received from an expression to a type binding that can be used in a declaration signature. 
  	 * Anonymous types are normalized, to the super class or interface. For null or void bindings
  	 * <code>null</code> is returned. 
  	 * @param binding the binding to normalize
  	 * @return the normalized binding
  	 */
  	public static ITypeBinding normalizeTypeBinding(ITypeBinding binding) {
  		if (binding != null && !binding.isNullType() && !isVoidType(binding)) {
  			if (binding.isAnonymous()) {
  				return binding.getSuperclass();
  			}
  			return binding;
  		}
  		return null;
  	}
  	
  	public static boolean isVoidType(ITypeBinding binding) {
  		return "void".equals(binding.getName()); //$NON-NLS-1$
  	}
  
  	/**
  	 * Normalizes the binding so that it can be used as a type inside a declaration
  	 * (e.g. variable declaration, method return type, parameter type, ...). For
  	 * null bindings Object is returned.
  	 * @param binding binding to normalize
  	 * @param ast current ast
  	 * 
  	 * @return the normalized type to be used in declarations
  	 */
  	public static ITypeBinding normalizeForDeclarationUse(ITypeBinding binding, AST ast) {
  		if (binding.isNullType())
  			return ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
  		if (binding.isPrimitive())
  			return binding;
  		binding= normalizeTypeBinding(binding);
  		return binding;
  	}
  
  	/**
  	 * Returns the type binding of the node's parent type declaration.
  	 * @param node
  	 * @return the type binding of the node's parent type declaration
  	 */
  	public static ITypeBinding getBindingOfParentType(ASTNode node) {
  		while (node != null) {
  			if (node instanceof AbstractTypeDeclaration) {
  				return ((AbstractTypeDeclaration) node).resolveBinding();
  			} else if (node instanceof AnonymousClassDeclaration) {
  				return ((AnonymousClassDeclaration) node).resolveBinding();
  			} else if (node instanceof JavaScriptUnit) {
  				return ((JavaScriptUnit) node).resolveBinding();
  			}
  			node= node.getParent();
  		}
  		return null;
  	}
  	
  	/**
  	 * Returns the type binding of the node's type context or null if the node is an annotation, type parameter or super type declaration of a tope level type.
  	 * The result of this method is equal to the result of {@link #getBindingOfParentType(ASTNode)} for nodes in the type's body.
  	 * @param node
  	 * @return the type binding of the node's parent type context
  	 */
  	public static ITypeBinding getBindingOfParentTypeContext(ASTNode node) {
  		StructuralPropertyDescriptor lastLocation= null;
  
  		while (node != null) {
  			if (node instanceof AbstractTypeDeclaration) {
  				AbstractTypeDeclaration decl= (AbstractTypeDeclaration) node;
  				if (lastLocation == decl.getBodyDeclarationsProperty()) {
  					return decl.resolveBinding();
  				}
  			} else if (node instanceof AnonymousClassDeclaration) {
  				return ((AnonymousClassDeclaration) node).resolveBinding();
  			}
  			lastLocation= node.getLocationInParent();
  			node= node.getParent();
  		}
  		return null;
  	}
  
  	
  	public static String getRawName(ITypeBinding binding) {
  		String name= binding.getName();
  		return name;
  	}
  	
  
  	public static String getRawQualifiedName(ITypeBinding binding) {
  		final String EMPTY= ""; //$NON-NLS-1$
  		
  		if (binding.isAnonymous() || binding.isLocal()) {
  			return EMPTY; 
  		}
  		
  		if (binding.isPrimitive() || binding.isNullType()) {
  			return binding.getName();
  		}
  		
  		if (binding.isArray()) {
  			String elementTypeQualifiedName = getRawQualifiedName(binding.getElementType());
  			if (elementTypeQualifiedName.length() != 0) {
  				StringBuffer stringBuffer= new StringBuffer(elementTypeQualifiedName);
  				stringBuffer.append('[').append(']');
  				return stringBuffer.toString();
  			} else {
  				return EMPTY;
  			}
  		}
  		if (binding.isMember()) {
  			String outerName= getRawQualifiedName(binding.getDeclaringClass());
  			if (outerName.length() > 0) {
  				StringBuffer buffer= new StringBuffer();
  				buffer.append(outerName);
  				buffer.append('.');
  				buffer.append(getRawName(binding));
  				return buffer.toString();
  			} else {
  				return EMPTY;
  			}
  
  		} else if (binding.isTopLevel()) {
  			IPackageBinding packageBinding= binding.getPackage();
  			StringBuffer buffer= new StringBuffer();
  			if (packageBinding != null && packageBinding.getName().length() > 0) {
  				buffer.append(packageBinding.getName()).append('.');
  			}
  			buffer.append(getRawName(binding));
  			return buffer.toString();
  		}
  		return EMPTY;
  	}
  	
  
  	/**
  	 * Tests if the given node is a declaration, not a instance of a generic type, method or field.
  	 * Declarations can be found in AST with JavaScriptUnit.findDeclaringNode
  	 * @param binding binding to test
  	 * @return returns <code>true</code> if the binding is a declaration binding
  	 */
  	public static boolean isDeclarationBinding(IBinding binding) {
  		switch (binding.getKind()) {
  			case IBinding.TYPE:
  				return ((ITypeBinding) binding).getTypeDeclaration() == binding;
  			case IBinding.VARIABLE:
  				return ((IVariableBinding) binding).getVariableDeclaration() == binding;
  			case IBinding.METHOD:
  				return ((IFunctionBinding) binding).getMethodDeclaration() == binding;
  		}
  		return true;
  	}
  	
  	
  	public static IBinding getDeclaration(IBinding binding) {
  		switch (binding.getKind()) {
  			case IBinding.TYPE:
  				return ((ITypeBinding) binding).getTypeDeclaration();
  			case IBinding.VARIABLE:
  				return ((IVariableBinding) binding).getVariableDeclaration();
  			case IBinding.METHOD:
  				return ((IFunctionBinding) binding).getMethodDeclaration();
  		}
  		return binding;
  	}
  
  
  	/**
  	 * @deprecated Need to review: Use {@link #isSubsignature(IFunctionBinding, IFunctionBinding)} if the two bindings
  	 * are in the same hierarchy (directly overrides each other), or {@link #findMethodInHierarchy(ITypeBinding, String, ITypeBinding[])}
  	 * else.
  	 */
  	public static boolean containsSignatureEquivalentConstructor(IFunctionBinding[] candidates, IFunctionBinding overridable) {
  		for (int index= 0; index < candidates.length; index++) {
  			if (isSignatureEquivalentConstructor(candidates[index], overridable))
  				return true;
  		}
  		return false;
  	}
  
  	private static boolean isSignatureEquivalentConstructor(IFunctionBinding overridden, IFunctionBinding overridable) {
  
  		if (!overridden.isConstructor() || !overridable.isConstructor())
  			return false;
  		
  		if (overridden.isDefaultConstructor())
  			return false;
  		
  		return areSubTypeCompatible(overridden, overridable);
  	}
  	
  	/**
  	 * @deprecated Need to review: Use {@link #isSubsignature(IFunctionBinding, IFunctionBinding)} if the two bindings
  	 * are in the same hierarchy (directly overrides each other), or {@link #findMethodInHierarchy(ITypeBinding, String, ITypeBinding[])}
  	 * else.
  	 */
  	public static boolean areOverriddenMethods(IFunctionBinding overridden, IFunctionBinding overridable) {
  
  		if (!overridden.getName().equals(overridable.getName()))
  			return false;
  
  		return areSubTypeCompatible(overridden, overridable);
  	}
  
  	private static boolean areSubTypeCompatible(IFunctionBinding overridden, IFunctionBinding overridable) {
  		
  		if (overridden.getParameterTypes().length != overridable.getParameterTypes().length)
  			return false;
  		
  		ITypeBinding overriddenReturn= overridden.getReturnType();
  		ITypeBinding overridableReturn= overridable.getReturnType();
  		if (overriddenReturn == null || overridableReturn == null)
  			return false;
  		
  		if (!overriddenReturn.getErasure().isSubTypeCompatible(overridableReturn.getErasure()))
  			return false;
  		
  		ITypeBinding[] overriddenTypes= overridden.getParameterTypes();
  		ITypeBinding[] overridableTypes= overridable.getParameterTypes();
  		Assert.isTrue(overriddenTypes.length == overridableTypes.length);
  		for (int index= 0; index < overriddenTypes.length; index++) {
  			final ITypeBinding overridableErasure= overridableTypes[index].getErasure();
  			final ITypeBinding overriddenErasure= overriddenTypes[index].getErasure();
  			if (!overridableErasure.isSubTypeCompatible(overriddenErasure) || !overridableErasure.getKey().equals(overriddenErasure.getKey()))
  				return false;
  		}
  		return true;
  	}
  	
  }
  