package org.apache.ode.bpel.obj.migrate; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Traverse an Object with help of an {@link ObjectVisitor}, taking into consideration of cyclic references. * */ public class ObjectTraverser{ private static final Logger __log = LoggerFactory.getLogger(ObjectTraverser.class); private HandleTable htab = new HandleTable(1000, 0.8f); private ObjectVisitor visitor; public HandleTable getHtab() { return htab; } public void accept(ObjectVisitor visitor){ this.visitor = visitor; visitor.setTraverse(this); } public Object traverseObject(Object obj){ return traverseObject(obj, true); } /** * * @param obj current object * @param assign should we record this visit. Usually, this should be true to avoid infinite loop of cyclic reference. * Sometime set to false when we need visit an object more than once. * @return anything the visitor returns. */ public Object traverseObject(Object obj, boolean assign){ __log.debug("current object " + obj); if (obj == null) return null; if (htab.lookup(obj) != -1){ return visitor.visited(obj); } if (assign) htab.assign(obj); Object n; n = visitor.visit(obj); return n; } /** * Stole from openjdk ObjectOutputStream * Lightweight identity hash table which maps objects to integer handles, * assigned in ascending order. */ public static class HandleTable { /* number of mappings in table/next available handle */ private int size; /* size threshold determining when to expand hash spine */ private int threshold; /* factor for computing size threshold */ private final float loadFactor; /* maps hash value -> candidate handle value */ private int[] spine; /* maps handle value -> next candidate handle value */ private int[] next; /* maps handle value -> associated object */ private Object[] objs; /** * Creates new HandleTable with given capacity and load factor. */ HandleTable(int initialCapacity, float loadFactor) { this.loadFactor = loadFactor; spine = new int[initialCapacity]; next = new int[initialCapacity]; objs = new Object[initialCapacity]; threshold = (int) (initialCapacity * loadFactor); clear(); } /** * Assigns next available handle to given object, and returns handle * value. Handles are assigned in ascending order starting at 0. */ int assign(Object obj) { if (size >= next.length) { growEntries(); } if (size >= threshold) { growSpine(); } insert(obj, size); return size++; } /** * Looks up and returns handle associated with given object, or -1 if * no mapping found. */ int lookup(Object obj) { if (size == 0) { return -1; } int index = hash(obj) % spine.length; for (int i = spine[index]; i >= 0; i = next[i]) { if (objs[i] == obj) { return i; } } return -1; } /** * Resets table to its initial (empty) state. */ void clear() { Arrays.fill(spine, -1); Arrays.fill(objs, 0, size, null); size = 0; } /** * Returns the number of mappings currently in table. */ int size() { return size; } /** * Inserts mapping object -> handle mapping into table. Assumes table * is large enough to accommodate new mapping. */ private void insert(Object obj, int handle) { int index = hash(obj) % spine.length; objs[handle] = obj; next[handle] = spine[index]; spine[index] = handle; } /** * Expands the hash "spine" -- equivalent to increasing the number of * buckets in a conventional hash table. */ private void growSpine() { spine = new int[(spine.length << 1) + 1]; threshold = (int) (spine.length * loadFactor); Arrays.fill(spine, -1); for (int i = 0; i < size; i++) { insert(objs[i], i); } } /** * Increases hash table capacity by lengthening entry arrays. */ private void growEntries() { int newLength = (next.length << 1) + 1; int[] newNext = new int[newLength]; System.arraycopy(next, 0, newNext, 0, size); next = newNext; Object[] newObjs = new Object[newLength]; System.arraycopy(objs, 0, newObjs, 0, size); objs = newObjs; } /** * Returns hash value for given object. */ private int hash(Object obj) { return System.identityHashCode(obj) & 0x7FFFFFFF; } } }