content: /*******************************************************************************
   * Copyright (c) 2000, 2009 IBM Corporation and others.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     IBM Corporation - initial API and implementation
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.corext.codemanipulation;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.NamingConventions;
  import org.eclipse.wst.jsdt.core.dom.AST;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.Assignment;
  import org.eclipse.wst.jsdt.core.dom.Block;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.FieldAccess;
  import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
  import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
  import org.eclipse.wst.jsdt.core.dom.IBinding;
  import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
  import org.eclipse.wst.jsdt.core.dom.IPackageBinding;
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  import org.eclipse.wst.jsdt.core.dom.IVariableBinding;
  import org.eclipse.wst.jsdt.core.dom.JSdoc;
  import org.eclipse.wst.jsdt.core.dom.Modifier;
  import org.eclipse.wst.jsdt.core.dom.PrimitiveType;
  import org.eclipse.wst.jsdt.core.dom.ReturnStatement;
  import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration;
  import org.eclipse.wst.jsdt.core.dom.Statement;
  import org.eclipse.wst.jsdt.core.dom.SuperConstructorInvocation;
  import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation;
  import org.eclipse.wst.jsdt.core.dom.Type;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
  import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodeFactory;
  import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
  import org.eclipse.wst.jsdt.internal.corext.dom.Bindings;
  import org.eclipse.wst.jsdt.ui.CodeGeneration;
  
  /**
  *
  * 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 final class StubUtility2 {
  
  	public static FunctionDeclaration createConstructorStub(IJavaScriptUnit unit, ASTRewrite rewrite, ImportRewrite imports, IFunctionBinding binding, String type, int modifiers, boolean omitSuperForDefConst, boolean todo, CodeGenerationSettings settings) throws CoreException {
  		AST ast= rewrite.getAST();
  		FunctionDeclaration decl= ast.newFunctionDeclaration();
  		decl.modifiers().addAll(ASTNodeFactory.newModifiers(ast, modifiers & ~Modifier.ABSTRACT & ~Modifier.NATIVE));
  		decl.setName(ast.newSimpleName(type));
  		decl.setConstructor(true);
  
  		List parameters= createParameters(unit, imports, ast, binding, decl, null);
  
  		Block body= ast.newBlock();
  		decl.setBody(body);
  
  		String delimiter= StubUtility.getLineDelimiterUsed(unit);
  		String bodyStatement= ""; //$NON-NLS-1$
  		if (!omitSuperForDefConst || !parameters.isEmpty()) {
  			SuperConstructorInvocation invocation= ast.newSuperConstructorInvocation();
  			SingleVariableDeclaration varDecl= null;
  			for (Iterator iterator= parameters.iterator(); iterator.hasNext();) {
  				varDecl= (SingleVariableDeclaration) iterator.next();
  				invocation.arguments().add(ast.newSimpleName(varDecl.getName().getIdentifier()));
  			}
  			bodyStatement= ASTNodes.asFormattedString(invocation, 0, delimiter, unit.getJavaScriptProject().getOptions(true));
  		}
  
  		if (todo) {
  			String placeHolder= CodeGeneration.getMethodBodyContent(unit, type, binding.getName(), true, bodyStatement, delimiter);
  			if (placeHolder != null) {
  				ASTNode todoNode= rewrite.createStringPlaceholder(placeHolder, ASTNode.RETURN_STATEMENT);
  				body.statements().add(todoNode);
  			}
  		} else {
  			ASTNode statementNode= rewrite.createStringPlaceholder(bodyStatement, ASTNode.RETURN_STATEMENT);
  			body.statements().add(statementNode);
  		}
  
  		if (settings != null && settings.createComments) {
  			String string= CodeGeneration.getMethodComment(unit, type, decl, binding, delimiter);
  			if (string != null) {
  				JSdoc javadoc= (JSdoc) rewrite.createStringPlaceholder(string, ASTNode.JSDOC);
  				decl.setJavadoc(javadoc);
  			}
  		}
  		return decl;
  	}
  
  	public static FunctionDeclaration createConstructorStub(IJavaScriptUnit unit, ASTRewrite rewrite, ImportRewrite imports, ITypeBinding typeBinding, AST ast, IFunctionBinding superConstructor, IVariableBinding[] variableBindings, int modifiers, CodeGenerationSettings settings) throws CoreException {
  
  		FunctionDeclaration decl= ast.newFunctionDeclaration();
  		decl.modifiers().addAll(ASTNodeFactory.newModifiers(ast, modifiers & ~Modifier.ABSTRACT & ~Modifier.NATIVE));
  		decl.setName(ast.newSimpleName(typeBinding.getName()));
  		decl.setConstructor(true);
  
  		List parameters= decl.parameters();
  		if (superConstructor != null) {
  
  			createParameters(unit, imports, ast, superConstructor, decl, null);
  		}
  
  		Block body= ast.newBlock();
  		decl.setBody(body);
  
  		String delimiter= StubUtility.getLineDelimiterUsed(unit);
  
  		if (superConstructor != null) {
  			SuperConstructorInvocation invocation= ast.newSuperConstructorInvocation();
  			SingleVariableDeclaration varDecl= null;
  			for (Iterator iterator= parameters.iterator(); iterator.hasNext();) {
  				varDecl= (SingleVariableDeclaration) iterator.next();
  				invocation.arguments().add(ast.newSimpleName(varDecl.getName().getIdentifier()));
  			}
  			body.statements().add(invocation);
  		}
  
  		List prohibited= new ArrayList();
  		for (final Iterator iterator= parameters.iterator(); iterator.hasNext();)
  			prohibited.add(((SingleVariableDeclaration) iterator.next()).getName().getIdentifier());
  		String param= null;
  		List list= new ArrayList(prohibited);
  		String[] excluded= null;
  		for (int i= 0; i < variableBindings.length; i++) {
  			SingleVariableDeclaration var= ast.newSingleVariableDeclaration();
  			var.setType(imports.addImport(variableBindings[i].getType(), ast));
  			excluded= new String[list.size()];
  			list.toArray(excluded);
  			param= getParameterName(unit, variableBindings[i], excluded);
  			list.add(param);
  			var.setName(ast.newSimpleName(param));
  			parameters.add(var);
  		}
  
  		list= new ArrayList(prohibited);
  		for (int i= 0; i < variableBindings.length; i++) {
  			excluded= new String[list.size()];
  			list.toArray(excluded);
  			final String paramName= getParameterName(unit, variableBindings[i], excluded);
  			list.add(paramName);
  			final String fieldName= variableBindings[i].getName();
  			Expression expression= null;
  			if (paramName.equals(fieldName) || settings.useKeywordThis) {
  				FieldAccess access= ast.newFieldAccess();
  				access.setExpression(ast.newThisExpression());
  				access.setName(ast.newSimpleName(fieldName));
  				expression= access;
  			} else
  				expression= ast.newSimpleName(fieldName);
  			Assignment assignment= ast.newAssignment();
  			assignment.setLeftHandSide(expression);
  			assignment.setRightHandSide(ast.newSimpleName(paramName));
  			assignment.setOperator(Assignment.Operator.ASSIGN);
  			body.statements().add(ast.newExpressionStatement(assignment));
  		}
  
  		if (settings != null && settings.createComments) {
  			String string= CodeGeneration.getMethodComment(unit, typeBinding.getName(), decl, superConstructor, delimiter);
  			if (string != null) {
  				JSdoc javadoc= (JSdoc) rewrite.createStringPlaceholder(string, ASTNode.JSDOC);
  				decl.setJavadoc(javadoc);
  			}
  		}
  		return decl;
  	}
  
  	public static FunctionDeclaration createDelegationStub(IJavaScriptUnit unit, ASTRewrite rewrite, ImportRewrite imports, AST ast, IBinding[] bindings, CodeGenerationSettings settings) throws CoreException {
  		Assert.isNotNull(bindings);
  		Assert.isNotNull(settings);
  		Assert.isTrue(bindings.length == 2);
  		Assert.isTrue(bindings[0] instanceof IVariableBinding);
  		Assert.isTrue(bindings[1] instanceof IFunctionBinding);
  
  		IVariableBinding variableBinding= (IVariableBinding) bindings[0];
  		IFunctionBinding methodBinding= (IFunctionBinding) bindings[1];
  
  		FunctionDeclaration decl= ast.newFunctionDeclaration();
  		decl.modifiers().addAll(ASTNodeFactory.newModifiers(ast, methodBinding.getModifiers() & ~Modifier.SYNCHRONIZED & ~Modifier.ABSTRACT & ~Modifier.NATIVE));
  
  		decl.setName(ast.newSimpleName(methodBinding.getName()));
  		decl.setConstructor(false);
  
  		decl.setReturnType2(imports.addImport(methodBinding.getReturnType(), ast));
  
  		List parameters= decl.parameters();
  		ITypeBinding[] params= methodBinding.getParameterTypes();
  		String[] paramNames= StubUtility.suggestArgumentNames(unit.getJavaScriptProject(), methodBinding);
  		for (int i= 0; i < params.length; i++) {
  			SingleVariableDeclaration varDecl= ast.newSingleVariableDeclaration();
  			
  			if (methodBinding.isVarargs() && params[i].isArray() && i == params.length - 1) {
  				StringBuffer buffer= new StringBuffer(imports.addImport(params[i].getElementType()));
  				for (int dim= 1; dim < params[i].getDimensions(); dim++)
  					buffer.append("[]"); //$NON-NLS-1$
  				varDecl.setType(ASTNodeFactory.newType(ast, buffer.toString()));
  				varDecl.setVarargs(true);
  			} else
  				varDecl.setType(imports.addImport(params[i], ast));
  			
  			varDecl.setName(ast.newSimpleName(paramNames[i]));
  			parameters.add(varDecl);
  		}
  
  		Block body= ast.newBlock();
  		decl.setBody(body);
  
  		String delimiter= StubUtility.getLineDelimiterUsed(unit);
  
  		Statement statement= null;
  		FunctionInvocation invocation= ast.newFunctionInvocation();
  		invocation.setName(ast.newSimpleName(methodBinding.getName()));
  		List arguments= invocation.arguments();
  		for (int i= 0; i < params.length; i++)
  			arguments.add(ast.newSimpleName(paramNames[i]));
  		if (settings.useKeywordThis) {
  			FieldAccess access= ast.newFieldAccess();
  			access.setExpression(ast.newThisExpression());
  			access.setName(ast.newSimpleName(variableBinding.getName()));
  			invocation.setExpression(access);
  		} else
  			invocation.setExpression(ast.newSimpleName(variableBinding.getName()));
  		if (methodBinding.getReturnType().isPrimitive() && methodBinding.getReturnType().getName().equals("void")) {//$NON-NLS-1$
  			statement= ast.newExpressionStatement(invocation);
  		} else {
  			ReturnStatement returnStatement= ast.newReturnStatement();
  			returnStatement.setExpression(invocation);
  			statement= returnStatement;
  		}
  		body.statements().add(statement);
  
  		ITypeBinding declaringType= variableBinding.getDeclaringClass();
  		if (declaringType == null) { // can be null for
  			return decl;
  		}
  
  		String qualifiedName= declaringType.getQualifiedName();
  		IPackageBinding packageBinding= declaringType.getPackage();
  		if (packageBinding != null) {
  			if (packageBinding.getName().length() > 0 && qualifiedName.startsWith(packageBinding.getName()))
  				qualifiedName= qualifiedName.substring(packageBinding.getName().length());
  		}
  
  		if (settings.createComments) {
  			/*
  			 * TODO: have API for delegate method comments This is an inlined
  			 * version of
  			 * {@link CodeGeneration#getMethodComment(IJavaScriptUnit, String, FunctionDeclaration, IFunctionBinding, String)}
  			 */
  			methodBinding= methodBinding.getMethodDeclaration();
  			String declaringClassQualifiedName= methodBinding.getDeclaringClass().getQualifiedName();
  			String linkToMethodName= methodBinding.getName();
  			String[] parameterTypesQualifiedNames= StubUtility.getParameterTypeNamesForSeeTag(methodBinding);
  			String string= StubUtility.getMethodComment(unit, qualifiedName, decl, methodBinding.isDeprecated(), linkToMethodName, declaringClassQualifiedName, parameterTypesQualifiedNames, true, delimiter);
  			if (string != null) {
  				JSdoc javadoc= (JSdoc) rewrite.createStringPlaceholder(string, ASTNode.JSDOC);
  				decl.setJavadoc(javadoc);
  			}
  		}
  		return decl;
  	}
  
  	public static FunctionDeclaration createImplementationStub(IJavaScriptUnit unit, ASTRewrite rewrite, ImportRewrite imports, AST ast, IFunctionBinding binding, String type, CodeGenerationSettings settings, boolean deferred, ImportRewriteContext context) throws CoreException {
  
  		FunctionDeclaration decl= ast.newFunctionDeclaration();
  		decl.modifiers().addAll(getImplementationModifiers(ast, binding, deferred));
  
  		decl.setName(ast.newSimpleName(binding.getName()));
  		decl.setConstructor(false);
  
  		decl.setReturnType2(imports.addImport(binding.getReturnType(), ast, context));
  
  		List parameters= createParameters(unit, imports, ast, binding, decl, context);
  
  		String delimiter= StubUtility.getLineDelimiterUsed(unit);
  		if (!deferred) {
  			Map options= unit.getJavaScriptProject().getOptions(true);
  
  			Block body= ast.newBlock();
  			decl.setBody(body);
  
  			String bodyStatement= ""; //$NON-NLS-1$
  			if (Modifier.isAbstract(binding.getModifiers())) {
  				Expression expression= ASTNodeFactory.newDefaultExpression(ast, decl.getReturnType2(), decl.getExtraDimensions());
  				if (expression != null) {
  					ReturnStatement returnStatement= ast.newReturnStatement();
  					returnStatement.setExpression(expression);
  					bodyStatement= ASTNodes.asFormattedString(returnStatement, 0, delimiter, options);
  				}
  			} else {
  				SuperMethodInvocation invocation= ast.newSuperMethodInvocation();
  				invocation.setName(ast.newSimpleName(binding.getName()));
  				SingleVariableDeclaration varDecl= null;
  				for (Iterator iterator= parameters.iterator(); iterator.hasNext();) {
  					varDecl= (SingleVariableDeclaration) iterator.next();
  					invocation.arguments().add(ast.newSimpleName(varDecl.getName().getIdentifier()));
  				}
  				Expression expression= invocation;
  				Type returnType= decl.getReturnType2();
  				if (returnType != null && (returnType instanceof PrimitiveType) && ((PrimitiveType) returnType).getPrimitiveTypeCode().equals(PrimitiveType.VOID)) {
  					bodyStatement= ASTNodes.asFormattedString(ast.newExpressionStatement(expression), 0, delimiter, options);
  				} else {
  					ReturnStatement returnStatement= ast.newReturnStatement();
  					returnStatement.setExpression(expression);
  					bodyStatement= ASTNodes.asFormattedString(returnStatement, 0, delimiter, options);
  				}
  			}
  
  			String placeHolder= CodeGeneration.getMethodBodyContent(unit, type, binding.getName(), false, bodyStatement, delimiter);
  			if (placeHolder != null) {
  				ASTNode todoNode= rewrite.createStringPlaceholder(placeHolder, ASTNode.RETURN_STATEMENT);
  				body.statements().add(todoNode);
  			}
  		}
  
  		if (settings.createComments) {
  			String string= CodeGeneration.getMethodComment(unit, type, decl, binding, delimiter);
  			if (string != null) {
  				JSdoc javadoc= (JSdoc) rewrite.createStringPlaceholder(string, ASTNode.JSDOC);
  				decl.setJavadoc(javadoc);
  			}
  		}
  
  		return decl;
  	}
  
  	public static FunctionDeclaration createImplementationStub(IJavaScriptUnit unit, ASTRewrite rewrite, ImportRewrite importRewrite, IFunctionBinding binding, String type, boolean deferred, CodeGenerationSettings settings) throws CoreException {
  		AST ast= rewrite.getAST();
  		FunctionDeclaration decl= ast.newFunctionDeclaration();
  		decl.modifiers().addAll(getImplementationModifiers(ast, binding, deferred));
  
  		decl.setName(ast.newSimpleName(binding.getName()));
  		decl.setConstructor(false);
  
  		decl.setReturnType2(createTypeNode(importRewrite, binding.getReturnType(), ast));
  
  		List parameters= createParameters(unit, importRewrite, ast, binding, decl);
  
  		String delimiter= StubUtility.getLineDelimiterUsed(unit);
  		if (!deferred) {
  			Map options= unit.getJavaScriptProject().getOptions(true);
  			
  			Block body= ast.newBlock();
  			decl.setBody(body);
  
  			String bodyStatement= ""; //$NON-NLS-1$
  			if (Modifier.isAbstract(binding.getModifiers())) {
  				Expression expression= ASTNodeFactory.newDefaultExpression(ast, decl.getReturnType2(), decl.getExtraDimensions());
  				if (expression != null) {
  					ReturnStatement returnStatement= ast.newReturnStatement();
  					returnStatement.setExpression(expression);
  					bodyStatement= ASTNodes.asFormattedString(returnStatement, 0, delimiter, options);
  				}
  			} else {
  				SuperMethodInvocation invocation= ast.newSuperMethodInvocation();
  				invocation.setName(ast.newSimpleName(binding.getName()));
  				SingleVariableDeclaration varDecl= null;
  				for (Iterator iterator= parameters.iterator(); iterator.hasNext();) {
  					varDecl= (SingleVariableDeclaration) iterator.next();
  					invocation.arguments().add(ast.newSimpleName(varDecl.getName().getIdentifier()));
  				}
  				Expression expression= invocation;
  				Type returnType= decl.getReturnType2();
  				if (returnType instanceof PrimitiveType && ((PrimitiveType) returnType).getPrimitiveTypeCode().equals(PrimitiveType.VOID)) {
  					bodyStatement= ASTNodes.asFormattedString(ast.newExpressionStatement(expression), 0, delimiter, options);
  				} else {
  					ReturnStatement returnStatement= ast.newReturnStatement();
  					returnStatement.setExpression(expression);
  					bodyStatement= ASTNodes.asFormattedString(returnStatement, 0, delimiter, options);
  				}
  			}
  
  			String placeHolder= CodeGeneration.getMethodBodyContent(unit, type, binding.getName(), false, bodyStatement, delimiter);
  			if (placeHolder != null) {
  				ASTNode todoNode= rewrite.createStringPlaceholder(placeHolder, ASTNode.RETURN_STATEMENT);
  				body.statements().add(todoNode);
  			}
  		}
  
  		if (settings != null && settings.createComments) {
  			String string= CodeGeneration.getMethodComment(unit, type, decl, binding, delimiter);
  			if (string != null) {
  				JSdoc javadoc= (JSdoc) rewrite.createStringPlaceholder(string, ASTNode.JSDOC);
  				decl.setJavadoc(javadoc);
  			}
  		}
  		return decl;
  	}
  
  	private static List createParameters(IJavaScriptUnit unit, ImportRewrite imports, AST ast, IFunctionBinding binding, FunctionDeclaration decl, ImportRewriteContext context) {
  		List parameters= decl.parameters();
  		ITypeBinding[] params= binding.getParameterTypes();
  		String[] paramNames= StubUtility.suggestArgumentNames(unit.getJavaScriptProject(), binding);
  		for (int i= 0; i < params.length; i++) {
  			SingleVariableDeclaration var= ast.newSingleVariableDeclaration();
  			if (binding.isVarargs() && params[i].isArray() && i == params.length - 1) {
  				StringBuffer buffer= new StringBuffer(imports.addImport(params[i].getElementType(), context));
  				for (int dim= 1; dim < params[i].getDimensions(); dim++)
  					buffer.append("[]"); //$NON-NLS-1$
  				var.setType(ASTNodeFactory.newType(ast, buffer.toString()));
  				var.setVarargs(true);
  			} else
  				var.setType(imports.addImport(params[i], ast, context));
  			var.setName(ast.newSimpleName(paramNames[i]));
  			parameters.add(var);
  		}
  		return parameters;
  	}
  
  	private static List createParameters(IJavaScriptUnit unit, ImportRewrite imports, AST ast, IFunctionBinding binding, FunctionDeclaration decl) {
  		List parameters= decl.parameters();
  		ITypeBinding[] params= binding.getParameterTypes();
  		String[] paramNames= StubUtility.suggestArgumentNames(unit.getJavaScriptProject(), binding);
  		for (int i= 0; i < params.length; i++) {
  			SingleVariableDeclaration var= ast.newSingleVariableDeclaration();
  			if (binding.isVarargs() && params[i].isArray() && i == params.length - 1) {
  				final ITypeBinding elementType= params[i].getElementType();
  				StringBuffer buffer= new StringBuffer(imports != null ? imports.addImport(elementType) : elementType.getQualifiedName());
  				for (int dim= 1; dim < params[i].getDimensions(); dim++)
  					buffer.append("[]"); //$NON-NLS-1$
  				var.setType(ASTNodeFactory.newType(ast, buffer.toString()));
  				var.setVarargs(true);
  			} else
  				var.setType(createTypeNode(imports, params[i], ast));
  			var.setName(ast.newSimpleName(paramNames[i]));
  			parameters.add(var);
  		}
  		return parameters;
  	}
  
  	private static Type createTypeNode(ImportRewrite importRewrite, ITypeBinding binding, AST ast) {
  		if (importRewrite != null)
  			return importRewrite.addImport(binding, ast);
  		return createTypeNode(binding, ast);
  	}
  
  	private static Type createTypeNode(ITypeBinding binding, AST ast) {
  		if (binding.isPrimitive())
  			return ast.newPrimitiveType(PrimitiveType.toCode(binding.getName()));
  		ITypeBinding normalized= Bindings.normalizeTypeBinding(binding);
  		if (normalized == null)
  			return ast.newSimpleType(ast.newSimpleName("invalid")); //$NON-NLS-1$
  		else if (normalized.isArray())
  			return ast.newArrayType(createTypeNode(normalized.getElementType(), ast), normalized.getDimensions());
  		String qualified= Bindings.getRawQualifiedName(normalized);
  		if (qualified.length() > 0) {
  			return ast.newSimpleType(ASTNodeFactory.newName(ast, qualified));
  		}
  		return ast.newSimpleType(ASTNodeFactory.newName(ast, Bindings.getRawName(normalized)));
  	}
  
  	private static IFunctionBinding findMethodBinding(IFunctionBinding method, List allMethods) {
  		for (int i= 0; i < allMethods.size(); i++) {
  			IFunctionBinding curr= (IFunctionBinding) allMethods.get(i);
  			if (Bindings.isSubsignature(method, curr)) {
  				return curr;
  			}
  		}
  		return null;
  	}
  
  	private static IFunctionBinding findOverridingMethod(IFunctionBinding method, List allMethods) {
  		for (int i= 0; i < allMethods.size(); i++) {
  			IFunctionBinding curr= (IFunctionBinding) allMethods.get(i);
  			if (Bindings.areOverriddenMethods(curr, method) || Bindings.isSubsignature(curr, method))
  				return curr;
  		}
  		return null;
  	}
  
  	public static IBinding[][] getDelegatableMethods(AST ast, ITypeBinding binding) {
  		final List tuples= new ArrayList();
  		final List declared= new ArrayList();
  		IFunctionBinding[] typeMethods= binding.getDeclaredMethods();
  		for (int index= 0; index < typeMethods.length; index++)
  			declared.add(typeMethods[index]);
  		IVariableBinding[] typeFields= binding.getDeclaredFields();
  		for (int index= 0; index < typeFields.length; index++) {
  			IVariableBinding fieldBinding= typeFields[index];
  			if (fieldBinding.isField())
  				getDelegatableMethods(ast, tuples, new ArrayList(declared), fieldBinding, fieldBinding.getType(), binding);
  		}
  		// list of tuple<IVariableBinding, IFunctionBinding>
  		return (IBinding[][]) tuples.toArray(new IBinding[tuples.size()][2]);
  	}
  
  	private static void getDelegatableMethods(AST ast, List tuples, List methods, IVariableBinding fieldBinding, ITypeBinding typeBinding, ITypeBinding binding) {
  		boolean match= false;
  		IFunctionBinding[] candidates= getDelegateCandidates(typeBinding, binding);
  		for (int index= 0; index < candidates.length; index++) {
  			match= false;
  			final IFunctionBinding methodBinding= candidates[index];
  			for (int offset= 0; offset < methods.size() && !match; offset++) {
  				if (Bindings.areOverriddenMethods((IFunctionBinding) methods.get(offset), methodBinding))
  					match= true;
  			}
  			if (!match) {
  				tuples.add(new IBinding[] { fieldBinding, methodBinding });
  				methods.add(methodBinding);
  			}
  		}
  		final ITypeBinding superclass= typeBinding.getSuperclass();
  		if (superclass != null)
  			getDelegatableMethods(ast, tuples, methods, fieldBinding, superclass, binding);
  	}
  
  	private static IFunctionBinding[] getDelegateCandidates(ITypeBinding binding, ITypeBinding hierarchy) {
  		List allMethods= new ArrayList();
  		IFunctionBinding[] typeMethods= binding.getDeclaredMethods();
  		for (int index= 0; index < typeMethods.length; index++) {
  			final int modifiers= typeMethods[index].getModifiers();
  			if (!typeMethods[index].isConstructor() && !Modifier.isStatic(modifiers) && (Modifier.isPublic(modifiers))) {
  //				IFunctionBinding result= Bindings.findOverriddenMethodInHierarchy(hierarchy, typeMethods[index]);
  //				ITypeBinding[] parameterBindings= typeMethods[index].getParameterTypes();
  				boolean upper= false;
  				if (!upper)
  					allMethods.add(typeMethods[index]);
  			}
  		}
  		return (IFunctionBinding[]) allMethods.toArray(new IFunctionBinding[allMethods.size()]);
  	}
  
  	private static List getImplementationModifiers(AST ast, IFunctionBinding method, boolean deferred) {
  		int modifiers= method.getModifiers() & ~Modifier.ABSTRACT & ~Modifier.NATIVE & ~Modifier.PRIVATE;
  		if (deferred) {
  			modifiers= modifiers & ~Modifier.PROTECTED;
  			modifiers= modifiers | Modifier.PUBLIC;
  		}
  		return ASTNodeFactory.newModifiers(ast, modifiers);
  	}
  
  	public static IFunctionBinding[] getOverridableMethods(AST ast, ITypeBinding typeBinding, boolean isSubType) {
  		List allMethods= new ArrayList();
  		IFunctionBinding[] typeMethods= typeBinding.getDeclaredMethods();
  		for (int index= 0; index < typeMethods.length; index++) {
  			final int modifiers= typeMethods[index].getModifiers();
  			if (!typeMethods[index].isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers))
  				allMethods.add(typeMethods[index]);
  		}
  		ITypeBinding clazz= typeBinding.getSuperclass();
  		while (clazz != null) {
  			IFunctionBinding[] methods= clazz.getDeclaredMethods();
  			for (int offset= 0; offset < methods.length; offset++) {
  				final int modifiers= methods[offset].getModifiers();
  				if (!methods[offset].isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) {
  					if (findOverridingMethod(methods[offset], allMethods) == null)
  						allMethods.add(methods[offset]);
  				}
  			}
  			clazz= clazz.getSuperclass();
  		}
  		clazz= typeBinding;
  		while (clazz != null) {
  			clazz= clazz.getSuperclass();
  		}
  		if (!isSubType)
  			allMethods.removeAll(Arrays.asList(typeMethods));
  		int modifiers= 0;
  		
  		for (int index= allMethods.size() - 1; index >= 0; index--) {
  			IFunctionBinding method= (IFunctionBinding) allMethods.get(index);
  			modifiers= method.getModifiers();
  			if (Modifier.isFinal(modifiers))
  				allMethods.remove(index);
  		}
  		
  		return (IFunctionBinding[]) allMethods.toArray(new IFunctionBinding[allMethods.size()]);
  	}
  
  //	private static void getOverridableMethods(AST ast, ITypeBinding superBinding, List allMethods) {
  //		IFunctionBinding[] methods= superBinding.getDeclaredMethods();
  //		for (int offset= 0; offset < methods.length; offset++) {
  //			final int modifiers= methods[offset].getModifiers();
  //			if (!methods[offset].isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) {
  //				if (findOverridingMethod(methods[offset], allMethods) == null && !Modifier.isStatic(modifiers))
  //					allMethods.add(methods[offset]);
  //			}
  //		}
  //	}
  
  	private static String getParameterName(IJavaScriptUnit unit, IVariableBinding binding, String[] excluded) {
  		final String name= NamingConventions.removePrefixAndSuffixForFieldName(unit.getJavaScriptProject(), binding.getName(), binding.getModifiers());
  		return StubUtility.suggestArgumentName(unit.getJavaScriptProject(), name, excluded);
  	}
  
  	public static IFunctionBinding[] getUnimplementedMethods(ITypeBinding typeBinding) {
  		ArrayList allMethods= new ArrayList();
  		ArrayList toImplement= new ArrayList();
  
  		IFunctionBinding[] typeMethods= typeBinding.getDeclaredMethods();
  		for (int i= 0; i < typeMethods.length; i++) {
  			IFunctionBinding curr= typeMethods[i];
  			int modifiers= curr.getModifiers();
  			if (!curr.isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) {
  				allMethods.add(curr);
  			}
  		}
  
  		ITypeBinding superClass= typeBinding.getSuperclass();
  		while (superClass != null) {
  			typeMethods= superClass.getDeclaredMethods();
  			for (int i= 0; i < typeMethods.length; i++) {
  				IFunctionBinding curr= typeMethods[i];
  				int modifiers= curr.getModifiers();
  				if (!curr.isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) {
  					if (findMethodBinding(curr, allMethods) == null) {
  						allMethods.add(curr);
  					}
  				}
  			}
  			superClass= superClass.getSuperclass();
  		}
  
  		for (int i= 0; i < allMethods.size(); i++) {
  			IFunctionBinding curr= (IFunctionBinding) allMethods.get(i);
  			int modifiers= curr.getModifiers();
  			if ((Modifier.isAbstract(modifiers)) && (typeBinding != curr.getDeclaringClass())) {
  				// implement all abstract methods
  				toImplement.add(curr);
  			}
  		}
  
  		ITypeBinding curr= typeBinding;
  		while (curr != null) {
  			curr= curr.getSuperclass();
  		}
  
  		return (IFunctionBinding[]) toImplement.toArray(new IFunctionBinding[toImplement.size()]);
  	}
  
  	public static IFunctionBinding[] getVisibleConstructors(ITypeBinding binding, boolean accountExisting, boolean proposeDefault) {
  		List constructorMethods= new ArrayList();
  		List existingConstructors= null;
  		ITypeBinding superType= binding.getSuperclass();
  		if (superType == null)
  			return new IFunctionBinding[0];
  		if (accountExisting) {
  			IFunctionBinding[] methods= binding.getDeclaredMethods();
  			existingConstructors= new ArrayList(methods.length);
  			for (int index= 0; index < methods.length; index++) {
  				IFunctionBinding method= methods[index];
  				if (method.isConstructor() && !method.isDefaultConstructor())
  					existingConstructors.add(method);
  			}
  		}
  		if (existingConstructors != null)
  			constructorMethods.addAll(existingConstructors);
  		IFunctionBinding[] methods= binding.getDeclaredMethods();
  		IFunctionBinding[] superMethods= superType.getDeclaredMethods();
  		for (int index= 0; index < superMethods.length; index++) {
  			IFunctionBinding method= superMethods[index];
  			if (method.isConstructor()) {
  				if (Bindings.isVisibleInHierarchy(method, binding.getPackage()) && (!accountExisting || !Bindings.containsSignatureEquivalentConstructor(methods, method)))
  					constructorMethods.add(method);
  			}
  		}
  		if (existingConstructors != null)
  			constructorMethods.removeAll(existingConstructors);
  		if (constructorMethods.isEmpty()) {
  			superType= binding;
  			while (superType.getSuperclass() != null)
  				superType= superType.getSuperclass();
  			IFunctionBinding method= Bindings.findMethodInType(superType, "Object", new ITypeBinding[0]); //$NON-NLS-1$
  			if (method != null) {
  				if ((proposeDefault || (!accountExisting || (existingConstructors == null || existingConstructors.isEmpty()))) && (!accountExisting || !Bindings.containsSignatureEquivalentConstructor(methods, method)))
  					constructorMethods.add(method);
  			}
  		}
  		return (IFunctionBinding[]) constructorMethods.toArray(new IFunctionBinding[constructorMethods.size()]);
  	}
  
  
  
  	/**
  	 * Creates a new stub utility.
  	 */
  	private StubUtility2() {
  		// Not for instantiation
  	}
  }
  