001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.object.walker;
006:
007: import java.util.Collection;
008: import java.util.IdentityHashMap;
009: import java.util.LinkedList;
010: import java.util.Map;
011:
012: public class ObjectGraphWalker {
013:
014: private final VisitedSet visited;
015: private final LinkedList backtrack = new LinkedList();
016: private final Visitor visitor;
017: private final MemberValue root;
018: private int currentDepth = 0;
019: private int maxDepth = -1;
020: private final WalkTest walkTest;
021:
022: public ObjectGraphWalker(Object root, WalkTest walkTest,
023: Visitor visitor) {
024: if (root == null) {
025: throw new IllegalArgumentException(
026: "refusing to traverse null");
027: }
028: if (walkTest == null) {
029: throw new NullPointerException("null walk test");
030: }
031: if (visitor == null) {
032: throw new NullPointerException("null visitor");
033: }
034: this .root = MemberValue.rootValue(root);
035: this .visitor = visitor;
036: this .visited = new VisitedSet(this .root);
037: this .walkTest = walkTest;
038: }
039:
040: public void setMaxDepth(int maxDepth) {
041: this .maxDepth = maxDepth;
042: }
043:
044: public void walk() {
045: visitor.visitRootObject(root);
046: currentDepth++;
047:
048: if (!walkTest.shouldTraverse(root)) {
049: return;
050: }
051:
052: backtrack.addFirst(makeNode(root.getValueObject()));
053:
054: while (backtrack.size() > 0) {
055: Node current = (Node) backtrack.getFirst();
056: visit(current);
057: }
058: }
059:
060: private Node makeNode(Object o) {
061: if (o == null) {
062: throw new NullPointerException();
063: }
064:
065: if (o.getClass().isArray()) {
066: return new ArrayNode(o);
067: } else if (o instanceof Collection) {
068: return new CollectionNode((Collection) o, walkTest);
069: } else if (o instanceof Map) {
070: return new MapNode((Map) o, walkTest);
071: } else if (o instanceof MapEntry) {
072: return new MapEntryNode((MapEntry) o);
073: } else {
074: return new PlainNode(o, walkTest);
075: }
076: }
077:
078: private void visit(Node node) {
079: while (!node.done()) {
080: MemberValue value = node.next();
081:
082: if (value instanceof MapEntry) {
083: MapEntry entry = (MapEntry) value;
084: visitor.visitMapEntry(entry.getIndex(), currentDepth);
085: currentDepth++;
086: backtrack.addFirst(makeNode(entry));
087: return;
088: }
089:
090: Object o = value.getValueObject();
091:
092: boolean shouldTraverse = (maxDepth <= 0 || currentDepth < maxDepth)
093: && walkTest.shouldTraverse(value);
094:
095: if (o != null && shouldTraverse) {
096: visited.visit(value);
097: }
098:
099: visitor.visitValue(value, currentDepth);
100:
101: if (o != null && shouldTraverse && !value.isRepeated()) {
102: Node next = makeNode(o);
103: currentDepth++;
104: backtrack.addFirst(next);
105: return;
106: }
107: }
108:
109: currentDepth--;
110: backtrack.removeFirst();
111: }
112:
113: private static class VisitedSet {
114: private final IdentityHashMap visited = new IdentityHashMap();
115:
116: VisitedSet(MemberValue root) {
117: visit(root);
118: }
119:
120: void visit(MemberValue value) {
121: Object o = value.getValueObject();
122: if (o == null) {
123: throw new AssertionError("null value not expected");
124: }
125:
126: Integer id = (Integer) visited.get(o);
127: if (id == null) {
128: id = new Integer(visited.size());
129: visited.put(o, id);
130: } else {
131: value.setRepeated(true);
132: }
133:
134: value.setId(id.intValue());
135: }
136: }
137: }
|