package org.rascalmpl.library.lang.java.m3.internal;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IListWriter;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.value.type.TypeStore;
import org.rascalmpl.values.ValueFactoryFactory;
public abstract class JavaToRascalConverter extends ASTVisitor {
protected static final IValueFactory values = ValueFactoryFactory.getValueFactory();
protected static final TypeFactory TF = TypeFactory.getInstance();
protected final TypeStore typeStore;
protected IValue ownValue;
private static final String DATATYPE_RASCAL_AST_TYPE_NODE = "Type";
private static final String DATATYPE_RASCAL_AST_MODIFIER_NODE = "Modifier";
private static final String DATATYPE_RASCAL_AST_DECLARATION_NODE = "Declaration";
private static final String DATATYPE_RASCAL_AST_EXPRESSION_NODE = "Expression";
private static final String DATATYPE_RASCAL_AST_STATEMENT_NODE = "Statement";
private static final String DATATYPE_RASCAL_MESSAGE = "Message";
private static final String DATATYPE_RASCAL_MESSAGE_ERROR = "error";
private final org.rascalmpl.value.type.Type DATATYPE_RASCAL_AST_DECLARATION_NODE_TYPE;
private final org.rascalmpl.value.type.Type DATATYPE_RASCAL_AST_EXPRESSION_NODE_TYPE;
private final org.rascalmpl.value.type.Type DATATYPE_RASCAL_AST_STATEMENT_NODE_TYPE;
protected static org.rascalmpl.value.type.Type DATATYPE_RASCAL_AST_TYPE_NODE_TYPE;
protected static org.rascalmpl.value.type.Type DATATYPE_RASCAL_AST_MODIFIER_NODE_TYPE;
protected static org.rascalmpl.value.type.Type DATATYPE_RASCAL_MESSAGE_DATA_TYPE;
protected static org.rascalmpl.value.type.Type DATATYPE_RASCAL_MESSAGE_ERROR_NODE_TYPE;
protected CompilationUnit compilUnit;
protected ISourceLocation loc;
protected final BindingsResolver bindingsResolver;
protected final boolean collectBindings;
protected IListWriter messages;
protected final Map<String, ISourceLocation> locationCache;
JavaToRascalConverter(final TypeStore typeStore, Map<String, ISourceLocation> cache, boolean collectBindings) {
super(true);
this.typeStore = typeStore;
this.bindingsResolver = new BindingsResolver(typeStore, cache, collectBindings);
this.collectBindings = collectBindings;
DATATYPE_RASCAL_AST_TYPE_NODE_TYPE = this.typeStore.lookupAbstractDataType(DATATYPE_RASCAL_AST_TYPE_NODE);
DATATYPE_RASCAL_AST_MODIFIER_NODE_TYPE = this.typeStore.lookupAbstractDataType(DATATYPE_RASCAL_AST_MODIFIER_NODE);
this.DATATYPE_RASCAL_AST_DECLARATION_NODE_TYPE = typeStore.lookupAbstractDataType(DATATYPE_RASCAL_AST_DECLARATION_NODE);
this.DATATYPE_RASCAL_AST_EXPRESSION_NODE_TYPE = typeStore.lookupAbstractDataType(DATATYPE_RASCAL_AST_EXPRESSION_NODE);
this.DATATYPE_RASCAL_AST_STATEMENT_NODE_TYPE = typeStore.lookupAbstractDataType(DATATYPE_RASCAL_AST_STATEMENT_NODE);
JavaToRascalConverter.DATATYPE_RASCAL_MESSAGE_DATA_TYPE = typeStore.lookupAbstractDataType(DATATYPE_RASCAL_MESSAGE);
JavaToRascalConverter.DATATYPE_RASCAL_MESSAGE_ERROR_NODE_TYPE = typeStore.lookupConstructor(DATATYPE_RASCAL_MESSAGE_DATA_TYPE, DATATYPE_RASCAL_MESSAGE_ERROR).iterator().next();
this.locationCache = cache;
messages = values.listWriter();
}
protected ISourceLocation resolveBinding(String packageComponent) {
ISourceLocation packageBinding = new BindingsResolver(typeStore, locationCache, this.collectBindings) {
public ISourceLocation resolveBinding(String packageC) {
try {
if (collectBindings) {
if (locationCache.containsKey(packageC)) {
return locationCache.get(packageC);
}
return values.sourceLocation("java+package", null, packageC);
}
return values.sourceLocation("unknown", null, null);
} catch (URISyntaxException e) {
throw new RuntimeException("Should not happen", e);
}
}
}.resolveBinding(packageComponent);
locationCache.put(packageComponent, packageBinding);
return packageBinding;
}
protected ISourceLocation resolveBinding(CompilationUnit node) {
ISourceLocation compilationUnit = new BindingsResolver(typeStore, locationCache, true) {
public ISourceLocation resolveBinding(CompilationUnit node) {
return makeBinding("java+compilationUnit", null, loc.getPath());
}
}.resolveBinding(node);
return compilationUnit;
}
protected ISourceLocation resolveBinding(IBinding binding) {
ISourceLocation resolvedBinding = bindingsResolver.resolveBinding(binding);
if (binding != null)
locationCache.put(binding.getKey(), resolvedBinding);
return resolvedBinding;
}
protected ISourceLocation resolveDeclaringClass(IBinding binding) {
ISourceLocation resolvedBinding;
if (binding instanceof ITypeBinding) {
resolvedBinding = bindingsResolver.resolveBinding(((ITypeBinding) binding).getDeclaringClass());
} else if (binding instanceof IMethodBinding) {
resolvedBinding = bindingsResolver.resolveBinding(((IMethodBinding) binding).getDeclaringClass());
} else if (binding instanceof IVariableBinding) {
resolvedBinding = bindingsResolver.resolveBinding(((IVariableBinding) binding).getDeclaringClass());
} else {
binding = null;
resolvedBinding = bindingsResolver.resolveBinding(binding);
}
return resolvedBinding;
}
protected ISourceLocation resolveBinding(ASTNode node) {
if (node instanceof CompilationUnit) {
return resolveBinding((CompilationUnit) node);
}
return bindingsResolver.resolveBinding(node, true);
}
protected ISourceLocation getSourceLocation(ASTNode node) {
try {
int nodeLength = compilUnit.getExtendedLength(node);
if (nodeLength > 0) {
int start = compilUnit.getExtendedStartPosition(node);
int end = start + nodeLength -1;
if (end < start && ((node.getFlags() & 9) > 0)) {
insert(messages, values.constructor(DATATYPE_RASCAL_MESSAGE_ERROR_NODE_TYPE,
values.string("Recovered/Malformed node, guessing the length"),
values.sourceLocation(loc, 0, 0)));
nodeLength = node.toString().length();
end = start + nodeLength - 1;
}
return values.sourceLocation(loc,
start, nodeLength,
compilUnit.getLineNumber(start), compilUnit.getLineNumber(end),
// TODO: only adding 1 at the end seems to work, need to test.
compilUnit.getColumnNumber(start), compilUnit.getColumnNumber(end)+1);
}
} catch (IllegalArgumentException e) {
insert(messages, values.constructor(DATATYPE_RASCAL_MESSAGE_ERROR_NODE_TYPE,
values.string("Most probably missing dependency"),
values.sourceLocation(loc, 0, 0)));
}
return values.sourceLocation(loc, 0, 0, 0, 0, 0, 0);
}
protected IValue[] removeNulls(IValue... withNulls) {
List<IValue> withOutNulls = new ArrayList<IValue>();
for (IValue child : withNulls) {
if (!(child == null)) {
withOutNulls.add(child);
}
}
return withOutNulls.toArray(new IValue[withOutNulls.size()]);
}
protected IValueList parseModifiers(int modifiers) {
IValueList extendedModifierList = new IValueList(values);
for (String constructor: java.lang.reflect.Modifier.toString(modifiers).split(" ")) {
Set<org.rascalmpl.value.type.Type> exConstr = typeStore.lookupConstructor(DATATYPE_RASCAL_AST_MODIFIER_NODE_TYPE, constructor);
for (org.rascalmpl.value.type.Type con: exConstr) {
extendedModifierList.add(values.constructor(con));
}
}
return extendedModifierList;
}
@SuppressWarnings({"rawtypes"})
protected IValueList parseExtendedModifiers(List ext) {
IValueList extendedModifierList = new IValueList(values);
for (Iterator it = ext.iterator(); it.hasNext();) {
ASTNode p = (ASTNode) it.next();
IValue val = visitChild(p);
if (p instanceof Annotation) {
val = constructModifierNode("annotation", val);
}
extendedModifierList.add(val);
}
return extendedModifierList;
}
@SuppressWarnings("deprecation")
protected IValueList parseExtendedModifiers(BodyDeclaration node) {
if (node.getAST().apiLevel() == AST.JLS2) {
return parseModifiers(node.getModifiers());
} else {
return parseExtendedModifiers(node.modifiers());
}
}
protected IValue visitChild(ASTNode node) {
node.accept(this);
return this.getValue();
}
public IValue getValue() {
return this.ownValue;
}
protected IConstructor constructModifierNode(String constructor, IValue... children) {
org.rascalmpl.value.type.Type args = TF.tupleType(removeNulls(children));
org.rascalmpl.value.type.Type constr = typeStore.lookupConstructor(DATATYPE_RASCAL_AST_MODIFIER_NODE_TYPE, constructor, args);
return values.constructor(constr, removeNulls(children));
}
protected void setAnnotation(String annoName, IValue annoValue) {
if(this.ownValue == null) {
return ;
}
if (annoValue != null && ownValue.getType().declaresAnnotation(this.typeStore, annoName)) {
ownValue = ((IConstructor) ownValue).asAnnotatable().setAnnotation(annoName, annoValue);
}
}
protected void setAnnotation(String annoName, IValueList annoList) {
IList annos = (IList) annoList.asList();
if(this.ownValue == null) {
return ;
}
if (annoList != null && this.ownValue.getType().declaresAnnotation(this.typeStore, annoName) && !annos.isEmpty()) {
this.ownValue = ((IConstructor) this.ownValue).asAnnotatable().setAnnotation(annoName, annos);
}
}
protected IValue constructDeclarationNode(String constructor, IValue... children) {
org.rascalmpl.value.type.Type args = TF.tupleType(removeNulls(children));
org.rascalmpl.value.type.Type constr = typeStore.lookupConstructor(DATATYPE_RASCAL_AST_DECLARATION_NODE_TYPE, constructor, args);
return values.constructor(constr, removeNulls(children));
}
protected IValue constructExpressionNode(String constructor, IValue... children) {
org.rascalmpl.value.type.Type args = TF.tupleType(removeNulls(children));
org.rascalmpl.value.type.Type constr = typeStore.lookupConstructor(DATATYPE_RASCAL_AST_EXPRESSION_NODE_TYPE, constructor, args);
return values.constructor(constr, removeNulls(children));
}
protected IValue constructStatementNode(String constructor, IValue... children) {
org.rascalmpl.value.type.Type args = TF.tupleType(removeNulls(children));
org.rascalmpl.value.type.Type constr = typeStore.lookupConstructor(DATATYPE_RASCAL_AST_STATEMENT_NODE_TYPE, constructor, args);
return values.constructor(constr, removeNulls(children));
}
protected IValue constructTypeNode(String constructor, IValue... children) {
org.rascalmpl.value.type.Type args = TF.tupleType(removeNulls(children));
org.rascalmpl.value.type.Type constr = typeStore.lookupConstructor(DATATYPE_RASCAL_AST_TYPE_NODE_TYPE, constructor, args);
return values.constructor(constr, removeNulls(children));
}
protected void insertCompilationUnitMessages(boolean insertErrors, IList otherMessages) {
org.rascalmpl.value.type.Type args = TF.tupleType(TF.stringType(), TF.sourceLocationType());
IValueList result = new IValueList(values);
if (otherMessages != null) {
for (IValue message : otherMessages) {
result.add(message);
}
}
if (insertErrors) {
int i;
IProblem[] problems = compilUnit.getProblems();
for (i = 0; i < problems.length; i++) {
int offset = problems[i].getSourceStart();
int length = problems[i].getSourceEnd() - offset + 1;
int sl = problems[i].getSourceLineNumber();
ISourceLocation pos = values.sourceLocation(loc, offset, length, sl, sl, 0, 0);
org.rascalmpl.value.type.Type constr;
if (problems[i].isError()) {
constr = typeStore.lookupConstructor(this.typeStore.lookupAbstractDataType("Message"), "error", args);
} else {
constr = typeStore.lookupConstructor(this.typeStore.lookupAbstractDataType("Message"), "warning", args);
}
result.add(values.constructor(constr, values.string(problems[i].getMessage()), pos));
}
}
setAnnotation("messages", result.asList());
}
public void insert(IListWriter listW, IValue message) {
if (message.getType().isConstructor() && message.getType().getAbstractDataType().getName().equals("Message")) {
listW.insert(message);
}
}
public void convert(CompilationUnit root, ASTNode node, ISourceLocation loc) {
this.compilUnit = root;
this.loc = loc;
node.accept(this);
}
}