All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jadex.rules.tools.stateviewer.OAVTreeModel Maven / Gradle / Ivy

There is a newer version: 2.4
Show newest version
package jadex.rules.tools.stateviewer;

import jadex.commons.SUtil;
import jadex.commons.gui.SGUI;
import jadex.rules.state.IOAVState;
import jadex.rules.state.IOAVStateListener;
import jadex.rules.state.OAVAttributeType;
import jadex.rules.state.OAVJavaType;
import jadex.rules.state.OAVObjectType;

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.Collection;
import java.util.HashSet;
import java.util.IdentityHashMap;
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;


/**
 *  Swing Tree model for an OAV state. Enables displaying an 
 *  oav state in a swing tree.
 */
public class OAVTreeModel implements TreeModel
{

	/**
	 * flag to indicate if java objects should be inspectable in the tree
	 * TO DO: make configurable via GUI?
	 */
	protected final static boolean enableObjectInspection = true;
	
	//-------- static part --------
	
	/**
	 * The image icons.
	 */
	protected static UIDefaults icons = new UIDefaults(new Object[]
	{
		// Tab icons.
		"object", SGUI.makeIcon(OAVTreeModel.class, "/jadex/rules/tools/stateviewer/images/object.png"),
		"attribute", SGUI.makeIcon(OAVTreeModel.class, "/jadex/rules/tools/stateviewer/images/attribute.png"),
		"value", SGUI.makeIcon(OAVTreeModel.class, "/jadex/rules/tools/stateviewer/images/value.png"),
		"javaobject", SGUI.makeIcon(OAVTreeModel.class, "/jadex/rules/tools/stateviewer/images/bean.png"),
		"javaattribute", SGUI.makeIcon(OAVTreeModel.class, "/jadex/rules/tools/stateviewer/images/javaattribute.png"),
		"javavalue", SGUI.makeIcon(OAVTreeModel.class, "/jadex/rules/tools/stateviewer/images/value.png")
	});
	
	/**
	 * The list of timers to update the object inspector tree nodes
	 */
	protected static List timerList;

	
	//-------- attributes --------
	
	/** The root node. */
	protected RootNode	root;

	/** The local copy of the state (synchronized to swing thread). */
	protected CopyState	copy;
	
	/** The listeners. */
	protected Set	listeners;

	/** The pending notification flag. */
	protected boolean	notify;
	
	/** Nodes for objects to allow fine-tuned tree redraw (object-id -> {node1, node2, ...}).
	 *  Because the state is a (possibly cyclic) graph, there may be more than one node for a single object! */
	protected Map	nodes;
	
	/** list for all created Attribute inspector nodes */
	protected List inspectors;
	
//	/** Random to generate unique(?) IDs*/
//	protected Random rng;
	
	/** UUID counter */
	private int uuidcounter;

	
	//-------- constructors --------

	/**
	 *  Create new OAV tree model.
	 *  @param id	The root object id.
	 *  @param state	The OAV state.
	 *  @param showempty	Flag, if empty attributes should be shown.
	 */
	public OAVTreeModel(IOAVState state)
	{
		// use identity hash for different (java) objects being equal (e.g. empty list).
		// todo: mixed identity map like used in state?
		this.nodes	= new IdentityHashMap();
		this.root	= new RootNode();
		
		this.inspectors = new ArrayList();
//		this.rng = new Random(System.currentTimeMillis());
		this.uuidcounter = 0;
		
		Timer refreshTimer = new Timer(5000, new ObjectInspectorRefreshAction(this));
		refreshTimer.start();
		OAVTreeModel.addRefreshTimer(refreshTimer);
		
		// Todo: create copy state on state thread.
		this.copy	= new CopyState(state, new SwingSynchronizator());
		
		// could this anonymous inner class result in a cyclic reference  that prevents the TreeModel 
		// to be removed from gc when Introspector plugin is closed?
		// model -> copystate -> listener -> model$this0
		// 				|-> state -> listener -> copystate-lister$this0
		//					  ^-<- Agent
		// IMHO the OAVTreeModel for the Introspector plugin will be removed only
		// if the Agent that was introspected is removed too
		copy.addStateListener(new IOAVStateListener()
		{
			/**
			 *  Notification when an attribute value of an object has been set.
			 *  @param id The object id.
			 *  @param attr The attribute type.
			 *  @param oldvalue The oldvalue.
			 *  @param newvalue The newvalue.
			 */
			public void objectModified(Object id, OAVObjectType type,  
				final OAVAttributeType attr, Object oldvalue, Object newvalue)
			{
//				System.out.println("modified: "+id+"."+attr.getName()+": "+oldvalue+" -> "+newvalue);
				Object	tmp	= nodes.get(id);
				if(tmp instanceof ObjectNode)
				{
					// Find object and attribute node.
					ObjectNode	node	= (ObjectNode)tmp;
					List	children	= node.getChildren();
					AttributeNode	attrnode	= null;
					for(int i=0; attrnode==null && i1)
							{
								Object	child	= oldvalue;
								if(!(attr.getType() instanceof OAVJavaType))
									child = new ObjectNode(attrnode, child);
								else if(isInspectable(child))
									// objectInspector Node
									child = new ObjectInspectorNode(attrnode, child.getClass(), null, child);
								// else use plain value

//									int index	= attrnode.children.indexOf(child);
								int index	= getIndexForChild(attrnode.children, child);
//								System.out.println("modified removing: "+child+", "+index);
								
								if(attrnode.children.get(index) instanceof ObjectNode)
									((ObjectNode)attrnode.children.get(index)).drop();
								if(attrnode.children.get(index) instanceof AbstractInspectorNode)
									((AbstractInspectorNode)attrnode.children.get(index)).drop();
								attrnode.children.remove(index);
								
								if(listeners!=null)
								{
									TreeModelEvent	event	= new TreeModelEvent(this, attrnode.getPath(), new int[]{index}, new Object[]{child});
									TreeModelListener[]	alisteners	= (TreeModelListener[])listeners.toArray(new TreeModelListener[listeners.size()]);
									for(int i=0; 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 ObjectNode) && ((ObjectNode)children.get(i)).equals(child, false)) { index = i; } else 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(); // drop child if it is an inspector node if (entry.getValue() instanceof ObjectInspectorNode) ((ObjectInspectorNode) entry.getValue()).drop(); // 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); // drop newchild, it was replaced by the old one if (newchild instanceof ObjectInspectorNode) ((ObjectInspectorNode) newchild).drop(); } // 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 = timerList.iterator(); timers.hasNext();) { Timer timer = (Timer) timers.next(); timer.setDelay(millis); if (!timer.isRunning()) timer.start(); } else for (Iterator timers = timerList.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 (timerList == null) timerList = new ArrayList(); timerList.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 (timerList == null) timerList.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 void drop() { }; public int hashCode() { return nodeUUID; } } /** * A node representing an attribute value. */ public class AttributeNode { //-------- attributes -------- /** The attribute. */ protected OAVAttributeType attribute; /** The object node. */ protected ObjectNode parent; /** The children. */ protected List children; /** The path from the root node to this node. */ protected Object[] path; /** A unique id for this node */ protected int nodeUUID; //-------- constructors -------- /** * Create a new attribute node. * @param parent The parent. * @param attribute The attribute. */ public AttributeNode(ObjectNode parent, OAVAttributeType attribute) { this.parent = parent; this.attribute = attribute; this.nodeUUID = getNextNodeUUID(); } //-------- methods -------- /** * Get the children of this node. */ public List getChildren() { if(children==null) { children = new ArrayList(); if(OAVAttributeType.NONE.equals(attribute.getMultiplicity())) { Object child = copy.getAttributeValue(parent.object, attribute); if(!(attribute.getType() instanceof OAVJavaType)) child = new ObjectNode(this, child); else if(isInspectable(child)) // objectInspector Node child = new ObjectInspectorNode(this, child.getClass(), null, child); // else use plain value children.add(child); } else { Collection coll = copy.getAttributeValues(parent.object, attribute); if(coll!=null) { for(Iterator it=coll.iterator(); it.hasNext(); ) { Object child = it.next(); if(!(attribute.getType() instanceof OAVJavaType)) child = new ObjectNode(this, child); else if(isInspectable(child)) // objectInspector Node child = new ObjectInspectorNode(this, child.getClass(), null, child); else // else use wrapped plain value, as JTree does not allow duplicates. child = new ObjectInspectorValueNode(this, null, child); children.add(child); } } } } return children; } /** * 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(parent.getPath(), new Object[]{this}); } else { path = new Object[]{this}; } } return path; } /** * Unregister a node and its subnodes. */ public void drop() { if(children!=null) { for(int i=0; itrue=do a compete equals check
false=do a sematically equals check */ protected boolean equals(Object obj, boolean checkUUID) { boolean ret = obj instanceof ObjectNode && ((ObjectNode)obj).object==object; if (checkUUID && ret) ret = ret && ((ObjectNode)obj).nodeUUID==nodeUUID; return ret; } public boolean equals(Object obj) { return equals(obj, true); } public int hashCode() { // int ret = 31 + object.hashCode(); // //ret = ret*31 + nodeUUID; // //ret = ret*31 + (nodeUUID != null ? nodeUUID.hashCode() : 0); // return ret; return nodeUUID; } } /** * The root node containing the nodes for the root objects of the state. */ public class RootNode { //-------- attributes -------- /** The children. */ protected List children; //-------- constructors -------- /** * Create a new object node. */ public RootNode() { } //-------- methods -------- /** * Get the children of this node. */ public List getChildren() { if(children==null) { children = new ArrayList(); for(Iterator it=copy.getRootObjects(); it.hasNext(); ) { Object child = it.next(); if(!(copy.getType(child) instanceof OAVJavaType)) child = new ObjectNode(this, child); else if(isInspectable(child)) // objectInspector Node child = new ObjectInspectorNode(this, child.getClass(), null, child); children.add(child); } } return children; } } /** * TreeModel node for java object inspection * @author claas * */ 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(); nodes.put(object, this); } // --- 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 AttributeNode) { path = (Object[])SUtil.joinArrays(((AttributeNode)parent).getPath(), new Object[]{this}); } else 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; } /** * Unregister a node and its subnodes. */ public void drop() { nodes.remove(nodeObject); if(children!=null) { for(int i=0; itrue=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 * @author claas * */ 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 (OAVTreeModel.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 (OAVTreeModel.isInspectable(attributeValue)) { children.add(new ObjectInspectorNode(this, type, name, 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; } } /** * Unregister a node and its subnodes. */ public void drop() { inspectors.remove(this); if(children!=null) { for(int i=0; itrue=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 */ 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 OAVTreeCellRenderer 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 ObjectNode) setIcon(icons.getIcon("object")); else if(value instanceof AttributeNode) setIcon(icons.getIcon("attribute")); else 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 treeModel; /** * Create a ActionListener with a weak reference to the OAVTreeModel to update */ public ObjectInspectorRefreshAction(OAVTreeModel treeModel) { this.treeModel = new WeakReference(treeModel); } /** * Perform OAVTreeModel refresh if TreeModel reference exist else remove * Timer from Timer list */ public void actionPerformed(ActionEvent e) { OAVTreeModel model = (OAVTreeModel) treeModel.get(); if (model != null) { model.refreshInspectorNodes(); //System.gc(); } else { Object obj = e.getSource(); if (obj instanceof Timer) OAVTreeModel.removeRefreshTimer((Timer) obj); // System.err.println("removed timer! - " + obj); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy