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