CommonASTNode.java
package edu.udel.cis.vsl.abc.ast.node.common;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import edu.udel.cis.vsl.abc.ast.IF.AST;
import edu.udel.cis.vsl.abc.ast.IF.ASTException;
import edu.udel.cis.vsl.abc.ast.IF.ASTs;
import edu.udel.cis.vsl.abc.ast.IF.DifferenceObject;
import edu.udel.cis.vsl.abc.ast.IF.DifferenceObject.DiffKind;
import edu.udel.cis.vsl.abc.ast.entity.IF.Scope;
import edu.udel.cis.vsl.abc.ast.node.IF.ASTNode;
import edu.udel.cis.vsl.abc.ast.node.IF.AttributeKey;
import edu.udel.cis.vsl.abc.ast.node.IF.NodePredicate;
import edu.udel.cis.vsl.abc.token.IF.Source;
public abstract class CommonASTNode implements ASTNode {
private static long instanceCount = 0;
private long instanceId;
private int id = -1;
private AST owner = null;
private ASTNode parent;
private int childIndex = -1;
private ArrayList<ASTNode> children;
private Source source;
private ArrayList<Object> attributes = null;
private Scope scope;
private void checkModifiable() {
if (owner != null)
throw new ASTException(
"Cannot modify node until released from AST:\n" + this);
}
protected static <T extends ASTNode> T duplicate(T node) {
if (node == null)
return null;
else {
@SuppressWarnings("unchecked")
T copy = (T) node.copy();
return copy;
}
}
protected static <T extends ASTNode> List<T> duplicate(List<T> list) {
LinkedList<T> newList = new LinkedList<T>();
for (T node : list)
newList.add(duplicate(node));
return newList;
}
public CommonASTNode(Source source,
Iterator<? extends ASTNode> childIterator) {
int childCount = 0;
instanceId = instanceCount++;
this.source = source;
children = new ArrayList<ASTNode>();
while (childIterator.hasNext()) {
CommonASTNode child = (CommonASTNode) childIterator.next();
children.add(child);
if (child != null) {
if (child.parent() != null)
throw new ASTException("Cannot make\n" + child
+ "\na child of node\n" + this
+ "\nbecause it is already a child of\n"
+ child.parent());
child.parent = this;
child.childIndex = childCount;
}
childCount++;
}
}
public CommonASTNode(Source source,
Iterable<? extends ASTNode> childCollection) {
this(source, childCollection.iterator());
}
public CommonASTNode(Source source, ASTNode[] childArray) {
this(source, Arrays.asList(childArray).iterator());
}
public CommonASTNode(Source source) {
this.source = source;
children = new ArrayList<ASTNode>();
}
public CommonASTNode(Source source, ASTNode child) {
this(source, new ASTNode[]{child});
}
public CommonASTNode(Source source, ASTNode child0, ASTNode child1) {
this(source, new ASTNode[]{child0, child1});
}
public CommonASTNode(Source source, ASTNode child0, ASTNode child1,
ASTNode child2) {
this(source, new ASTNode[]{child0, child1, child2});
}
public CommonASTNode(Source source, ASTNode child0, ASTNode child1,
ASTNode child2, ASTNode child3) {
this(source, new ASTNode[]{child0, child1, child2, child3});
}
@Override
public int id() {
return id;
}
@Override
public void setId(int id) {
this.id = id;
}
@Override
public void setOwner(AST owner) {
this.owner = owner;
}
@Override
public AST getOwner() {
return owner;
}
@Override
public ASTNode parent() {
return parent;
}
@Override
public int childIndex() {
return childIndex;
}
@Override
public int numChildren() {
return children.size();
}
@Override
public ASTNode child(int index) throws NoSuchElementException {
return children.get(index);
}
@Override
public Iterable<ASTNode> children() {
return children;
}
@Override
public void print(String prefix, PrintStream out, boolean includeSource) {
out.print(prefix);
if (childIndex >= 0)
out.print(childIndex);
out.print("[" + id + "]: ");
printBody(out);
if (scope != null) {
out.print(" (scope " + scope.getId() + ")");
} else {
out.print(" (scope UNKNOWN)");
}
if (includeSource && source != null) {
out.println();
out.print(prefix + "| source: " + source.getSummary(false, false));
}
printExtras(prefix + "| ", out);
}
protected abstract void printBody(PrintStream out);
protected void printExtras(String prefix, PrintStream out) {
}
@Override
public Object getAttribute(AttributeKey key) {
if (attributes == null)
return null;
else {
int id = ((CommonAttributeKey) key).getId();
if (id >= attributes.size())
return null;
return attributes.get(id);
}
}
@Override
public void setAttribute(AttributeKey key, Object value) {
int id = ((CommonAttributeKey) key).getId();
Class<? extends Object> attributeClass = key.getAttributeClass();
int size;
if (!(attributeClass.isInstance(value)))
throw new IllegalArgumentException(
"Attribute " + ((CommonAttributeKey) key).getAttributeName()
+ " has type " + attributeClass + " but given "
+ value + " of type " + value.getClass());
if (attributes == null)
attributes = new ArrayList<Object>();
size = attributes.size();
while (id >= size) {
attributes.add(null);
size++;
}
attributes.set(id, value);
}
@Override
public Source getSource() {
return source;
}
protected int addChild(ASTNode child) {
int index = numChildren();
checkModifiable();
children.add(child);
if (child != null) {
if (child.parent() != null)
throw new ASTException(
"Cannot make\n" + child + "\na child of node\n" + this
+ "\nbecause it is already a child of\n"
+ child.parent());
((CommonASTNode) child).parent = this;
((CommonASTNode) child).childIndex = index;
}
return index;
}
@Override
public ASTNode setChild(int index, ASTNode child) {
int numChildren = children.size();
ASTNode oldChild;
checkModifiable();
if (index < 0)
throw new ASTException(
"Negative index " + index + " used in setChild on " + this);
if (child != null && child.parent() != null)
throw new ASTException("Cannot make\n" + child
+ "\na child of node\n" + this
+ "\nbecause it is already a child of\n" + child.parent());
while (index >= numChildren) {
children.add(null);
numChildren++;
}
oldChild = children.get(index);
if (oldChild != null) {
((CommonASTNode) oldChild).parent = null;
((CommonASTNode) oldChild).childIndex = -1;
}
children.set(index, child);
if (child != null) {
((CommonASTNode) child).parent = this;
((CommonASTNode) child).childIndex = index;
}
return oldChild;
}
/**
* Insert a sequence of nodes into the child list of this node.
*
* @param index
* an integer in [0,numChildren]
* @param list
* a non-null list of free nodes, any of which may be null
*/
protected void insertChildrenAt(int index, List<? extends ASTNode> list) {
int oldSize = children.size();
int listSize = list.size();
int newSize = oldSize + listSize;
checkModifiable();
if (index < 0)
throw new ASTException("Negative index " + index
+ " used in insertChildren on " + this);
if (index > oldSize)
throw new ASTException("Index " + index + " exceeds size " + oldSize
+ " in insertChildren on " + this);
children.addAll(index, list);
for (int i = index; i < index + listSize; i++) {
ASTNode child = children.get(i);
if (child != null) {
if (child.parent() != null)
throw new ASTException("Cannot make\n" + child
+ "\na child of node\n" + this
+ "\nbecause it is already a child of\n"
+ child.parent());
((CommonASTNode) child).parent = this;
((CommonASTNode) child).childIndex = i;
}
}
for (int i = index + listSize; i < newSize; i++) {
ASTNode child = children.get(i);
if (child != null) {
((CommonASTNode) child).childIndex = i;
}
}
}
@Override
public ASTNode removeChild(int index) {
int numChildren = children.size();
ASTNode oldChild;
checkModifiable();
if (index < 0 || index >= numChildren)
throw new ASTException("Index " + index + " out of range [0,"
+ (numChildren - 1) + "]:" + this);
oldChild = children.get(index);
if (oldChild != null) {
((CommonASTNode) oldChild).parent = null;
((CommonASTNode) oldChild).childIndex = -1;
}
children.set(index, null);
return oldChild;
}
@Override
public void remove() {
if (parent != null) {
parent.removeChild(childIndex);
}
}
/**
* Default implementation, for non-sequence nodes. Must be overridden for
* sequence nodes.
*/
@Override
public void keepOnly(NodePredicate keep) {
int numChildren = numChildren();
checkModifiable();
for (int i = 0; i < numChildren; i++) {
ASTNode child = child(i);
if (child != null) {
if (keep.holds(child)) {
// // add the file name to the file name map
// TokenUtils.addFileName(TokenUtils.getShortFilename(this
// .getSource().getFirstToken(), false));
child.keepOnly(keep);
} else
removeChild(i);
}
}
}
/**
* Removes children and shifts down to remove the gaps; also applies
* keepOnly to children not removed. This method is meant to be applied to
* sequence nodes.
*
* @param keep
* a node predicate telling which nodes to keep
*/
protected void keepOnlyAndShift(NodePredicate keep) {
int numChildren = numChildren();
int count = 0; // number of children to keep
checkModifiable();
for (int i = 0; i < numChildren; i++) {
ASTNode child = child(i);
if (child != null) {
if (keep.holds(child)) {
child.keepOnly(keep);
if (count < i) {
children.set(count, child);
((CommonASTNode) child).childIndex = count;
}
count++;
} else
removeChild(i);
}
}
children.subList(count, numChildren).clear();
}
@Override
public void setScope(Scope scope) {
this.scope = scope;
}
@Override
public Scope getScope() {
return scope;
}
@Override
public boolean equiv(ASTNode that) {
return diff(that) == null;
}
@Override
public DifferenceObject diff(ASTNode that) {
DifferenceObject diff;
if (that == null)
return new DifferenceObject(this, false);
diff = diffWork(that);
if (diff != null)
return diff;
if (this.numChildren() != that.numChildren())
return new DifferenceObject(this, that, DiffKind.NUM_CHILDREN);
for (int i = 0; i < this.numChildren(); i++) {
ASTNode thisChild = this.child(i), thatChild = that.child(i);
if (thisChild != null) {
diff = thisChild.diff(thatChild);
if (diff != null)
return diff;
} else if (thatChild != null)
return new DifferenceObject(that, true);
}
return null;
}
protected DifferenceObject diffWork(ASTNode that) {
if (this.nodeKind() == that.nodeKind())
return null;
return new DifferenceObject(this, that);
}
@Override
public String toString() {
return "Node[" + id + ", " + instanceId + ", "
+ source.getSummary(false, false) + "]";
}
@Override
public ASTNode nextDFS() {
// look for a child...
for (ASTNode child : children) {
if (child != null)
return child;
}
// if no child, backtrack...
{
ASTNode node = this;
while (true) {
int index = node.childIndex();
node = node.parent();
if (node == null)
return null;
else {
int numChildren = node.numChildren();
for (int i = index + 1; i < numChildren; i++) {
ASTNode child = node.child(i);
if (child != null)
return child;
}
}
}
}
}
@Override
public void prettyPrint(PrintStream out) {
ASTs.prettyPrint(this, out);
}
@Override
public StringBuffer prettyRepresentation() {
return ASTs.prettyRepresentation(this, -1);
}
@Override
public StringBuffer prettyRepresentation(int maxLength) {
return ASTs.prettyRepresentation(this, maxLength);
}
}