package edu.ucsd.arcum.interpreter.query;
import static edu.ucsd.arcum.ArcumPlugin.DEBUG;
import java.util.*;
import org.eclipse.jdt.core.dom.ASTNode;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import edu.ucsd.arcum.exceptions.ArcumError;
import edu.ucsd.arcum.interpreter.ast.FormalParameter;
import edu.ucsd.arcum.interpreter.ast.TraitSignature;
import edu.ucsd.arcum.interpreter.fragments.ASTNodeReplacer;
import edu.ucsd.arcum.interpreter.fragments.Union;
import edu.ucsd.arcum.interpreter.satisfier.BindingMap;
import edu.ucsd.arcum.util.Accessor;
public class EntityTuple implements Comparable<EntityTuple>
{
public static Function<EntityTuple, ASTNode> getRootNode = Accessor.getFunction(
EntityTuple.class, ASTNode.class, "getRootNode");
private TraitSignature type;
private @Union("Entity") SortedMap<String, Object> values;
private ASTNode rootNode;
// if rootNode is null then the EntityTuple was probably matched via
// given parameters and can thus be without a root
public EntityTuple(TraitSignature type, Map<String, Object> values, ASTNode rootNode)
{
this.type = type;
this.values = new TreeMap<String, Object>(values);
this.rootNode = rootNode;
// toString(); FIXME: uncomment this to reveal hidden bugs
}
// returns the name and types of the variables bound by this tuple
public Set<FormalParameter> getBoundVariables() {
return new HashSet<FormalParameter>(type.getFormals());
}
public @Union("Entity") Object lookupEntity(String variable) {
return values.get(variable);
}
@Override public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(type.getName());
builder.append("<");
appendTupleMembers(builder);
builder.append(">");
return builder.toString();
}
public String toDisplayOfMembersString() {
StringBuilder builder = new StringBuilder();
appendTupleMembers(builder);
return builder.toString();
}
private void appendTupleMembers(StringBuilder builder) {
Iterator<FormalParameter> iterator = type.getFormals().iterator();
while (iterator.hasNext()) {
FormalParameter formal = iterator.next();
String id = formal.getIdentifier();
builder.append(id);
builder.append(":");
@Union("Entity") Object entity = values.get(id);
builder.append(Entity.getDisplayString(entity));
// if (DEBUG){
// builder.append(" ");
// builder.append(StringUtil.debugDisplay(entity));
// }
if (iterator.hasNext()) {
builder.append(", ");
}
}
}
public TraitSignature getType() {
return type;
}
public Map<String, Object> getValues() {
return values;
}
// may return null
public ASTNode getRootNode() {
return rootNode;
}
// Return the first element if it's an ASTNode, otherwise returns the so-called
// root node.
public ASTNode getMainDisplayNode() {
String firstID = type.getFormals().get(0).getIdentifier();
Object value = values.get(firstID);
if (value instanceof ASTNode) {
return (ASTNode)value;
}
else {
return getRootNode();
}
}
@Override public int compareTo(EntityTuple that) {
int n = this.type.getName().compareTo(that.type.getName());
if (n == 0) {
Iterator<Object> i = this.values.values().iterator();
Iterator<Object> j = that.values.values().iterator();
while (i.hasNext()) {
@Union("Entity") Object e = i.next();
@Union("Entity") Object f = j.next();
int k = Entity.compareToWithLocations(e, f);
if (k != 0) {
return k;
}
}
}
return n;
}
// An arg in the "args" list is either a program entity or a VariablePlaceholder.
// Arguments are assumed to be in order. Returns a BindingMap (potentially empty)
// if there is a match; otherwise there is no match and null is returned.
public BindingMap matches(List<FormalParameter> formals, List<Object> args) {
BindingMap result = BindingMap.newEmptyMap();
int size = formals.size();
argChecking: for (int i = 0; i < size; ++i) {
Object arg = args.get(i);
FormalParameter formal = formals.get(i);
Object foundEntity = values.get(formal.getIdentifier());
// TASK: use a different kind of entity instead
if (arg instanceof VariablePlaceholder) {
VariablePlaceholder variable = (VariablePlaceholder)arg;
if (variable.isSpecialAnyVariable()) {
continue argChecking;
}
else {
String name = variable.getName();
// GETDONE: Always binding, because the Modifiers, Signature cases
// need to be rethought anyway. What we should be doing instead is
// statically type checking the use of relations and the variables
// related to them... currently, we could get some weird stuff, like
// binding a class to an expression variable
result.bind(name, foundEntity, formal.getType());
// EntityType entityType = variable.getType();
// if (entityType.isInstance(foundEntity)) {
// result.bind(name, foundEntity);
// }
// else {
// if (DEBUG) {
// System.out.printf("Filtering out %s because it isn't a %s%n",
// Entity.getDisplayString(foundEntity), entityType);
// }
// return null;
// }
}
}
else {
if (Entity.compareToWithLocations(arg, foundEntity) != 0) {
return null;
}
}
}
return result;
}
// public void updateDescendants(Map<ASTNode, ASTNode> updatedNodeLookup) {
//// ASTTraverseTable table = new ASTTraverseTable();
//// IASTVisitor traverser = new NodeUpdater(updatedNodeLookup);
//// table.traverseAST(rootNode, traverser);
// ASTNode originalRootNode = rootNode;
// this.rootNode = updateDescendantsOfNode(rootNode, updatedNodeLookup);
// ASTUtil.recordSugaredNode(originalRootNode, rootNode);
//
// updatedNodeLookup = Maps.newHashMap(updatedNodeLookup);
// updatedNodeLookup.put(originalRootNode, rootNode);
// Set<String> members = Sets.newHashSet(values.keySet());
// for (String key : members) {
// Object object = values.get(key);
// if (updatedNodeLookup.containsKey(object)) {
// values.put(key, updatedNodeLookup.get(object));
// }
// }
// }
//
// private ASTNode updateDescendantsOfNode(ASTNode node, Map<ASTNode, ASTNode> lookup) {
// if (lookup.containsKey(node)) {
// return lookup.get(node);
// }
// else if (node instanceof QualifiedName) {
// QualifiedName qname = (QualifiedName)node;
// Name qualifier = qname.getQualifier();
// if (lookup.containsKey(qualifier)) {
// AST ast = node.getAST();
// FieldAccess fieldAccess = ast.newFieldAccess();
// Expression expression = (Expression)lookup.get(qualifier);
// expression = (Expression)Conversion.cleanseASTNode(ast, expression);
// fieldAccess.setExpression(expression);
// SimpleName name = (SimpleName)Conversion.cleanseASTNode(ast, qname.getName());
// fieldAccess.setName(name);
// ASTUtil.recordSugaredNode(node, fieldAccess);
// return fieldAccess;
// }
// return node;
// }
// if (alwaysTrue())
// return node;
// else {
// StructuralPropertyDescriptor[] spds = ASTTraverseTable.getProperties(node);
// edges: for (StructuralPropertyDescriptor spd : spds) {
// Object property = node.getStructuralProperty(spd);
// if (property == null) {
// continue edges;
// }
// if (spd.isChildProperty()) {
// ASTNode n = updateDescendantsOfNode((ASTNode)property, lookup);
// if (n != null) {
// node.setStructuralProperty(spd, n);
// }
// }
// else if (spd.isChildListProperty()) {
// List children = (List)property;
// for (int i = 0; i < children.size(); ++i) {
// ASTNode n = updateDescendantsOfNode((ASTNode)children.get(i), lookup);
// if (n != null) {
// children.set(i, n);
// }
// }
// }
// }
// }
// return ASTNode.copySubtree(node.getAST(), node);
// }
//
// private boolean alwaysTrue() {
// return true;
// }
// private class NodeUpdater extends ASTVisitorAdaptor
// {
// private Map<ASTNode, ASTNode> updatedNodeLookup;
//
// public NodeUpdater(Map<ASTNode, ASTNode> updatedNodeLookup) {
// this.updatedNodeLookup = updatedNodeLookup;
// }
//
// @Override public boolean beforeVisitEdge(ASTNode node,
// StructuralPropertyDescriptor edge)
// {
// if (node == null)
// return false;
// ASTNode replacement = updatedNodeLookup.get(node);
// if (replacement != null) {
// System.err.printf("I saw that %s [%x] needed to be %s [%x]%n", node,
// identityHashCode(node), replacement, identityHashCode(replacement));
//
// ASTNode parent = node.getParent();
// StructuralPropertyDescriptor spd = node.getLocationInParent();
// ASTNode unparentedReplacement = Entity.copySubtree(node.getAST(),
// replacement);
// if (spd.isChildProperty()) {
// parent.setStructuralProperty(spd, unparentedReplacement);
// }
// else if (spd.isChildListProperty()) {
// ChildListPropertyDescriptor clpd = (ChildListPropertyDescriptor)spd;
// List list = (List)parent.getStructuralProperty(clpd);
// int i = list.indexOf(node);
// if (i == -1) {
// ArcumError
// .fatalError("Internal NodeUpdater error: shouldn't happen%n");
// }
// list.set(i, unparentedReplacement);
// }
// }
// return true;
// }
//
// @Override public boolean visitASTNode(ASTNode node,
// StructuralPropertyDescriptor edge)
// {
// if (node == null) {
// return false;
// }
// return super.visitASTNode(node, edge);
// }
// }
public static Map<String, Object> values(List<String> names, Object... values)
{
Map<String, Object> result = Maps.newHashMap();
if (names.size() != values.length) {
ArcumError.fatalError("Internal error detected in values method");
}
for (int i=0; i<names.size(); ++i) {
result.put(names.get(i), values[i]);
}
return result;
}
public ASTNode getReplacementNode(ASTNode original) {
if (rootNode != null) {
return rootNode;
}
else {
for (Object member : values.values()) {
if (member instanceof ASTNodeReplacer) {
ASTNodeReplacer nodeReplacer = (ASTNodeReplacer)member;
return nodeReplacer.generateReplacement(original);
}
}
ArcumError.fatalError("Internal error: No ASTNodeReplacer found");
return null;
}
}
public Map<String, EntityType> getTypes() {
List<FormalParameter> formals = type.getFormals();
Map<String, EntityType> result = Maps.newTreeMap();
for (FormalParameter formal : formals) {
result.put(formal.getIdentifier(), formal.getType());
}
return result;
}
}