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
   *     Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
   *       bug "inline method - doesn't handle implicit cast" (see
   *       https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.corext.dom;
  
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  import org.eclipse.wst.jsdt.core.dom.PrimitiveType;
  import org.eclipse.wst.jsdt.core.dom.PrimitiveType.Code;
  /**
  *
  * 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 class TypeRules {
  
  	/**
  	 * Tests if a two primitive types are assign compatible
  	 * @param toAssignCode The binding of the type to assign
  	 * @param definedTypeCode The type of the object that is assigned
  	 * @return boolean Returns true if definedType = typeToAssign is true
  	 */
  	public static boolean canAssignPrimitive(PrimitiveType.Code toAssignCode, PrimitiveType.Code definedTypeCode) {
  		//	definedTypeCode = typeCodeToAssign;
  		if (toAssignCode == definedTypeCode) {
  			return true;
  		}
  		if (definedTypeCode == PrimitiveType.BOOLEAN || toAssignCode == PrimitiveType.BOOLEAN) {
  			return false;
  		}
  		if (definedTypeCode == PrimitiveType.CHAR && toAssignCode == PrimitiveType.BYTE) {
  			return false;
  		}
  		return getTypeOrder(definedTypeCode) > getTypeOrder(toAssignCode);
  	}
  		
  	/**
  	 * Tests if two types are assign compatible. Void types are never compatible.
  	 * @param typeToAssign The binding of the type to assign
  	 * @param definedType The type of the object that is assigned
  	 * @return boolean Returns true if definedType = typeToAssign is true
  	 */
  	public static boolean canAssign(ITypeBinding typeToAssign, ITypeBinding definedType) {
  		//see bug 80715 
  		
  		// definedType = typeToAssign;
  		
  		String voidName= PrimitiveType.VOID.toString();
  		if (voidName.equals(typeToAssign.getName()) || voidName.equals(definedType.getName())) {
  			return false;
  		}
  
  		if (typeToAssign.isNullType()) {
  			return !definedType.isPrimitive();
  		}
  		if (definedType.isArray()) {
  			if (!typeToAssign.isArray()) {
  				return false; // can not assign a non-array type to an array
  			}
  			int definedDim= definedType.getDimensions();
  			int toAssignDim= typeToAssign.getDimensions();
  			if (definedDim == toAssignDim) {
  				definedType= definedType.getElementType();
  				typeToAssign= typeToAssign.getElementType();
  				if (typeToAssign.isPrimitive() && typeToAssign != definedType) {
  					return false; // can't assign arrays of different primitive types to each other
  				}
  				// fall through
  			} else if (definedDim < toAssignDim) {
  				return isArrayCompatible(definedType.getElementType());
  			} else {
  				return false;
  			}
  		}
  
  		if (typeToAssign.isPrimitive()) {
  			if (!definedType.isPrimitive()) {
  				return false;
  			}
  			PrimitiveType.Code toAssignCode= PrimitiveType.toCode(typeToAssign.getName());
  			PrimitiveType.Code definedTypeCode= PrimitiveType.toCode(definedType.getName());
  			return canAssignPrimitive(toAssignCode, definedTypeCode);
  		} else {
  			if (definedType.isPrimitive()) {
  				return false;
  			}
  
  			if (typeToAssign.isArray()) {
  				return isArrayCompatible(definedType);
  			}
  			if (isJavaLangObject(definedType)) { 
  				return true;
  			}
  			return Bindings.isSuperType(definedType, typeToAssign);
  		}
  	}
  
  	private static int getTypeOrder(Code type) {
  		if (type == PrimitiveType.BYTE)
  			return 2;
  		if (type == PrimitiveType.CHAR)
  			return 3;
  		if (type == PrimitiveType.SHORT)
  			return 3;
  		if (type == PrimitiveType.INT)
  			return 4;
  		if (type == PrimitiveType.LONG)
  			return 5;
  		if (type == PrimitiveType.FLOAT)
  			return 6;
  		if (type == PrimitiveType.DOUBLE)
  			return 7;
  		return 0;
  	}
  	
  	public static boolean isArrayCompatible(ITypeBinding definedType) {
  		if (definedType.isTopLevel()) {
  			if (definedType.isClass()) {
  				return "Object".equals(definedType.getName()) && "java.lang".equals(definedType.getPackage().getName());  //$NON-NLS-1$//$NON-NLS-2$
  			} else {
  				String qualifiedName= definedType.getQualifiedName();
  				return "java.io.Serializable".equals(qualifiedName) || "java.lang.Cloneable".equals(qualifiedName); //$NON-NLS-1$ //$NON-NLS-2$
  			}
  		} 
  		return false;
  	}
  	
  	public static boolean isJavaLangObject(ITypeBinding definedType) {
  		return definedType.isTopLevel() && definedType.isClass() && "Object".equals(definedType.getName()) && "java.lang".equals(definedType.getPackage().getName());  //$NON-NLS-1$//$NON-NLS-2$	
  	}
  
  	/**
  	 * Tests if a two types are cast compatible
  	 * @param castType The binding of the type to cast to
  	 * @param bindingToCast The binding ef the expression to cast.
  	 * @return boolean Returns true if (castType) bindingToCast is a valid cast expression (can be unnecessary, but not invalid).
  	 */
  	public static boolean canCast(ITypeBinding castType, ITypeBinding bindingToCast) {
  		//see bug 80715 
  		
  		String voidName= PrimitiveType.VOID.toString();
  		
  		if (castType.isAnonymous() || castType.isNullType() || voidName.equals(castType.getName())) {
  			throw new IllegalArgumentException();
  		}
  		
  		if (castType == bindingToCast) {
  			return true;
  		}
  		
  		if (voidName.equals(bindingToCast.getName())) {
  			return false;
  		}
  		
  		if (bindingToCast.isArray()) {
  			if (!castType.isArray()) {
  				return isArrayCompatible(castType); // can not cast an arraytype to a non array type (except to Object, Serializable...)
  			}
  
  			int toCastDim= bindingToCast.getDimensions();
  			int castTypeDim= castType.getDimensions();
  			if (toCastDim == castTypeDim) {
  				bindingToCast= bindingToCast.getElementType();
  				castType= castType.getElementType();
  				if (castType.isPrimitive() && castType != bindingToCast) {
  					return false; // can't assign arrays of different primitive types to each other
  				}
  				// fall through
  			} else if (toCastDim < castTypeDim) {
  				return isArrayCompatible(bindingToCast.getElementType());
  			} else {
  				return isArrayCompatible(castType.getElementType());
  			}
  		}
  		if (castType.isPrimitive()) {
  			if (!bindingToCast.isPrimitive()) {
  				return false;
  			}
  			String boolName= PrimitiveType.BOOLEAN.toString();
  			return (!boolName.equals(castType.getName()) && !boolName.equals(bindingToCast.getName()));
  		} else {
  			if (bindingToCast.isPrimitive()) {
  				return false;
  			}
  			if (castType.isArray()) {
  				return isArrayCompatible(bindingToCast);
  			}
  			if (isJavaLangObject(castType)) {
  				return true;
  			}
  			
  			return Bindings.isSuperType(bindingToCast, castType) || Bindings.isSuperType(castType, bindingToCast);
  		}
  	}
  	
  }
  