blob: f3296b72dc54a8277e981af2d6ccb95111b9c198 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.jdt.internal.corext.refactoring.typeconstraints.types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jdt.core.BindingKey;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
public class TypeEnvironment {
private static class ProjectKeyPair {
private final IJavaProject fProject;
private final String fBindingKey;
public ProjectKeyPair(IJavaProject project, String bindingKey) {
fProject= project;
fBindingKey= bindingKey;
}
public boolean equals(Object other) {
if (this == other)
return true;
if (! (other instanceof ProjectKeyPair))
return false;
ProjectKeyPair otherPair= (ProjectKeyPair) other;
return fProject.equals(otherPair.fProject) && fBindingKey.equals(otherPair.fBindingKey);
}
public int hashCode() {
return fProject.hashCode() + fBindingKey.hashCode();
}
}
/** Type code for the primitive type "int". */
public final PrimitiveType INT= new PrimitiveType(this, PrimitiveType.INT, BindingKey.createTypeBindingKey("int")); //$NON-NLS-1$
/** Type code for the primitive type "char". */
public final PrimitiveType CHAR = new PrimitiveType(this, PrimitiveType.CHAR, BindingKey.createTypeBindingKey("char")); //$NON-NLS-1$
/** Type code for the primitive type "boolean". */
public final PrimitiveType BOOLEAN = new PrimitiveType(this, PrimitiveType.BOOLEAN, BindingKey.createTypeBindingKey("boolean")); //$NON-NLS-1$
/** Type code for the primitive type "short". */
public final PrimitiveType SHORT = new PrimitiveType(this, PrimitiveType.SHORT, BindingKey.createTypeBindingKey("short")); //$NON-NLS-1$
/** Type code for the primitive type "long". */
public final PrimitiveType LONG = new PrimitiveType(this, PrimitiveType.LONG, BindingKey.createTypeBindingKey("long")); //$NON-NLS-1$
/** Type code for the primitive type "float". */
public final PrimitiveType FLOAT = new PrimitiveType(this, PrimitiveType.FLOAT, BindingKey.createTypeBindingKey("float")); //$NON-NLS-1$
/** Type code for the primitive type "double". */
public final PrimitiveType DOUBLE = new PrimitiveType(this, PrimitiveType.DOUBLE, BindingKey.createTypeBindingKey("double")); //$NON-NLS-1$
/** Type code for the primitive type "byte". */
public final PrimitiveType BYTE = new PrimitiveType(this, PrimitiveType.BYTE, BindingKey.createTypeBindingKey("byte")); //$NON-NLS-1$
/** Type code for the primitive type "null". */
public final NullType NULL= new NullType(this);
public final VoidType VOID= new VoidType(this);
final PrimitiveType[] PRIMITIVE_TYPES= {INT, CHAR, BOOLEAN, SHORT, LONG, FLOAT, DOUBLE, BYTE};
private static final String[] BOXED_PRIMITIVE_NAMES= new String[] {
"java.lang.Integer", //$NON-NLS-1$
"java.lang.Character", //$NON-NLS-1$
"java.lang.Boolean", //$NON-NLS-1$
"java.lang.Short", //$NON-NLS-1$
"java.lang.Long", //$NON-NLS-1$
"java.lang.Float", //$NON-NLS-1$
"java.lang.Double", //$NON-NLS-1$
"java.lang.Byte"}; //$NON-NLS-1$
private TType OBJECT_TYPE= null;
private Map[] fArrayTypes= new Map[] { new HashMap() };
private Map fStandardTypes= new HashMap();
private Map fGenericTypes= new HashMap();
private Map fParameterizedTypes= new HashMap();
private Map fRawTypes= new HashMap();
private Map fTypeVariables= new HashMap();
private Map fCaptureTypes= new HashMap();
private Map fExtendsWildcardTypes= new HashMap();
private Map fSuperWildcardTypes= new HashMap();
private UnboundWildcardType fUnboundWildcardType= null;
private static final int MAX_ENTRIES= 1024;
private Map/*<TypeTuple, Boolean>*/ fSubTypeCache= new LinkedHashMap(50, 0.75f, true) {
private static final long serialVersionUID= 1L;
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
};
/**
* Map from TType to its known subtypes, or <code>null</code> iff subtype
* information was not requested in the constructor.
*/
private Map/*<TType, List<TType>>*/ fSubTypes;
public static ITypeBinding[] createTypeBindings(TType[] types, IJavaProject project) {
final Map mapping= new HashMap();
List keys= new ArrayList();
for (int i= 0; i < types.length; i++) {
TType type= types[i];
String bindingKey= type.getBindingKey();
mapping.put(bindingKey, type);
keys.add(bindingKey);
}
ASTParser parser= ASTParser.newParser(AST.JLS3);
parser.setProject(project);
parser.setResolveBindings(true);
parser.createASTs(new ICompilationUnit[0], (String[])keys.toArray(new String[keys.size()]),
new ASTRequestor() {
public void acceptBinding(String bindingKey, IBinding binding) {
mapping.put(bindingKey, binding);
}
}, null);
ITypeBinding[] result= new ITypeBinding[types.length];
for (int i= 0; i < types.length; i++) {
TType type= types[i];
String bindingKey= type.getBindingKey();
Object value= mapping.get(bindingKey);
if (value instanceof ITypeBinding) {
result[i]= (ITypeBinding)value;
}
}
return result;
}
private boolean fIdentityTest;
public TypeEnvironment() {
this(false);
}
public TypeEnvironment(boolean rememberSubtypes) {
if (rememberSubtypes) {
fSubTypes= new HashMap();
}
}
public boolean isIdentityTest() {
return fIdentityTest;
}
Map/*<TypeTuple, Boolean>*/ getSubTypeCache() {
return fSubTypeCache;
}
public TType create(ITypeBinding binding) {
if (binding.isPrimitive()) {
return createPrimitiveType(binding);
} else if (binding.isArray()) {
return createArrayType(binding);
} else if (binding.isRawType()) {
return createRawType(binding);
} else if (binding.isGenericType()) {
return createGenericType(binding);
} else if (binding.isParameterizedType()) {
return createParameterizedType(binding);
} else if (binding.isTypeVariable()) {
return createTypeVariable(binding);
} else if (binding.isWildcardType()) {
if (binding.getBound() == null) {
return createUnboundWildcardType(binding);
} else if (binding.isUpperbound()) {
return createExtendsWildCardType(binding);
} else {
return createSuperWildCardType(binding);
}
} else if (binding.isCapture()) {
return createCaptureType(binding);
}
if ("null".equals(binding.getName())) //$NON-NLS-1$
return NULL;
return createStandardType(binding);
}
public TType[] create(ITypeBinding[] bindings) {
TType[] result= new TType[bindings.length];
for (int i= 0; i < bindings.length; i++) {
result[i]= create(bindings[i]);
}
return result;
}
/**
* Returns the TType for java.lang.Object.
* <p>
* Warning: currently returns <code>null</code> unless this type environment
* has already created its first hierarchy type.
*
* @return the TType for java.lang.Object
*/
public TType getJavaLangObject() {
return OBJECT_TYPE;
}
void initializeJavaLangObject(ITypeBinding object) {
if (OBJECT_TYPE != null)
return;
TType objectType= createStandardType(object);
Assert.isTrue(objectType.isJavaLangObject());
}
PrimitiveType createUnBoxed(StandardType type) {
String name= type.getPlainPrettySignature();
for (int i= 0; i < BOXED_PRIMITIVE_NAMES.length; i++) {
if (BOXED_PRIMITIVE_NAMES[i].equals(name))
return PRIMITIVE_TYPES[i];
}
return null;
}
StandardType createBoxed(PrimitiveType type, IJavaProject focus) {
String fullyQualifiedName= BOXED_PRIMITIVE_NAMES[type.getId()];
try {
IType javaElementType= focus.findType(fullyQualifiedName);
StandardType result= (StandardType)fStandardTypes.get(javaElementType);
if (result != null)
return result;
ASTParser parser= ASTParser.newParser(AST.JLS3);
parser.setProject(focus);
IBinding[] bindings= parser.createBindings(new IJavaElement[] {javaElementType} , null);
return createStandardType((ITypeBinding)bindings[0]);
} catch (JavaModelException e) {
// fall through
}
return null;
}
Map/*<TType, List<TType>>*/ getSubTypes() {
return fSubTypes;
}
private void cacheSubType(TType supertype, TType result) {
if (fSubTypes == null)
return;
if (supertype == null)
supertype= OBJECT_TYPE;
ArrayList subtypes= (ArrayList) fSubTypes.get(supertype);
if (subtypes == null) {
subtypes= new ArrayList(5);
fSubTypes.put(supertype, subtypes);
} else {
Assert.isTrue(! subtypes.contains(result));
}
subtypes.add(result);
}
private void cacheSubTypes(TType[] interfaces, TType result) {
for (int i= 0; i < interfaces.length; i++) {
cacheSubType(interfaces[i], result);
}
}
private TType createPrimitiveType(ITypeBinding binding) {
String name= binding.getName();
String[] names= PrimitiveType.NAMES;
for (int i= 0; i < names.length; i++) {
if (name.equals(names[i])) {
return PRIMITIVE_TYPES[i];
}
}
Assert.isTrue(false, "Primitive type " + name + "unkown"); //$NON-NLS-1$//$NON-NLS-2$
return null;
}
private ArrayType createArrayType(ITypeBinding binding) {
int index= binding.getDimensions() - 1;
TType elementType= create(binding.getElementType());
Map arrayTypes= getArrayTypesMap(index);
ArrayType result= (ArrayType)arrayTypes.get(elementType);
if (result != null)
return result;
result= new ArrayType(this);
arrayTypes.put(elementType, result);
result.initialize(binding, elementType);
return result;
}
public ArrayType createArrayType(TType elementType, int dimensions) {
Assert.isTrue(! elementType.isArrayType());
Assert.isTrue(! elementType.isAnonymous());
Assert.isTrue(dimensions > 0);
int index= dimensions - 1;
Map arrayTypes= getArrayTypesMap(index);
ArrayType result= (ArrayType)arrayTypes.get(elementType);
if (result != null)
return result;
result= new ArrayType(this, BindingKey.createArrayTypeBindingKey(elementType.getBindingKey(), dimensions));
arrayTypes.put(elementType, result);
result.initialize(elementType, dimensions);
return result;
}
private Map getArrayTypesMap(int index) {
int oldLength= fArrayTypes.length;
if (index >= oldLength) {
Map[] newArray= new Map[index + 1];
System.arraycopy(fArrayTypes, 0, newArray, 0, oldLength);
fArrayTypes= newArray;
}
Map arrayTypes= fArrayTypes[index];
if (arrayTypes == null) {
arrayTypes= new HashMap();
fArrayTypes[index]= arrayTypes;
}
return arrayTypes;
}
private StandardType createStandardType(ITypeBinding binding) {
IJavaElement javaElement= binding.getJavaElement();
StandardType result= (StandardType)fStandardTypes.get(javaElement);
if (result != null)
return result;
result= new StandardType(this);
fStandardTypes.put(javaElement, result);
result.initialize(binding, (IType)javaElement);
if (OBJECT_TYPE == null && result.isJavaLangObject())
OBJECT_TYPE= result;
return result;
}
private GenericType createGenericType(ITypeBinding binding) {
IJavaElement javaElement= binding.getJavaElement();
GenericType result= (GenericType)fGenericTypes.get(javaElement);
if (result != null)
return result;
result= new GenericType(this);
fGenericTypes.put(javaElement, result);
result.initialize(binding, (IType)javaElement);
cacheSubType(result.getSuperclass(), result);
cacheSubTypes(result.getInterfaces(), result);
return result;
}
private ParameterizedType createParameterizedType(ITypeBinding binding) {
ParameterizedType key= new ParameterizedType(this);
key.initialize(binding, (IType)binding.getJavaElement());
fIdentityTest= false;
ParameterizedType result= null;
try {
result= (ParameterizedType)fParameterizedTypes.get(key);
} finally {
fIdentityTest= true;
}
if (result != null)
return result;
result= key;
fParameterizedTypes.put(key, result);
cacheSubType(result.getSuperclass(), result);
cacheSubTypes(result.getInterfaces(), result);
return result;
}
private RawType createRawType(ITypeBinding binding) {
IJavaElement javaElement= binding.getJavaElement();
RawType result= (RawType)fRawTypes.get(javaElement);
if (result != null)
return result;
result= new RawType(this);
fRawTypes.put(javaElement, result);
result.initialize(binding, (IType)javaElement);
cacheSubType(result.getSuperclass(), result);
cacheSubTypes(result.getInterfaces(), result);
return result;
}
private TType createUnboundWildcardType(ITypeBinding binding) {
if (fUnboundWildcardType == null) {
fUnboundWildcardType= new UnboundWildcardType(this);
fUnboundWildcardType.initialize(binding);
}
return fUnboundWildcardType;
}
private TType createExtendsWildCardType(ITypeBinding binding) {
TType bound= create(binding.getBound());
ExtendsWildcardType result= (ExtendsWildcardType)fExtendsWildcardTypes.get(bound);
if (result != null)
return result;
result= new ExtendsWildcardType(this);
fExtendsWildcardTypes.put(bound, result);
result.initialize(binding);
return result;
}
private TType createSuperWildCardType(ITypeBinding binding) {
TType bound= create(binding.getBound());
SuperWildcardType result= (SuperWildcardType)fSuperWildcardTypes.get(bound);
if (result != null)
return result;
result= new SuperWildcardType(this);
fSuperWildcardTypes.put(bound, result);
result.initialize(binding);
return result;
}
private TypeVariable createTypeVariable(ITypeBinding binding) {
IJavaElement javaElement= binding.getJavaElement();
TypeVariable result= (TypeVariable)fTypeVariables.get(javaElement);
if (result != null)
return result;
result= new TypeVariable(this);
fTypeVariables.put(javaElement, result);
result.initialize(binding, (ITypeParameter)javaElement);
return result;
}
private CaptureType createCaptureType(ITypeBinding binding) {
IJavaProject javaProject= binding.getDeclaringClass().getJavaElement().getJavaProject();
String bindingKey= binding.getKey();
ProjectKeyPair pair= new ProjectKeyPair(javaProject, bindingKey);
CaptureType result= (CaptureType)fCaptureTypes.get(pair);
if (result != null)
return result;
result= new CaptureType(this);
fCaptureTypes.put(pair, result);
result.initialize(binding, javaProject);
return result;
}
}