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  *******************************************************************************/
14 package org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types;
15 
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.LinkedHashMap;
20 import java.util.List;
21 import java.util.Map;
22 
23 import org.eclipse.core.runtime.Assert;
24 
25 import org.eclipse.jdt.core.BindingKey;
26 import org.eclipse.jdt.core.ICompilationUnit;
27 import org.eclipse.jdt.core.IJavaElement;
28 import org.eclipse.jdt.core.IJavaProject;
29 import org.eclipse.jdt.core.IType;
30 import org.eclipse.jdt.core.ITypeParameter;
31 import org.eclipse.jdt.core.JavaModelException;
32 import org.eclipse.jdt.core.dom.ASTParser;
33 import org.eclipse.jdt.core.dom.ASTRequestor;
34 import org.eclipse.jdt.core.dom.IBinding;
35 import org.eclipse.jdt.core.dom.ITypeBinding;
36 
37 import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
38 
39 /**
40  * A type environment comprises a set of {@link TType}s that stand for Java {@link ITypeBinding}s.
41  * In contrast to type bindings, TTypes of the same type environment also work across project boundaries and
42  * across compiler environments, i.e. a type environment can handle bindings from multiple {@link ASTParser} sessions.
43  *
44  * @see TType
45  */
46 public class TypeEnvironment {
47 
48 	private static class ProjectKeyPair {
49 		private final IJavaProject fProject;
50 		private final String fBindingKey;
51 
ProjectKeyPair(IJavaProject project, String bindingKey)52 		public ProjectKeyPair(IJavaProject project, String bindingKey) {
53 			fProject= project;
54 			fBindingKey= bindingKey;
55 		}
56 
57 		@Override
equals(Object other)58 		public boolean equals(Object other) {
59 			if (this == other)
60 				return true;
61 			if (! (other instanceof ProjectKeyPair))
62 				return false;
63 			ProjectKeyPair otherPair= (ProjectKeyPair) other;
64 			return fProject.equals(otherPair.fProject) && fBindingKey.equals(otherPair.fBindingKey);
65 		}
66 
67 		@Override
hashCode()68 		public int hashCode() {
69 			return fProject.hashCode() + fBindingKey.hashCode();
70 		}
71 	}
72 
73 	/** Type code for the primitive type "int". */
74 	public final PrimitiveType INT= new PrimitiveType(this, PrimitiveType.INT, BindingKey.createTypeBindingKey("int")); //$NON-NLS-1$
75 	/** Type code for the primitive type "char". */
76 	public final PrimitiveType CHAR = new PrimitiveType(this, PrimitiveType.CHAR, BindingKey.createTypeBindingKey("char")); //$NON-NLS-1$
77 	/** Type code for the primitive type "boolean". */
78 	public final PrimitiveType BOOLEAN = new PrimitiveType(this, PrimitiveType.BOOLEAN, BindingKey.createTypeBindingKey("boolean")); //$NON-NLS-1$
79 	/** Type code for the primitive type "short". */
80 	public final PrimitiveType SHORT = new PrimitiveType(this, PrimitiveType.SHORT, BindingKey.createTypeBindingKey("short")); //$NON-NLS-1$
81 	/** Type code for the primitive type "long". */
82 	public final PrimitiveType LONG = new PrimitiveType(this, PrimitiveType.LONG, BindingKey.createTypeBindingKey("long")); //$NON-NLS-1$
83 	/** Type code for the primitive type "float". */
84 	public final PrimitiveType FLOAT = new PrimitiveType(this, PrimitiveType.FLOAT, BindingKey.createTypeBindingKey("float")); //$NON-NLS-1$
85 	/** Type code for the primitive type "double". */
86 	public final PrimitiveType DOUBLE = new PrimitiveType(this, PrimitiveType.DOUBLE, BindingKey.createTypeBindingKey("double")); //$NON-NLS-1$
87 	/** Type code for the primitive type "byte". */
88 	public final PrimitiveType BYTE = new PrimitiveType(this, PrimitiveType.BYTE, BindingKey.createTypeBindingKey("byte")); //$NON-NLS-1$
89 
90 	/** Type code for the primitive type "null". */
91 	public final NullType NULL= new NullType(this);
92 
93 	public final VoidType VOID= new VoidType(this);
94 
95 	final PrimitiveType[] PRIMITIVE_TYPES= {INT, CHAR, BOOLEAN, SHORT, LONG, FLOAT, DOUBLE, BYTE};
96 
97 	private static final String[] BOXED_PRIMITIVE_NAMES= new String[] {
98 		"java.lang.Integer",  //$NON-NLS-1$
99 		"java.lang.Character",  //$NON-NLS-1$
100 		"java.lang.Boolean",  //$NON-NLS-1$
101 		"java.lang.Short",  //$NON-NLS-1$
102 		"java.lang.Long",  //$NON-NLS-1$
103 		"java.lang.Float",  //$NON-NLS-1$
104 		"java.lang.Double",  //$NON-NLS-1$
105 		"java.lang.Byte"};  //$NON-NLS-1$
106 
107 	private TType OBJECT_TYPE= null;
108 
109 	private List<Map<TType, ArrayType>>      fArrayTypes= new ArrayList<>();
110 	private Map<IJavaElement, StandardType>  fStandardTypes= new HashMap<>();
111 	private Map<IJavaElement, GenericType>   fGenericTypes= new HashMap<>();
112 	private Map<ProjectKeyPair, ParameterizedType> fParameterizedTypes= new HashMap<>();
113 	private Map<IJavaElement, RawType>       fRawTypes= new HashMap<>();
114 	private Map<IJavaElement, TypeVariable>  fTypeVariables= new HashMap<>();
115 	private Map<ProjectKeyPair, CaptureType> fCaptureTypes= new HashMap<>();
116 	private Map<TType, ExtendsWildcardType>  fExtendsWildcardTypes= new HashMap<>();
117 	private Map<TType, SuperWildcardType>    fSuperWildcardTypes= new HashMap<>();
118 	private UnboundWildcardType fUnboundWildcardType= null;
119 
120 	private static final int MAX_ENTRIES= 1024;
121 	private Map<TypeTuple, Boolean> fSubTypeCache= new LinkedHashMap<TypeTuple, Boolean>(50, 0.75f, true) {
122 		private static final long serialVersionUID= 1L;
123 		@Override
124 		protected boolean removeEldestEntry(Map.Entry<TypeTuple, Boolean> eldest) {
125 			return size() > MAX_ENTRIES;
126 		}
127 	};
128 
129 	/**
130 	 * Map from TType to its known subtypes, or <code>null</code> iff subtype
131 	 * information was not requested in the constructor.
132 	 */
133 	private Map<TType, ArrayList<TType>> fSubTypes;
134 	/**
135 	 * If <code>true</code>, replace all capture types by their wildcard type.
136 	 * @since 3.7
137 	 */
138 	private final boolean fRemoveCapures;
139 
createTypeBindings(TType[] types, IJavaProject project)140 	public static ITypeBinding[] createTypeBindings(TType[] types, IJavaProject project) {
141 		final Map<String, Object> mapping= new HashMap<>();
142 		List<String> keys= new ArrayList<>();
143 		for (TType type : types) {
144 			String bindingKey= type.getBindingKey();
145 			mapping.put(bindingKey, type);
146 			keys.add(bindingKey);
147 		}
148 		ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
149 		parser.setProject(project);
150 		parser.setResolveBindings(true);
151 		parser.createASTs(new ICompilationUnit[0], keys.toArray(new String[keys.size()]),
152 			new ASTRequestor() {
153 				@Override
154 				public void acceptBinding(String bindingKey, IBinding binding) {
155 					mapping.put(bindingKey, binding);
156 				}
157 			}, null);
158 		ITypeBinding[] result= new ITypeBinding[types.length];
159 		for (int i= 0; i < types.length; i++) {
160 			TType type= types[i];
161 			String bindingKey= type.getBindingKey();
162 			Object value= mapping.get(bindingKey);
163 			if (value instanceof ITypeBinding) {
164 				result[i]= (ITypeBinding)value;
165 			}
166 		}
167 		return result;
168 	}
169 
TypeEnvironment()170 	public TypeEnvironment() {
171 		this(false);
172 	}
173 
TypeEnvironment(boolean rememberSubtypes)174 	public TypeEnvironment(boolean rememberSubtypes) {
175 		this(rememberSubtypes, false);
176 	}
177 
TypeEnvironment(boolean rememberSubtypes, boolean removeCapures)178 	public TypeEnvironment(boolean rememberSubtypes, boolean removeCapures) {
179 		if (rememberSubtypes) {
180 			fSubTypes= new HashMap<>();
181 		}
182 		fRemoveCapures= removeCapures;
183 	}
184 
getSubTypeCache()185 	Map<TypeTuple, Boolean> getSubTypeCache() {
186 		return fSubTypeCache;
187 	}
188 
create(ITypeBinding binding)189 	public TType create(ITypeBinding binding) {
190 		if (binding.isPrimitive()) {
191 			return createPrimitiveType(binding);
192 		} else if (binding.isArray()) {
193 			return createArrayType(binding);
194 		} else if (binding.isRawType()) {
195 			return createRawType(binding);
196 		} else if (binding.isGenericType()) {
197 			return createGenericType(binding);
198 		} else if (binding.isParameterizedType()) {
199 			return createParameterizedType(binding);
200 		} else if (binding.isTypeVariable()) {
201 			return createTypeVariable(binding);
202 		} else if (binding.isWildcardType()) {
203 			if (binding.getBound() == null) {
204 				return createUnboundWildcardType(binding);
205 			} else if (binding.isUpperbound()) {
206 				return createExtendsWildCardType(binding);
207 			} else {
208 				return createSuperWildCardType(binding);
209 			}
210 		} else if (binding.isCapture()) {
211 			if (fRemoveCapures) {
212 				return create(binding.getWildcard());
213 			} else {
214 				return createCaptureType(binding);
215 			}
216 		}
217 		if ("null".equals(binding.getName())) //$NON-NLS-1$
218 			return NULL;
219 		return createStandardType(binding);
220 	}
221 
create(ITypeBinding[] bindings)222 	public TType[] create(ITypeBinding[] bindings) {
223 		TType[] result= new TType[bindings.length];
224 		for (int i= 0; i < bindings.length; i++) {
225 			result[i]= create(bindings[i]);
226 		}
227 		return result;
228 	}
229 
230 	/**
231 	 * Returns the TType for java.lang.Object.
232 	 * <p>
233 	 * Warning: currently returns <code>null</code> unless this type environment
234 	 * has already created its first hierarchy type or it has been initialized explicitly.
235 	 *
236 	 * @return the TType for java.lang.Object
237 	 *
238 	 * @see #initializeJavaLangObject(IJavaProject)
239 	 */
getJavaLangObject()240 	public TType getJavaLangObject() {
241 		return OBJECT_TYPE;
242 	}
243 
initializeJavaLangObject(IJavaProject project)244 	public void initializeJavaLangObject(IJavaProject project) {
245 		if (OBJECT_TYPE != null)
246 			return;
247 
248 		TType objectType= createStandardType("java.lang.Object", project); //$NON-NLS-1$
249 		Assert.isTrue(objectType.isJavaLangObject());
250 	}
251 
initializeJavaLangObject(ITypeBinding object)252 	void initializeJavaLangObject(ITypeBinding object) {
253 		if (OBJECT_TYPE != null)
254 			return;
255 
256 		TType objectType= createStandardType(object);
257 		Assert.isTrue(objectType.isJavaLangObject());
258 	}
259 
createUnBoxed(StandardType type)260 	PrimitiveType createUnBoxed(StandardType type) {
261 		String name= type.getPlainPrettySignature();
262 		for (int i= 0; i < BOXED_PRIMITIVE_NAMES.length; i++) {
263 			if (BOXED_PRIMITIVE_NAMES[i].equals(name))
264 				return PRIMITIVE_TYPES[i];
265 		}
266 		return null;
267 	}
268 
createBoxed(PrimitiveType type, IJavaProject focus)269 	StandardType createBoxed(PrimitiveType type, IJavaProject focus) {
270 		String fullyQualifiedName= BOXED_PRIMITIVE_NAMES[type.getId()];
271 		return createStandardType(fullyQualifiedName, focus);
272 	}
273 
createStandardType(String fullyQualifiedName, IJavaProject focus)274 	private StandardType createStandardType(String fullyQualifiedName, IJavaProject focus) {
275 		try {
276 			IType javaElementType= focus.findType(fullyQualifiedName);
277 			StandardType result= fStandardTypes.get(javaElementType);
278 			if (result != null)
279 				return result;
280 			ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
281 			parser.setProject(focus);
282 			IBinding[] bindings= parser.createBindings(new IJavaElement[] {javaElementType} , null);
283 			return createStandardType((ITypeBinding)bindings[0]);
284 		} catch (JavaModelException e) {
285 			// fall through
286 		}
287 		return null;
288 	}
289 
getSubTypes()290 	Map<TType, ArrayList<TType>> getSubTypes() {
291 		return fSubTypes;
292 	}
293 
cacheSubType(TType supertype, TType result)294 	private void cacheSubType(TType supertype, TType result) {
295 		if (fSubTypes == null)
296 			return;
297 		if (supertype == null)
298 			supertype= OBJECT_TYPE;
299 
300 		ArrayList<TType> subtypes= fSubTypes.get(supertype);
301 		if (subtypes == null) {
302 			subtypes= new ArrayList<>(5);
303 			fSubTypes.put(supertype, subtypes);
304 		} else {
305 			Assert.isTrue(! subtypes.contains(result));
306 		}
307 		subtypes.add(result);
308 	}
309 
cacheSubTypes(TType[] interfaces, TType result)310 	private void cacheSubTypes(TType[] interfaces, TType result) {
311 		for (TType intf : interfaces) {
312 			cacheSubType(intf, result);
313 		}
314 	}
315 
createPrimitiveType(ITypeBinding binding)316 	private TType createPrimitiveType(ITypeBinding binding) {
317 		String name= binding.getName();
318 		String[] names= PrimitiveType.NAMES;
319 		for (int i= 0; i < names.length; i++) {
320 			if (name.equals(names[i])) {
321 				return PRIMITIVE_TYPES[i];
322 			}
323 		}
324 		Assert.isTrue(false, "Primitive type " + name + "unkown");  //$NON-NLS-1$//$NON-NLS-2$
325 		return null;
326 	}
327 
createArrayType(ITypeBinding binding)328 	private ArrayType createArrayType(ITypeBinding binding) {
329 		int index= binding.getDimensions() - 1;
330 		TType elementType= create(binding.getElementType());
331 		Map<TType, ArrayType> arrayTypes= getArrayTypesMap(index);
332 		ArrayType result= arrayTypes.get(elementType);
333 		if (result != null)
334 			return result;
335 		result= new ArrayType(this);
336 		arrayTypes.put(elementType, result);
337 		result.initialize(binding, elementType);
338 		return result;
339 	}
340 
createArrayType(TType elementType, int dimensions)341 	public ArrayType createArrayType(TType elementType, int dimensions) {
342 		Assert.isTrue(! elementType.isArrayType());
343 		Assert.isTrue(! elementType.isAnonymous());
344 		Assert.isTrue(dimensions > 0);
345 
346 		int index= dimensions - 1;
347 		Map<TType, ArrayType> arrayTypes= getArrayTypesMap(index);
348 		ArrayType result= arrayTypes.get(elementType);
349 		if (result != null)
350 			return result;
351 		result= new ArrayType(this, BindingKey.createArrayTypeBindingKey(elementType.getBindingKey(), dimensions));
352 		arrayTypes.put(elementType, result);
353 		result.initialize(elementType, dimensions);
354 		return result;
355 	}
356 
getArrayTypesMap(int index)357 	private Map<TType, ArrayType> getArrayTypesMap(int index) {
358 		int oldLength= fArrayTypes.size();
359 		if (index >= oldLength) {
360 			fArrayTypes.addAll(Collections.<Map<TType,ArrayType>>nCopies(index + 1 - oldLength, null));
361 		}
362 		Map<TType, ArrayType> arrayTypes= fArrayTypes.get(index);
363 		if (arrayTypes == null) {
364 			arrayTypes= new HashMap<>();
365 			fArrayTypes.set(index, arrayTypes);
366 		}
367 		return arrayTypes;
368 	}
369 
createStandardType(ITypeBinding binding)370 	private StandardType createStandardType(ITypeBinding binding) {
371 		IJavaElement javaElement= binding.getJavaElement();
372 		StandardType result= fStandardTypes.get(javaElement);
373 		if (result != null)
374 			return result;
375 		result= new StandardType(this);
376 		fStandardTypes.put(javaElement, result);
377 		result.initialize(binding, (IType)javaElement);
378 		if (OBJECT_TYPE == null && result.isJavaLangObject())
379 			OBJECT_TYPE= result;
380 		return result;
381 	}
382 
createGenericType(ITypeBinding binding)383 	private GenericType createGenericType(ITypeBinding binding) {
384 		IJavaElement javaElement= binding.getJavaElement();
385 		GenericType result= fGenericTypes.get(javaElement);
386 		if (result != null)
387 			return result;
388 		result= new GenericType(this);
389 		fGenericTypes.put(javaElement, result);
390 		result.initialize(binding, (IType)javaElement);
391 		cacheSubType(result.getSuperclass(), result);
392 		cacheSubTypes(result.getInterfaces(), result);
393 		return result;
394 	}
395 
createParameterizedType(ITypeBinding binding)396 	private ParameterizedType createParameterizedType(ITypeBinding binding) {
397 		IJavaProject javaProject= binding.getJavaElement().getJavaProject();
398 		String bindingKey= binding.getKey();
399 		ProjectKeyPair pair= new ProjectKeyPair(javaProject, bindingKey);
400 		ParameterizedType result= fParameterizedTypes.get(pair);
401 		if (result != null)
402 			return result;
403 		result= new ParameterizedType(this);
404 		fParameterizedTypes.put(pair, result);
405 		result.initialize(binding, (IType)binding.getJavaElement());
406 		cacheSubType(result.getSuperclass(), result);
407 		cacheSubTypes(result.getInterfaces(), result);
408 		return result;
409 	}
410 
createRawType(ITypeBinding binding)411 	private RawType createRawType(ITypeBinding binding) {
412 		IJavaElement javaElement= binding.getJavaElement();
413 		RawType result= fRawTypes.get(javaElement);
414 		if (result != null)
415 			return result;
416 		result= new RawType(this);
417 		fRawTypes.put(javaElement, result);
418 		result.initialize(binding, (IType)javaElement);
419 		cacheSubType(result.getSuperclass(), result);
420 		cacheSubTypes(result.getInterfaces(), result);
421 		return result;
422 	}
423 
createUnboundWildcardType(ITypeBinding binding)424 	private TType createUnboundWildcardType(ITypeBinding binding) {
425 		if (fUnboundWildcardType == null) {
426 			fUnboundWildcardType= new UnboundWildcardType(this);
427 			fUnboundWildcardType.initialize(binding);
428 		}
429 		return fUnboundWildcardType;
430 	}
431 
createExtendsWildCardType(ITypeBinding binding)432 	private TType createExtendsWildCardType(ITypeBinding binding) {
433 		TType bound= create(binding.getBound());
434 		ExtendsWildcardType result= fExtendsWildcardTypes.get(bound);
435 		if (result != null)
436 			return result;
437 		result= new ExtendsWildcardType(this);
438 		fExtendsWildcardTypes.put(bound, result);
439 		result.initialize(binding);
440 		return result;
441 	}
442 
createSuperWildCardType(ITypeBinding binding)443 	private TType createSuperWildCardType(ITypeBinding binding) {
444 		TType bound= create(binding.getBound());
445 		SuperWildcardType result= fSuperWildcardTypes.get(bound);
446 		if (result != null)
447 			return result;
448 		result= new SuperWildcardType(this);
449 		fSuperWildcardTypes.put(bound, result);
450 		result.initialize(binding);
451 		return result;
452 	}
453 
createTypeVariable(ITypeBinding binding)454 	private TypeVariable createTypeVariable(ITypeBinding binding) {
455 		IJavaElement javaElement= binding.getJavaElement();
456 		TypeVariable result= fTypeVariables.get(javaElement);
457 		if (result != null)
458 			return result;
459 		result= new TypeVariable(this);
460 		fTypeVariables.put(javaElement, result);
461 		result.initialize(binding, (ITypeParameter)javaElement);
462 		return result;
463 	}
464 
createCaptureType(ITypeBinding binding)465 	private CaptureType createCaptureType(ITypeBinding binding) {
466 		IJavaProject javaProject= binding.getDeclaringClass().getJavaElement().getJavaProject();
467 		String bindingKey= binding.getKey();
468 		ProjectKeyPair pair= new ProjectKeyPair(javaProject, bindingKey);
469 		CaptureType result= fCaptureTypes.get(pair);
470 		if (result != null)
471 			return result;
472 		result= new CaptureType(this);
473 		fCaptureTypes.put(pair, result);
474 		result.initialize(binding, javaProject);
475 		return result;
476 	}
477 }
478