package edu.ucsd.arcum.interpreter.fragments;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ITypeBinding;
import edu.ucsd.arcum.exceptions.ArcumError;
import edu.ucsd.arcum.interpreter.query.Entity;
import edu.ucsd.arcum.interpreter.query.EntityType;
import edu.ucsd.arcum.interpreter.query.IEntityLookup;
import edu.ucsd.arcum.interpreter.satisfier.BindingMap;
import edu.ucsd.arcum.util.DynamicScope;
import edu.ucsd.arcum.util.Indenter;
public abstract class ProgramFragment
{
private static DynamicScope<Indenter> indenter = DynamicScope.newInstance();
// returns bindings matched on the specific entity, or null if there is no match
public final BindingMap matches(Object entity) {
if (entity == null) {
ArcumError.fatalError("Might need to be wrapped in empty entity");
}
if (entity instanceof ASTNode) {
return matchesASTNode((ASTNode)entity);
}
else if (entity instanceof ITypeBinding) {
return matchesTypeBinding((ITypeBinding)entity);
}
else if (entity instanceof EntityList) {
return matchesEntityList((EntityList)entity);
}
else if (entity instanceof ModifierElement) {
return matchesModifierElement((ModifierElement)entity);
}
else if (entity instanceof SignatureEntity) {
return matchesSignatureEntity((SignatureEntity)entity);
}
else if (entity instanceof EmptyEntityInfo) {
return matchesEmpty((EmptyEntityInfo)entity);
}
else {
return matchesSimpleProperty(entity);
}
}
protected BindingMap matchesASTNode(ASTNode astNode) {
return null;
}
protected BindingMap matchesTypeBinding(ITypeBinding typeBinding) {
return null;
}
protected BindingMap matchesEntityList(EntityList list) {
return null;
}
// Some SubtreeNodes, like ModifiersList, can represent lists of things
// that may be empty. This is to check for those situations.
protected BindingMap matchesEmpty(EmptyEntityInfo emptyEntityInfo) {
return null;
}
protected BindingMap matchesModifierElement(ModifierElement modifier) {
return null;
}
protected BindingMap matchesSignatureEntity(SignatureEntity signature) {
return null;
}
protected BindingMap matchesSimpleProperty(Object simple) {
return null;
}
@Override public final int hashCode() {
return super.hashCode();
}
@Override public final boolean equals(Object obj) {
return super.equals(obj);
}
// each top-level call to toString starts a new indenter; the below-levels
// exist at the "buildString" level and should only make other calls to
// "buildString" unless the item is unknown and a typical toString is called
// for (in which case, it must be on one line to work)
public final String toString() {
try {
indenter.push(new Indenter());
StringBuilder buff = new StringBuilder();
this.buildString(buff);
return buff.toString();
}
finally {
indenter.pop();
}
}
// Creates a binding with the special result binding
protected final BindingMap bindRoot(@Union("Entity") Object entity) {
BindingMap bindingMap = new BindingMap(entity);
return bindingMap;
}
// Creates a binding with the special result binding, in additional to naming it
// the given name (if the passed id is null, then this call will be equivalent
// to bindRoot)
protected BindingMap bindNamedRoot(Object entity, String id, EntityType type) {
BindingMap result = bindRoot(entity);
if (id != null) {
result.bind(id, entity, type);
}
return result;
}
// Quick short-hand notation for making an unparented copy of an existing ASTNode,
// which will belong to the target ast.
protected static <T extends ASTNode> T copy(AST ast, Class<T> clazz, ASTNode astNode) {
return clazz.cast(Entity.copySubtree(ast, astNode));
}
// The logic behind a buildString method is: Assume that a new line has
// been created for you. Insert the correct amount of indentation. Each time
// you create a new line of your own, insert the correct amount of
// indentation, unless it's your final newline. Leave the indenter at the
// same level with which you found it.
protected abstract void buildString(StringBuilder buff);
protected Indenter getIndenter() {
return indenter.peek();
}
public abstract BindingMap generateNode(IEntityLookup lookup, AST ast);
// Does this fragment represent a literal value? If so, that value can be
// accessed via matchResolvedEntity
public boolean isResolved() {
return false;
}
public BindingMap matchResolvedEntity() {
ArcumError.fatalError("Internal programming error: should only be called"
+ "when the fragment is resolved to a literal value with a single match");
return null/*unreachable*/;
}
}