blob: 56a991d1735a97c43769c2ab188855b5ccf5f9ba [file] [log] [blame]
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;
}
}
}