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.refactoring.typeconstraints2;
  
  import java.util.Iterator;
  import java.util.Stack;
  
  import org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.types.AbstractTypeVariable;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.types.HierarchyType;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.typeconstraints.types.TType;
  
  public class TTypes {
  	
  	private static class AllSupertypesIterator implements Iterator {
  		private final Stack fWorklist;
  		
  		public AllSupertypesIterator(TType type) {
  			fWorklist= new Stack();
  			pushSupertypes(type);
  		}
  	
  		public boolean hasNext() {
  			return ! fWorklist.empty();
  		}
  	
  		public Object next() {
  			TType result= (TType) fWorklist.pop();
  			pushSupertypes(result);
  			return result;
  		}
  	
  		private void pushSupertypes(TType type) {
  			if (type.isJavaLangObject())
  				return;
  			
  			if (type.isTypeVariable() || type.isCaptureType()) {
  				TType[] bounds= ((AbstractTypeVariable) type).getBounds();
  				for (int i= 0; i < bounds.length; i++)
  					fWorklist.push(bounds[i].getTypeDeclaration());
  			
  			} else {
  				TType superclass= type.getSuperclass();
  				if (superclass == null) {
  					if (type.isInterface())
  						fWorklist.push(type.getEnvironment().getJavaLangObject());
  				} else {
  					fWorklist.push(superclass.getTypeDeclaration());
  				}
  				TType[] interfaces= type.getInterfaces();
  				for (int i= 0; i < interfaces.length; i++)
  					fWorklist.push(interfaces[i].getTypeDeclaration());
  			}
  		}
  	
  		public void remove() {
  			throw new UnsupportedOperationException();
  		}
  	}
  	
  	private static class AllSubtypesIterator implements Iterator {
  		private final Stack fWorklist;
  		
  		public AllSubtypesIterator(TType type) {
  			fWorklist= new Stack();
  			fWorklist.push(type.getTypeDeclaration());
  		}
  	
  		public boolean hasNext() {
  			return ! fWorklist.empty();
  		}
  	
  		public Object next() {
  			TType result= (TType) fWorklist.pop();
  			TType[] subTypes= result.getSubTypes();
  			for (int i= 0; i < subTypes.length; i++)
  				fWorklist.push(subTypes[i].getTypeDeclaration());
  			
  			return result;
  		}
  	
  		public void remove() {
  			throw new UnsupportedOperationException();
  		}
  	}
  	
  	private TTypes() {
  		// no instances
  	}
  
  	public static TType createArrayType(TType elementType, int dimensions) {
  		return elementType.getEnvironment().createArrayType(elementType, dimensions);
  	}
  
  	/**
  	 * @return all subtypes of this type (including this type)
  	 */
  	public static Iterator getAllSubTypesIterator(TType type) {
  		return new AllSubtypesIterator(type);
  	}
  
  	/**
  	 * @return all proper supertypes of this type
  	 */
  	public static Iterator getAllSuperTypesIterator(TType type) {
  		return new AllSupertypesIterator(type);
  	}
  	
  	/**
  	 * @param rhs
  	 * @param lhs
  	 * @return <code>true</code> iff an expression of type 'rhs' can be assigned to a variable of type 'lhs'.
  	 * Type arguments of generic / raw / parameterized types are <b>not</b> considered.
  	 */
  	public static boolean canAssignTo(TType rhs, TType lhs) {
  		if (rhs.isHierarchyType() && lhs.isHierarchyType()) {
  			HierarchyType rhsGeneric= (HierarchyType) rhs.getTypeDeclaration();
  			HierarchyType lhsGeneric= (HierarchyType) lhs.getTypeDeclaration();
  			return lhs.isJavaLangObject() || rhsGeneric.equals(lhsGeneric) || rhsGeneric.isSubType(lhsGeneric);
  			
  		} else if (rhs.isTypeVariable()) {
  			if (rhs.canAssignTo(lhs))
  				return true;
  			return lhs.isJavaLangObject();
  			
  		} else {
  			return rhs.canAssignTo(lhs);
  		}
  	}
  	
  }
  