package net.cakenet.jsaton.script.debug;
import net.cakenet.jsaton.script.debug.collections.AbstractDebugArrayProvider;
import net.cakenet.jsaton.script.debug.collections.DebugArrayElement;
import net.cakenet.jsaton.script.debug.collections.DebugArrayObject;
import net.cakenet.jsaton.script.debug.collections.DebugArrayProvider;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
public class JavaExtractor implements VariableExtractor<Object> {
private static Map<Object, DebugObject> created = new HashMap<>();
public static JavaExtractor instance = new JavaExtractor();
public DebugObject extractFrom(Object o) {
return new OnDemandDebugObject(new JavaDebugObjectProvider(o));
}
private DebugObject provide(final Object o) {
if (o == null)
return new SimpleDebugObject();
if (o instanceof DebugObject)
return (DebugObject) o;
try {
Class c = o.getClass();
if(c.isArray()) {
final int len = Array.getLength(o);
return new DebugArrayObject(new DebugArrayProvider() {
private BitSet map = new BitSet(len);
private DebugArrayElement[] provided = new DebugArrayElement[len];
public int size() {
return len;
}
public DebugArrayElement get(int index) {
if(map.get(index))
return provided[index];
DebugObject v = extractFrom(Array.get(o, index));
DebugArrayElement created = new DebugArrayElement(index, v);
provided[index] = created;
map.set(index);
return created;
}
public int indexOf(Object child) {
for(int i = 0; i < provided.length; i++) {
Object p = map.get(i)? provided[i]: get(i);
if(p == child || (p != null && p.equals(child)))
return i;
}
return -1;
}
});
}
if(Collection.class.isAssignableFrom(c)) {
final Collection col = (Collection) o;
final int len = col.size();
final Iterator it = col.iterator();
return new DebugArrayObject(new AbstractDebugArrayProvider() {
protected DebugArrayElement next() {
return new DebugArrayElement(this.index, extractFrom(it.next()));
}
public int size() {
return len;
}
});
}
// array, iterable or map...
if (c.isArray() || Iterable.class.isAssignableFrom(c) || Map.class.isAssignableFrom(c) || c.isPrimitive())
return new SimpleDebugObject(null, c.getCanonicalName(), o);
DebugObject base = null;
DebugObject root = null;
for (Class cur = c; cur != null; cur = cur.getSuperclass()) {
List<DebugObject> variables = new LinkedList<>();
SimpleDebugObject obj = new SimpleDebugObject(null, c.getCanonicalName(), o);
Field[] fields = c.getDeclaredFields();
for (Field f : fields) {
if((f.getModifiers() & Modifier.STATIC) != 0)
continue; // Skip static fields...
boolean origAccessible = f.isAccessible();
if (!origAccessible)
f.setAccessible(true);
String name = f.getName();
Object o1 = f.get(o);
Class type = f.getType();
DebugObject val = extractFrom(o1);
val.type = type.getCanonicalName();
val.name = name;
variables.add(val);
if (!origAccessible)
f.setAccessible(false);
}
if (!variables.isEmpty())
obj.variables = variables;
// next...
if (base == null) {
base = obj;
root = base;
} else {
base.parent = obj;
base = obj;
}
}
return root;
} catch (Exception e) {
e.printStackTrace();
return extractFrom(o);
}
}
class JavaDebugObjectProvider implements DebugObjectProvider {
private Object source;
public JavaDebugObjectProvider(Object source) {
this.source = source;
}
public DebugObject provide() {
return JavaExtractor.this.provide(source);
}
}
}