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
   *******************************************************************************/
  
  
  package org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints;
  
  import java.util.HashMap;
  import java.util.Map;
  
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  
  public class TypeConstraintFactory implements ITypeConstraintFactory {
  
  	private Map/*<ConstraintVariable, Map<ConstraintVariable, Map<ConstraintOperator, SimpleTypeConstraint>>*/ fSimpleConstraints= new HashMap();
  	private Map/*<ConstraintVariable, Map<String, CompositeOrTypeConstraint>>*/ fOrConstraints= new HashMap();
  	
  	protected static final boolean PRINT_STATS= false;
  	protected int fNrCreated= 0;
  	protected int fNrFiltered= 0;
  	protected int fNrRetrieved= 0;	
  	
  	// Only to be called by the createXXXConstraint() methods
  	private SimpleTypeConstraint createSimpleTypeConstraint(ConstraintVariable v1, ConstraintVariable v2, ConstraintOperator operator) {
  		if (fSimpleConstraints.containsKey(v1)){
  			Map m2= (Map)fSimpleConstraints.get(v1);
  			if (m2.containsKey(v2)){
  				Map m3= (Map)m2.get(v2);
  				if (m3.containsKey(operator)){
  					if (PRINT_STATS) fNrRetrieved++;
  					if (PRINT_STATS) dumpStats();
  					return (SimpleTypeConstraint)m3.get(operator);
  				} else {
  					return storeConstraint(v1, v2, operator, m3);
  				}
  			} else {
  				Map m3= new HashMap();
  				m2.put(v2, m3);
  				return storeConstraint(v1, v2, operator, m3);
  			}
  		} else {
  			Map m2= new HashMap();
  			fSimpleConstraints.put(v1, m2);
  			Map m3= new HashMap();
  			m2.put(v2, m3);
  			return storeConstraint(v1, v2, operator, m3);
  		}
  	}
  	
  	private SimpleTypeConstraint storeConstraint(ConstraintVariable v1, ConstraintVariable v2, ConstraintOperator operator, Map m3) {
  		SimpleTypeConstraint constraint= new SimpleTypeConstraint(v1, v2, operator);
  		m3.put(operator, constraint);
  		if (PRINT_STATS) fNrCreated++;
  		if (PRINT_STATS) dumpStats();
  		return constraint;
  	}
  	
  	public ITypeConstraint[] createConstraint(ConstraintVariable v1, ConstraintVariable v2, ConstraintOperator operator){
  		if (filter(v1, v2, operator)){
  			return new ITypeConstraint[0];
  		} else {
  			return new ITypeConstraint[]{ createSimpleTypeConstraint(v1, v2, operator) };
  		}
  	}
  	
  	public ITypeConstraint[] createSubtypeConstraint(ConstraintVariable v1, ConstraintVariable v2){
  		return createConstraint(v1, v2, ConstraintOperator.createSubTypeOperator());
  	}
  	
  	public ITypeConstraint[] createStrictSubtypeConstraint(ConstraintVariable v1, ConstraintVariable v2){
  		return createConstraint(v1, v2, ConstraintOperator.createStrictSubtypeOperator());
  	}
  	
  	public ITypeConstraint[] createEqualsConstraint(ConstraintVariable v1, ConstraintVariable v2){
  		return createConstraint(v1, v2, ConstraintOperator.createEqualsOperator());
  	}
  	
  	public ITypeConstraint[] createDefinesConstraint(ConstraintVariable v1, ConstraintVariable v2){
  		return createConstraint(v1, v2, ConstraintOperator.createDefinesOperator());
  	}
  
  	/**
  	 * {@inheritDoc}
  	 * Avoid creating constraints involving primitive types and self-constraints.
  	 */
  	public boolean filter(ConstraintVariable v1, ConstraintVariable v2, ConstraintOperator operator) {
  		if ((v1.getBinding() != null && v1.getBinding().isPrimitive() &&
  				v2.getBinding() != null && v2.getBinding().isPrimitive()) ||
  					v1 == v2) {
  			if (PRINT_STATS) fNrFiltered++;
  			if (PRINT_STATS) dumpStats();
  			return true;
  		}
  		return false;
  	}
  
  	/*
  	 * (non-Javadoc)
  	 * 
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.ITypeConstraintFactory#createCompositeOrTypeConstraint(org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.ITypeConstraint[])
  	 */
  	public CompositeOrTypeConstraint createCompositeOrTypeConstraint(ITypeConstraint[] constraints){
  		ConstraintVariable left= ((SimpleTypeConstraint)constraints[0]).getLeft();
  		StringBuilder boundsBuilder= new StringBuilder();
  		for (int i= 0; i < constraints.length; i++){
  			ConstraintVariable right= ((SimpleTypeConstraint)constraints[i]).getRight();
  			ITypeBinding binding= right.getBinding();
  			boundsBuilder.append(binding.getQualifiedName());
  			boundsBuilder.append(',');
  		}
  		String bounds = boundsBuilder.toString();
  		
  		if (fOrConstraints.containsKey(left)){
  			Map m2= (Map)fOrConstraints.get(left);
  			if (m2.containsKey(bounds)){
  				if (PRINT_STATS) fNrRetrieved++;
  				if (PRINT_STATS) dumpStats();
  				return (CompositeOrTypeConstraint)m2.get(bounds);
  			} else {
  				CompositeOrTypeConstraint constraint= new CompositeOrTypeConstraint(constraints);
  				m2.put(bounds, constraint);
  				if (PRINT_STATS) dumpStats();
  				if (PRINT_STATS) fNrCreated++;
  				return constraint;
  			}
  		} else {
  			Map m2= new HashMap();
  			fOrConstraints.put(left, m2);
  			CompositeOrTypeConstraint constraint= new CompositeOrTypeConstraint(constraints);
  			m2.put(bounds, constraint);
  			if (PRINT_STATS) dumpStats();
  			if (PRINT_STATS) fNrCreated++;
  			return constraint;
  		}
  	}
  	
  	protected void dumpStats() {
  		System.out.println("Constraints: " + fNrCreated + " created, " + fNrRetrieved + " retrieved, " + fNrFiltered + " filtered");	 //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
  	}
  	
  }
  