package edu.neu.ccs.demeter.dj;
import edu.neu.ccs.demeter.aplib.EdgeI;
import edu.neu.ccs.demeter.aplib.NoSuchClassGraphNodeException;
import java.util.*;
import java.lang.reflect.*;
/** A subgraph of an object graph, rooted at the object graph's root,
determined by a strategy. */
public class ObjectGraphSlice {
protected Traversal tg;
protected ObjectGraph og;
protected Object root;
protected Traversal.NodeSet startSet;
public boolean debug;
/** The subgraph of o determined by t. */
public ObjectGraphSlice(ObjectGraph o, Traversal t) {
if (o.getClassGraph() != t.getClassGraph())
throw new IncompatibleClassGraphsException(o, t);
tg = t;
og = o;
root = o.getRoot();
// FIXME: should either disallow root == null,
// or handle it in Traverser.traverse()
startSet = getStartSet(root.getClass());
if (startSet == null)
throw new TraversalSourceException(root, tg);
}
/** The subgraph of the object graph rooted at o determined by t. */
public ObjectGraphSlice(Object o, Traversal t)
{ this(new ObjectGraph(o, (ClassGraph) t.getClassGraph()), t); }
/** The subgraph of o determined by s. */
public ObjectGraphSlice(ObjectGraph o, Strategy s)
throws TraversalException
{ this(o, new Traversal(s, o.getClassGraph())); }
/** The subgraph of o determined by strategy s. */
public ObjectGraphSlice(ObjectGraph o, String s)
throws TraversalException
{ this(o, new Strategy(s)); }
/** The traversal that determines the slice. */
public Traversal getTraversal() { return tg; }
/** The object graph that this is a slice of. */
public ObjectGraph getObjectGraph() { return og; }
/** The root of the object graph slice. */
public Object getRoot() { return root; }
/** The class graph that the object graph slice is an instance of. */
public ClassGraph getClassGraph() { return (ClassGraph) tg.getClassGraph(); }
/** The strategy expression that was used to build the traversal. */
public Strategy getStrategy() { return (Strategy) tg.getStrategy(); }
/** Compare the traversals and roots with equals(). */
public boolean equals(Object ogs) {
if (ogs == this) return true;
if (!(ogs instanceof ObjectGraphSlice)) return false;
return (((ObjectGraphSlice) ogs).tg.equals(tg) &&
((ObjectGraphSlice) ogs).root.equals(root));
}
/** Visit the object graph slice with v
, returning
v.getReturnValue()
. */
public Object traverse(Visitor v) {
return traverse(new Visitor[] { v }, true);
}
/** Visit the object graph slice with v
, returning
v.getReturnValue()
. Traverse edges in left-to-right
order if forward
is true, otherwise traverse in
right-to-left order. */
public Object traverse(Visitor v, boolean forward) {
return traverse(new Visitor[] { v }, forward);
}
/** Visit the object graph slice with the visitors in v
,
returning v[0].getReturnValue()
. */
public Object traverse(Visitor[] v) {
return traverse(v, true);
}
/** Visit the object graph slice with the visitors in v, returning
v[0].getReturnValue(). Traverse edges in left-to-right order if
forward
is true, otherwise traverse in right-to-left
order. */
public Object traverse(Visitor[] v, boolean forward) {
for (int i = 0; i < v.length; i++) v[i].start();
Object ret = new VisitorTraverser(v, forward).traverse(root, startSet);
for (int i = v.length-1; i >= 0; i--) v[i].finish();
return ret;
}
/** Return the target object in the object graph slice reachable by
following the strategy. There must be a unique path to it, or else
a FetchException is thrown. */
public Object fetch() throws FetchException {
List t = getTraversal().getFinishSet();
if (t.isEmpty()) {
throw new FetchException("Cannot fetch without a target.");
}
if (t.size() > 1) {
throw new FetchException("Cannot fetch multiple targets.");
}
return new Fetcher().traverse(root, startSet);
}
/** Return a list of all target objects in the object graph slice
reachable by following the strategy. Traverse the graph in
left-to-right prefix order. */
public List gather() { return gather(true); }
/** Return a list of all target objects in the object graph slice
reachable by following the strategy. Traverse the graph in
left-to-right prefix order if forward
is true,
otherwise traverse in right-to-left postfix order. */
public List gather(boolean forward) {
return (List) new Gatherer(forward).traverse(root, startSet);
}
/** A fixed-size List backed by the object graph slice. The
elements of the list are the target objects reachable by following
the strategy through the graph in left-to-right prefix order. Note
that this list (like the List returned by {@link Arrays#asList},
but unlike the Vector returned by {@link #gather()}) is
write-through, i.e. modifying it will modify the underlying object
graph, and vice versa. */
public List asList() { return asList(true); }
/** A fixed-size List backed by the object graph slice. The
elements of the list are the target objects reachable by following
the strategy through the graph. If forward
is true,
the graph is traversed in left-to-right prefix order, otherwise it
is traversed in right-to-left postfix order. Note that this list
(like the List returned by {@link Arrays#asList}, but unlike the
Vector returned by {@link #gather()}) is write-through,
i.e. modifying it will modify the underlying object graph, and vice
versa. */
public List asList(boolean forward) {
return new APCollection(forward);
}
// Here ends the public interface of ObjectGraphSlice.
class MutableFlag {
boolean flag;
MutableFlag(boolean f) { flag = f; }
}
// FIXME: explain this...
abstract class Traverser {
boolean forward = true;
Traverser() { }
Traverser(boolean f) { forward = f; }
Object traverse(Object o, Traversal.NodeSet tokens) {
traverseNode(o, getTokensClass(tokens), tokens,
false, new MutableFlag(false));
return getReturnValue();
}
int nest = 0;
void indent(Object msg) {
for (int i = 0; i < nest; i++) System.out.print(" ");
System.out.println(msg);
}
void traverseNode(Object o, Class nodeClass,
Traversal.NodeSet tokens,
boolean lastWasInheritance,
MutableFlag traversedCollection)
{
Class cl = o.getClass();
List ancestors = null;
if (debug) indent("object type = " + cl + ", tokens = " + tokens);
if (cl.equals(nodeClass) ||
// Primitive member values get wrapped, so the class might not
// match the member type; however, we don't have to actually
// check that the wrapper is the right type, since the value
// of a primitive member can't be any other type anyway.
nodeClass.isPrimitive())
{
before(o, ancestors = getAncestors(nodeClass));
}
// FIXME: around methods?
nest++;
if (cl.isArray()) {
traverseArray(o, nodeClass, tokens);
} else {
traverseEdges(o, nodeClass, tokens.getOutgoingEdgeSets(),
lastWasInheritance, traversedCollection);
}
nest--;
if (ancestors != null) {
after(o, ancestors);
}
}
void before(Object o, List ancestors) {
ListIterator it = ancestors.listIterator();
while (it.hasNext())
before(o, (Class) it.next());
}
void before(Object o, Class cl) {
// FIXME: compare token set with finish set
if (forward && isTargetClass(cl))
atTarget(o, cl);
}
void after(Object o, List ancestors) {
ListIterator it = ancestors.listIterator(ancestors.size());
while (it.hasPrevious())
after(o, (Class) it.previous());
}
void after(Object o, Class cl) {
// FIXME: compare token set with finish set
if (!forward && isTargetClass(cl))
atTarget(o, cl);
}
void atTarget(Object o, Class cl) {
if (debug) indent("at " + cl);
}
void traverseArray(Object o, Class cl, Traversal.NodeSet tokens) {
int l = Array.getLength(o);
int i = (forward ? 0 : l - 1);
while (forward ? i < l : i >= 0) {
traverseArrayElement(o, i, cl, tokens);
if (forward) i++; else i--;
}
}
void traverseArrayElement(Object o, int i, Class cl,
Traversal.NodeSet tokens)
{
Object target = Array.get(o, i);
if (target != null) {
// FIXME: repetition edge wrappers
traverseNode(target, cl, tokens, false, new MutableFlag(false));
}
}
void traverseEdges(Object o, Class cl, List edgeSets,
boolean lastWasInheritance,
MutableFlag traversedCollection) {
// FIXME: does this traverse the edges in the "right" order?
int i = (forward ? 0 : edgeSets.size());
ListIterator it = edgeSets.listIterator(i);
while (forward ? it.hasNext() : it.hasPrevious()) {
Traversal.EdgeSet edgeTokens
= (Traversal.EdgeSet) (forward ? it.next() : it.previous());
traverseEdge(o, cl, edgeTokens,
lastWasInheritance, traversedCollection);
}
}
void traverseEdge(Object o, Class cl, Traversal.EdgeSet tokens,
boolean lastWasInheritance,
MutableFlag traversedCollection)
{
Traversal.NodeSet nextTokens = tokens.getTargetNodeSet();
Class nextClass = getTokensClass(nextTokens);
EdgeI edge = tokens.getEdge();
if (lastWasInheritance || o.getClass().equals(cl)) {
if (edge.isConstructionEdge()) {
if (isRepetitionEdge(edge)) {
if (debug) indent(Traversal.edgeKey(edge));
traverseCollection(o, nextClass, nextTokens, traversedCollection);
} else {
if (debug) indent(Traversal.edgeKey(edge));
traverseConstructionEdge(o, cl, edge, nextClass, nextTokens);
}
} else if (edge.isInheritanceEdge()) {
if (debug) indent(Traversal.edgeKey(edge));
traverseSuperclass(o, nextClass, nextTokens, traversedCollection);
}
} else if (edge.isAlternationEdge()) {
if (!lastWasInheritance && // knowledge path constraint
nextClass.isInstance(o)) // premature termination
{
if (debug) indent(Traversal.edgeKey(edge));
traverseSubclass(o, nextClass, nextTokens, traversedCollection);
}
}
}
void traverseCollection(Object o, Class nextClass,
Traversal.NodeSet nextTokens,
MutableFlag traversedCollection)
{
if (!traversedCollection.flag) {
// Make sure we don't traverse a collection more than once
// due to multiple inheritance paths to the collection
// interface.
traversedCollection.flag = true;
ListIterator it = getListIterator(o);
while (forward ? it.hasNext() : it.hasPrevious())
traverseCollectionElement(it, forward ? it.next() : it.previous(),
nextClass, nextTokens, traversedCollection);
}
}
void traverseCollectionElement(ListIterator it, Object o, Class cl,
Traversal.NodeSet tokens,
MutableFlag traversedCollection)
{
if (o != null) {
// FIXME: repetition edge wrappers
traverseNode(o, cl, tokens, false, traversedCollection);
}
}
void traverseConstructionEdge(Object o, Class cl, EdgeI edge,
Class targetClass,
Traversal.NodeSet tokens)
{
Object target = getTargetObject(o, cl, edge);
if (target != null) {
before(o, cl, edge, target, targetClass);
traverseNode(target, targetClass, tokens, false,
new MutableFlag(false));
after(o, cl, edge, target, targetClass);
}
}
void traverseSubclass(Object o, Class subclass,
Traversal.NodeSet tokens,
MutableFlag traversedCollection)
{
// FIXME: alternation edge wrappers
traverseNode(o, subclass, tokens, false, traversedCollection);
}
void traverseSuperclass(Object o, Class superclass,
Traversal.NodeSet tokens,
MutableFlag traversedCollection)
{
// FIXME: inheritance edge wrappers
traverseNode(o, superclass, tokens, true, traversedCollection);
}
void before(Object source, Class sourceClass, EdgeI edge,
Object target, Class targetClass) {
}
void after(Object source, Class sourceClass, EdgeI edge,
Object target, Class targetClass) {
}
Object returnValue;
Object getReturnValue() { return returnValue; }
}
class VisitorTraverser extends Traverser {
Visitor visitors[];
VisitorTraverser(Visitor v[], boolean f) {
super(f);
visitors = v;
}
void before(Object o, Class cl) {
for (int i = 0; i < visitors.length; i++)
visitors[i].before(o, cl);
}
void before(Object source, Class sourceClass, EdgeI edge,
Object target, Class targetClass) {
for (int i = 0; i < visitors.length; i++)
visitors[i].before(source, sourceClass, edge, target, targetClass);
}
void after(Object o, Class cl) {
for (int i = visitors.length - 1; i >= 0; i--)
visitors[i].after(o, cl);
}
void after(Object source, Class sourceClass, EdgeI edge,
Object target, Class targetClass) {
for (int i = visitors.length - 1; i >= 0; i--)
visitors[i].after(source, sourceClass, edge, target, targetClass);
}
Object getReturnValue() { return visitors[0].getReturnValue(); }
}
class Fetcher extends Traverser {
void atTarget(Object o, Class cl) {
// if (returnValue != null)
// throw new FetchException("Non-unique path.");
returnValue = o;
super.atTarget(o, cl);
}
void traverseArray(Object o, Class cl, Traversal.NodeSet tokens) {
throw new FetchException("Cannot fetch through arrays: " + o.getClass());
}
void traverseEdges(Object o, Class cl, List edgeSets,
boolean lastWasInheritance,
MutableFlag traversedCollection)
{
if (edgeSets.size() > 1) {
Iterator it = edgeSets.iterator();
int i = 0;
while (it.hasNext()) {
Traversal.EdgeSet edgeSet = (Traversal.EdgeSet) it.next();
if (edgeSet.getEdge().isConstructionEdge())
if (i++ == 1)
throw new FetchException("Non-unique path.");
}
}
super.traverseEdges(o, cl, edgeSets,
lastWasInheritance, traversedCollection);
}
void traverseCollection(Object o, Class nextClass,
Traversal.NodeSet nextTokens,
MutableFlag traversedCollection)
{
throw new FetchException("Cannot fetch through collections: "
+ o.getClass());
}
}
class Gatherer extends Traverser {
Gatherer(boolean f) {
super(f);
returnValue = new ArrayList();
}
Object last;
void atTarget(Object o, Class cl) {
// Don't add the same object multiple times in a row, because
// multiple classes in its hierarchy might be target classes.
if (o != last) ((List) returnValue).add(o);
last = o;
super.atTarget(o, cl);
}
}
class ConcurrentTraverser extends Traverser {
ConcurrentTraverser(boolean f) { super(f); }
abstract class Pointer { abstract void set(Object o); }
Pointer pointer;
void traverseArrayElement(final Object o, final int i, Class cl,
Traversal.NodeSet tokens)
{
pointer = new Pointer() {
public void set(Object o2) {
Array.set(o, i, o2);
}
};
super.traverseArrayElement(o, i, cl, tokens);
}
void traverseCollectionElement(final ListIterator it, Object o, Class cl,
Traversal.NodeSet tokens,
MutableFlag traversedCollection)
{
pointer = new Pointer() {
// Remember where we are now...
int i = it.nextIndex();
public void set(Object o2) {
int j = it.nextIndex();
// ...so we can go back to it if we need to replace.
while (forward ? it.nextIndex() >= i : it.nextIndex() <= i)
if (forward) it.previous(); else it.next();
// We need to set through this iterator because if the collection
// changes by some other method, the iterator might fail-fast.
it.set(o2);
while (forward ? it.nextIndex() < j : it.nextIndex() > j)
if (forward) it.next(); else it.previous();
}
};
super.traverseCollectionElement(it, o, cl, tokens, traversedCollection);
}
void traverseConstructionEdge(final Object o, final Class cl,
final EdgeI edge, Class targetClass,
Traversal.NodeSet tokens)
{
pointer = new Pointer() {
public void set(Object o2) {
setTargetObject(o, cl, edge, o2);
}
};
super.traverseConstructionEdge(o, cl, edge, targetClass, tokens);
}
Pointer returnValuePointer;
void atTarget(Object o, Class cl) {
super.atTarget(o, cl);
returnValue = o;
this.notify(); // signal that the return value is ready
try {
this.wait(); // wait for return value to be read
} catch (InterruptedException e) {
// FIXME: exit more gracefully?
throw new RuntimeException(e.toString());
}
returnValuePointer = pointer; // don't allow set until the return
// value has actually been read
}
void set(Object o) {
returnValuePointer.set(o);
}
}
class APCollection extends AbstractSequentialList
{
boolean forward = true;
/** Construct a collection-view of the target objects in the
enclosing object graph slice reachable by following the
strategy. */
APCollection(boolean f) { forward = f; }
/** No way to get the enclosing object from outside this... */
ObjectGraphSlice getOGS() { return ObjectGraphSlice.this; }
List gather() { return ObjectGraphSlice.this.gather(forward); }
/** The number of target objects in the collection view.
Unfortunately this has to be linear time-- we can't even cache the
result, because the structure is mutable behind our back. */
public int size() {
return gather().size();
}
/** The maximum size of this container, or Integer.MAX_VALUE,
whichever is smaller. */
public int maxSize() {
// Use this if add() is implemented.
// return Integer.MAX_VALUE;
return size();
}
// Override the AbstractCollection/AbstractList implementations of
// this stuff to improve performance-- gather() is faster than
// stepping through the iterator.
public boolean contains(Object obj) { return gather().contains(obj); }
public boolean isEmpty() { return !iterator().hasNext(); }
public Object[] toArray() { return gather().toArray(); }
public Object[] toArray(Object[] a) { return gather().toArray(a); }
public String toString() { return gather().toString(); }
public int hashCode() { return gather().hashCode(); }
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof List)) return false;
if (obj instanceof APCollection) {
if (((APCollection) obj).getOGS().equals(getOGS()))
return true;
}
return super.equals(obj); // Go ahead and iterate...
}
/** A ListIterator for traversing the object graph. */
public ListIterator listIterator() {
return new Itr();
}
/** A ListIterator for traversing the object graph, starting at
the nth element. Why is this abstract on
AbstractSequentialList?? */
public ListIterator listIterator(int n) {
ListIterator i = listIterator();
while (n-- > 0) i.next();
return i;
}
/** A class that implements ListIterator. It iterates over all
objects in an object graph which are targets of the strategy. */
class Itr implements ListIterator {
ConcurrentTraverser traverser;
volatile Thread gatherThread;
Itr() {
traverser = new ConcurrentTraverser(forward);
gatherThread = new Thread() {
public void run() {
synchronized (traverser) { // wait for the parent to be waiting
traverser.traverse(root, startSet);
gatherThread = null;
traverser.notify(); // signal that we fell off the end
}
}
};
gatherThread.setDaemon(true);
synchronized (traverser) { // don't let the child start
// until we're waiting
gatherThread.start();
try {
traverser.wait(); // wait for a return value to be ready
} catch (InterruptedException e) {
// FIXME: exit more gracefully?
throw new RuntimeException(e.toString());
}
}
}
public boolean hasNext() {
synchronized (traverser) {
return gatherThread != null;
}
}
public Object next() {
synchronized (traverser) {
// FIXME: check forward
Object ret = traverser.getReturnValue();
traverser.notify(); // signal that the return value was read
try {
traverser.wait(); // wait for the next return value to be ready
} catch (InterruptedException e) {
// FIXME: exit more gracefully?
throw new RuntimeException(e.toString());
}
return ret;
}
}
public void set(Object o) {
traverser.set(o);
}
UnsupportedOperationException bomb() {
return new UnsupportedOperationException();
}
public boolean hasPrevious() { throw bomb(); }
public Object previous() { throw bomb(); }
public void add(Object o) { throw bomb(); }
public void remove() { throw bomb(); }
public int nextIndex() { throw bomb(); }
public int previousIndex() { throw bomb(); }
}
// DEAD CODE. Kept for historical/educational/entertainment purposes...
class Itr2 implements ListIterator {
Frame next, prev;
int index;
boolean lastMoveWasForward;
/** Traverse the collection view. */
Itr2() { moveToBegin(); }
void moveToBegin() {
next = new Frame(true, root, startSet, null, null);
move(true);
index = 0;
}
/** No way to get the enclosing object from outside this... */
ObjectGraphSlice getOGS() { return ObjectGraphSlice.this; }
// ListIterator stuff.
public boolean hasNext() { return next != null; }
public boolean hasPrevious() { return prev != null; }
public Object next() {
if (debug) System.out.println("** next() **");
if (!hasNext()) throw new NoSuchElementException();
prev = (Frame) next.clone();
next = move(true);
return prev.obj;
}
public Object previous() {
if (debug) System.out.println("** previous() **");
if (!hasPrevious()) throw new NoSuchElementException();
next = (Frame) prev.clone();
prev = move(false);
return next.obj;
}
public int nextIndex() { return index; }
public int previousIndex() { return index-1; }
UnsupportedOperationException bomb()
{ return new UnsupportedOperationException(); }
// Unsupported stuff.
public void add(Object o) { throw bomb(); }
public void remove() { throw bomb(); }
/** Is the iterator equal to this? Only if it has the same slice and
continuation. */
public boolean equals(Object iterator) {
if (!(iterator instanceof Itr2)) return false;
Itr2 tgi = (Itr2) iterator;
if (!tgi.getOGS().equals(getOGS())) return false;
if (!(tgi.next == null && next == null) &&
(tgi.next == null || next == null)) return false;
if (!tgi.next.equals(next)) return false;
// FIXME: do we need to compare prev and lastMoveWasForward too?
return true;
}
/** Replace the last returned object in the object graph. Throws
IllegalAccessException if we haven't returned anything yet.
Throws UnsupportedOperationException if:
* the current object is the root of the object graph
* we're in the middle of an unmodifiable enumeration
* obj is of the wrong type
*/
public void set(Object obj) {
if (debug) System.out.println("** set(" + obj + ") **");
Frame frame = lastMoveWasForward ? prev : next;
if (frame == null)
throw new IllegalStateException();
Frame p = frame.parent;
if (p == null)
throw new UnsupportedOperationException(
"Cannot replace the root of the object graph.");
p.it.set(obj);
// FIXME: go up/down alt/inh links?
frame.obj = obj;
}
/** Move one position, to the next element if forward is true,
otherwise to the previous element. Return the current frame. */
Frame move(boolean forward) {
lastMoveWasForward = forward;
if (forward) index++; else index--;
Frame frame = forward ? next : prev;
boolean moved = false;
do {
if (debug) System.out.println(frame);
if (frame.atBegin) {
if (moved && isTarget(frame.tokens)) {
return frame;
}
if (forward) {
frame.atBegin = false;
moved = true;
} else {
frame = frame.parent;
moved = false;
}
continue;
}
Frame.SuccessorIterator it = frame.it;
if (it == null) {
it = frame.setupSuccessorIterator(forward);
}
Object succ;
if (!forward) {
if (!it.hasPrevious()) {
frame.atBegin = true;
moved = true;
continue;
}
succ = it.previous();
moved = true;
} else {
if (!it.hasNext()) {
frame = frame.parent;
moved = false;
continue;
}
succ = it.next();
moved = true;
}
if (succ != null) {
frame = new Frame(forward, succ, it.getTokens(), null, frame);
continue;
}
} while (frame != null);
return frame;
}
/** Emulate the current continuation as a stack of frames. */
class Frame implements Cloneable {
boolean atBegin; Object obj; Traversal.NodeSet tokens;
SuccessorIterator it; Frame parent;
Frame(boolean a, Object o, Traversal.NodeSet n,
SuccessorIterator t, Frame p)
{ atBegin = a; obj = o; tokens = n; it = t; parent = p; }
public Object clone() {
return new Frame(atBegin, obj, tokens,
(SuccessorIterator) (it == null ? null : it.clone()),
(Frame) (parent == null ? null : parent.clone()));
}
public boolean equals(Object o) {
if (!(o instanceof Frame)) return false;
Frame frame = (Frame) o;
return atBegin == frame.atBegin
&& obj == frame.obj
&& tokens.equals(frame.tokens)
&& ((it == null && frame.it == null) || it.equals(frame.it))
&& ((parent == null && frame.parent == null)
|| parent.equals(frame.parent));
}
public String toString() {
return "<" + (atBegin ? "begin " : "") + obj +
" " + tokens + " (" + it + ")>\n" +
(parent == null ? "" : parent.toString());
}
SuccessorIterator setupSuccessorIterator(boolean atBegin) {
if (obj.getClass().isArray())
it = new ArrayIterator(atBegin);
else
it = new EdgeIterator(atBegin);
return it;
}
/** This is an abstraction for iterating through the successors
of the object in this frame (i.e. the targets of outgoing edges,
or the elements of the array). */
abstract class SuccessorIterator implements Cloneable {
abstract boolean hasNext();
abstract boolean hasPrevious();
abstract Object next();
abstract Object previous();
abstract Traversal.NodeSet getTokens();
abstract void set(Object o);
abstract public Object clone();
}
class ArrayIterator extends SuccessorIterator {
int i;
ArrayIterator(boolean atBegin) {
i = atBegin ? 0 : Array.getLength(obj);
}
boolean hasNext() { return i < Array.getLength(obj); }
boolean hasPrevious() { return i > 0; }
Object next() { return Array.get(obj, i++); }
Object previous() { return Array.get(obj, --i); }
Traversal.NodeSet getTokens() { return tokens; }
void set(Object o) { Array.set(obj, i, o); }
protected ArrayIterator() { }
public Object clone() {
ArrayIterator ret = new ArrayIterator();
ret.i = i;
return ret;
}
public boolean equals(Object o) {
return o instanceof ArrayIterator && ((ArrayIterator) o).i == i;
}
public String toString() { return i + ""; }
}
class EdgeIterator extends SuccessorIterator {
ListIterator edgeIt; // outgoing EdgeSet objects
Traversal.EdgeSet edgeSet; // current EdgeSet
RepetitionIterator it; // (if current edge is repetition)
EdgeIterator(boolean atBegin) {
List edgeSets = tokens.getOutgoingEdgeSets();
if (atBegin) {
edgeIt = edgeSets.listIterator();
} else {
edgeIt = edgeSets.listIterator(edgeSets.size());
}
}
boolean hasNext() {
return it != null && it.hasNext() || edgeIt.hasNext();
}
boolean hasPrevious() {
return it != null && it.hasPrevious() || edgeIt.hasPrevious();
}
Object next() { return move(true); }
Object previous() { return move(false); }
Object move(boolean forward) {
if (it != null) {
if (forward ? it.hasNext() : it.hasPrevious())
return (forward ? it.next() : it.previous());
it = null;
}
edgeSet = (Traversal.EdgeSet)
(forward ? edgeIt.next() : edgeIt.previous());
EdgeI edge = edgeSet.getEdge();
if (edge.isConstructionEdge()) {
if (isRepetitionEdge(edge)) {
// FIXME: don't traverse same collection twice
if (obj instanceof List)
it = new ListRepetitionIterator(forward);
else
it = new RepetitionIterator(forward);
} else {
Member member = getMember(getTokensClass(tokens), edge);
Class type = getTargetClass(member);
// FIXME: allow traversal to primitive types
// need to add them to the class graph; also need to
// remember that they're primitive values and not
// wrapper objects.
if (!type.isPrimitive())
return getTargetObject(obj, member);
}
} else if (edge.isAlternationEdge()) {
// FIXME: knowledge path
if (forward &&
// !lastWasInheritance && // knowledge path constraint
// premature termination:
getTokensClass(tokens).isInstance(obj)) {
if (debug) System.out.println(obj + " isa " + getTokensClass(tokens));
return obj;
}
} else if (edge.isInheritanceEdge()) {
// FIXME: knowledge path
return obj;
}
return null;
}
Traversal.NodeSet getTokens() {
return edgeSet.getTargetNodeSet();
}
void set(Object o) {
if (it != null) it.set(o);
EdgeI edge = edgeSet.getEdge();
if (getNodeClass(edge.getTarget())
.isAssignableFrom(o.getClass()))
throw new UnsupportedOperationException(
"Incompatible object.");
setTargetObject(obj, getTokensClass(tokens), edge, o);
// FIXME: go up or down alt/inh edges?
}
protected EdgeIterator() { }
public Object clone() {
EdgeIterator ret = new EdgeIterator();
ret.edgeIt = cloneListIterator(); // edgeIt.clone();
ret.edgeSet = edgeSet;
if (it != null) ret.it = (RepetitionIterator) it.clone();
return ret;
}
protected ListIterator cloneListIterator() {
ListIterator ret = null;
if (edgeIt instanceof Cloneable) {
try {
ret = (ListIterator)
edgeIt
.getClass()
.getMethod("clone", null)
.invoke(edgeIt, null);
} catch (Exception e) {
throw new RuntimeException("\n" + e);
}
} else {
List edgeSets = tokens.getOutgoingEdgeSets();
ret = edgeSets.listIterator(edgeIt.nextIndex());
}
return ret;
}
public boolean equals(Object o) {
if (!(o instanceof EdgeIterator)) return false;
EdgeIterator eit = (EdgeIterator) o;
return
eit.edgeIt.equals(edgeIt) &&
eit.edgeSet.equals(edgeSet) &&
(eit.it == null && it == null ||
eit.it != null && it != null && eit.it.equals(it));
}
public String toString() {
return edgeSet + ": " + String.valueOf(it);
}
}
class RepetitionIterator {
Iterator iter;
int i;
RepetitionIterator(boolean atBegin) {
if (!atBegin)
throw new UnsupportedOperationException(
"Don't know how to iterate backward over "
+ getTokensClass(tokens));
iter = getIterator(obj);
}
boolean hasNext() { return iter.hasNext(); }
boolean hasPrevious() { return i > 0; }
Object next() {
i++;
return iter.next();
}
Object previous() {
throw new UnsupportedOperationException(
"Don't know how to iterate backward over "
+ getTokensClass(tokens));
}
void set(Object o) {
throw new UnsupportedOperationException(
"Don't know how to modify " + iter.getClass());
}
protected RepetitionIterator() { }
protected RepetitionIterator make() {
return new RepetitionIterator();
}
public Object clone() {
RepetitionIterator o = make();
o.iter = cloneIterator();
o.i = i;
return o;
}
protected Iterator cloneIterator() {
Iterator copy_of_iter;
if (iter instanceof Cloneable) {
/*
// Iterator is not a subinterface of Cloneable, so we
// have to go through this rigamarole...
try {
copy_of_iter = (Iterator) ((Cloneable) iter).clone();
} catch (CloneNotSupportedException e) {
}
*/
// that doesn't work, clone() is not on Cloneable and
// Object.clone() is not public!
// see: http://developer.java.sun.com/developer/bugParade/bugs/4098033.html
// Instead, use reflection, which is slooow...
try {
copy_of_iter = (Iterator)
iter
.getClass()
.getMethod("clone", null)
.invoke(iter, null);
} catch (Exception e) {
throw new RuntimeException("\n" + e);
}
} else {
// FIXME
// It's not cloneable, so we have to find our way back
// here manually... ugh. Hope nothing got removed in
// the meanwhile!
copy_of_iter = getIterator(obj);
for (int j = 0; j <= i; j++)
copy_of_iter.next();
}
return copy_of_iter;
}
public boolean equals(Object o) {
if (!(o instanceof RepetitionIterator)) return false;
RepetitionIterator c = (RepetitionIterator) o;
// Iterators aren't likely to have a useful equals(), so
// just hope that comparing the index is enough.
return c.i == i;
}
public String toString() { return i + ""; }
}
class ListRepetitionIterator extends RepetitionIterator {
ListRepetitionIterator(boolean atBegin) {
List l = (List) obj;
iter = l.listIterator(atBegin ? 0 : (i = l.size()-1));
}
boolean hasPrevious() { return ((ListIterator) iter).hasPrevious(); }
Object previous() { return ((ListIterator) iter).previous(); }
void set(Object o) { ((ListIterator) iter).set(o); }
protected ListRepetitionIterator() { }
protected RepetitionIterator make() {
return new ListRepetitionIterator();
}
protected Iterator cloneIterator() {
if (iter instanceof Cloneable) return super.cloneIterator();
return ((List) obj).listIterator(i);
}
public boolean equals(Object o) {
return o instanceof ListRepetitionIterator && super.equals(o);
}
} // end class ListRepetitionIterator
} // end class Frame
} // end class Itr2
} // end class APCollection
/** A NodeSet representing the start set of tokens (copy indices)
for class cl, or null if cl has no start copies in
the traversal. */
Traversal.NodeSet getStartSet(Class cl) {
// FIXME: is it better to search the ancestors of cl or to search
// the list of start sets? Do the former, for now.
Traversal.NodeSet startSet = null;
for (Iterator it = getAncestors(cl).iterator(); it.hasNext();)
try {
Object v = getNode((Class) it.next());
if ((startSet = tg.getStartSet(v)) != null) break;
} catch (NoSuchClassGraphNodeException e) {
}
return startSet;
}
/** Does the node set include a target of the traversal? */
boolean isTarget(Traversal.NodeSet nodes) {
// FIXME: check the indices
return isTargetNode(nodes.getNode());
}
/** Is the class a target of the traversal? */
boolean isTargetClass(Class cl) {
try {
return isTargetNode(getNode(cl));
} catch (NoSuchClassGraphNodeException e) {
return false;
}
}
/** Is the class graph node a target of the traversal? */
boolean isTargetNode(Object v) {
Traversal.NodeSet finish = getTraversal().getFinishSet(v);
return finish != null;
}
/** The method or field on cl representing the edge, or null if edge
is not a construction edge. */
Member getMember(Class cl, EdgeI edge) {
Member member = null;
if (members.containsKey(edge))
return (Member) members.get(edge);
ClassGraph cg = getClassGraph();
if (edge.isConstructionEdge()) {
String name = cg.memberName(edge);
if (cg.isDerivedEdge(edge))
try {
member = cl.getDeclaredMethod(name, null);
} catch (NoSuchMethodException e) {
throw new RuntimeException("\n" + name + ": " + e);
}
else
try {
member = cl.getDeclaredField(name);
} catch (NoSuchFieldException e) {
throw new RuntimeException("\n" + name + ": " + e);
}
}
members.put(edge, member);
return member;
}
static Map members = new HashMap();
/** The target class of the edge. */
Class getTargetClass(Class src, EdgeI edge) {
if (edge.isAlternationEdge())
throw new RuntimeException("Can't get subclass");
else if (edge.isInheritanceEdge())
return src.getSuperclass();
else if (edge.isConstructionEdge())
return getTargetClass(getMember(src, edge));
else
return null;
}
static Class getTargetClass(Member member) {
if (member instanceof Field)
return ((Field) member).getType();
else
return ((Method) member).getReturnType();
}
/** The target object of the edge in the object graph outgoing from the
source object, or null if we can't access it. */
Object getTargetObject(Object source, Class cl, EdgeI edge) {
if (edge.isAlternationEdge() || edge.isInheritanceEdge())
// Same object, different class.
return source;
else if (edge.isConstructionEdge())
return getTargetObject(source, getMember(cl, edge));
else
return null;
}
Object getTargetObject(Object source, Member member) {
try {
((AccessibleObject) member).setAccessible(true);
if (member instanceof Field)
return ((Field) member).get(source);
else
return ((Method) member).invoke(source, null);
} catch (SecurityException e) {
if (debug) System.out.println("Couldn't set accessible " + member);
} catch (IllegalAccessException e) {
if (debug) System.out.println("Couldn't access " + member);
} catch (InvocationTargetException e) {
throw new RuntimeException("\n" + e.getTargetException());
}
return null;
}
/** Set the target object of the edge in the object graph outgoing
from the source object. Throws a RuntimeException if the edge
is derived or is not a construction edge. */
void setTargetObject(Object source, Class cl, EdgeI edge,
Object target)
{
if (!edge.isConstructionEdge())
throw new RuntimeException("Not a construction edge: " + edge);
setTargetObject(source, getMember(cl, edge), target);
}
void setTargetObject(Object source, Member member, Object target)
{
if (member instanceof Field)
try {
((AccessibleObject) member).setAccessible(true);
((Field) member).set(source, target);
} catch (SecurityException e) {
if (debug) System.out.println("Couldn't set accessible " + member);
} catch (IllegalAccessException e) {
if (debug) System.out.println("Couldn't access " + member);
} catch (Exception e) {
// Shouldn't happen...
throw new RuntimeException("\n" + member + ": " + e);
}
else
// FIXME: find the setter method
throw new RuntimeException("Derived edge: " + member);
}
/** The class corresponding to a traversal node set. */
Class getTokensClass(Traversal.NodeSet nodes) {
return getNodeClass(nodes.getNode());
}
/** The class corresponding to a class graph node. */
Class getNodeClass(Object node) {
return getClassGraph().getNodeClass(node);
}
/** The class graph node corresponding to a class. */
Object getNode(Class cl) throws NoSuchClassGraphNodeException {
return getClassGraph().getNode(cl.getName());
}
boolean isRepetitionEdge(EdgeI edge) {
return getClassGraph().isRepetitionEdge(edge);
}
boolean isCollection(Object obj) {
return getClassGraph().isCollectionClass(obj.getClass());
}
static Iterator getIterator(Object obj) {
if (obj instanceof java.util.Collection)
return ((java.util.Collection) obj).iterator();
final Enumeration e = ((edu.neu.ccs.demeter.dj.Collection) obj).elements();
return new Iterator() {
public boolean hasNext() { return e.hasMoreElements(); }
public Object next() { return e.nextElement(); }
public void remove() { throw new UnsupportedOperationException(); }
};
}
static ListIterator getListIterator(Object obj) {
if (obj instanceof java.util.List)
return ((java.util.List) obj).listIterator();
final Iterator it = getIterator(obj);
return new ListIterator() {
int i = 0;
public boolean hasNext() { return it.hasNext(); }
public boolean hasPrevious() { return i > 0; }
public Object next() { i++; return it.next(); }
public void remove() { i--; it.remove(); }
public int nextIndex() { return i; }
public int previousIndex() { return i-1; }
UnsupportedOperationException bomb()
{ return new UnsupportedOperationException(); }
public Object previous() { throw bomb(); }
public void add(Object o) { throw bomb(); }
public void set(Object o) { throw bomb(); }
};
}
// FIXME: memoize?
static List getAncestors(Class cl) {
List l = new ArrayList();
addAncestors(cl, l);
return l;
}
static void addAncestors(Class cl, List l) {
if (cl == null || l.contains(cl)) return;
addAncestors(cl.getSuperclass(), l);
Class ifs[] = cl.getInterfaces();
for (int i = 0; i < ifs.length; i++)
addAncestors(ifs[i], l);
l.add(cl);
}
}