- /*
- * @(#)DefaultTreeModel.java 1.55 04/05/05
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package javax.swing.tree;
-
- import java.util.*;
- import java.awt.*;
- import java.io.*;
- import javax.swing.event.*;
-
- /**
- * A simple tree data model that uses TreeNodes.
- * For further information and examples that use DefaultTreeModel,
- * see <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
- * in <em>The Java Tutorial.</em>
- * <p>
- * <strong>Warning:</strong>
- * Serialized objects of this class will not be compatible with
- * future Swing releases. The current serialization support is
- * appropriate for short term storage or RMI between applications running
- * the same version of Swing. As of 1.4, support for long term storage
- * of all JavaBeans<sup><font size="-2">TM</font></sup>
- * has been added to the <code>java.beans</code> package.
- * Please see {@link java.beans.XMLEncoder}.
- *
- * @version 1.55 05/05/04
- * @author Rob Davis
- * @author Ray Ryan
- * @author Scott Violet
- */
- public class DefaultTreeModel implements Serializable, TreeModel {
- /** Root of the tree. */
- protected TreeNode root;
- /** Listeners. */
- protected EventListenerList listenerList = new EventListenerList();
- /**
- * Determines how the <code>isLeaf</code> method figures
- * out if a node is a leaf node. If true, a node is a leaf
- * node if it does not allow children. (If it allows
- * children, it is not a leaf node, even if no children
- * are present.) That lets you distinguish between <i>folder</i>
- * nodes and <i>file</i> nodes in a file system, for example.
- * <p>
- * If this value is false, then any node which has no
- * children is a leaf node, and any node may acquire
- * children.
- *
- * @see TreeNode#getAllowsChildren
- * @see TreeModel#isLeaf
- * @see #setAsksAllowsChildren
- */
- protected boolean asksAllowsChildren;
-
-
- /**
- * Creates a tree in which any node can have children.
- *
- * @param root a TreeNode object that is the root of the tree
- * @see #DefaultTreeModel(TreeNode, boolean)
- */
- public DefaultTreeModel(TreeNode root) {
- this(root, false);
- }
-
- /**
- * Creates a tree specifying whether any node can have children,
- * or whether only certain nodes can have children.
- *
- * @param root a TreeNode object that is the root of the tree
- * @param asksAllowsChildren a boolean, false if any node can
- * have children, true if each node is asked to see if
- * it can have children
- * @see #asksAllowsChildren
- */
- public DefaultTreeModel(TreeNode root, boolean asksAllowsChildren) {
- super();
- this.root = root;
- this.asksAllowsChildren = asksAllowsChildren;
- }
-
- /**
- * Sets whether or not to test leafness by asking getAllowsChildren()
- * or isLeaf() to the TreeNodes. If newvalue is true, getAllowsChildren()
- * is messaged, otherwise isLeaf() is messaged.
- */
- public void setAsksAllowsChildren(boolean newValue) {
- asksAllowsChildren = newValue;
- }
-
- /**
- * Tells how leaf nodes are determined.
- *
- * @return true if only nodes which do not allow children are
- * leaf nodes, false if nodes which have no children
- * (even if allowed) are leaf nodes
- * @see #asksAllowsChildren
- */
- public boolean asksAllowsChildren() {
- return asksAllowsChildren;
- }
-
- /**
- * Sets the root to <code>root</code>. A null <code>root</code> implies
- * the tree is to display nothing, and is legal.
- */
- public void setRoot(TreeNode root) {
- Object oldRoot = this.root;
- this.root = root;
- if (root == null && oldRoot != null) {
- fireTreeStructureChanged(this, null);
- }
- else {
- nodeStructureChanged(root);
- }
- }
-
- /**
- * Returns the root of the tree. Returns null only if the tree has
- * no nodes.
- *
- * @return the root of the tree
- */
- public Object getRoot() {
- return root;
- }
-
- /**
- * Returns the index of child in parent.
- * If either the parent or child is <code>null</code>, returns -1.
- * @param parent a note in the tree, obtained from this data source
- * @param child the node we are interested in
- * @return the index of the child in the parent, or -1
- * if either the parent or the child is <code>null</code>
- */
- public int getIndexOfChild(Object parent, Object child) {
- if(parent == null || child == null)
- return -1;
- return ((TreeNode)parent).getIndex((TreeNode)child);
- }
-
- /**
- * Returns the child of <I>parent</I> at index <I>index</I> in the parent's
- * child array. <I>parent</I> must be a node previously obtained from
- * this data source. This should not return null if <i>index</i>
- * is a valid index for <i>parent</i> (that is <i>index</i> >= 0 &&
- * <i>index</i> < getChildCount(<i>parent</i>)).
- *
- * @param parent a node in the tree, obtained from this data source
- * @return the child of <I>parent</I> at index <I>index</I>
- */
- public Object getChild(Object parent, int index) {
- return ((TreeNode)parent).getChildAt(index);
- }
-
- /**
- * Returns the number of children of <I>parent</I>. Returns 0 if the node
- * is a leaf or if it has no children. <I>parent</I> must be a node
- * previously obtained from this data source.
- *
- * @param parent a node in the tree, obtained from this data source
- * @return the number of children of the node <I>parent</I>
- */
- public int getChildCount(Object parent) {
- return ((TreeNode)parent).getChildCount();
- }
-
- /**
- * Returns whether the specified node is a leaf node.
- * The way the test is performed depends on the
- * <code>askAllowsChildren</code> setting.
- *
- * @param node the node to check
- * @return true if the node is a leaf node
- *
- * @see #asksAllowsChildren
- * @see TreeModel#isLeaf
- */
- public boolean isLeaf(Object node) {
- if(asksAllowsChildren)
- return !((TreeNode)node).getAllowsChildren();
- return ((TreeNode)node).isLeaf();
- }
-
- /**
- * Invoke this method if you've modified the TreeNodes upon which this
- * model depends. The model will notify all of its listeners that the
- * model has changed.
- */
- public void reload() {
- reload(root);
- }
-
- /**
- * This sets the user object of the TreeNode identified by path
- * and posts a node changed. If you use custom user objects in
- * the TreeModel you're going to need to subclass this and
- * set the user object of the changed node to something meaningful.
- */
- public void valueForPathChanged(TreePath path, Object newValue) {
- MutableTreeNode aNode = (MutableTreeNode)path.getLastPathComponent();
-
- aNode.setUserObject(newValue);
- nodeChanged(aNode);
- }
-
- /**
- * Invoked this to insert newChild at location index in parents children.
- * This will then message nodesWereInserted to create the appropriate
- * event. This is the preferred way to add children as it will create
- * the appropriate event.
- */
- public void insertNodeInto(MutableTreeNode newChild,
- MutableTreeNode parent, int index){
- parent.insert(newChild, index);
-
- int[] newIndexs = new int[1];
-
- newIndexs[0] = index;
- nodesWereInserted(parent, newIndexs);
- }
-
- /**
- * Message this to remove node from its parent. This will message
- * nodesWereRemoved to create the appropriate event. This is the
- * preferred way to remove a node as it handles the event creation
- * for you.
- */
- public void removeNodeFromParent(MutableTreeNode node) {
- MutableTreeNode parent = (MutableTreeNode)node.getParent();
-
- if(parent == null)
- throw new IllegalArgumentException("node does not have a parent.");
-
- int[] childIndex = new int[1];
- Object[] removedArray = new Object[1];
-
- childIndex[0] = parent.getIndex(node);
- parent.remove(childIndex[0]);
- removedArray[0] = node;
- nodesWereRemoved(parent, childIndex, removedArray);
- }
-
- /**
- * Invoke this method after you've changed how node is to be
- * represented in the tree.
- */
- public void nodeChanged(TreeNode node) {
- if(listenerList != null && node != null) {
- TreeNode parent = node.getParent();
-
- if(parent != null) {
- int anIndex = parent.getIndex(node);
- if(anIndex != -1) {
- int[] cIndexs = new int[1];
-
- cIndexs[0] = anIndex;
- nodesChanged(parent, cIndexs);
- }
- }
- else if (node == getRoot()) {
- nodesChanged(node, null);
- }
- }
- }
-
- /**
- * Invoke this method if you've modified the TreeNodes upon which this
- * model depends. The model will notify all of its listeners that the
- * model has changed below the node <code>node</code> (PENDING).
- */
- public void reload(TreeNode node) {
- if(node != null) {
- fireTreeStructureChanged(this, getPathToRoot(node), null, null);
- }
- }
-
- /**
- * Invoke this method after you've inserted some TreeNodes into
- * node. childIndices should be the index of the new elements and
- * must be sorted in ascending order.
- */
- public void nodesWereInserted(TreeNode node, int[] childIndices) {
- if(listenerList != null && node != null && childIndices != null
- && childIndices.length > 0) {
- int cCount = childIndices.length;
- Object[] newChildren = new Object[cCount];
-
- for(int counter = 0; counter < cCount; counter++)
- newChildren[counter] = node.getChildAt(childIndices[counter]);
- fireTreeNodesInserted(this, getPathToRoot(node), childIndices,
- newChildren);
- }
- }
-
- /**
- * Invoke this method after you've removed some TreeNodes from
- * node. childIndices should be the index of the removed elements and
- * must be sorted in ascending order. And removedChildren should be
- * the array of the children objects that were removed.
- */
- public void nodesWereRemoved(TreeNode node, int[] childIndices,
- Object[] removedChildren) {
- if(node != null && childIndices != null) {
- fireTreeNodesRemoved(this, getPathToRoot(node), childIndices,
- removedChildren);
- }
- }
-
- /**
- * Invoke this method after you've changed how the children identified by
- * childIndicies are to be represented in the tree.
- */
- public void nodesChanged(TreeNode node, int[] childIndices) {
- if(node != null) {
- if (childIndices != null) {
- int cCount = childIndices.length;
-
- if(cCount > 0) {
- Object[] cChildren = new Object[cCount];
-
- for(int counter = 0; counter < cCount; counter++)
- cChildren[counter] = node.getChildAt
- (childIndices[counter]);
- fireTreeNodesChanged(this, getPathToRoot(node),
- childIndices, cChildren);
- }
- }
- else if (node == getRoot()) {
- fireTreeNodesChanged(this, getPathToRoot(node), null, null);
- }
- }
- }
-
- /**
- * Invoke this method if you've totally changed the children of
- * node and its childrens children... This will post a
- * treeStructureChanged event.
- */
- public void nodeStructureChanged(TreeNode node) {
- if(node != null) {
- fireTreeStructureChanged(this, getPathToRoot(node), null, null);
- }
- }
-
- /**
- * Builds the parents of node up to and including the root node,
- * where the original node is the last element in the returned array.
- * The length of the returned array gives the node's depth in the
- * tree.
- *
- * @param aNode the TreeNode to get the path for
- */
- public TreeNode[] getPathToRoot(TreeNode aNode) {
- return getPathToRoot(aNode, 0);
- }
-
- /**
- * Builds the parents of node up to and including the root node,
- * where the original node is the last element in the returned array.
- * The length of the returned array gives the node's depth in the
- * tree.
- *
- * @param aNode the TreeNode to get the path for
- * @param depth an int giving the number of steps already taken towards
- * the root (on recursive calls), used to size the returned array
- * @return an array of TreeNodes giving the path from the root to the
- * specified node
- */
- protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
- TreeNode[] retNodes;
- // This method recurses, traversing towards the root in order
- // size the array. On the way back, it fills in the nodes,
- // starting from the root and working back to the original node.
-
- /* Check for null, in case someone passed in a null node, or
- they passed in an element that isn't rooted at root. */
- if(aNode == null) {
- if(depth == 0)
- return null;
- else
- retNodes = new TreeNode[depth];
- }
- else {
- depth++;
- if(aNode == root)
- retNodes = new TreeNode[depth];
- else
- retNodes = getPathToRoot(aNode.getParent(), depth);
- retNodes[retNodes.length - depth] = aNode;
- }
- return retNodes;
- }
-
- //
- // Events
- //
-
- /**
- * Adds a listener for the TreeModelEvent posted after the tree changes.
- *
- * @see #removeTreeModelListener
- * @param l the listener to add
- */
- public void addTreeModelListener(TreeModelListener l) {
- listenerList.add(TreeModelListener.class, l);
- }
-
- /**
- * Removes a listener previously added with <B>addTreeModelListener()</B>.
- *
- * @see #addTreeModelListener
- * @param l the listener to remove
- */
- public void removeTreeModelListener(TreeModelListener l) {
- listenerList.remove(TreeModelListener.class, l);
- }
-
- /**
- * Returns an array of all the tree model listeners
- * registered on this model.
- *
- * @return all of this model's <code>TreeModelListener</code>s
- * or an empty
- * array if no tree model listeners are currently registered
- *
- * @see #addTreeModelListener
- * @see #removeTreeModelListener
- *
- * @since 1.4
- */
- public TreeModelListener[] getTreeModelListeners() {
- return (TreeModelListener[])listenerList.getListeners(
- TreeModelListener.class);
- }
-
- /**
- * Notifies all listeners that have registered interest for
- * notification on this event type. The event instance
- * is lazily created using the parameters passed into
- * the fire method.
- *
- * @param source the node being changed
- * @param path the path to the root node
- * @param childIndices the indices of the changed elements
- * @param children the changed elements
- * @see EventListenerList
- */
- protected void fireTreeNodesChanged(Object source, Object[] path,
- int[] childIndices,
- Object[] children) {
- // Guaranteed to return a non-null array
- Object[] listeners = listenerList.getListenerList();
- TreeModelEvent e = null;
- // Process the listeners last to first, notifying
- // those that are interested in this event
- for (int i = listeners.length-2; i>=0; i-=2) {
- if (listeners[i]==TreeModelListener.class) {
- // Lazily create the event:
- if (e == null)
- e = new TreeModelEvent(source, path,
- childIndices, children);
- ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
- }
- }
- }
-
- /**
- * Notifies all listeners that have registered interest for
- * notification on this event type. The event instance
- * is lazily created using the parameters passed into
- * the fire method.
- *
- * @param source the node where new elements are being inserted
- * @param path the path to the root node
- * @param childIndices the indices of the new elements
- * @param children the new elements
- * @see EventListenerList
- */
- protected void fireTreeNodesInserted(Object source, Object[] path,
- int[] childIndices,
- Object[] children) {
- // Guaranteed to return a non-null array
- Object[] listeners = listenerList.getListenerList();
- TreeModelEvent e = null;
- // Process the listeners last to first, notifying
- // those that are interested in this event
- for (int i = listeners.length-2; i>=0; i-=2) {
- if (listeners[i]==TreeModelListener.class) {
- // Lazily create the event:
- if (e == null)
- e = new TreeModelEvent(source, path,
- childIndices, children);
- ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
- }
- }
- }
-
- /**
- * Notifies all listeners that have registered interest for
- * notification on this event type. The event instance
- * is lazily created using the parameters passed into
- * the fire method.
- *
- * @param source the node where elements are being removed
- * @param path the path to the root node
- * @param childIndices the indices of the removed elements
- * @param children the removed elements
- * @see EventListenerList
- */
- protected void fireTreeNodesRemoved(Object source, Object[] path,
- int[] childIndices,
- Object[] children) {
- // Guaranteed to return a non-null array
- Object[] listeners = listenerList.getListenerList();
- TreeModelEvent e = null;
- // Process the listeners last to first, notifying
- // those that are interested in this event
- for (int i = listeners.length-2; i>=0; i-=2) {
- if (listeners[i]==TreeModelListener.class) {
- // Lazily create the event:
- if (e == null)
- e = new TreeModelEvent(source, path,
- childIndices, children);
- ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
- }
- }
- }
-
- /**
- * Notifies all listeners that have registered interest for
- * notification on this event type. The event instance
- * is lazily created using the parameters passed into
- * the fire method.
- *
- * @param source the node where the tree model has changed
- * @param path the path to the root node
- * @param childIndices the indices of the affected elements
- * @param children the affected elements
- * @see EventListenerList
- */
- protected void fireTreeStructureChanged(Object source, Object[] path,
- int[] childIndices,
- Object[] children) {
- // Guaranteed to return a non-null array
- Object[] listeners = listenerList.getListenerList();
- TreeModelEvent e = null;
- // Process the listeners last to first, notifying
- // those that are interested in this event
- for (int i = listeners.length-2; i>=0; i-=2) {
- if (listeners[i]==TreeModelListener.class) {
- // Lazily create the event:
- if (e == null)
- e = new TreeModelEvent(source, path,
- childIndices, children);
- ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
- }
- }
- }
-
- /*
- * Notifies all listeners that have registered interest for
- * notification on this event type. The event instance
- * is lazily created using the parameters passed into
- * the fire method.
- *
- * @param source the node where the tree model has changed
- * @param path the path to the root node
- * @see EventListenerList
- */
- private void fireTreeStructureChanged(Object source, TreePath path) {
- // Guaranteed to return a non-null array
- Object[] listeners = listenerList.getListenerList();
- TreeModelEvent e = null;
- // Process the listeners last to first, notifying
- // those that are interested in this event
- for (int i = listeners.length-2; i>=0; i-=2) {
- if (listeners[i]==TreeModelListener.class) {
- // Lazily create the event:
- if (e == null)
- e = new TreeModelEvent(source, path);
- ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
- }
- }
- }
-
- /**
- * Returns an array of all the objects currently registered
- * as <code><em>Foo</em>Listener</code>s
- * upon this model.
- * <code><em>Foo</em>Listener</code>s are registered using the
- * <code>add<em>Foo</em>Listener</code> method.
- *
- * <p>
- *
- * You can specify the <code>listenerType</code> argument
- * with a class literal,
- * such as
- * <code><em>Foo</em>Listener.class</code>.
- * For example, you can query a
- * <code>DefaultTreeModel</code> <code>m</code>
- * for its tree model listeners with the following code:
- *
- * <pre>TreeModelListener[] tmls = (TreeModelListener[])(m.getListeners(TreeModelListener.class));</pre>
- *
- * If no such listeners exist, this method returns an empty array.
- *
- * @param listenerType the type of listeners requested; this parameter
- * should specify an interface that descends from
- * <code>java.util.EventListener</code>
- * @return an array of all objects registered as
- * <code><em>Foo</em>Listener</code>s on this component,
- * or an empty array if no such
- * listeners have been added
- * @exception ClassCastException if <code>listenerType</code>
- * doesn't specify a class or interface that implements
- * <code>java.util.EventListener</code>
- *
- * @see #getTreeModelListeners
- *
- * @since 1.3
- */
- public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
- return listenerList.getListeners(listenerType);
- }
-
- // Serialization support.
- private void writeObject(ObjectOutputStream s) throws IOException {
- Vector values = new Vector();
-
- s.defaultWriteObject();
- // Save the root, if its Serializable.
- if(root != null && root instanceof Serializable) {
- values.addElement("root");
- values.addElement(root);
- }
- s.writeObject(values);
- }
-
- private void readObject(ObjectInputStream s)
- throws IOException, ClassNotFoundException {
- s.defaultReadObject();
-
- Vector values = (Vector)s.readObject();
- int indexCounter = 0;
- int maxCounter = values.size();
-
- if(indexCounter < maxCounter && values.elementAt(indexCounter).
- equals("root")) {
- root = (TreeNode)values.elementAt(++indexCounter);
- indexCounter++;
- }
- }
-
-
- } // End of class DefaultTreeModel
-
-