content: /*******************************************************************************
   * Copyright (c) 2000, 2007 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:
   *   Robert M. Fuhrer (rfuhrer@watson.ibm.com), IBM Corporation - initial API and implementation
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets;
  
  import java.util.Iterator;
  
  import org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.types.ArrayType;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.types.TType;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints2.TTypes;
  
  public class SubTypesSet extends TypeSet {
  	/**
  	 * The set of "base types" defining the upper bounds of this set.
  	 */
  	private TypeSet fUpperBounds;
  
  	SubTypesSet(TypeSet superTypes) {
  		super(superTypes.getTypeSetEnvironment());
  		fUpperBounds= superTypes;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#isUniverse()
  	 */
  	public boolean isUniverse() {
  		return fUpperBounds.isUniverse() || fUpperBounds.contains(getJavaLangObject());
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#makeClone()
  	 */
  	public TypeSet makeClone() {
  		return this; //new SubTypesSet(fUpperBounds.makeClone());
  	}
  
  	/* (non-Javadoc)
  	 * @see java.lang.Object#equals(java.lang.Object)
  	 */
  	public boolean equals(Object o) {
  		if (o instanceof SubTypesSet) {
  			SubTypesSet other= (SubTypesSet) o;
  			return other.fUpperBounds.equals(fUpperBounds);
  //		} else if (o instanceof TypeSet) {
  //			TypeSet other= (TypeSet) o;
  //			if (other.isUniverse() && isUniverse())
  //				return true;
  //			return enumerate().equals(other.enumerate());
  		} else
  			return false;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#intersectedWith(org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.EnumeratedTypeSet)
  	 */
  	protected TypeSet specialCasesIntersectedWith(TypeSet s2) {
  		if (fUpperBounds.equals(s2))
  			return s2; // xsect(subTypes(A),A) = A
  		if (s2 instanceof SubTypesSet) {
  			SubTypesSet st2= (SubTypesSet) s2;
  
  			if (fUpperBounds.isSingleton() && st2.fUpperBounds.isSingleton()) {
  				TType t1= this.fUpperBounds.anyMember();
  				TType t2= st2.fUpperBounds.anyMember();
  
  				if (TTypes.canAssignTo(t2, t1))
  					return new SubTypesSet(st2.fUpperBounds);
  			} else if (fUpperBounds instanceof SubTypesSet) {
  				// xsect(subTypes(superTypes(A)), subTypes(A)) = subTypes(A)
  				SubTypesSet myUpperSubTypes= (SubTypesSet) fUpperBounds;
  
  				if (myUpperSubTypes.lowerBound().equals(st2.lowerBound()))
  					return st2;
  			}
  		}
  		if (s2 instanceof SubTypesOfSingleton) {
  			SubTypesOfSingleton st2= (SubTypesOfSingleton) s2;
  
  			if (fUpperBounds.isSingleton()) {
  				TType t1= this.fUpperBounds.anyMember();
  				TType t2= st2.uniqueUpperBound();
  
  				if (TTypes.canAssignTo(t2, t1))
  					return getTypeSetEnvironment().createSubTypesOfSingleton(t2);
  			} else if (fUpperBounds instanceof SubTypesOfSingleton) {
  				// xsect(subTypes(superTypes(A)), subTypes(A)) = subTypes(A)
  				SubTypesOfSingleton myUpperSubTypes= (SubTypesOfSingleton) fUpperBounds;
  
  				if (myUpperSubTypes.uniqueLowerBound().equals(st2.uniqueLowerBound()))
  					return st2;
  			}
  		}
  		
  		if (s2 instanceof SuperTypesSet) {
  			SuperTypesSet st2= (SuperTypesSet) s2;
  
  			if (fUpperBounds.equals(st2.lowerBound()))
  				return fUpperBounds;
  
  			if (fUpperBounds instanceof TypeSetIntersection) {
  				// (intersect (subTypes (intersect (superTypes A) B))
  				//            (superTypes A)) =>
  				// (intersect (superTypes A) (subTypes B))
  				TypeSetIntersection lbXSect= (TypeSetIntersection) fUpperBounds;
  				TypeSet xsectLeft= lbXSect.getLHS();
  				TypeSet xsectRight= lbXSect.getRHS();
  
  				if (xsectLeft.equals(st2.lowerBound()))
  					return new TypeSetIntersection(s2, new SubTypesSet(xsectRight));
  			}
  		}
  		return null;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#subTypes()
  	 */
  	public TypeSet subTypes() {
  		return this; // makeClone();
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#isEmpty()
  	 */
  	public boolean isEmpty() {
  		return fUpperBounds.isEmpty();
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#contains(TType)
  	 */
  	public boolean contains(TType t) {
  		if (fEnumCache != null) return fEnumCache.contains(t);
  
  		if (fUpperBounds.contains(t))
  			return true;
  
  		// Find the "upper frontier", i.e. the upper bound, and see whether
  		// the given type is a subtype of any of those.
  		Iterator ubIter= fUpperBounds.upperBound().iterator();
  
  		for(; ubIter.hasNext(); ) {
  			TType ub= (TType) ubIter.next();
  
  			if (TTypes.canAssignTo(t, ub))
  				return true;
  		}
  		return false;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#containsAll(org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.EnumeratedTypeSet)
  	 */
  	public boolean containsAll(TypeSet s) {
  		if (fEnumCache != null) return fEnumCache.containsAll(s);
  
  		if (fUpperBounds.containsAll(s))
  			return true;
  
  		// Make sure all elements of s are contained in this set
  		for(Iterator sIter= s.iterator(); sIter.hasNext(); ) {
  			TType t= (TType) sIter.next();
  			boolean found= false;
  
  			// Scan the "upper frontier", i.e. the upper bound set, and see whether
  			// 't' is a subtype of any of those.
  			for(Iterator ubIter= fUpperBounds /*.upperBound() */.iterator(); ubIter.hasNext(); ) {
  				TType ub= (TType) ubIter.next();
  
  				if (TTypes.canAssignTo(t, ub)) {
  					found= true;
  					break;
  				}
  			}
  			if (!found) return false;
  		}
  		return true;
  	}
  
  	/**
  	 * Returns the element type of the given TType, if an array type, or the
  	 * given TType itself, otherwise.
  	 */
  	private TType getElementTypeOf(TType t) {
  		if (t instanceof ArrayType)
  			return ((ArrayType) t).getElementType();
  		return t;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#isSingleton()
  	 */
  	public boolean isSingleton() {
  		if (!fUpperBounds.isSingleton())
  			return false;
  
  		TType t= fUpperBounds.anyMember();
  
  		return getElementTypeOf(t).getSubTypes().length == 0;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#anyMember()
  	 */
  	public TType anyMember() {
  		return fUpperBounds.anyMember();
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#upperBound()
  	 */
  	public TypeSet upperBound() {
  		return fUpperBounds; // perhaps should be unmodifiable?
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#lowerBound()
  	 */
  	public TypeSet lowerBound() {
  		return enumerate().lowerBound();
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#iterator()
  	 */
  	public Iterator iterator() {
  		return enumerate().iterator();
  	}
  
  	/* (non-Javadoc)
  	 * @see java.lang.Object#toString()
  	 */
  	public String toString() {
  		return "<" + fID + ": subTypes(" + fUpperBounds + ")>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#hasUniqueLowerBound()
  	 */
  	public boolean hasUniqueLowerBound() {
  		return false;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#hasUniqueUpperBound()
  	 */
  	public boolean hasUniqueUpperBound() {
  		return fUpperBounds.isSingleton();
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#uniqueLowerBound()
  	 */
  	public TType uniqueLowerBound() {
  		return null;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#uniqueUpperBound()
  	 */
  	public TType uniqueUpperBound() {
  		return fUpperBounds.isSingleton() ? fUpperBounds.anyMember() : null;
  	}
  
  	private EnumeratedTypeSet fEnumCache= null;
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.typesets.TypeSet#enumerate()
  	 */
  	public EnumeratedTypeSet enumerate() {
  		if (fEnumCache == null) {
  			fEnumCache= new EnumeratedTypeSet(getTypeSetEnvironment());
  
  			for(Iterator iter= fUpperBounds.iterator(); iter.hasNext(); ) {
  				TType ub= (TType) iter.next();
  
  				if (ub instanceof ArrayType) {
  					ArrayType at= (ArrayType) ub;
  					int numDims= at.getDimensions();
  					for(Iterator elemSubIter=TTypes.getAllSubTypesIterator(at.getElementType()); elemSubIter.hasNext(); )
  						fEnumCache.add(TTypes.createArrayType((TType) elemSubIter.next(), numDims));
  				} else {
  					for (Iterator iterator= TTypes.getAllSubTypesIterator(ub); iterator.hasNext();) {
  						fEnumCache.fMembers.add(iterator.next());
  					}
  				}
  				fEnumCache.add(ub);
  			}
  //			fEnumCache.initComplete();
  		}
  		return fEnumCache;
  	}
  }
  