package edu.ucsd.arcum.interpreter.parser;
import java.util.Collection;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import edu.ucsd.arcum.exceptions.ArcumError;
import edu.ucsd.arcum.exceptions.JavaFragmentCompilationProblem;
import edu.ucsd.arcum.exceptions.SourceLocation;
import edu.ucsd.arcum.interpreter.query.Entity;
import edu.ucsd.arcum.interpreter.query.EntityDataBase;
import edu.ucsd.arcum.util.StringUtil;
public class FragmentParser
{
private static int serialCounter = 0;
private String imports;
private IJavaProject project;
private boolean matchingMode;
public FragmentParser() {
this("", null);
}
public FragmentParser(String imports, IProject project) {
this.imports = imports;
this.project = JavaCore.create(project);
this.matchingMode = true;
}
public static Type getType(String simpleOrQualifiedName) {
String className = newTempName();
String generatedSource = String.format("class %s { %s o; }", className,
simpleOrQualifiedName);
AbstractTypeDeclaration decl = parseTypeDeclaration(generatedSource);
BodyDeclaration bodyDecl = (BodyDeclaration)decl.bodyDeclarations().get(0);
FieldDeclaration field = (FieldDeclaration)bodyDecl;
return field.getType();
}
public AbstractTypeDeclaration getTypeDeclaration(String typeString) {
String generatedSource = String.format("%s%n%s", imports, typeString);
return parseTypeDeclaration(generatedSource);
}
// XXX: may want to deprecate this one and use the AST node version instead
public ITypeBinding getTypeBinding(String typeString)
throws JavaFragmentCompilationProblem
{
SingleVariableDeclaration var = declareParameterOfType(typeString);
IVariableBinding binding = (IVariableBinding)EntityDataBase
.resolveBindingNullOK(var);
if (binding == null)
return null;
ITypeBinding type = binding.getType();
return type;
}
public static class TypeBindingWithModifiers {
public ITypeBinding typeBinding;
public List<IExtendedModifier> modifiers;
}
public TypeBindingWithModifiers getTypeBindingWithModifiers(String typeString) throws JavaFragmentCompilationProblem {
SingleVariableDeclaration var = declareParameterOfType(typeString);
TypeBindingWithModifiers result = new TypeBindingWithModifiers();
result.typeBinding = (ITypeBinding)EntityDataBase.resolveBinding(var.getType());
result.modifiers = var.modifiers();
return result;
}
public MethodDeclaration getMethod(String methodString)
throws JavaFragmentCompilationProblem
{
String name = newTempName();
String src = String.format("%s%nclass %s {%n %s}", imports, name, methodString);
AbstractTypeDeclaration decl = parseTypeDeclaration(name, src);
BodyDeclaration bodyDecl = (BodyDeclaration)decl.bodyDeclarations().get(0);
MethodDeclaration method = (MethodDeclaration)bodyDecl;
return method;
}
// TODO: All other methods like this one in FragmentParser should also pass in
// a location, so that error messages can be seen. Usually these errors would be
// due to a typo or a missing import.
// TODO: All other methods like this one should return a copy of the subtree, so
// that it doesn't have the fake parent associated with it (the fake parent is
// the dummy compilation unit that we make)
public Annotation getAnnotation(String anntString, SourceLocation location)
throws JavaFragmentCompilationProblem
{
String name = newTempName();
String src = String.format("%s%n %s class %s {}", imports, anntString, name);
AbstractTypeDeclaration decl = parseTypeDeclaration(name, src);
List modifiers = decl.modifiers();
if (modifiers.size() != 1 && !(modifiers.get(0) instanceof Annotation)) {
ArcumError.fatalError("Internal parsing error in getAnnotation");
}
Annotation annotation = (Annotation)modifiers.get(0);
IAnnotationBinding binding = annotation.resolveAnnotationBinding();
if (binding == null) {
ArcumError.fatalUserError(location, "%s cannot be resolved to an annotation",
annotation.toString());
}
return Entity.copySubtree(annotation.getAST(), annotation);
}
public Expression getExpression(String exprString, Collection<String> locals)
throws JavaFragmentCompilationProblem
{
String methodString = String.format("void f(%s) { Object o = %s; }", StringUtil
.separate(locals, ", "), exprString);
MethodDeclaration method = getMethod(methodString);
Block body = method.getBody();
VariableDeclarationStatement statement = (VariableDeclarationStatement)body
.statements().get(0);
VariableDeclarationFragment varFrag = (VariableDeclarationFragment)statement
.fragments().get(0);
Expression expression = varFrag.getInitializer();
return expression;
}
public Statement getStatement(String stmtString)
throws JavaFragmentCompilationProblem
{
String methodString = String.format("void f() { %s }", stmtString);
MethodDeclaration method = getMethod(methodString);
Block body = method.getBody();
return (Statement)body.statements().get(0);
}
// WEDNESDAY: Consider having this be a return type instead, so that the void
// case can be used legally too.
private SingleVariableDeclaration declareParameterOfType(String typeString)
throws JavaFragmentCompilationProblem
{
String name = newTempName();
StringBuilder buff = new StringBuilder();
buff.append(imports);
buff.append(String.format("%ninterface %s {%n", name));
buff.append(String.format(" void f(%s param); }", typeString));
String generatedSource = buff.toString();
AbstractTypeDeclaration decl = parseTypeDeclaration(name, generatedSource);
BodyDeclaration bodyDecl = (BodyDeclaration)decl.bodyDeclarations().get(0);
MethodDeclaration signature = (MethodDeclaration)bodyDecl;
SingleVariableDeclaration var = (SingleVariableDeclaration)signature.parameters()
.get(0);
return var;
}
private static String newTempName() {
return String.format("F%d", serialCounter++);
}
private AbstractTypeDeclaration parseTypeDeclaration(String name,
String generatedSource) throws JavaFragmentCompilationProblem
{
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(generatedSource.toCharArray());
parser.setCompilerOptions(new CompilerOptions().getMap());
parser.setProject(project);
parser.setUnitName(name);
parser.setResolveBindings(true);
CompilationUnit cu = (CompilationUnit)parser.createAST(null);
AbstractTypeDeclaration decl = (AbstractTypeDeclaration)cu.types().get(0);
Message[] messages = cu.getMessages();
boolean nonResolutionError = false;
for (Message message : messages) {
if (!message.getMessage().endsWith("cannot be resolved")) {
nonResolutionError = true;
break;
}
}
if (nonResolutionError && messages.length != 0 && matchingMode) {
// URGENT !!!!! -- No Typo Checking Available When Commented Out!
// throw new JavaFragmentCompilationProblem(messages);
}
return decl;
}
// parse without resolution
private static AbstractTypeDeclaration parseTypeDeclaration(String generatedSource) {
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(generatedSource.toCharArray());
CompilationUnit result = (CompilationUnit)parser.createAST(null);
AbstractTypeDeclaration decl = (AbstractTypeDeclaration)result.types().get(0);
return decl;
}
public void setMatchingMode(boolean matchingMode) {
this.matchingMode = matchingMode;
}
}