package jde.debugger.gui; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import javax.swing.SwingUtilities; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeNode; import com.sun.jdi.ArrayReference; import com.sun.jdi.ClassNotLoadedException; import com.sun.jdi.Field; import com.sun.jdi.LocalVariable; import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.ObjectReference; import com.sun.jdi.PrimitiveValue; import com.sun.jdi.Type; import com.sun.jdi.Value; import jde.debugger.JDE; import jde.debugger.JDEException; import jde.debugger.Protocol; /** A TreeNode for displaying local variables. This class has no * public constructors. Instead, it uses a factory method { @link * #makeTreeNode } to create objects. This will probably return a * subclass based on the type of variable we are displaying.

* * This class implements MutableTreeNode mainly because the * DefaultTreeModel requires it. Most of the methods in that * interface simply throw exceptions. * * @author Troy Daniels * @since 2.3.2 * @version $Revision: 1.2 $ */ abstract class LVTreeNode implements MutableTreeNode, Protocol { /** LVTreeNode constructor for subclasses. * @param localVar A reference to a local variable. * @param val The value of localVar. */ protected LVTreeNode(String name, String typeName, Value val, DefaultTreeModel model) { m_name = name; m_typeName = typeName; m_model = model; setValue(val, false); } /** Create a new LVTreeNode object. Based on the arguments, * Different classes will be returned, customized to actual * values. * @param name The name of the variable * @param type The Type of the variable * @param val The value of the variable. * @param model The tree model. * @return A TreeNode for use in a JTree. */ public static MutableTreeNode makeTreeNode(String name, String typeName, Value val, DefaultTreeModel model) throws JDEException { if (val instanceof PrimitiveValue) return new PrimitiveTreeNode(name, typeName, val, model); else if ((val instanceof ObjectReference) || (val == null)) return new ReferenceTreeNode(name, typeName, val, model); else throw new JDEException("Unknown variable type " + val); } /** Create a new LVTreeNode object. Based on the arguments, * different classes will be returned, customized to actual * values. * @param localVar A reference to a local variable. * @param val The value of localVar. * @param model The tree model. * @return A TreeNode for use in a JTree. */ public static MutableTreeNode makeTreeNode(LocalVariable localVar, Value val, DefaultTreeModel model) throws JDEException { // it appears that: localVar.type() returns a ClassType about the // class, ObjectReference.referenceType returns the type of the // value, which is what we want to use. return makeTreeNode(localVar.name(), localVar.typeName(), val, model); } /** Create a new LVTreeNode object. Based on the arguments, * different classes will be returned, customized to actual * values. * @param field A field within an object * @param val The value of the field. * @return A TreeNode for use in a JTree. */ public static MutableTreeNode makeTreeNode(Field field, Value val, DefaultTreeModel model) throws JDEException { return makeTreeNode(field.name(), field.typeName(), val, model); } // Implementation of javax.swing.tree.TreeNode /** Get the parent of this node */ public TreeNode getParent() { return m_parent; } /** Set the parent of this node */ public void setParent(MutableTreeNode parent) { m_parent = parent; } /** * Describe remove method here. * * @param n an int value */ public void remove(int n) { throw new IllegalArgumentException("Attempt to remove a node " + "from the local variables tree"); } /** * Describe remove method here. * * @param mutableTreeNode a MutableTreeNode value */ public void remove(MutableTreeNode mutableTreeNode) { throw new IllegalArgumentException("Attempt to remove a node " + "from the local variables tree"); } /** * Describe insert method here. * * @param mutableTreeNode a MutableTreeNode value * @param n an int value */ public void insert(MutableTreeNode mutableTreeNode, int n) { throw new IllegalArgumentException("Attempt to insert a node " + "from the local variables tree"); } /** * Describe setUserObject method here. * * @param object an Object value */ public void setUserObject(Object object) { } /** * Describe removeFromParent method here. * */ public void removeFromParent() { throw new IllegalArgumentException("Attempt to remove a node from its parent " + "in the local variables tree"); } /** Get the name of the variable */ String getName() { return m_name; } /** Get the name of the type of the variable */ String getTypeName() { return m_typeName; } /** Set the value of the object. * @param val The new value */ public void setValue(Value val) { setValue(val, true); } /** Set the value of the object.

* * In the constructor, we don't want to notify the model, since that * can cause an infinite loop if the object refers to itself. Also, * notification is intended for changes to existing nodes. * * @param val The new value * @param notify Should we notify the tree model */ private void setValue(Value val, boolean notify) { int oldNumChildren = getChildCount(); // Store the value and the type of the value if (val != null) m_type = val.type(); else m_type = null; valueChanged(val); // Tell the TreeModel that we changed. if (notify) handleChildChange(oldNumChildren); } /** Called when the value has changed. Default implementation does nothing. * @param oldValue The old value * @param newValue THe new value */ abstract void valueChanged(Value newValue); private void handleChildChange(int oldNumChildren) { int numChildren = getChildCount(); JDE.debug(GUI, "handleChildChange: old=" + oldNumChildren + ",new=" + numChildren + ",name=" + getName()); if (numChildren == oldNumChildren) { m_model.nodeChanged(this); JDE.debug(GUI, "nodeChanged(" + getName() + ")"); } else if (numChildren > oldNumChildren) { // } else if (numChildren > oldNumChildren) { int added[] = new int[numChildren - oldNumChildren]; for (int index = 0; index < added.length; ++index) added[index] = oldNumChildren + index; m_model.nodesWereInserted(this, added); JDE.debug(GUI, "nodesWereInserted(" + getName() + ")"); } else { m_model.nodeStructureChanged(this); // m_model.nodeChanged(this); // int removed[] = new int[oldNumChildren - numChildren]; // for (int index = 0; index < removed.length; ++index) // removed[index] = numChildren + index; // m_model.nodesWereRemoved(this, removed); JDE.debug(GUI, "nodeStructureChanged(" + getName() + ")"); } } /** Get the string to display the value of the variable */ abstract String getValue(); /** Get the TreeModel for this Node */ protected DefaultTreeModel getModel() { return m_model; } public final String toString() { return getClass().getName() + "[" + paramString() + "]"; } protected String paramString() { return "m_name=" + m_name + "," + "m_typeName=" + m_typeName + "," + "m_type=" + m_type + "," + "m_model=" + m_model + "," + "m_parent=" + m_parent; } protected final String m_name; protected final String m_typeName; protected Type m_type; private DefaultTreeModel m_model; private TreeNode m_parent; }