com.persistit.ui.ValueInspectorTreeNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of akiban-persistit Show documentation
Show all versions of akiban-persistit Show documentation
Java B+Tree Key-Value Store Library
/**
* Copyright © 2005-2012 Akiban Technologies, Inc. All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This program may also be available under different license terms.
* For more information, see www.akiban.com or contact [email protected].
*
* Contributors:
* Akiban Technologies, Inc.
*/
package com.persistit.ui;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.Timer;
import java.util.TimerTask;
import java.util.WeakHashMap;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreeNode;
import com.persistit.Key;
import com.persistit.util.Util;
/**
* @author Peter Beaman
* @version 1.0
*/
class ValueInspectorTreeNode implements TreeNode {
public final static long TO_STRING_TIMEOUT = 1000;
public final static int MAX_DISPLAYABLE_LENGTH = 512;
private static WeakHashMap _fieldArrayMap = new WeakHashMap();
private String _displayable;
private final String _fieldName;
private final Object _object;
private String _toString;
private final ValueInspectorTreeNode _parent;
private int _index = -1;
private final Class _type;
private final ValueInspectorTreeNode[] _children;
ValueInspectorTreeNode(final ValueInspectorTreeNode parent, final Object object, final String name, final Class type) {
_parent = parent;
_object = object;
_type = type;
_fieldName = name;
int childCount = 0;
if (type.isArray()) {
if (object != null)
childCount = arrayLength(type, object);
} else if (!type.isPrimitive()) {
if (object != null)
childCount = getFields(type).length;
if (type.getSuperclass() != null && type.getSuperclass() != Object.class) {
childCount++;
}
}
_children = new ValueInspectorTreeNode[childCount];
}
ValueInspectorTreeNode(final ValueInspectorTreeNode parent, final Object object, final String name,
final Class type, final int index) {
this(parent, object, name, type);
_index = index;
}
String displayable(final String fieldName, final Class type, final Object object, final int arrayIndex) {
final StringBuilder sb = new StringBuilder();
if (arrayIndex >= 0) {
sb.append('[');
sb.append(arrayIndex);
sb.append(']');
} else {
sb.append(fieldName);
}
if (type.isPrimitive()) {
if (arrayIndex == -1) {
sb.append(" (");
sb.append(type.getName());
sb.append(")");
}
sb.append(" = ");
if (type == boolean.class || type == float.class || type == double.class) {
sb.append(object.toString());
} else if (type == byte.class) {
sb.append(object);
sb.append(" [0x");
Util.hex(sb, ((Byte) object).byteValue(), 2);
sb.append("]");
} else if (type == short.class) {
sb.append(object);
sb.append(" [0x");
Util.hex(sb, ((Short) object).shortValue(), 4);
sb.append("]");
} else if (type == char.class) {
sb.append(object);
sb.append(" [0x");
Util.hex(sb, ((Character) object).charValue(), 4);
sb.append("]");
} else if (type == int.class) {
sb.append(object);
sb.append(" [0x");
Util.hex(sb, ((Integer) object).intValue(), 8);
sb.append("]");
} else if (type == long.class) {
sb.append(object);
sb.append(" [0x");
Util.hex(sb, ((Long) object).longValue(), 16);
sb.append("]");
} else
throw new RuntimeException("type=" + type);
} else if (_parent != null) {
if (!type.isPrimitive() || _index < 0) {
sb.append(" (");
if (type.isArray()) {
sb.append(componentTypeName(type));
sb.append('[');
sb.append(arrayLength(type, object));
sb.append(']');
for (int index = dimensions(type); index-- > 1;) {
sb.append("[]");
}
} else if (object == null) {
sb.append(type.getName());
} else {
sb.append(object.getClass().getName());
}
sb.append(")");
}
if (object == null) {
sb.append(" = null");
} else if (object instanceof String) {
sb.append(" = \"");
Util.appendQuotedString(sb, object.toString(), 0, MAX_DISPLAYABLE_LENGTH - sb.length());
sb.append('\"');
} else if (object instanceof Date) {
sb.append(" = ");
sb.append(Key.SDF.format((Date) object));
} else if (type == Boolean.class || type == Byte.class || type == Short.class || type == Character.class
|| type == Integer.class || type == Long.class || type == Float.class || type == Double.class
|| type == BigInteger.class || type == BigDecimal.class) {
sb.append(" = ");
sb.append(object);
}
if (object != null && !object.getClass().isPrimitive()) {
sb.append(" (id=");
sb.append(System.identityHashCode(object));
sb.append(")");
}
}
if (sb.length() > MAX_DISPLAYABLE_LENGTH) {
sb.setLength(MAX_DISPLAYABLE_LENGTH);
sb.append("...");
}
return sb.toString();
}
int dimensions(Class type) {
int count = 0;
while (type.isArray()) {
count++;
type = type.getComponentType();
}
return count;
}
String componentTypeName(final Class type) {
if (type.isArray()) {
return componentTypeName(type.getComponentType());
}
return type.getName();
}
static int arrayLength(final Class type, final Object object) {
if (type.isArray()) {
final Class elementType = type.getComponentType();
if (elementType.isPrimitive()) {
if (elementType == boolean.class) {
return ((boolean[]) object).length;
}
if (elementType == byte.class) {
return ((byte[]) object).length;
}
if (elementType == short.class) {
return ((short[]) object).length;
}
if (elementType == char.class) {
return ((char[]) object).length;
}
if (elementType == int.class) {
return ((int[]) object).length;
}
if (elementType == long.class) {
return ((long[]) object).length;
}
if (elementType == float.class) {
return ((float[]) object).length;
}
if (elementType == double.class) {
return ((double[]) object).length;
}
} else {
return ((Object[]) object).length;
}
}
return 0;
}
@Override
public Enumeration children() {
if (_object == null || _type.isPrimitive())
return null;
return new ChildEnumeration();
}
@Override
public boolean getAllowsChildren() {
return true;
}
@Override
public TreeNode getChildAt(final int childIndex) {
if (_object == null || _type.isPrimitive())
return null;
if (_children[childIndex] == null) {
if (_type.isArray()) {
_children[childIndex] = getArrayChild(childIndex);
} else {
_children[childIndex] = getFieldValue(childIndex);
}
}
return _children[childIndex];
}
@Override
public int getChildCount() {
return _children.length;
}
@Override
public int getIndex(final TreeNode node) {
if (_parent == null)
return -1;
final ValueInspectorTreeNode[] siblings = _parent._children;
for (int index = 0; index < siblings.length; index++) {
if (node == siblings[index])
return index;
}
return -1;
}
@Override
public TreeNode getParent() {
return _parent;
}
@Override
public boolean isLeaf() {
return _children.length == 0;
}
@Override
public String toString() {
if (_displayable == null) {
_displayable = displayable(_fieldName, _type, _object, _index);
}
return _displayable;
}
String getEvalString() {
return _toString;
}
void evalString(final Runnable runnable) {
new ToStringRunner(_object, runnable).start();
}
private static String evalString(final Object object) {
if (object == null)
return "null";
if (object.getClass().isArray()) {
final StringBuilder sb = new StringBuilder();
final Class elementType = object.getClass().getComponentType();
final boolean primitive = elementType.isPrimitive();
final int size = arrayLength(object.getClass(), object);
sb.append("{");
for (int index = 0; index < size; index++) {
if (index > 0)
sb.append(", ");
if (primitive) {
if (elementType == boolean.class) {
sb.append(((boolean[]) object)[index] ? Boolean.TRUE : Boolean.FALSE);
} else if (elementType == byte.class) {
sb.append(((byte[]) object)[index]);
} else if (elementType == short.class) {
sb.append(((short[]) object)[index]);
} else if (elementType == char.class) {
sb.append((int) ((char[]) object)[index]);
} else if (elementType == int.class) {
sb.append(((int[]) object)[index]);
} else if (elementType == long.class) {
sb.append(((long[]) object)[index]);
} else if (elementType == float.class) {
sb.append(((float[]) object)[index]);
} else if (elementType == double.class) {
sb.append(((double[]) object)[index]);
} else
throw new RuntimeException();
} else {
sb.append(evalString(((Object[]) object)[index]));
}
}
sb.append("}");
return sb.toString();
}
if (object instanceof String) {
final StringBuilder sb = new StringBuilder();
sb.append('\"');
Util.appendQuotedString(sb, object.toString(), 0, Integer.MAX_VALUE);
sb.append('\"');
return sb.toString();
} else {
try {
return object.toString();
} catch (final Throwable t) {
return t + " while invoking " + object.getClass().getName() + ".toString()";
}
}
}
private ValueInspectorTreeNode getFieldValue(int index) {
if (_type.getSuperclass() != null && _type.getSuperclass() != Object.class) {
if (index == 0) {
return new ValueInspectorTreeNode(this, _object, _type.getSuperclass().toString(),
_type.getSuperclass());
} else {
index--;
}
}
Object childValue = null;
final Field field = getFields(_type)[index];
try {
childValue = field.get(_object);
} catch (final IllegalAccessException iae) {
childValue = "{{" + iae.toString() + "}}";
}
return _children[index] = new ValueInspectorTreeNode(this, childValue, field.getName(), field.getType());
// childValue.getClass());
}
private ValueInspectorTreeNode getArrayChild(final int index) {
Object element;
final Class elementType = _type.getComponentType();
if (elementType.isPrimitive()) {
if (elementType == boolean.class) {
element = ((boolean[]) _object)[index] ? Boolean.TRUE : Boolean.FALSE;
} else if (elementType == byte.class) {
element = new Byte(((byte[]) _object)[index]);
} else if (elementType == short.class) {
element = new Short(((short[]) _object)[index]);
} else if (elementType == char.class) {
element = new Character(((char[]) _object)[index]);
} else if (elementType == int.class) {
element = new Integer(((int[]) _object)[index]);
} else if (elementType == long.class) {
element = new Long(((long[]) _object)[index]);
} else if (elementType == float.class) {
element = new Float(((float[]) _object)[index]);
} else if (elementType == double.class) {
element = new Double(((double[]) _object)[index]);
} else
throw new RuntimeException();
} else {
element = ((Object[]) _object)[index];
}
final Class type = elementType.isPrimitive() || element == null ? elementType : element.getClass();
return new ValueInspectorTreeNode(this, element, "[" + index + "]", type, index);
}
private Field[] getFields(final Class type) {
Field[] array = (Field[]) _fieldArrayMap.get(type.getName());
if (array == null) {
array = type.getDeclaredFields();
final ArrayList selected = new ArrayList();
for (int index = 0; index < array.length; index++) {
final Field field = array[index];
if (!Modifier.isStatic(field.getModifiers())) {
selected.add(field);
}
}
array = (Field[]) selected.toArray(new Field[selected.size()]);
Arrays.sort(array, new FieldComparator());
AccessibleObject.setAccessible(array, true);
_fieldArrayMap.put(type.getName(), array);
}
return array;
}
private class FieldComparator implements Comparator {
@Override
public int compare(final Object a, final Object b) {
final Field fieldA = (Field) a;
final Field fieldB = (Field) b;
final String nameA = fieldA.getName();
final String nameB = fieldB.getName();
return (nameA.compareTo(nameB));
}
}
private class ChildEnumeration implements Enumeration {
int _enumerationIndex = 0;
@Override
public boolean hasMoreElements() {
return _enumerationIndex < _children.length;
}
@Override
public Object nextElement() {
return getChildAt(_enumerationIndex++);
}
}
private class ToStringRunner extends Thread {
private final Object _object;
private final Runnable _runnable;
private boolean _done;
ToStringRunner(final Object object, final Runnable runnable) {
_object = object;
_runnable = runnable;
}
@Override
public void run() {
try {
_toString = null;
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (!_done) {
_toString = "???";
// Note: This is a deprecated method that is
// generally considered unsafe. Nonetheless,
// we need to stop the runaway Thread.
ToStringRunner.this.stop();
}
}
}, TO_STRING_TIMEOUT);
_toString = evalString(_object);
_done = true;
timer.cancel();
} finally {
SwingUtilities.invokeLater(_runnable);
}
}
}
}