jadex.base.gui.ObjectTreeModel Maven / Gradle / Ivy
Show all versions of jadex-tools-base-swing Show documentation
package jadex.base.gui;
import jadex.commons.SUtil;
import jadex.commons.gui.SGUI;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.ref.WeakReference;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.swing.JTree;
import javax.swing.Timer;
import javax.swing.UIDefaults;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
/**
* Tree model for inspecting objects.
*/
public class ObjectTreeModel implements TreeModel
{
//-------- static part --------
/**
* flag to indicate if java objects should be inspectable in the tree
* TO DO: make configurable via GUI?
*/
protected final static boolean enableObjectInspection = true;
/** The image icons. */
protected static UIDefaults icons = new UIDefaults(new Object[]
{
"javaobject", SGUI.makeIcon(ObjectTreeModel.class, "/jadex/base/gui/images/bean.png"),
"javaattribute", SGUI.makeIcon(ObjectTreeModel.class, "/jadex/base/gui/images/attribute.png"),
"javavalue", SGUI.makeIcon(ObjectTreeModel.class, "/jadex/base/gui/images/value.png")
});
// /** The list of timers to update the object inspector tree nodes .*/
// protected static List timers;
//-------- attributes --------
/** The root node. */
protected ObjectInspectorNode root;
/** The listeners. */
protected Set listeners;
/** The pending notification flag. */
protected boolean notify;
/** list for all created Attribute inspector nodes */
protected List inspectors;
/** UUID counter */
protected int uuidcounter;
//-------- constructors --------
/**
* Create new OAV tree model.
* @param showempty Flag, if empty attributes should be shown.
*/
public ObjectTreeModel(Object root)
{
// use identity hash for different (java) objects being equal (e.g. empty list).
this.root = new ObjectInspectorNode(null, root.getClass(), null, root);
this.inspectors = new ArrayList();
this.uuidcounter = 0;
Timer timer = new Timer(5000, new ObjectInspectorRefreshAction(this));
timer.start();
// ObjectTreeModel.addRefreshTimer(timer);
}
//-------- TreeModel interface --------
/**
* Get the root node of the tree.
*/
public Object getRoot()
{
return root;
}
/**
* Get the number of children of the given node.
*/
public int getChildCount(Object parent)
{
// System.out.println("getChildCount: "+parent);
int count;
// Node for an ObjectInspector object.
if(parent instanceof ObjectInspectorNode)
{
count = ((ObjectInspectorNode)parent).getChildren().size();
}
// Node for an ObjectAttributeInspector object.
else if(parent instanceof ObjectInspectorAttributeNode)
{
count = ((ObjectInspectorAttributeNode)parent).getChildren().size();
}
// Node is value.
else
{
count = 0;
}
return count;
}
/**
* Get the given child of a node.
*/
public Object getChild(Object parent, int index)
{
// System.out.println("getChild: "+parent+", "+index);
Object ret;
// Node for an ObjectInspector object.
if(parent instanceof ObjectInspectorNode)
{
ret = ((ObjectInspectorNode)parent).getChildren().get(index);
}
// Node for an ObjectAttributeInspector object.
else if(parent instanceof ObjectInspectorAttributeNode)
{
ret = ((ObjectInspectorAttributeNode)parent).getChildren().get(index);
}
// Value node has no children.
else
{
throw new IllegalArgumentException("Node has no children: "+parent);
}
return ret;
}
/**
* Get the index of a given child.
*/
public int getIndexOfChild(Object parent, Object child)
{
int index = -1;
// Hack!!! Inefficient implementation!?
int count = getChildCount(parent);
for(int i=0; index==-1 && i
* This method is using the "equals(Object o, boolean checkUID)" method
* to find a child with the checkUID parameter set to false and can be used
* to find a semantically equal child.
*
* @param children List to search for the child.
* @param child The child to search for.
* @return the FIRST index for the child in the children list
*/
protected int getIndexForChild(List children, Object child, int start)
{
assert children != null;
int index = -1;
for(int i=start; index==-1 && i=0 && index == -1; i--)
{
if ((children.get(i) instanceof ObjectInspectorNode)
&& ((ObjectInspectorNode)children.get(i)).equals(child, false))
{
index = i;
}
else if ((children.get(i) instanceof ObjectInspectorAttributeNode)
&& ((ObjectInspectorAttributeNode)children.get(i)).equals(child, false))
{
index = i;
}
else if ((children.get(i) instanceof ObjectInspectorValueNode)
&& ((ObjectInspectorValueNode)children.get(i)).equals(child, false))
{
index = i;
}
else if (children.get(i).equals(child))
{
index = i;
}
}
return index;
}
/**
* Refresh all displayed attributes
* @param oldRoot
*/
protected void refreshInspectorNodes()
{
// System.out.println("refresh called");
TreeModelEvent event = null;
// Object[] listener = listenerList.getListenerList();
Object[] inspectorNodes = inspectors.toArray(new Object[inspectors.size()]);
// the array contains all attribute nodes ordered in the path from root to leaf.
// e.g. a parent node will have a lower index than his children
//
// we could loop from leafs to root, to avoid drawing problems when updating a node that was
// already removed due to a parent object change
// OR
// null all (grand) children from a dropped node in the array to avoid later redrawing what will
// cause a drawing error / problem
//
// TO DO: decide which implementation is more efficient
for (int inspectorIndex = inspectorNodes.length-1; inspectorIndex >= 0; inspectorIndex--)
// for (int inspectorIndex = 0; inspectorIndex < inspectorNodes.length; inspectorIndex++)
{
if (inspectorNodes[inspectorIndex] instanceof ObjectInspectorAttributeNode)
{
ObjectInspectorAttributeNode node = (ObjectInspectorAttributeNode) inspectorNodes[inspectorIndex];
if (node.children!=null) // if childrens are not displayed, no update is needed
{
// Regenerate children and fire change event for changed values
// keep and restore old children as there may be expanded subtrees
List oldchildren = node.children;
node.children = null;
List newchildren = node.getChildren();
node.children = oldchildren;
if (node.isArrayNode())
{
// if we have a simple type we don't have to check subtrees,
// simply remove old and add new children
if (!isInspectable(node.type.getComponentType(), true))
{
//if (!oldchildren.equals(newchildren))
if (!testInspectorNodesListEquals(oldchildren, newchildren))
{
node.children = newchildren;
fireTreeStructureChanged(node.getPath());
}
}
// some array fields can be inspected, so assume that there may be a
// expanded subtree.
else
{
// List to save already selected children from newchildren
// Needed to avoid double select of the same value in an array
List prevSelectedChildren = new ArrayList();
// Handle removed children
Map removedChildren = new TreeMap();
for (int i = 0; i < oldchildren.size(); i++)
{
Object oldchild = oldchildren.get(i);
// use only not previous selected children
int index = getIndexForChild(newchildren, oldchild, 0);
while (index != -1 && prevSelectedChildren.contains(newchildren.get(index)))
{
prevSelectedChildren.add(newchildren.get(index));
index = getIndexForChild(newchildren, oldchild, index+1);
}
//if (!newchildren.contains(oldchild))
// value was removed
if (index == -1)
{
// add it to removedChildren
removedChildren.put(new Integer(i), oldchild);
// don't remove child from old children here! This will change
// index of other child's as well
}
}
// clear selected children after use
prevSelectedChildren.clear();
// remove the removed from oldchildren an store
// index and value in change event arrays
Object[] removed = removedChildren.entrySet().toArray();
int[] indexes = new int[removed.length];
Object[] childs = new Object[removed.length];
for (int i = 0; i < removed.length; i++)
{
Map.Entry entry = (Map.Entry) removed[i];
indexes[i] = ((Integer) entry.getKey()).intValue();
childs[i] = entry.getValue();
// remove from children list
oldchildren.remove(entry.getValue());
//oldchildren.remove(getIndexForChild(oldchildren, entry.getValue()));
}
// Handle inserted children
// replace the rest of old children at their position in new children
// save the not replaced children as added nodes in change event arrays
Map insertedChildren = new TreeMap();
for (int i = 0; i < newchildren.size(); i++)
{
Object newchild = newchildren.get(i);
// use only not previous selected children
int index = getIndexForChild(oldchildren, newchild, 0);
while (index != -1 && prevSelectedChildren.contains(newchildren.get(index)))
{
prevSelectedChildren.add(oldchildren.get(index));
index = getIndexForChild(oldchildren, newchild, index+1);
}
// replace new with old as there may be expanded sub trees
//if (oldchildren.contains(newchild))
if (index != -1)
{
//Object oldchild = oldchildren.get(oldchildren.indexOf(newchild));
Object oldchild = oldchildren.get(index);
// remove oldchild from oldchildren
//(needed to support same object twice in arrays)
oldchildren.remove(index);
newchildren.remove(i);
newchildren.add(i, oldchild);
}
// value was added
else
{
// register children for change event
insertedChildren.put(new Integer(i), newchild);
}
}
// clear selected children after use
prevSelectedChildren.clear();
// update the name prefix for children
for (int i = 0; i < newchildren.size(); i++)
{
Object obj = newchildren.get(i);
if (obj instanceof ObjectInspectorNode)
{
((ObjectInspectorNode) obj).namePrefix = "["+i+"] ";
}
else if (obj instanceof ObjectInspectorValueNode)
{
((ObjectInspectorValueNode) obj).namePrefix = "["+i+"] ";
}
}
// create the array for nodes inserted change event
Object[] inserted = insertedChildren.entrySet().toArray();
int[] insertedIndexes = new int[inserted.length];
Object[] insertedChilds = new Object[inserted.length];
for (int insertedIndex = 0; insertedIndex < inserted.length; insertedIndex++)
{
Map.Entry entry = (Map.Entry) inserted[insertedIndex];
insertedIndexes[insertedIndex] = ((Integer) entry.getKey()).intValue();
insertedChilds[insertedIndex] = entry.getValue();
}
// add the new children as node children
node.children = newchildren;
TreeModelListener[] alisteners = (TreeModelListener[])listeners.toArray(new TreeModelListener[listeners.size()]);
// create and fire event for removed children
if (removedChildren.size() > 0)
{
event = new TreeModelEvent(this, node.getPath(), indexes, childs);
for(int i=0; i 0)
{
event = new TreeModelEvent(this, node.getPath(), insertedIndexes, insertedChilds);
for(int i=0; i 0)
for (Iterator timers = timers.iterator(); timers.hasNext();)
{
Timer timer = (Timer) timers.next();
timer.setDelay(millis);
if (!timer.isRunning())
timer.start();
}
else
for (Iterator timers = timers.iterator(); timers.hasNext();)
{
Timer timer = (Timer) timers.next();
if (timer.isRunning())
timer.stop();
}
}
}*/
/**
* Add a timer to the static refresh timer list
* @param t
* /
protected static void addRefreshTimer(Timer t)
{
if(timers == null)
timers = new ArrayList();
timers.add(t);
}*/
/**
* Remove a timer from the static refresh timer list
* @param t
* /
protected static void removeRefreshTimer(Timer t)
{
if(t != null)
{
t.stop();
if (timers == null)
timers.remove(t);
}
}*/
/** A abstract node for this model */
abstract class AbstractInspectorNode
{
/** The parent node */
protected Object parent;
/** The children of this node (cached)*/
protected List children;
/** The path from the root node to this node. */
protected Object[] path;
/** A unique id for this node */
protected int nodeUUID;
// --- constructor ---
protected AbstractInspectorNode()
{
nodeUUID = getNextNodeUUID();
}
// --- abstract methods ---
public abstract List getChildren();
public abstract Object[] getPath();
protected abstract boolean equals(Object obj, boolean checkUUID);
// --- methods ---
public int hashCode()
{
return nodeUUID;
}
}
/**
* TreeModel node for java object inspection
*/
public class ObjectInspectorNode extends AbstractInspectorNode
{
/** The Class type for this node */
protected Class type;
/** The object represented by this node */
protected Object nodeObject;
/** The list of fields of the represented object*/
protected List fields;
/** The name for this node e.g. the objects name */
protected String name;
/** A prefix to display with name, e.g. for arrays "[index]" */
protected String namePrefix;
// ---- constructors ----
/**
* Create a ObjectInspectorNode
*
* @param type Class type for this object (e.g. myObject.class)
* @param name for this node
* @param object to inspect
*/
public ObjectInspectorNode(Object parent, Class type, String name, Object object)
{
this(parent, type, null, name, object);
}
/**
* Create a ObjectInspectorNode
*
* @param type Class type for this object (e.g. myObject.class)
* @param name for this node
* @param object to inspect
*/
public ObjectInspectorNode(Object parent, Class type, String namePrefix, String name, Object object)
{
this.parent = parent;
this.type = type;
this.name = name;
this.namePrefix = namePrefix;
this.nodeObject = object;
getFields();
}
// --- methods ----
/**
* Generate and return a List of all fields for the
* object represented by this node.
* @return List of accessible node object fields
*/
public List getFields()
{
// create list of fields only once,
//number or type of attributes can't change at runtime
if (fields==null)
{
this.fields = new ArrayList();
// find all fields for a class expect strings and null values
if (!type.isPrimitive() && !type.isArray() && !type.equals(String.class) && nodeObject != null)
{
// iterate over fields from the class and superclasses
for (Class clazz = nodeObject.getClass(); clazz != null; clazz = clazz.getSuperclass())
{
Field[] f = clazz.getDeclaredFields();
AccessibleObject.setAccessible(f, true);
for (int i = 0; i < f.length; i++)
{
// get only nonstatic fields
if ((f[i].getModifiers() & Modifier.STATIC) == 0)
fields.add(f[i]);
// TO-DO: Filter other fields as well?
}
}
}
}
return fields;
}
/**
* Get the children of this node.
*/
public List getChildren()
{
if(children==null)
{
children = new ArrayList();
Iterator it = fields.iterator();
while(it.hasNext())
{
Field f = (Field) it.next();
try
{
children.add(new ObjectInspectorAttributeNode(this, f, null));
}
catch (IllegalAccessException e)
{
// Field not accessible - ignore for children ?
children.add("-ERROR- Exception occurred: " +e);
}
}
}
return children;
}
/**
* Get the path of this node (inclusive) starting from the root node.
*/
public Object[] getPath()
{
if(path==null)
{
if (parent != null)
{
if(parent instanceof ObjectInspectorAttributeNode)
{
path = (Object[])SUtil.joinArrays(((ObjectInspectorAttributeNode)parent).getPath(), new Object[]{this});
}
else
{
path = new Object[]{parent, this};
}
}
else
{
path = new Object[]{this};
}
}
return path;
}
/**
* Access the object represented by this node
* @return the nodeObject Attribute
*/
protected Object getNodeObject()
{
// if (isInpsectionRootNode())
return nodeObject;
// else
// {
// try
// {
// Object obj = null;
// if (((ObjectInspectorAttributeNode)parent).isArrayNode())
// {
// // parent is an array attribute, use the attribute value for this node
// obj = ((ObjectInspectorAttributeNode)parent).getArrayValue();
// }
// else
// {
// // parent is an normal attribute, use the attribute value for this node
// obj = ((ObjectInspectorAttributeNode)parent).getFieldValue();
// }
//
// if (obj == nodeObject || (obj != null && obj.equals(nodeObject)))
// {
// // ignore, its the same object
// }
// else
// {
// // replace nodeObject
// nodeObject = obj;
// fields = null;
// }
//
// return nodeObject;
//
// } catch (Exception e)
// {
// return "-ERROR- Exception occurred: " + e;
// //e.printStackTrace();
// }
// }
}
protected boolean isInpsectionRootNode()
{
return (!(parent instanceof ObjectInspectorAttributeNode));
}
/**
* This method can be used to do a sematically equals check.
* E.g. check only the fields, not the unique identifier.
* @param obj Object to test for equals
* @param checkUUID flag to check the unique Identifier for the node.
true
=do a compete equals check
false
=do a sematically equals check
*/
protected boolean equals(Object obj, boolean checkUUID)
{
boolean ret = obj instanceof ObjectInspectorNode
&& ((ObjectInspectorNode)obj).parent==parent
&& ((ObjectInspectorNode)obj).type==type
&& ((ObjectInspectorNode)obj).name==name
&& (fields != null && fields.equals(((ObjectInspectorNode)obj).fields))
&& (nodeObject != null && nodeObject.equals(((ObjectInspectorNode) obj).nodeObject));
if (checkUUID && ret)
ret = ret && ((ObjectInspectorNode)obj).nodeUUID==nodeUUID;
return ret;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
return equals(obj, true);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
// int ret = 31 + (parent != null ? parent.hashCode() : 0);
// ret = ret*31 + (type!=null ? type.hashCode() : 0);
// ret = ret*31 + (name!=null ? name.hashCode() : 0);
// ret = ret*31 + (fields != null ? fields.hashCode() : 0);
// ret = ret*31 + (nodeObject != null ? nodeObject.hashCode() : 0);
// //ret = ret*31 + nodeUUID;
// //ret = ret*31 + (nodeUUID != null ? nodeUUID.hashCode() : 0);
// return ret;
return nodeUUID;
}
/**
* Get a string representation of this node
*/
public String toString()
{
getNodeObject();
return (namePrefix != null ? namePrefix : "")
+ (name!=null ? name : "")
// + (type.isPrimitive() ? " = " : "")
+ (nodeObject!=null ? nodeObject.toString() : "null") ;
}
} // class ObjectInspectorNode
/**
* Node for an Java object attribute
*/
class ObjectInspectorAttributeNode extends AbstractInspectorNode
{
/** The field represented by this node */
protected Field field;
/** The Class type for this attribute node */
protected Class type;
/** The name for this node e.g. the objects name */
protected String name;
/**
* The attribute value represented by this node
* This value will be updated every time getChildren() creates a list of children
*/
protected Object attributeValue;
// ---- constructor -----
/**
* Create a ObjectInspectorAttributeNode with a dynamic
* reference to the inspectable Field
* @param objectInspectorNode parent Node
* @param f the field to get from object parameter
* @param namePrefix the object to get the field from
*/
public ObjectInspectorAttributeNode(ObjectInspectorNode parent, Field f, String name)
throws IllegalAccessException
{
this.parent = parent;
this.field = f;
this.type = f.getType();
this.name = (name!=null?name:f.getName());
inspectors.add(this);
}
// ---- methods ----
/**
* Get the children of this node.
*/
public List getChildren()
{
if(children==null)
{
this.children = new ArrayList();
//this.attributeValue = field.get(attributeObject);
// get value for this node
// the parent Array-Field if this is an array, else the value itself
//Object value = null;
//if (type.isArray())
if(isArrayNode())
{
try
{
// attributeValue = getArrayValue();
attributeValue = getFieldValue();
if(attributeValue == null)
{
// children.add("null");
// IGNORE !
// don't add children for null valued arrays
//children.add(new ObjectInspectorValueNode(this, null, attributeValue));
}
else
{
for (int i = 0; i < Array.getLength(attributeValue); i++)
{
Object obj = Array.get(attributeValue, i);
if(ObjectTreeModel.isInspectable(obj))
{
children.add(new ObjectInspectorNode(this, obj.getClass(), "["+i+"] ", null, obj));
}
else
{
// children.add("["+i+"] "+obj);
children.add(new ObjectInspectorValueNode(this, "["+i+"] ", obj));
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
children.add("-ERROR- Exception occurred: " +e);
}
}
else
{
this.attributeValue = getFieldValue();
// create a new object inspector node for inspectable attribute
if(ObjectTreeModel.isInspectable(attributeValue))
{
children.add(new ObjectInspectorNode(this, type, null, attributeValue));
}
// else add a simple value node
else
{
//children.add((attributeValue!=null ? attributeValue : "null"));
children.add(new ObjectInspectorValueNode(this, null, attributeValue));
}
}
}
return children;
}
protected boolean isArrayNode()
{
attributeValue = getFieldValue();
return type.isArray() || (attributeValue != null && attributeValue.getClass().isArray());
}
/**
* returns the object that is represented by this attribute node as is.
* e.g. the field of the parent node object.
* If this object is an array, the array itself is returned. If you need access to the value
* of the index that is represented with this node, use getArrayValue() instead.
*/
protected Object getFieldValue()
{
try
{
return field.get(((ObjectInspectorNode)parent).getNodeObject());
}
catch (Exception e)
{
//e.printStackTrace();
return "-ERROR- Exception occurred: " + e;
}
}
/**
* Returns the value for the represented field from the parent object inspector node
* If this field is an array, the value from the array index represented
* by this node is returned. When access to the array is needed, e.g. to
* create the the node children, use getFieldValue() instead.
* @return
*/
protected Object getArrayValue()
{
try
{
if (isArrayNode())
{
return Array.get(field.get(((ObjectInspectorNode)parent).getNodeObject()), ((ObjectInspectorNode)parent).getChildren().indexOf(this)/*-1*/);
}
else
return "-ERROR- getArrayValue called on a non array type";
} catch (Exception e)
{
//e.printStackTrace();
return "-ERROR- Exception occurred: " + e;
}
}
/**
* Get the path of this node (inclusive) starting from the root node.
*/
public Object[] getPath()
{
if(path==null)
{
if(parent!=null)
{
path = (Object[])SUtil.joinArrays(((ObjectInspectorNode)parent).getPath(), new Object[]{this});
}
else
{
path = new Object[]{this};
}
}
return path;
}
/**
* This method can be used to do a sematically equals check.
* E.g. check only the fields, not the unique identifier.
* @param obj Object to test for equals
* @param checkUUID flag to check the unique Identifier for the node.
true
=do a compete equals check
false
=do a sematically equals check
*/
protected boolean equals(Object obj, boolean checkUUID)
{
boolean ret = obj instanceof ObjectInspectorAttributeNode
&& ((ObjectInspectorAttributeNode) obj).parent == parent
&& ((ObjectInspectorAttributeNode) obj).field == field
&& ((ObjectInspectorAttributeNode) obj).name == name
;
if (ret)
{
Object objValue = ((ObjectInspectorAttributeNode) obj).attributeValue;
// test if attribute values are equal if type is primitiv
if (type.isPrimitive())
ret = (attributeValue==null ? objValue==null : attributeValue.equals(objValue));
// else test reference
else
ret = (attributeValue==objValue);
}
if (checkUUID && ret)
ret = ret && ((ObjectInspectorAttributeNode)obj).nodeUUID==nodeUUID;
return ret;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
return equals(obj, true);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
// int ret = 31 + (parent != null ? parent.hashCode() : 0);
// ret = ret*31 + (field!=null ? field.hashCode() : 0);
// ret = ret*31 + (name!=null ? name.hashCode() : 0);
// ret = ret*31 + (attributeValue != null ? attributeValue.hashCode() : 0);
// //ret = ret*31 + nodeUUID;
//
// return ret;
return nodeUUID;
}
/**
* Get a string representation of this node
*/
public String toString()
{
String len = "";
try
{
//if (type.isArray())
if (isArrayNode())
{
Object arrayValue = getFieldValue();
len = (arrayValue !=null ? ""+ Array.getLength(arrayValue) : "null");
}
} catch (Exception e)
{
e.printStackTrace();
len = "-ERROR-";
}
return (name != null ? name : "?? INVALID NODE NAME ??")
//+ (type.isArray() ? "["+len+"]" : "")
+ (isArrayNode() ? "["+len+"]" : "")
//+ (attributeValue!=null? " = "+attributeValue: " (null)")
;
}
} // class ObjectInspectorAttributeNode
/**
* This class represents a simple value for ObjectInspectorAtttributeNode values.
* It is needed to display a prefix [index] for arrays
*
* @author claas
*
*/
class ObjectInspectorValueNode extends AbstractInspectorNode
{
// ---- attributes ----
/** A simple value node can have a displayed name prefix e.g. for Arrays */
protected String namePrefix;
/** The simple Object represented by this node */
protected Object value;
// --- constructor ----
/** create a simple value node */
public ObjectInspectorValueNode(Object parent, String namePrefix, Object value)
{
super.parent = parent;
this.namePrefix = namePrefix;
this.value = value;
}
// --- methods ---
public List getChildren()
{
return null;
}
public Object[] getPath()
{
return (Object[]) SUtil.joinArrays(((AbstractInspectorNode)super.parent).getPath(), new Object[]{this});
}
/**
* This method can be used to do a sematically equals check.
* E.g. check only the fields, not the unique identifier.
* @param obj Object to test for equals
* @param checkUUID flag to check the unique Identifier for the node.
true
=do a compete equals check
false
=do a sematically equals check
*/
protected boolean equals(Object obj, boolean checkUUID)
{
boolean ret = (obj instanceof ObjectInspectorValueNode)
&& ((ObjectInspectorValueNode)obj).parent == parent
&& (value == null ? ((ObjectInspectorValueNode)obj).value == null : value.equals(((ObjectInspectorValueNode)obj).value));
if (checkUUID && ret)
ret = ret && ((ObjectInspectorValueNode)obj).nodeUUID==nodeUUID;
return ret;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
return equals(obj, true);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
// int ret = 31 + (parent != null ? parent.hashCode() : 0);
// ret = ret*31 + (value!=null ? value.hashCode() : 0);
// //ret = ret*31 + nodeUUID;
// //ret = ret*31 + (nodeUUID != null ? nodeUUID.hashCode() : 0);
// return ret;
return nodeUUID;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString()
{
String ret = (namePrefix != null ? namePrefix : "") + (value != null ? value : "null");
// ret = "[ValueNode: " + ret + "]";
return ret;
}
} // class ObjectInspectorValueNode
/**
* OAV tree cell renderer displays right icons.
*/
public static class ObjectTreeCellRenderer extends DefaultTreeCellRenderer
{
/**
* Get the tree cell renderer component.
*/
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel,
boolean expanded, boolean leaf, int row, boolean hasFocus)
{
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
// System.out.println(value+" "+value.getClass());
if(value instanceof ObjectInspectorNode)
setIcon(icons.getIcon("javaobject"));
else if (value instanceof ObjectInspectorAttributeNode)
setIcon(icons.getIcon("javaattribute"));
else if (value instanceof ObjectInspectorValueNode)
setIcon(icons.getIcon("javavalue"));
else
setIcon(icons.getIcon("value"));
return this;
}
/* (non-Javadoc)
* @see javax.swing.tree.DefaultTreeCellRenderer#getPreferredSize()
*/
public Dimension getPreferredSize()
{
// change prefered size to disable the swing
// "..." bug in tree display
Dimension d = new Dimension(super.getPreferredSize());
d.setSize(d.getWidth()+10, d.getHeight());
return d;
}
}
}
/**
* Action class to update the tree model e.g. with a timer
*/
class ObjectInspectorRefreshAction implements ActionListener
{
/**
* A WeakReference to the tree model to allow the JVM to remove the
* model from heap when introspector plugin is closed
*/
WeakReference model;
/**
* Create a ActionListener with a weak reference to the OAVTreeModel to update
*/
public ObjectInspectorRefreshAction(ObjectTreeModel treeModel)
{
this.model = new WeakReference(treeModel);
}
/**
* Perform TreeModel refresh if TreeModel reference exist else remove
* Timer from Timer list
*/
public void actionPerformed(ActionEvent e)
{
ObjectTreeModel m = (ObjectTreeModel)model.get();
if(m!= null)
{
m.refreshInspectorNodes();
//System.gc();
}
else
{
Object obj = e.getSource();
if(obj instanceof Timer)
{
Timer t = (Timer)obj;
if(t.isRunning())
t.stop();
// ObjectTreeModel.removeRefreshTimer((Timer) obj);
}
// System.err.println("removed timer! - " + obj);
}
}
}