1 /*******************************************************************************
2  * Copyright (c) 2000, 2016 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
14  *       bug "inline method - doesn't handle implicit cast" (see
15  *       https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
16  *******************************************************************************/
17 package org.eclipse.jdt.internal.corext.dom;
18 
19 import org.eclipse.jdt.core.dom.ITypeBinding;
20 import org.eclipse.jdt.core.dom.Modifier;
21 import org.eclipse.jdt.core.dom.PrimitiveType;
22 
23 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType;
24 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TypeEnvironment;
25 
26 /**
27  * Helper class to check if objects are assignable to each other.
28  * Methods with multiple arguments also work across bindings environments.
29  */
30 // @see JDTUIHelperClasses
31 public class TypeRules {
32 
33 	/**
34 	 * Tests if two types are assign compatible. Void types are never compatible.
35 	 *
36 	 * @param typeToAssign The binding of the type to assign
37 	 * @param definedType The type of the object that is assigned
38 	 * @return <code>true</code> iff definedType = typeToAssign is a valid assignment
39 	 */
canAssign(ITypeBinding typeToAssign, ITypeBinding definedType)40 	public static boolean canAssign(ITypeBinding typeToAssign, ITypeBinding definedType) {
41 		TypeEnvironment typeEnvironment= new TypeEnvironment(false, true);
42 		TType defined= typeEnvironment.create(definedType);
43 		TType toAssign= typeEnvironment.create(typeToAssign);
44 		return toAssign.canAssignTo(defined);
45 	}
46 
isArrayCompatible(ITypeBinding definedType)47 	public static boolean isArrayCompatible(ITypeBinding definedType) {
48 		if (definedType.isTopLevel()) {
49 			if (definedType.isClass()) {
50 				return "Object".equals(definedType.getName()) && "java.lang".equals(definedType.getPackage().getName());  //$NON-NLS-1$//$NON-NLS-2$
51 			} else {
52 				String qualifiedName= definedType.getQualifiedName();
53 				return "java.io.Serializable".equals(qualifiedName) || "java.lang.Cloneable".equals(qualifiedName); //$NON-NLS-1$ //$NON-NLS-2$
54 			}
55 		}
56 		return false;
57 	}
58 
isJavaLangObject(ITypeBinding definedType)59 	public static boolean isJavaLangObject(ITypeBinding definedType) {
60 		return definedType.isTopLevel() && definedType.isClass() && "Object".equals(definedType.getName()) && "java.lang".equals(definedType.getPackage().getName());  //$NON-NLS-1$//$NON-NLS-2$
61 	}
62 
63 	/**
64 	 * Tests if a two types are cast compatible
65 	 * @param castType The binding of the type to cast to
66 	 * @param bindingToCast The binding ef the expression to cast.
67 	 * @return boolean Returns true if (castType) bindingToCast is a valid cast expression (can be unnecessary, but not invalid).
68 	 */
canCast(ITypeBinding castType, ITypeBinding bindingToCast)69 	public static boolean canCast(ITypeBinding castType, ITypeBinding bindingToCast) {
70 		//see bug 80715
71 
72 		String voidName= PrimitiveType.VOID.toString();
73 
74 		if (castType.isAnonymous() || castType.isNullType() || voidName.equals(castType.getName())) {
75 			throw new IllegalArgumentException();
76 		}
77 
78 		if (castType == bindingToCast) {
79 			return true;
80 		}
81 
82 		if (voidName.equals(bindingToCast.getName())) {
83 			return false;
84 		}
85 
86 		if (bindingToCast.isArray()) {
87 			if (!castType.isArray()) {
88 				return isArrayCompatible(castType); // can not cast an arraytype to a non array type (except to Object, Serializable...)
89 			}
90 
91 			int toCastDim= bindingToCast.getDimensions();
92 			int castTypeDim= castType.getDimensions();
93 			if (toCastDim == castTypeDim) {
94 				bindingToCast= bindingToCast.getElementType();
95 				castType= castType.getElementType();
96 				if (castType.isPrimitive() && castType != bindingToCast) {
97 					return false; // can't assign arrays of different primitive types to each other
98 				}
99 				// fall through
100 			} else if (toCastDim < castTypeDim) {
101 				return isArrayCompatible(bindingToCast.getElementType());
102 			} else {
103 				return isArrayCompatible(castType.getElementType());
104 			}
105 		}
106 		if (castType.isPrimitive()) {
107 			if (!bindingToCast.isPrimitive()) {
108 				return false;
109 			}
110 			String boolName= PrimitiveType.BOOLEAN.toString();
111 			return (!boolName.equals(castType.getName()) && !boolName.equals(bindingToCast.getName()));
112 		} else {
113 			if (bindingToCast.isPrimitive()) {
114 				return false;
115 			}
116 			if (castType.isArray()) {
117 				return isArrayCompatible(bindingToCast);
118 			}
119 			if (castType.isInterface()) {
120 				if ((bindingToCast.getModifiers() & Modifier.FINAL) != 0) {
121 					return Bindings.isSuperType(castType, bindingToCast);
122 				} else {
123 					return true;
124 				}
125 			}
126 			if (bindingToCast.isInterface()) {
127 				if ((castType.getModifiers() & Modifier.FINAL) != 0) {
128 					return Bindings.isSuperType(bindingToCast, castType);
129 				} else {
130 					return true;
131 				}
132 			}
133 			if (isJavaLangObject(castType)) {
134 				return true;
135 			}
136 
137 			return Bindings.isSuperType(bindingToCast, castType) || Bindings.isSuperType(castType, bindingToCast);
138 		}
139 	}
140 
TypeRules()141 	private TypeRules() {
142 	}
143 
144 }
145