package edu.ucsd.arcum.interpreter.query;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import com.google.common.collect.Maps;
import edu.ucsd.arcum.util.ClassImplements;
// A traverse table can inspect AST nodes and invoke the property descriptor
// accessor methods available from the node (in order to allow visits to all
// of the node's children).
//
// To avoid recomputing the same relations all instances share the same lookup
// table.
public class ASTTraverseTable
{
private static final Map<Class<? extends ASTNode>, StructuralPropertyDescriptor[]> lookup;
static {
// POSSIBLE_ECLIPSE_BUG: try: ASTTraversTable.lookup = ...
lookup = Maps.newConcurrentHashMap();
}
public void traverseAST(ASTNode node, IASTVisitor visitor) {
traverseNode(node, node.getLocationInParent(), visitor);
}
private void traverseNode(ASTNode node, StructuralPropertyDescriptor edge,
IASTVisitor visitor)
{
boolean visitChildren = visitor.visitASTNode(node, edge);
if (visitChildren) {
StructuralPropertyDescriptor[] spds = getProperties(node);
for (StructuralPropertyDescriptor spd : spds) {
boolean doVisit = visitor.beforeVisitEdge(node, spd);
if (!doVisit) {
continue;
}
Object property = node.getStructuralProperty(spd);
if (spd.isSimpleProperty()) {
visitor.visitSimpleProperty(property, spd);
}
else if (spd.isChildProperty()) {
ASTNode child = (ASTNode)property;
traverseNode(child, spd, visitor);
}
else if (spd.isChildListProperty()) {
List children = (List)property;
boolean iterate = visitor.preVisitASTNodeList(node, children, spd);
if (iterate) {
for (Object obj : children) {
traverseNode((ASTNode)obj, spd, visitor);
visitor.postVisitASTNodeListElement(spd, children);
}
}
}
else
assert false;
visitor.afterVisitEdge(node, spd);
}
visitor.afterVisitASTNodesChildren(node);
}
}
public static StructuralPropertyDescriptor[] getProperties(
@ClassImplements(PropertyDescriptorAccessor.class) ASTNode node)
{
// List properties = (List)ClassMethod.invoke(PropertyDescriptorAccessor.class, node.getClass(), AST.JLS3);
Class<? extends ASTNode> nodeClass = node.getClass();
StructuralPropertyDescriptor[] spds = lookup.get(nodeClass);
if (spds == null) {
try {
Method method;
List properties;
method = nodeClass.getMethod("propertyDescriptors", int.class);
properties = (List)method.invoke(nodeClass, AST.JLS3);
spds = toArray(properties);
lookup.put(nodeClass, spds);
}
catch (RuntimeException e) {
throw e;
}
catch (Exception e) {
e.printStackTrace();
}
}
return spds;
}
private static StructuralPropertyDescriptor[] toArray(List list) {
StructuralPropertyDescriptor[] result;
result = new StructuralPropertyDescriptor[list.size()];
int i = 0;
for (Object o : list) {
result[i++] = (StructuralPropertyDescriptor)o;
}
return result;
}
private static List<ASTNode> subNodes(ASTNode node) {
StructuralPropertyDescriptor[] properties = getProperties(node);
List<ASTNode> result = new ArrayList<ASTNode>();
for (StructuralPropertyDescriptor spd : properties) {
Object property = node.getStructuralProperty(spd);
if (spd.isChildProperty()) {
result.add((ASTNode)property);
}
else if (spd.isChildListProperty()) {
List objects = (List)property;
for (Object object : objects) {
result.add((ASTNode)object);
}
}
}
return result;
}
}