/*******************************************************************************
* Copyright (c) 2009-2011 CWI
* 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:
* *
* * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
* * Bas Basten - Bas.Basten@cwi.nl (CWI)
* * Mark Hills - Mark.Hills@cwi.nl (CWI)
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
* * Anastasia Izmaylova - A.Izmaylova@cwi.nl - CWI
*******************************************************************************/
package lang.java.jdt.internal;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.ADT_ENTITY;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.ADT_ID;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_ABSTRACT;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_ANONYMOUS_CLASS;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_ARRAY;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_CAPTURE;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_CLASS;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_CONSTRUCTOR;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_DEPRECATED;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_ENTITY;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_ENUM;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_ENUM_CONSTANT;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_EXTENDS;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_FIELD;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_FINAL;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_GENERIC_CLASS;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_GENERIC_INTERFACE;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_INITIALIZER_NUMBERED;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_INTERFACE;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_METHOD;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_NATIVE;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_PACKAGE;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_PARAMETER;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_PRIMITIVE;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_PRIVATE;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_PROTECTED;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_PUBLIC;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_STATIC;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_STRICTFP;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_SUPER;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_SYNCHRONIZED;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_TRANSIENT;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_TYPE_PARAMETER;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_VARIABLE;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_VOLATILE;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_WILDCARD;
import static org.rascalmpl.eclipse.library.lang.java.jdt.internal.Java.CONS_WILDCARD_BOUND;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.eclipse.imp.pdb.facts.IList;
import org.eclipse.imp.pdb.facts.IListWriter;
import org.eclipse.imp.pdb.facts.IValue;
import org.eclipse.imp.pdb.facts.IValueFactory;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.IExtendedModifier;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IPackageBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.Modifier;
import org.rascalmpl.values.ValueFactoryFactory;
public class BindingConverter extends ASTVisitor {
private static final IValueFactory VF = ValueFactoryFactory.getValueFactory();
public static final Map<String, IValue> primitiveTypes;
public static final IValue javaLangObject;
public static final IValue deprecatedModifier;
public static final IList arrayLengthField;
private Stack<Integer> anonymousClassCounterStack = new Stack<Integer>();
private Stack<Integer> initializerCounterStack = new Stack<Integer>();
private int anonymousClassCounter = 0;
private int initializerCounter = 0;
private Map<Object, IList> idStore = new HashMap<Object, IList>();
static {
primitiveTypes = new HashMap<String, IValue>();
primitiveTypes.put("byte", VF.constructor(Java.CONS_BYTE));
primitiveTypes.put("short", VF.constructor(Java.CONS_SHORT));
primitiveTypes.put("int", VF.constructor(Java.CONS_INT));
primitiveTypes.put("long", VF.constructor(Java.CONS_LONG));
primitiveTypes.put("float", VF.constructor(Java.CONS_FLOAT));
primitiveTypes.put("double", VF.constructor(Java.CONS_DOUBLE));
primitiveTypes.put("char", VF.constructor(Java.CONS_CHAR));
primitiveTypes.put("boolean", VF.constructor(Java.CONS_BOOLEAN));
primitiveTypes.put("void", VF.constructor(Java.CONS_VOID));
primitiveTypes.put("null", VF.constructor(Java.CONS_NULL));
IListWriter lw = VF.listWriter(ADT_ID);
lw.append(VF.constructor(CONS_PACKAGE, VF.string("java")));
lw.append(VF.constructor(CONS_PACKAGE, VF.string("lang")));
lw.append(VF.constructor(CONS_CLASS, VF.string("Object")));
javaLangObject = createEntity(lw.done());
arrayLengthField = VF.list(VF.constructor(CONS_FIELD, (VF
.string("length"))));
deprecatedModifier = VF.constructor(CONS_DEPRECATED);
}
public BindingConverter() {
super();
}
private static IValue createEntity(IList ids) {
return VF.constructor(CONS_ENTITY, ids);
}
public IValue getEntity(IPackageBinding binding) {
return createEntity(getIds(binding));
}
public IValue getEntity(ITypeBinding binding) {
return createEntity(getIds(binding));
}
public IValue getEntity(ITypeBinding binding, Initializer possibleParent) {
return createEntity(getIds(binding, possibleParent));
}
public IValue getEntity(IMethodBinding binding) {
return createEntity(getIds(binding));
}
public IValue getEntity(IVariableBinding binding) {
return createEntity(getIds(binding, null));
}
public IValue getEntity(IVariableBinding binding, Initializer possibleParent) {
return createEntity(getIds(binding, possibleParent));
}
public IValue getEntity(Initializer binding) {
return createEntity(getIds(binding, null));
}
public IValue getEntity(Initializer binding, ITypeBinding possibleParent) {
return createEntity(getIds(binding, possibleParent));
}
public List<IValue> getModifiers(List<?> list) {
List<IValue> result = new ArrayList<IValue>();
for (Object element : list) {
IExtendedModifier extMod = (IExtendedModifier) element;
// otherwise the modifier is an annotation. This might be @Deprecated
// which already has a CONS_ due to the Javadoc @deprecated tag.
if (extMod.isModifier()) {
Modifier mod = (Modifier) extMod;
if (mod.isAbstract()) {
result.add(VF.constructor(CONS_ABSTRACT));
} else if (mod.isFinal()) {
result.add(VF.constructor(CONS_FINAL));
} else if (mod.isNative()) {
result.add(VF.constructor(CONS_NATIVE));
} else if (mod.isPrivate()) {
result.add(VF.constructor(CONS_PRIVATE));
} else if (mod.isProtected()) {
result.add(VF.constructor(CONS_PROTECTED));
} else if (mod.isPublic()) {
result.add(VF.constructor(CONS_PUBLIC));
} else if (mod.isStatic()) {
result.add(VF.constructor(CONS_STATIC));
} else if (mod.isStrictfp()) {
result.add(VF.constructor(CONS_STRICTFP));
} else if (mod.isSynchronized()) {
result.add(VF.constructor(CONS_SYNCHRONIZED));
} else if (mod.isTransient()) {
result.add(VF.constructor(CONS_TRANSIENT));
} else if (mod.isVolatile()) {
result.add(VF.constructor(CONS_VOLATILE));
}
}
}
return result;
}
public List<IValue> getModifiers(int mods) {
List<IValue> result = new ArrayList<IValue>();
if (Modifier.isAbstract(mods))
result.add(VF.constructor(CONS_ABSTRACT));
if (Modifier.isFinal(mods))
result.add(VF.constructor(CONS_FINAL));
if (Modifier.isNative(mods))
result.add(VF.constructor(CONS_NATIVE));
if (Modifier.isPrivate(mods))
result.add(VF.constructor(CONS_PRIVATE));
if (Modifier.isProtected(mods))
result.add(VF.constructor(CONS_PROTECTED));
if (Modifier.isPublic(mods))
result.add(VF.constructor(CONS_PUBLIC));
if (Modifier.isStatic(mods))
result.add(VF.constructor(CONS_STATIC));
if (Modifier.isStrictfp(mods))
result.add(VF.constructor(CONS_STRICTFP));
if (Modifier.isSynchronized(mods))
result.add(VF.constructor(CONS_SYNCHRONIZED));
if (Modifier.isTransient(mods))
result.add(VF.constructor(CONS_TRANSIENT));
if (Modifier.isVolatile(mods))
result.add(VF.constructor(CONS_VOLATILE));
return result;
}
public void put(Object key, IList value) {
idStore.put(key, value);
}
public void pushAnonymousClassCounterStack() {
anonymousClassCounterStack.push(Integer.valueOf(anonymousClassCounter));
anonymousClassCounter = 0;
}
public void popAnonymousClassCounterStack() {
anonymousClassCounter = anonymousClassCounterStack.pop().intValue();
}
public void pushInitializerCounterStack() {
initializerCounterStack.push(Integer.valueOf(initializerCounter));
initializerCounter = 0;
}
public void popInitializerCounterStack() {
initializerCounter = initializerCounterStack.pop().intValue();
}
private IList getIds(IPackageBinding pb) {
String key = pb.getKey();
IList l = idStore.get(key);
if (l == null) {
l = importPackageBinding(pb);
idStore.put(key, l);
}
return l;
}
private IList getIds(ITypeBinding tb) {
String key = tb.getKey();
IList l = idStore.get(key);
if (l == null) {
l = importTypeBinding(tb, null);
idStore.put(key, l);
}
return l;
}
private IList getIds(ITypeBinding tb, Initializer possibleParent) {
String key = tb.getKey();
IList l = idStore.get(key);
if (l == null) {
l = importTypeBinding(tb, possibleParent);
idStore.put(key, l);
}
return l;
}
private IList getIds(IMethodBinding mb) {
String key = mb.getKey();
IList l = idStore.get(key);
if (l == null) {
l = importMethodBinding(mb);
idStore.put(key, l);
}
return l;
}
private IList getIds(IVariableBinding vb, Initializer possibleParent) {
String key = vb.getKey();
IList l = idStore.get(key);
if (l == null) {
l = importVariableBinding(vb, possibleParent);
idStore.put(key, l);
}
return l;
}
private IList getIds(Initializer init, ITypeBinding possibleParent) {
IList l = idStore.get(init);
if (l == null) {
l = importInitializer(init, possibleParent);
idStore.put(init, l);
}
return l;
}
private IList importTypeBinding(ITypeBinding tb, Initializer possibleParent) {
IListWriter lw = VF.listWriter(ADT_ID);
IList prefix = VF.list(ADT_ID);
IPackageBinding pb = tb.getPackage();
if (!tb.isTypeVariable()) {
// anonymous classes have a declaring method and a declaring class
// therefore, prefer method prefix
IMethodBinding declaringMethod = tb.getDeclaringMethod();
if (declaringMethod != null) {
prefix = getIds(declaringMethod);
} else {
ITypeBinding declaringClass = tb.getDeclaringClass();
/*
* Parameterized types and raw types are handled as a special case
*/
if(tb.isParameterizedType() || tb.isRawType()) {
if(tb.isLocal()) {
// if parameterized type is of a recorded local generic type,
// then prefix is shared with a recorded local generic type
IList recordedLocalGenericType = idStore.get(tb.getTypeDeclaration().getKey());
if(recordedLocalGenericType != null) {
prefix = recordedLocalGenericType.sublist(0, recordedLocalGenericType.length() - 1);
}
} else if(declaringClass != null) {
prefix = getIds(declaringClass);
} else if(pb != null) {
prefix = getIds(pb);
}
} else {
if (declaringClass != null && possibleParent == null) {
// for innerclasses within initializers, getDeclaringClass()
// returns the parent class of the initializer :-(
prefix = getIds(declaringClass);
} else {
/*
* The initializer is taken as a parent only if the type is local,
* this check is required for uses of non-local types within initializers
*/
if (possibleParent != null && tb.isLocal()) {
prefix = getIds(possibleParent, null);
} else if (declaringClass != null) {
prefix = getIds(declaringClass);
} else if (pb != null) {
prefix = getIds(pb);
}
}
}
/*
if (declaringClass != null && possibleParent == null) {
// for innerclasses within initializers, getDeclaringClass()
// returns the parent class of the initializer :-(
prefix = getIds(declaringClass);
} else {
if (possibleParent != null) {
prefix = getIds(possibleParent, null);
} else {
if (pb != null) {
prefix = getIds(pb);
}
}
}
*/
}
}
IValue id = getId(tb);
if(id != null) lw.append(id);
return prefix.concat(lw.done());
}
private IList importTypeBindings(ITypeBinding tbs[]) {
IListWriter lw = VF.listWriter(ADT_ENTITY);
for (ITypeBinding tb : tbs) {
lw.append(createEntity(getIds(tb)));
}
return lw.done();
}
private IList importPackageBinding(IPackageBinding pb) {
IListWriter lw = VF.listWriter(ADT_ID);
String components[] = pb.getNameComponents();
for (String c : components) {
lw.append(VF.constructor(CONS_PACKAGE, VF.string(c)));
}
return lw.done();
}
private IList importMethodBinding(IMethodBinding mb) {
IList prefix = getIds(mb.getDeclaringClass());
IListWriter lw = VF.listWriter(ADT_ID);
lw.append(getId(mb));
return prefix.concat(lw.done());
}
private IList importVariableBinding(IVariableBinding vb,
Initializer possibleParent) {
IListWriter lw = VF.listWriter(ADT_ID);
IList prefix = VF.list(ADT_ID);
ITypeBinding declaringClass = vb.getDeclaringClass();
if (declaringClass != null) {
prefix = getIds(declaringClass);
} else {
IMethodBinding declaringMethod = vb.getDeclaringMethod();
if (declaringMethod != null) {
prefix = getIds(declaringMethod);
} else {
/*
* Excerpt from VariableBinding.getDeclaringMethod():
*
* ASTNode node = this.resolver.findDeclaringNode(this); while
* (true) { if (node == null) break; switch(node.getNodeType())
* { case ASTNode.INITIALIZER : return null;
*
* (btw: this is not seen in TypeBinding.getDeclaringMethod())
*/
// local variable in initializer
if (possibleParent != null) {
// initializer should be in cache already
/*
* The field length of an array type has no declaring class
*/
if(!vb.isField())
prefix = getIds(possibleParent, null);
} else {
// let prefix remain empty
System.err.println("dangling var " + vb.getName());
}
}
}
lw.append(getId(vb));
return prefix.concat(lw.done());
}
private IList importInitializer(Initializer init, ITypeBinding possibleParent) {
IListWriter lw = VF.listWriter(ADT_ID);
lw.append(VF.constructor(CONS_INITIALIZER_NUMBERED, VF
.integer(initializerCounter++)));
IList l = lw.done();
if (possibleParent != null) {
IList parentName = getIds(possibleParent);
l = parentName.concat(l);
} else {
System.err.println("dangling initializer " + init.toString());
}
return l;
}
public IValue getId(IMethodBinding mb) {
IList params = importTypeBindings(mb.getParameterTypes());
IValue returnType = createEntity(getIds(mb.getReturnType()));
// TBD: mb.isVarargs() ?
// TBD: check generic and parameterized?
if (mb.isConstructor()) {
return VF.constructor(CONS_CONSTRUCTOR, params);
} else {
return VF.constructor(CONS_METHOD, VF.string(mb.getName()), params, returnType);
}
}
public IValue getId(ITypeBinding tb) {
if (tb.getPackage() != null) {
// class, interface or enum
if (tb.isClass()) {
if (tb.isGenericType()) {
/*
* The assignment below ensures type parameters,
* required, e.g., in case of static imports of generic types
*/
tb = tb.getTypeDeclaration();
IValue params = importTypeBindings(tb.getTypeParameters());
return VF.constructor(CONS_GENERIC_CLASS, VF.string(tb.getName()), params);
} else if (tb.isParameterizedType()) {
IValue params = importTypeBindings(tb.getTypeArguments());
return VF.constructor(CONS_GENERIC_CLASS, VF
/*
* Type arguments are not part of a name
*/
.string(tb.getErasure().getName()), params);
} else if (tb.isAnonymous()) {
return VF.constructor(CONS_ANONYMOUS_CLASS, VF.integer(anonymousClassCounter++));
} else { // regular class
return VF.constructor(CONS_CLASS, VF.string(tb.getName()));
}
} else if (tb.isInterface()) {
if (tb.isGenericType()) {
/*
* The assignment below ensures type parameters,
* required, e.g., in case of static imports of generic types
*/
tb = tb.getTypeDeclaration();
IValue params = importTypeBindings(tb.getTypeParameters());
return VF.constructor(CONS_GENERIC_INTERFACE, VF.string(tb.getName()), params);
} else if (tb.isParameterizedType()) {
IValue params = importTypeBindings(tb.getTypeArguments());
return VF.constructor(CONS_GENERIC_INTERFACE, VF
/*
* Type arguments are not part of a name
*/
.string(tb.getErasure().getName()), params);
} else { // regular interface
return VF.constructor(CONS_INTERFACE, VF.string(tb.getName()));
}
} else if (tb.isEnum()) {
// TBD tb.getName() can be "", when it should refer to the enum
// constant name
return VF.constructor(CONS_ENUM, VF.string(tb.getName()));
} else {
// TBD: unknown type?
return null;
}
} else {
// primitive type, array type, null type, type variable, wildcard
// type or capture binding
if (tb.isArray()) {
IValue arrayType = createEntity(getIds(tb.getElementType()));
return VF.constructor(CONS_ARRAY, arrayType);
} else if (tb.isPrimitive() || tb.isNullType()) {
IValue pt = primitiveTypes.get(tb.getName());
if (pt != null) {
return VF.constructor(CONS_PRIMITIVE, pt);
} // TBD: else ?
return null;
} else if (tb.isTypeVariable()) {
return VF.constructor(CONS_TYPE_PARAMETER, VF.string(tb.getName()));
} else if (tb.isWildcardType()) {
ITypeBinding bound = tb.getBound();
if (bound == null) {
return VF.constructor(CONS_WILDCARD);
} else {
IValue bnd = VF.constructor(
tb.isUpperbound() ? CONS_EXTENDS : CONS_SUPER,
createEntity(getIds(bound)));
return VF.constructor(CONS_WILDCARD_BOUND, bnd);
}
} else if (tb.isCapture()) {
/*
* Captures have declaring classes and no declaring methods
*/
return VF.constructor(CONS_CAPTURE, getEntity(tb.getWildcard()));
} else {
// TBD: unkown type?
return null;
}
}
}
public IValue getId(IVariableBinding vb) {
if (vb.isEnumConstant()) {
return VF.constructor(CONS_ENUM_CONSTANT, VF.string(vb.getName()));
} else if (vb.isField()) {
// fields also include enum constants,
// so they should be handled first
return VF.constructor(CONS_FIELD, VF.string(vb.getName()));
} else if (vb.isParameter()) {
return VF.constructor(CONS_PARAMETER, VF.string(vb.getName()));
} else {
// local variable
return VF.constructor(CONS_VARIABLE, VF.string(vb.getName()), VF.integer(vb.getVariableId()));
}
}
}