/*******************************************************************************
* Copyright (c) 2000, 2011 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.che.ide.ext.java.jdt.internal.corext.refactoring.types;
import org.eclipse.che.ide.ext.java.jdt.core.BindingKey;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTParser;
import org.eclipse.che.ide.ext.java.jdt.core.dom.DefaultBindingResolver;
import org.eclipse.che.ide.ext.java.jdt.core.dom.DefaultBindingResolver.BindingTables;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ITypeBinding;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.CompilationResult;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.IErrorHandlingPolicy;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.ISourceType;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.impl.ITypeRequestor;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.Scope;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.che.ide.ext.java.worker.WorkerMessageHandler;
import org.eclipse.che.ide.runtime.Assert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* A type environment comprises a set of {@link TType}s that stand for Java {@link ITypeBinding}s.
* In contrast to type bindings, TTypes of the same type environment also work across project boundaries and
* across compiler environments, i.e. a type environment can handle bindings from multiple {@link ASTParser} sessions.
*
* @see TType
*/
public class TypeEnvironment implements TypeConstants {
/** 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 List<Map<TType, ArrayType>> fArrayTypes = new ArrayList<Map<TType, ArrayType>>();
private Map<String, StandardType> fStandardTypes = new HashMap<String, StandardType>();
private Map<String, GenericType> fGenericTypes = new HashMap<String, GenericType>();
private Map<String, ParameterizedType> fParameterizedTypes = new HashMap<String, ParameterizedType>();
private Map<String, RawType> fRawTypes = new HashMap<String, RawType>();
private Map<String, TypeVariable> fTypeVariables = new HashMap<String, TypeVariable>();
private Map<TType, ExtendsWildcardType> fExtendsWildcardTypes = new HashMap<TType, ExtendsWildcardType>();
private Map<TType, SuperWildcardType> fSuperWildcardTypes = new HashMap<TType, SuperWildcardType>();
private UnboundWildcardType fUnboundWildcardType = null;
private static final int MAX_ENTRIES = 1024;
private Map<TypeTuple, Boolean> fSubTypeCache = new LinkedHashMap<TypeTuple, Boolean>(50, 0.75f, true) {
private static final long serialVersionUID = 1L;
@Override
protected boolean removeEldestEntry(Map.Entry<TypeTuple, Boolean> 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, ArrayList<TType>> fSubTypes;
/**
* If <code>true</code>, replace all capture types by their wildcard type.
*
* @since 3.7
*/
private final boolean fRemoveCapures;
private LookupEnvironment env;
public TypeEnvironment() {
this(false);
}
public TypeEnvironment(boolean rememberSubtypes) {
this(rememberSubtypes, false);
}
public TypeEnvironment(boolean rememberSubtypes, boolean removeCapures) {
if (rememberSubtypes) {
fSubTypes = new HashMap<TType, ArrayList<TType>>();
}
fRemoveCapures = removeCapures;
}
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()) {
if (fRemoveCapures) {
return create(binding.getWildcard());
} else {
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) {
StandardType result = fStandardTypes.get(type.getPlainPrettySignature());
if (result != null)
return result;
ProblemReporter problemReporter = new ProblemReporter(new IErrorHandlingPolicy() {
@Override
public boolean stopOnFirstError() {
return false;
}
@Override
public boolean proceedOnErrors() {
return true;
}
}, new CompilerOptions(), new DefaultProblemFactory());
ITypeRequestor typeRequestor = new ITypeRequestor() {
@Override
public void accept(ISourceType[] sourceType, PackageBinding packageBinding, AccessRestriction accessRestriction) {
}
@Override
public void accept(ICompilationUnit unit, AccessRestriction accessRestriction) {
}
@Override
public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) {
env.cacheBinaryType(binaryType, accessRestriction);
}
};
env =
new LookupEnvironment(typeRequestor, new CompilerOptions(), problemReporter,
WorkerMessageHandler.get().getNameEnvironment());
TypeBinding knownType = Scope.getBaseType(type.getName().toCharArray());
TypeBinding boxingType = env.computeBoxingType(knownType);
boxingType.tagBits |= TagBits.HasMissingType;
DefaultBindingResolver res = new DefaultBindingResolver(env, new BindingTables(), true, true);
res.scope().referenceContext =
new CompilationUnitDeclaration(problemReporter, new CompilationResult(new char[]{'e'}, 0, 1, 100), 1);
return createStandardType(res.getTypeBinding(boxingType));
}
Map<TType, ArrayList<TType>> getSubTypes() {
return fSubTypes;
}
private void cacheSubType(TType supertype, TType result) {
if (fSubTypes == null)
return;
if (supertype == null)
supertype = OBJECT_TYPE;
ArrayList<TType> subtypes = fSubTypes.get(supertype);
if (subtypes == null) {
subtypes = new ArrayList<TType>(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<TType, ArrayType> arrayTypes = getArrayTypesMap(index);
ArrayType result = 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<TType, ArrayType> arrayTypes = getArrayTypesMap(index);
ArrayType result = 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<TType, ArrayType> getArrayTypesMap(int index) {
int oldLength = fArrayTypes.size();
if (index >= oldLength) {
fArrayTypes.addAll(Collections.<Map<TType, ArrayType>>nCopies(index + 1 - oldLength, null));
}
Map<TType, ArrayType> arrayTypes = fArrayTypes.get(index);
if (arrayTypes == null) {
arrayTypes = new HashMap<TType, ArrayType>();
fArrayTypes.set(index, arrayTypes);
}
return arrayTypes;
}
private StandardType createStandardType(ITypeBinding binding) {
// IJavaElement javaElement = binding.getJavaElement();
StandardType result = fStandardTypes.get(binding.getBinaryName());
if (result != null)
return result;
result = new StandardType(this);
fStandardTypes.put(binding.getBinaryName(), result);
result.initialize(binding);
if (OBJECT_TYPE == null && result.isJavaLangObject())
OBJECT_TYPE = result;
return result;
}
private GenericType createGenericType(ITypeBinding binding) {
// IJavaElement javaElement = binding.getJavaElement();
GenericType result = fGenericTypes.get(binding.getQualifiedName());
if (result != null)
return result;
result = new GenericType(this);
fGenericTypes.put(binding.getQualifiedName(), result);
result.initialize(binding);
cacheSubType(result.getSuperclass(), result);
cacheSubTypes(result.getInterfaces(), result);
return result;
}
private ParameterizedType createParameterizedType(ITypeBinding binding) {
// IJavaProject javaProject = binding.getJavaElement().getJavaProject();
String bindingKey = binding.getKey();
// ProjectKeyPair pair = new ProjectKeyPair(javaProject, bindingKey);
// ParameterizedType result = fParameterizedTypes.get(pair);
// if (result != null)
// return result;
ParameterizedType result = new ParameterizedType(this);
// fParameterizedTypes.put(pair, result);
result.initialize(binding);
cacheSubType(result.getSuperclass(), result);
cacheSubTypes(result.getInterfaces(), result);
return result;
}
private RawType createRawType(ITypeBinding binding) {
// IJavaElement javaElement = binding.getJavaElement();
RawType result = fRawTypes.get(binding.getQualifiedName());
if (result != null)
return result;
result = new RawType(this);
fRawTypes.put(binding.getQualifiedName(), result);
result.initialize(binding);
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 = 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 = 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 = fTypeVariables.get(binding.getQualifiedName());
if (result != null)
return result;
result = new TypeVariable(this);
fTypeVariables.put(binding.getQualifiedName(), result);
result.initialize(binding);
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 = fCaptureTypes.get(pair);
// if (result != null)
// return result;
CaptureType result = new CaptureType(this);
// fCaptureTypes.put(pair, result);
result.initialize(binding);
return result;
}
}