content: /*******************************************************************************
   * Copyright (c) 2000, 2014 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
   *     Mickael Istria (Red Hat Inc.) - 426209 Java 6 + Warnings cleanup
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Set;
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.wst.jsdt.core.dom.ArrayCreation;
  import org.eclipse.wst.jsdt.core.dom.ArrayInitializer;
  import org.eclipse.wst.jsdt.core.dom.Assignment;
  import org.eclipse.wst.jsdt.core.dom.CatchClause;
  import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
  import org.eclipse.wst.jsdt.core.dom.ConditionalExpression;
  import org.eclipse.wst.jsdt.core.dom.ConstructorInvocation;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.FieldAccess;
  import org.eclipse.wst.jsdt.core.dom.FieldDeclaration;
  import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
  import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
  import org.eclipse.wst.jsdt.core.dom.IBinding;
  import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  import org.eclipse.wst.jsdt.core.dom.IVariableBinding;
  import org.eclipse.wst.jsdt.core.dom.InstanceofExpression;
  import org.eclipse.wst.jsdt.core.dom.Name;
  import org.eclipse.wst.jsdt.core.dom.ParenthesizedExpression;
  import org.eclipse.wst.jsdt.core.dom.QualifiedName;
  import org.eclipse.wst.jsdt.core.dom.ReturnStatement;
  import org.eclipse.wst.jsdt.core.dom.SimpleName;
  import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration;
  import org.eclipse.wst.jsdt.core.dom.SuperConstructorInvocation;
  import org.eclipse.wst.jsdt.core.dom.SuperFieldAccess;
  import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation;
  import org.eclipse.wst.jsdt.core.dom.ThisExpression;
  import org.eclipse.wst.jsdt.core.dom.Type;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclaration;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationExpression;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationStatement;
  import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
  import org.eclipse.wst.jsdt.internal.corext.dom.Bindings;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.rename.MethodChecks;
  
  /**
   * Default implementation of the creator. Creates all or nearly all constraints for program constructs.
   * Subclasses can provide additional checks to avoid creating constraints that are not useful for their purposes.
   */
  public class FullConstraintCreator extends ConstraintCreator{
  
  	private final IConstraintVariableFactory fConstraintVariableFactory;
  	private final ITypeConstraintFactory fTypeConstraintFactory;
  	private IContext fContext;
  
  	public FullConstraintCreator(){
  		this(new ConstraintVariableFactory(), new TypeConstraintFactory());
  	}
  	
  	public FullConstraintCreator(IConstraintVariableFactory cFactory,
  								 ITypeConstraintFactory tFactory) {
  		Assert.isTrue(cFactory != null);
  		fConstraintVariableFactory= cFactory;
  		fTypeConstraintFactory= tFactory;
  		fContext= new NullContext();
  	}
  
  	public IContext getContext() {
  		return fContext;
  	}
  	
  	public void setContext(IContext context) {
  		fContext= context;
  	}
  	
  	public ITypeConstraintFactory getConstraintFactory(){
  		return fTypeConstraintFactory;
  	}
  	
  	public IConstraintVariableFactory getConstraintVariableFactory(){
  		return fConstraintVariableFactory;
  	}
  	
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.ArrayInitializer)
  	 */
  	public ITypeConstraint[] create(ArrayInitializer arrayInitializer){
  		ITypeBinding arrayBinding= arrayInitializer.resolveTypeBinding();
  		Assert.isTrue(arrayBinding.isArray());
  		List<Expression> expressions= (List<Expression>)arrayInitializer.expressions();
  		List<ITypeConstraint> constraints= new ArrayList<ITypeConstraint>();
  		Type type= getTypeParent(arrayInitializer);
  		ConstraintVariable typeVariable= fConstraintVariableFactory.makeTypeVariable(type);
  		for (Expression each : expressions) {
  			ITypeConstraint[] c= fTypeConstraintFactory.createSubtypeConstraint(
  					fConstraintVariableFactory.makeExpressionOrTypeVariable(each, getContext()),
  					typeVariable);
  			constraints.addAll(Arrays.asList(c));
  		}		
  		return constraints.toArray(new ITypeConstraint[constraints.size()]);
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.Assignment)
  	 */
  	public ITypeConstraint[] create(Assignment assignment){
  		return fTypeConstraintFactory.createSubtypeConstraint(
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(assignment.getRightHandSide(), getContext()), 
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(assignment.getLeftHandSide(), getContext()));
  	}
  	
  	public ITypeConstraint[] create(CatchClause node) {
  		SingleVariableDeclaration exception= node.getException();
  		ConstraintVariable nameVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(exception.getName(), getContext());
  		
  		ITypeConstraint[] defines= fTypeConstraintFactory.createDefinesConstraint(
  				nameVariable,
  				fConstraintVariableFactory.makeTypeVariable(exception.getType()));
  		
  		ITypeBinding throwable= node.getAST().resolveWellKnownType("java.lang.Throwable"); //$NON-NLS-1$
  		ITypeConstraint[] catchBound= fTypeConstraintFactory.createSubtypeConstraint(
  				nameVariable,
  				fConstraintVariableFactory.makeRawBindingVariable(throwable));
  		
  		ArrayList<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
  		result.addAll(Arrays.asList(defines));
  		result.addAll(Arrays.asList(catchBound));
  		return result.toArray(new ITypeConstraint[result.size()]);		
  	}
  
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation)
  	 */
  	public ITypeConstraint[] create(ClassInstanceCreation instanceCreation){
  		List<ITypeConstraint> arguments= (List<ITypeConstraint>)instanceCreation.arguments();
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>(arguments.size());
  		IFunctionBinding methodBinding= instanceCreation.resolveConstructorBinding();
  		result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
  		if (instanceCreation.getAnonymousClassDeclaration() == null){
  			ConstraintVariable constructorVar= fConstraintVariableFactory.makeExpressionOrTypeVariable(instanceCreation, getContext());
  			ConstraintVariable typeVar= fConstraintVariableFactory.makeRawBindingVariable(instanceCreation.resolveTypeBinding());
  			result.addAll(Arrays.asList(fTypeConstraintFactory.createDefinesConstraint(constructorVar, typeVar)));
  		}
  		return result.toArray(new ITypeConstraint[result.size()]);		
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.ConstructorInvocation)
  	 */
  	public ITypeConstraint[] create(ConstructorInvocation invocation){
  		List<ITypeConstraint> arguments= (List<ITypeConstraint>)invocation.arguments();
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>(arguments.size());
  		IFunctionBinding methodBinding= invocation.resolveConstructorBinding();
  		result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
  		return result.toArray(new ITypeConstraint[result.size()]);
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.FieldAccess)
  	 */
  	public ITypeConstraint[] create(FieldAccess access){
  		Expression expression= access.getExpression();
  		SimpleName name= access.getName();
  		IBinding binding= name.resolveBinding();
  		if (! (binding instanceof IVariableBinding))
  			return new ITypeConstraint[0];	
  		IVariableBinding vb= (IVariableBinding)binding;
  		return createConstraintsForAccessToField(vb, expression, access);
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.FieldDeclaration)
  	 */
  	public ITypeConstraint[] create(FieldDeclaration fd){
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
  		result.addAll(Arrays.asList(getConstraintsFromFragmentList(fd.fragments(), fd.getType())));
  		result.addAll(getConstraintsForHiding(fd));
  		result.addAll(getConstraintsForFieldDeclaringTypes(fd));
  		return result.toArray(new ITypeConstraint[result.size()]);
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.InstanceofExpression)
  	 */
  	public ITypeConstraint[] create(InstanceofExpression instanceofExpression){
  		Expression expression= instanceofExpression.getLeftOperand();
  		Type type= instanceofExpression.getRightOperand();
  		if (isClassBinding(expression.resolveTypeBinding()) && isClassBinding(type.resolveBinding())) {
  			ConstraintVariable expressionVar= fConstraintVariableFactory.makeExpressionOrTypeVariable(expression, getContext());
  			ConstraintVariable typeVariable= fConstraintVariableFactory.makeTypeVariable(type);
  			return createOrOrSubtypeConstraint(expressionVar, typeVariable);
  		} else
  			return new ITypeConstraint[0];
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.ConditionalExpression)
  	 */
  	public ITypeConstraint[] create(ConditionalExpression node) {
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
  		Expression thenExpression= node.getThenExpression();
  		Expression elseExpression= node.getElseExpression();
  		ConstraintVariable whole= fConstraintVariableFactory.makeExpressionOrTypeVariable(node, getContext());
  		ConstraintVariable ev1= fConstraintVariableFactory.makeExpressionOrTypeVariable(thenExpression, getContext());
  		ConstraintVariable ev2= fConstraintVariableFactory.makeExpressionOrTypeVariable(elseExpression, getContext());
  		ITypeConstraint[] constraints1= fTypeConstraintFactory.createEqualsConstraint(ev1, ev2);
  		ITypeConstraint[] constraints2= fTypeConstraintFactory.createSubtypeConstraint(ev1, whole);
  		ITypeConstraint[] constraints3= fTypeConstraintFactory.createSubtypeConstraint(ev2, whole);
  		result.addAll(Arrays.asList(constraints1));
  		result.addAll(Arrays.asList(constraints2));
  		result.addAll(Arrays.asList(constraints3));
  		return result.toArray(new ITypeConstraint[result.size()]);
  	}
  	
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.FunctionDeclaration)
  	 */
  	public ITypeConstraint[] create(FunctionDeclaration declaration){
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
  		IFunctionBinding methodBinding= declaration.resolveBinding();
  		if (methodBinding == null)
  			return new ITypeConstraint[0];
  		ITypeConstraint[] constraints = fTypeConstraintFactory.createDefinesConstraint(
  				fConstraintVariableFactory.makeDeclaringTypeVariable(methodBinding),
  				fConstraintVariableFactory.makeRawBindingVariable(methodBinding.getDeclaringClass()));
  		result.addAll(Arrays.asList(constraints));
  		if (! methodBinding.isConstructor() && ! methodBinding.getReturnType().isPrimitive()){
  			ConstraintVariable returnTypeBindingVariable= fConstraintVariableFactory.makeReturnTypeVariable(methodBinding);
  			ConstraintVariable returnTypeVariable= fConstraintVariableFactory.makeTypeVariable(declaration.getReturnType2());
  			ITypeConstraint[] defines= fTypeConstraintFactory.createDefinesConstraint(
  					returnTypeBindingVariable, returnTypeVariable);
  			result.addAll(Arrays.asList(defines));
  		}
  		for (int i= 0, n= declaration.parameters().size(); i < n; i++) {
  			SingleVariableDeclaration paramDecl= (SingleVariableDeclaration)declaration.parameters().get(i);
  			ConstraintVariable parameterTypeVariable= fConstraintVariableFactory.makeParameterTypeVariable(methodBinding, i);
  			ConstraintVariable parameterNameVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(paramDecl.getName(), getContext());
  			ITypeConstraint[] constraint= fTypeConstraintFactory.createDefinesConstraint(
  					parameterTypeVariable, parameterNameVariable);
  			result.addAll(Arrays.asList(constraint));
  		}
  		if (MethodChecks.isVirtual(methodBinding)){
  			Collection constraintsForOverriding = getConstraintsForOverriding(methodBinding);
  			result.addAll(constraintsForOverriding);
  		}
  		return result.toArray(new ITypeConstraint[result.size()]);
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.ConstraintCreator#create(org.eclipse.wst.jsdt.core.dom.ParenthesizedExpression)
  	 */
  	public ITypeConstraint[] create(ParenthesizedExpression node) {
  		ConstraintVariable v1= fConstraintVariableFactory.makeExpressionOrTypeVariable(node, getContext());
  		ConstraintVariable v2= fConstraintVariableFactory.makeExpressionOrTypeVariable(node.getExpression(), getContext());
  		ITypeConstraint[] equal= fTypeConstraintFactory.createEqualsConstraint(v1, v2);
  		return equal;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.FunctionInvocation)
  	 */
  	public ITypeConstraint[] create(FunctionInvocation invocation){
  		List arguments= invocation.arguments();
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>(arguments.size());
  		IFunctionBinding methodBinding= invocation.resolveMethodBinding();
  		if (methodBinding == null)
  			return new ITypeConstraint[0];
  		ITypeConstraint[] returnTypeConstraint= getReturnTypeConstraint(invocation, methodBinding);
  		result.addAll(Arrays.asList(returnTypeConstraint));
  		result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
  		if (invocation.getExpression() != null){
  			if(MethodChecks.isVirtual(methodBinding)){
  				IFunctionBinding[] rootDefs= getRootDefs(methodBinding);		
  				Assert.isTrue(rootDefs.length > 0);
  				ConstraintVariable expressionVar= fConstraintVariableFactory.makeExpressionOrTypeVariable(invocation.getExpression(), getContext());
  				if (rootDefs.length == 1){
  					result.addAll(Arrays.asList(fTypeConstraintFactory.createSubtypeConstraint(expressionVar, fConstraintVariableFactory.makeDeclaringTypeVariable(rootDefs[0]))));
  				}else{	
  					Collection<ITypeConstraint> constraints= new ArrayList<ITypeConstraint>(); 
  					for (int i= 0; i < rootDefs.length; i++) {
  						ConstraintVariable rootDefTypeVar= fConstraintVariableFactory.makeDeclaringTypeVariable(rootDefs[i]);
  						ITypeConstraint[] tc= fTypeConstraintFactory.createSubtypeConstraint(expressionVar, rootDefTypeVar);
  						constraints.addAll(Arrays.asList(tc));
  					}
  					ITypeConstraint[] constraintsArray= constraints.toArray(new ITypeConstraint[constraints.size()]);
  					if (constraintsArray.length > 0){
  						result.add(fTypeConstraintFactory.createCompositeOrTypeConstraint(constraintsArray));
  					}
  				}
  			} else {
  				ConstraintVariable typeVar= fConstraintVariableFactory.makeDeclaringTypeVariable(methodBinding);
  				ConstraintVariable expressionVar= fConstraintVariableFactory.makeExpressionOrTypeVariable(invocation.getExpression(), getContext());
  				result.addAll(Arrays.asList(fTypeConstraintFactory.createSubtypeConstraint(expressionVar, typeVar)));
  			}
  		}
  		return result.toArray(new ITypeConstraint[result.size()]);
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.QualifiedName)
  	 */
  	public ITypeConstraint[] create(QualifiedName qualifiedName){
  		SimpleName name= qualifiedName.getName();
  		Name qualifier= qualifiedName.getQualifier();
  		IBinding nameBinding= name.resolveBinding();
  		if (nameBinding instanceof IVariableBinding){
  			IVariableBinding vb= (IVariableBinding)nameBinding;
  			if (vb.isField())
  				return createConstraintsForAccessToField(vb, qualifier, qualifiedName);
  		} //TODO other bindings 
  		return new ITypeConstraint[0];			
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.ReturnStatement)
  	 */
  	public ITypeConstraint[] create(ReturnStatement returnStatement){
  		if (returnStatement.getExpression() == null)
  			return new ITypeConstraint[0];
  		
  		ConstraintVariable returnTypeVariable= fConstraintVariableFactory.makeReturnTypeVariable(returnStatement);
  		return fTypeConstraintFactory.createSubtypeConstraint(
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(returnStatement.getExpression(), getContext()),
  				returnTypeVariable);
  	}
  	
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration)
  	 */
  	public ITypeConstraint[] create(SingleVariableDeclaration svd){
  		ITypeConstraint[] defines= fTypeConstraintFactory.createDefinesConstraint(
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(svd.getName(), getContext()),
  				fConstraintVariableFactory.makeTypeVariable(svd.getType()));
  		if (svd.getInitializer() == null)
  			return defines;	
  		ITypeConstraint[] constraints = fTypeConstraintFactory.createSubtypeConstraint(
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(svd.getInitializer(), getContext()),
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(svd.getName(), getContext()));
  		if (defines.length == 0 && constraints.length == 0){
  			return new ITypeConstraint[0];
  		} else if (defines.length == 0){
  			return constraints;
  		} else if (constraints.length == 0){
  			return defines;
  		} else {
  			List<ITypeConstraint> all= new ArrayList<ITypeConstraint>();
  			all.addAll(Arrays.asList(defines));
  			all.addAll(Arrays.asList(constraints));
  			return all.toArray(new ITypeConstraint[all.size()]);
  		}
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.SuperConstructorInvocation)
  	 */
  	public ITypeConstraint[] create(SuperConstructorInvocation invocation){
  		List<ITypeConstraint> arguments= invocation.arguments();
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>(arguments.size());
  		IFunctionBinding methodBinding= invocation.resolveConstructorBinding();
  		result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
  		return result.toArray(new ITypeConstraint[result.size()]);
  	}
  	
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.SuperFieldAccess)
  	 */
  	public ITypeConstraint[] create(SuperFieldAccess access){
  		SimpleName name= access.getName();
  		IBinding binding= name.resolveBinding();
  		if (! (binding instanceof IVariableBinding))
  			return new ITypeConstraint[0];	
  		IVariableBinding vb= (IVariableBinding)binding;
  		return createConstraintsForAccessToField(vb, null, access);
  	}
  		
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation)
  	 */
  	public ITypeConstraint[] create(SuperMethodInvocation invocation){
  		List<ITypeConstraint> arguments= invocation.arguments();
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>(arguments.size());
  		IFunctionBinding methodBinding= invocation.resolveMethodBinding();
  		ITypeConstraint[] returnTypeConstraint= getReturnTypeConstraint(invocation, methodBinding);
  		result.addAll(Arrays.asList(returnTypeConstraint));
  		result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
  		return result.toArray(new ITypeConstraint[result.size()]);
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.ThisExpression)
  	 */
  	public ITypeConstraint[] create(ThisExpression expression){
  		ConstraintVariable thisExpression= fConstraintVariableFactory.makeExpressionOrTypeVariable(expression, getContext());
  		ConstraintVariable declaringType= fConstraintVariableFactory.makeRawBindingVariable(expression.resolveTypeBinding());//TODO fix this - can't use Decl(M) because 'this' can live outside of methods
  		return fTypeConstraintFactory.createDefinesConstraint(thisExpression, declaringType);
  	}
  	
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.VariableDeclarationExpression)
  	 */
  	public ITypeConstraint[] create(VariableDeclarationExpression vde){
  		return getConstraintsFromFragmentList(vde.fragments(), vde.getType());
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment)
  	 */
  	public ITypeConstraint[] create(VariableDeclarationFragment vdf){
  		if (vdf.getInitializer() == null)
  			return new ITypeConstraint[0];	
  		return fTypeConstraintFactory.createSubtypeConstraint(
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(vdf.getInitializer(), getContext()),
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(vdf.getName(), getContext()));
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.VariableDeclarationStatement)
  	 */
  	public ITypeConstraint[] create(VariableDeclarationStatement vds){
  		return getConstraintsFromFragmentList(vds.fragments(), vds.getType());
  	}
  
  
  	//--------- private helpers ----------------//
  	
  	private Collection<ITypeConstraint> getConstraintsForFieldDeclaringTypes(FieldDeclaration fd) {
  		Collection<ITypeConstraint> result= new ArrayList<ITypeConstraint>(fd.fragments().size());
  		for (Object item : fd.fragments()) {
  			VariableDeclarationFragment varDecl= (VariableDeclarationFragment) item;
  			IVariableBinding binding= varDecl.resolveBinding();
  			Assert.isTrue(binding.isField());
  			result.addAll(Arrays.asList(fTypeConstraintFactory.createDefinesConstraint(
  					fConstraintVariableFactory.makeDeclaringTypeVariable(binding),
  					fConstraintVariableFactory.makeRawBindingVariable(binding.getDeclaringClass()))));
  		}
  		return result;
  	}
  	
  	private Collection<ITypeConstraint> getConstraintsForHiding(FieldDeclaration fd) {
  		Collection<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
  		for (Object item : fd.fragments()) {
  			result.addAll(getConstraintsForHiding((VariableDeclarationFragment) item));
  		}
  		return result;
  	}
  	
  	private Collection<ITypeConstraint> getConstraintsForHiding(VariableDeclarationFragment fragment) {
  		Collection<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
  		IVariableBinding fieldBinding= fragment.resolveBinding();
  		Assert.isTrue(fieldBinding.isField());
  		Set<ITypeBinding> declaringTypes= getDeclaringSuperTypes(fieldBinding);
  		ConstraintVariable hiddingFieldVar= fConstraintVariableFactory.makeDeclaringTypeVariable(fieldBinding);
  		for (ITypeBinding declaringSuperType : declaringTypes) {
  			IVariableBinding hiddenField= findField(fieldBinding, declaringSuperType);
  			Assert.isTrue(hiddenField.isField());
  			ConstraintVariable hiddenFieldVar= fConstraintVariableFactory.makeDeclaringTypeVariable(hiddenField);
  			result.addAll(Arrays.asList(fTypeConstraintFactory.createStrictSubtypeConstraint(hiddingFieldVar, hiddenFieldVar)));
  		}
  		return result;
  	}
  	
  	private ITypeConstraint[] getConstraintsFromFragmentList(List<VariableDeclarationFragment> fragments, Type type) {
  		ConstraintVariable typeVariable= fConstraintVariableFactory.makeTypeVariable(type);
  		int size = fragments.size();
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>((size * (size - 1))/2);
  		for (VariableDeclarationFragment fragment1 : fragments) {
  			SimpleName fragment1Name= fragment1.getName();
  			result.addAll(Arrays.asList(fTypeConstraintFactory.createDefinesConstraint(
  					fConstraintVariableFactory.makeExpressionOrTypeVariable(fragment1Name, getContext()),
  					typeVariable)));
  			for (VariableDeclarationFragment fragment2 : fragments) {
  				result.addAll(Arrays.asList(fTypeConstraintFactory.createEqualsConstraint(
  						fConstraintVariableFactory.makeExpressionOrTypeVariable(fragment1Name, getContext()),
  						fConstraintVariableFactory.makeExpressionOrTypeVariable(fragment2.getName(), getContext()))));
  			}
  		}
  		return result.toArray(new ITypeConstraint[result.size()]);
  	}
  	
  	private Collection<ITypeConstraint> getConstraintsForOverriding(IFunctionBinding overriddingMethod) {
  		Collection<ITypeConstraint> result= new ArrayList<ITypeConstraint>();
  		Set<ITypeBinding> declaringSupertypes= getDeclaringSuperTypes(overriddingMethod);
  		for (ITypeBinding superType : declaringSupertypes) {
  			IFunctionBinding overriddenMethod= findMethod(overriddingMethod, superType);
  			Assert.isNotNull(overriddenMethod);//because we asked for declaring types
  			if (Bindings.equals(overriddingMethod, overriddenMethod)) continue;
  			ITypeConstraint[] returnTypeConstraint= fTypeConstraintFactory.createEqualsConstraint(
  					fConstraintVariableFactory.makeReturnTypeVariable(overriddenMethod),
  					fConstraintVariableFactory.makeReturnTypeVariable(overriddingMethod));
  			result.addAll(Arrays.asList(returnTypeConstraint));
  			Assert.isTrue(overriddenMethod.getParameterTypes().length == overriddingMethod.getParameterTypes().length);
  			for (int i= 0, n= overriddenMethod.getParameterTypes().length; i < n; i++) {
  				ITypeConstraint[] parameterTypeConstraint= fTypeConstraintFactory.createEqualsConstraint(
  						fConstraintVariableFactory.makeParameterTypeVariable(overriddenMethod, i),
  						fConstraintVariableFactory.makeParameterTypeVariable(overriddingMethod, i));
  				result.addAll(Arrays.asList(parameterTypeConstraint));
  			}
  			ITypeConstraint[] declaringTypeConstraint= fTypeConstraintFactory.createStrictSubtypeConstraint(
  					fConstraintVariableFactory.makeDeclaringTypeVariable(overriddingMethod),
  					fConstraintVariableFactory.makeDeclaringTypeVariable(overriddenMethod));
  			result.addAll(Arrays.asList(declaringTypeConstraint));
  		}
  		return result;
  	}
  	
  	private ITypeConstraint[] getReturnTypeConstraint(Expression invocation, IFunctionBinding methodBinding){
  		if (methodBinding == null || methodBinding.isConstructor() || methodBinding.getReturnType().isPrimitive())
  			return new ITypeConstraint[0];
  		ConstraintVariable returnTypeVariable= fConstraintVariableFactory.makeReturnTypeVariable(methodBinding);
  		ConstraintVariable invocationVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(invocation, getContext());
  		return fTypeConstraintFactory.createDefinesConstraint(invocationVariable, returnTypeVariable);
  	}
  	
  	private ITypeConstraint[] getArgumentConstraints(List<?> arguments, IFunctionBinding methodBinding){
  		List<ITypeConstraint> result= new ArrayList<ITypeConstraint>(arguments.size());
  		for (int i= 0, n= arguments.size(); i < n; i++) {
  			if (arguments.get(i) instanceof Expression) {
  				Expression argument= (Expression) arguments.get(i);
  				ConstraintVariable expressionVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(argument, getContext());
  				ConstraintVariable parameterTypeVariable= fConstraintVariableFactory.makeParameterTypeVariable(methodBinding, i);
  				ITypeConstraint[] argConstraint= fTypeConstraintFactory.createSubtypeConstraint(expressionVariable, parameterTypeVariable);
  				result.addAll(Arrays.asList(argConstraint));
  			}
  		}
  		return result.toArray(new ITypeConstraint[result.size()]);		
  	}
  	
  	private static Type getTypeParent(ArrayInitializer arrayInitializer) {
  		if (arrayInitializer.getParent() instanceof ArrayCreation){
  			return ((ArrayCreation)arrayInitializer.getParent()).getType().getElementType();
  		} else if (arrayInitializer.getParent() instanceof ArrayInitializer){
  			return getTypeParent((ArrayInitializer) arrayInitializer.getParent());
  		} else if (arrayInitializer.getParent() instanceof VariableDeclaration){
  			VariableDeclaration parent= (VariableDeclaration)arrayInitializer.getParent();
  			
  			if (parent.getParent() instanceof VariableDeclarationStatement){
  				Type type= ((VariableDeclarationStatement)parent.getParent()).getType();
  				return ASTNodes.getElementType(type);
  			} else if (parent.getParent() instanceof VariableDeclarationExpression){
  				Type type= ((VariableDeclarationExpression)parent.getParent()).getType();
  				return ASTNodes.getElementType(type);
  			} else if (parent.getParent() instanceof FieldDeclaration){
  				Type type= ((FieldDeclaration)parent.getParent()).getType();
  				return ASTNodes.getElementType(type);
  			}
  		}
  		Assert.isTrue(false);//array initializers are allowed in only 2 places
  		return null;
  	}
  	
  	private ITypeConstraint[] createOrOrSubtypeConstraint(ConstraintVariable var1, ConstraintVariable var2){
  		ITypeConstraint[] c1= fTypeConstraintFactory.createSubtypeConstraint(var1, var2);
  		ITypeConstraint[] c2= fTypeConstraintFactory.createSubtypeConstraint(var2, var1);
  		if (c1.length == 0 && c2.length == 0){
  			return new ITypeConstraint[0];
  		}
  		return new ITypeConstraint[]{ fTypeConstraintFactory.createCompositeOrTypeConstraint(new ITypeConstraint[]{c1[0], c2[0]}) };
  	}
  	
  	private ITypeConstraint[] createConstraintsForAccessToField(IVariableBinding fieldBinding, Expression qualifier, Expression accessExpression){
  		Assert.isTrue(fieldBinding.isField());
  		ITypeConstraint[] defines= fTypeConstraintFactory.createDefinesConstraint(
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(accessExpression, getContext()),
  				fConstraintVariableFactory.makeRawBindingVariable(fieldBinding.getType()));
  		if (qualifier == null)
  			return defines;
  		ITypeConstraint[] subType= fTypeConstraintFactory.createSubtypeConstraint(
  				fConstraintVariableFactory.makeExpressionOrTypeVariable(qualifier, getContext()),
  				fConstraintVariableFactory.makeDeclaringTypeVariable(fieldBinding));
  		
  		if (defines.length == 0){
  			return subType;
  		} else if (subType.length == 0){
  			return defines;
  		} else {
  			return new ITypeConstraint[]{defines[0], subType[0]};
  		}
  	}
  	
  	private static IVariableBinding findField(IVariableBinding fieldBinding, ITypeBinding type) {
  		if (fieldBinding.getDeclaringClass().equals(type))
  			return fieldBinding;
  		return Bindings.findFieldInType(type, fieldBinding.getName());
  	}
  	
  	/* 
  	 * return Set of ITypeBindings
  	 */
  	private static Set<ITypeBinding> getDeclaringSuperTypes(IVariableBinding fieldBinding) {
  		ITypeBinding[] allSuperTypes= Bindings.getAllSuperTypes(fieldBinding.getDeclaringClass());
  		Set<ITypeBinding> result= new HashSet<ITypeBinding>();
  		for (ITypeBinding type : allSuperTypes) {
  			if (findField(fieldBinding, type) != null)
  				result.add(type);
  		}
  		return result;
  	}
  	
  	//--- RootDef ----//
  	protected static IFunctionBinding[] getRootDefs(IFunctionBinding methodBinding) {
  		Set<ITypeBinding> declaringSuperTypes= getDeclaringSuperTypes(methodBinding);
  		Set<IFunctionBinding> result= new LinkedHashSet<IFunctionBinding>();
  		for (ITypeBinding type : declaringSuperTypes) {
  			if (! containsASuperType(type, declaringSuperTypes))
  				result.add(findMethod(methodBinding, type));
  		}
  		
  		if (result.size() == 0){
  			result.add(methodBinding);
  		}
  		return result.toArray(new IFunctionBinding[result.size()]);
  	}
  	
  	/* 
  	 * @param declaringSuperTypes Set of ITypeBindings
  	 * @return <code>true</code> iff <code>declaringSuperTypes</code> contains a type
  	 * 		which is a strict supertype of <code>type</code>
  	 */
  	private static boolean containsASuperType(ITypeBinding type, Set<ITypeBinding> declaringSuperTypes) {
  		for (ITypeBinding maybeSuperType : declaringSuperTypes) {
  			if (! Bindings.equals(maybeSuperType, type) && Bindings.isSuperType(maybeSuperType, type))
  				return true;
  		}
  		return false;
  	}
  	
  	/* 
  	 * return Set of ITypeBindings
  	 */
  	protected static Set<ITypeBinding> getDeclaringSuperTypes(IFunctionBinding methodBinding) {
  		ITypeBinding superClass = methodBinding.getDeclaringClass();
  		Set<ITypeBinding> allSuperTypes= new LinkedHashSet<ITypeBinding>();
  		allSuperTypes.addAll(Arrays.asList(Bindings.getAllSuperTypes(superClass)));
  		if (allSuperTypes.isEmpty())
  			allSuperTypes.add(methodBinding.getDeclaringClass()); //TODO: Why only iff empty? The declaring class is not a supertype ...
  		Set<ITypeBinding> result= new HashSet<ITypeBinding>();
  		for (ITypeBinding type : allSuperTypes) {
  			if (findMethod(methodBinding, type) != null)
  				result.add(type);
  		}
  		return result;
  	}
  	
  	protected static IFunctionBinding findMethod(IFunctionBinding methodBinding, ITypeBinding type) {
  		if (methodBinding.getDeclaringClass().equals(type))
  			return methodBinding;
  		return Bindings.findOverriddenMethodInType(type, methodBinding);
  	}
  
  	private static boolean isClassBinding(ITypeBinding typeBinding){
  		return typeBinding != null && typeBinding.isClass();
  	}
  	
  }
  