1 /*
   2  * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.tree;
  27 
  28 import java.util.*;
  29 import java.io.*;
  30 import javax.swing.event.*;
  31 
  32 /**
  33  * A simple tree data model that uses TreeNodes.
  34  * For further information and examples that use DefaultTreeModel,
  35  * see <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
  36  * in <em>The Java Tutorial.</em>
  37  * <p>
  38  * <strong>Warning:</strong>
  39  * Serialized objects of this class will not be compatible with
  40  * future Swing releases. The current serialization support is
  41  * appropriate for short term storage or RMI between applications running
  42  * the same version of Swing.  As of 1.4, support for long term storage
  43  * of all JavaBeans<sup><font size="-2">TM</font></sup>
  44  * has been added to the <code>java.beans</code> package.
  45  * Please see {@link java.beans.XMLEncoder}.
  46  *
  47  * @author Rob Davis
  48  * @author Ray Ryan
  49  * @author Scott Violet
  50  */
  51 public class DefaultTreeModel implements Serializable, TreeModel {
  52     /** Root of the tree. */
  53     protected TreeNode root;
  54     /** Listeners. */
  55     protected EventListenerList listenerList = new EventListenerList();
  56     /**
  57       * Determines how the <code>isLeaf</code> method figures
  58       * out if a node is a leaf node. If true, a node is a leaf
  59       * node if it does not allow children. (If it allows
  60       * children, it is not a leaf node, even if no children
  61       * are present.) That lets you distinguish between <i>folder</i>
  62       * nodes and <i>file</i> nodes in a file system, for example.
  63       * <p>
  64       * If this value is false, then any node which has no
  65       * children is a leaf node, and any node may acquire
  66       * children.
  67       *
  68       * @see TreeNode#getAllowsChildren
  69       * @see TreeModel#isLeaf
  70       * @see #setAsksAllowsChildren
  71       */
  72     protected boolean asksAllowsChildren;
  73 
  74 
  75     /**
  76       * Creates a tree in which any node can have children.
  77       *
  78       * @param root a TreeNode object that is the root of the tree
  79       * @see #DefaultTreeModel(TreeNode, boolean)
  80       */
  81      public DefaultTreeModel(TreeNode root) {
  82         this(root, false);
  83     }
  84 
  85     /**
  86       * Creates a tree specifying whether any node can have children,
  87       * or whether only certain nodes can have children.
  88       *
  89       * @param root a TreeNode object that is the root of the tree
  90       * @param asksAllowsChildren a boolean, false if any node can
  91       *        have children, true if each node is asked to see if
  92       *        it can have children
  93       * @see #asksAllowsChildren
  94       */
  95     public DefaultTreeModel(TreeNode root, boolean asksAllowsChildren) {
  96         super();
  97         this.root = root;
  98         this.asksAllowsChildren = asksAllowsChildren;
  99     }
 100 
 101     /**
 102       * Sets whether or not to test leafness by asking getAllowsChildren()
 103       * or isLeaf() to the TreeNodes.  If newvalue is true, getAllowsChildren()
 104       * is messaged, otherwise isLeaf() is messaged.
 105       */
 106     public void setAsksAllowsChildren(boolean newValue) {
 107         asksAllowsChildren = newValue;
 108     }
 109 
 110     /**
 111       * Tells how leaf nodes are determined.
 112       *
 113       * @return true if only nodes which do not allow children are
 114       *         leaf nodes, false if nodes which have no children
 115       *         (even if allowed) are leaf nodes
 116       * @see #asksAllowsChildren
 117       */
 118     public boolean asksAllowsChildren() {
 119         return asksAllowsChildren;
 120     }
 121 
 122     /**
 123      * Sets the root to <code>root</code>. A null <code>root</code> implies
 124      * the tree is to display nothing, and is legal.
 125      */
 126     public void setRoot(TreeNode root) {
 127         Object oldRoot = this.root;
 128         this.root = root;
 129         if (root == null && oldRoot != null) {
 130             fireTreeStructureChanged(this, null);
 131         }
 132         else {
 133             nodeStructureChanged(root);
 134         }
 135     }
 136 
 137     /**
 138      * Returns the root of the tree.  Returns null only if the tree has
 139      * no nodes.
 140      *
 141      * @return  the root of the tree
 142      */
 143     public Object getRoot() {
 144         return root;
 145     }
 146 
 147     /**
 148      * Returns the index of child in parent.
 149      * If either the parent or child is <code>null</code>, returns -1.
 150      * @param parent a note in the tree, obtained from this data source
 151      * @param child the node we are interested in
 152      * @return the index of the child in the parent, or -1
 153      *    if either the parent or the child is <code>null</code>
 154      */
 155     public int getIndexOfChild(Object parent, Object child) {
 156         if(parent == null || child == null)
 157             return -1;
 158         return ((TreeNode)parent).getIndex((TreeNode)child);
 159     }
 160 
 161     /**
 162      * Returns the child of <I>parent</I> at index <I>index</I> in the parent's
 163      * child array.  <I>parent</I> must be a node previously obtained from
 164      * this data source. This should not return null if <i>index</i>
 165      * is a valid index for <i>parent</i> (that is <i>index</i> >= 0 &&
 166      * <i>index</i> < getChildCount(<i>parent</i>)).
 167      *
 168      * @param   parent  a node in the tree, obtained from this data source
 169      * @return  the child of <I>parent</I> at index <I>index</I>
 170      */
 171     public Object getChild(Object parent, int index) {
 172         return ((TreeNode)parent).getChildAt(index);
 173     }
 174 
 175     /**
 176      * Returns the number of children of <I>parent</I>.  Returns 0 if the node
 177      * is a leaf or if it has no children.  <I>parent</I> must be a node
 178      * previously obtained from this data source.
 179      *
 180      * @param   parent  a node in the tree, obtained from this data source
 181      * @return  the number of children of the node <I>parent</I>
 182      */
 183     public int getChildCount(Object parent) {
 184         return ((TreeNode)parent).getChildCount();
 185     }
 186 
 187     /**
 188      * Returns whether the specified node is a leaf node.
 189      * The way the test is performed depends on the
 190      * <code>askAllowsChildren</code> setting.
 191      *
 192      * @param node the node to check
 193      * @return true if the node is a leaf node
 194      *
 195      * @see #asksAllowsChildren
 196      * @see TreeModel#isLeaf
 197      */
 198     public boolean isLeaf(Object node) {
 199         if(asksAllowsChildren)
 200             return !((TreeNode)node).getAllowsChildren();
 201         return ((TreeNode)node).isLeaf();
 202     }
 203 
 204     /**
 205      * Invoke this method if you've modified the {@code TreeNode}s upon which
 206      * this model depends. The model will notify all of its listeners that the
 207      * model has changed.
 208      */
 209     public void reload() {
 210         reload(root);
 211     }
 212 
 213     /**
 214       * This sets the user object of the TreeNode identified by path
 215       * and posts a node changed.  If you use custom user objects in
 216       * the TreeModel you're going to need to subclass this and
 217       * set the user object of the changed node to something meaningful.
 218       */
 219     public void valueForPathChanged(TreePath path, Object newValue) {
 220         MutableTreeNode   aNode = (MutableTreeNode)path.getLastPathComponent();
 221 
 222         aNode.setUserObject(newValue);
 223         nodeChanged(aNode);
 224     }
 225 
 226     /**
 227      * Invoked this to insert newChild at location index in parents children.
 228      * This will then message nodesWereInserted to create the appropriate
 229      * event. This is the preferred way to add children as it will create
 230      * the appropriate event.
 231      */
 232     public void insertNodeInto(MutableTreeNode newChild,
 233                                MutableTreeNode parent, int index){
 234         parent.insert(newChild, index);
 235 
 236         int[]           newIndexs = new int[1];
 237 
 238         newIndexs[0] = index;
 239         nodesWereInserted(parent, newIndexs);
 240     }
 241 
 242     /**
 243      * Message this to remove node from its parent. This will message
 244      * nodesWereRemoved to create the appropriate event. This is the
 245      * preferred way to remove a node as it handles the event creation
 246      * for you.
 247      */
 248     public void removeNodeFromParent(MutableTreeNode node) {
 249         MutableTreeNode         parent = (MutableTreeNode)node.getParent();
 250 
 251         if(parent == null)
 252             throw new IllegalArgumentException("node does not have a parent.");
 253 
 254         int[]            childIndex = new int[1];
 255         Object[]         removedArray = new Object[1];
 256 
 257         childIndex[0] = parent.getIndex(node);
 258         parent.remove(childIndex[0]);
 259         removedArray[0] = node;
 260         nodesWereRemoved(parent, childIndex, removedArray);
 261     }
 262 
 263     /**
 264       * Invoke this method after you've changed how node is to be
 265       * represented in the tree.
 266       */
 267     public void nodeChanged(TreeNode node) {
 268         if(listenerList != null && node != null) {
 269             TreeNode         parent = node.getParent();
 270 
 271             if(parent != null) {
 272                 int        anIndex = parent.getIndex(node);
 273                 if(anIndex != -1) {
 274                     int[]        cIndexs = new int[1];
 275 
 276                     cIndexs[0] = anIndex;
 277                     nodesChanged(parent, cIndexs);
 278                 }
 279             }
 280             else if (node == getRoot()) {
 281                 nodesChanged(node, null);
 282             }
 283         }
 284     }
 285 
 286     /**
 287      * Invoke this method if you've modified the {@code TreeNode}s upon which
 288      * this model depends. The model will notify all of its listeners that the
 289      * model has changed below the given node.
 290      *
 291      * @param node the node below which the model has changed
 292      */
 293     public void reload(TreeNode node) {
 294         if(node != null) {
 295             fireTreeStructureChanged(this, getPathToRoot(node), null, null);
 296         }
 297     }
 298 
 299     /**
 300       * Invoke this method after you've inserted some TreeNodes into
 301       * node.  childIndices should be the index of the new elements and
 302       * must be sorted in ascending order.
 303       */
 304     public void nodesWereInserted(TreeNode node, int[] childIndices) {
 305         if(listenerList != null && node != null && childIndices != null
 306            && childIndices.length > 0) {
 307             int               cCount = childIndices.length;
 308             Object[]          newChildren = new Object[cCount];
 309 
 310             for(int counter = 0; counter < cCount; counter++)
 311                 newChildren[counter] = node.getChildAt(childIndices[counter]);
 312             fireTreeNodesInserted(this, getPathToRoot(node), childIndices,
 313                                   newChildren);
 314         }
 315     }
 316 
 317     /**
 318       * Invoke this method after you've removed some TreeNodes from
 319       * node.  childIndices should be the index of the removed elements and
 320       * must be sorted in ascending order. And removedChildren should be
 321       * the array of the children objects that were removed.
 322       */
 323     public void nodesWereRemoved(TreeNode node, int[] childIndices,
 324                                  Object[] removedChildren) {
 325         if(node != null && childIndices != null) {
 326             fireTreeNodesRemoved(this, getPathToRoot(node), childIndices,
 327                                  removedChildren);
 328         }
 329     }
 330 
 331     /**
 332       * Invoke this method after you've changed how the children identified by
 333       * childIndicies are to be represented in the tree.
 334       */
 335     public void nodesChanged(TreeNode node, int[] childIndices) {
 336         if(node != null) {
 337             if (childIndices != null) {
 338                 int            cCount = childIndices.length;
 339 
 340                 if(cCount > 0) {
 341                     Object[]       cChildren = new Object[cCount];
 342 
 343                     for(int counter = 0; counter < cCount; counter++)
 344                         cChildren[counter] = node.getChildAt
 345                             (childIndices[counter]);
 346                     fireTreeNodesChanged(this, getPathToRoot(node),
 347                                          childIndices, cChildren);
 348                 }
 349             }
 350             else if (node == getRoot()) {
 351                 fireTreeNodesChanged(this, getPathToRoot(node), null, null);
 352             }
 353         }
 354     }
 355 
 356     /**
 357       * Invoke this method if you've totally changed the children of
 358       * node and its childrens children...  This will post a
 359       * treeStructureChanged event.
 360       */
 361     public void nodeStructureChanged(TreeNode node) {
 362         if(node != null) {
 363            fireTreeStructureChanged(this, getPathToRoot(node), null, null);
 364         }
 365     }
 366 
 367     /**
 368      * Builds the parents of node up to and including the root node,
 369      * where the original node is the last element in the returned array.
 370      * The length of the returned array gives the node's depth in the
 371      * tree.
 372      *
 373      * @param aNode the TreeNode to get the path for
 374      */
 375     public TreeNode[] getPathToRoot(TreeNode aNode) {
 376         return getPathToRoot(aNode, 0);
 377     }
 378 
 379     /**
 380      * Builds the parents of node up to and including the root node,
 381      * where the original node is the last element in the returned array.
 382      * The length of the returned array gives the node's depth in the
 383      * tree.
 384      *
 385      * @param aNode  the TreeNode to get the path for
 386      * @param depth  an int giving the number of steps already taken towards
 387      *        the root (on recursive calls), used to size the returned array
 388      * @return an array of TreeNodes giving the path from the root to the
 389      *         specified node
 390      */
 391     protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
 392         TreeNode[]              retNodes;
 393         // This method recurses, traversing towards the root in order
 394         // size the array. On the way back, it fills in the nodes,
 395         // starting from the root and working back to the original node.
 396 
 397         /* Check for null, in case someone passed in a null node, or
 398            they passed in an element that isn't rooted at root. */
 399         if(aNode == null) {
 400             if(depth == 0)
 401                 return null;
 402             else
 403                 retNodes = new TreeNode[depth];
 404         }
 405         else {
 406             depth++;
 407             if(aNode == root)
 408                 retNodes = new TreeNode[depth];
 409             else
 410                 retNodes = getPathToRoot(aNode.getParent(), depth);
 411             retNodes[retNodes.length - depth] = aNode;
 412         }
 413         return retNodes;
 414     }
 415 
 416     //
 417     //  Events
 418     //
 419 
 420     /**
 421      * Adds a listener for the TreeModelEvent posted after the tree changes.
 422      *
 423      * @see     #removeTreeModelListener
 424      * @param   l       the listener to add
 425      */
 426     public void addTreeModelListener(TreeModelListener l) {
 427         listenerList.add(TreeModelListener.class, l);
 428     }
 429 
 430     /**
 431      * Removes a listener previously added with <B>addTreeModelListener()</B>.
 432      *
 433      * @see     #addTreeModelListener
 434      * @param   l       the listener to remove
 435      */
 436     public void removeTreeModelListener(TreeModelListener l) {
 437         listenerList.remove(TreeModelListener.class, l);
 438     }
 439 
 440     /**
 441      * Returns an array of all the tree model listeners
 442      * registered on this model.
 443      *
 444      * @return all of this model's <code>TreeModelListener</code>s
 445      *         or an empty
 446      *         array if no tree model listeners are currently registered
 447      *
 448      * @see #addTreeModelListener
 449      * @see #removeTreeModelListener
 450      *
 451      * @since 1.4
 452      */
 453     public TreeModelListener[] getTreeModelListeners() {
 454         return (TreeModelListener[])listenerList.getListeners(
 455                 TreeModelListener.class);
 456     }
 457 
 458     /**
 459      * Notifies all listeners that have registered interest for
 460      * notification on this event type.  The event instance
 461      * is lazily created using the parameters passed into
 462      * the fire method.
 463      *
 464      * @param source the node being changed
 465      * @param path the path to the root node
 466      * @param childIndices the indices of the changed elements
 467      * @param children the changed elements
 468      * @see EventListenerList
 469      */
 470     protected void fireTreeNodesChanged(Object source, Object[] path,
 471                                         int[] childIndices,
 472                                         Object[] children) {
 473         // Guaranteed to return a non-null array
 474         Object[] listeners = listenerList.getListenerList();
 475         TreeModelEvent e = null;
 476         // Process the listeners last to first, notifying
 477         // those that are interested in this event
 478         for (int i = listeners.length-2; i>=0; i-=2) {
 479             if (listeners[i]==TreeModelListener.class) {
 480                 // Lazily create the event:
 481                 if (e == null)
 482                     e = new TreeModelEvent(source, path,
 483                                            childIndices, children);
 484                 ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
 485             }
 486         }
 487     }
 488 
 489     /**
 490      * Notifies all listeners that have registered interest for
 491      * notification on this event type.  The event instance
 492      * is lazily created using the parameters passed into
 493      * the fire method.
 494      *
 495      * @param source the node where new elements are being inserted
 496      * @param path the path to the root node
 497      * @param childIndices the indices of the new elements
 498      * @param children the new elements
 499      * @see EventListenerList
 500      */
 501     protected void fireTreeNodesInserted(Object source, Object[] path,
 502                                         int[] childIndices,
 503                                         Object[] children) {
 504         // Guaranteed to return a non-null array
 505         Object[] listeners = listenerList.getListenerList();
 506         TreeModelEvent e = null;
 507         // Process the listeners last to first, notifying
 508         // those that are interested in this event
 509         for (int i = listeners.length-2; i>=0; i-=2) {
 510             if (listeners[i]==TreeModelListener.class) {
 511                 // Lazily create the event:
 512                 if (e == null)
 513                     e = new TreeModelEvent(source, path,
 514                                            childIndices, children);
 515                 ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
 516             }
 517         }
 518     }
 519 
 520     /**
 521      * Notifies all listeners that have registered interest for
 522      * notification on this event type.  The event instance
 523      * is lazily created using the parameters passed into
 524      * the fire method.
 525      *
 526      * @param source the node where elements are being removed
 527      * @param path the path to the root node
 528      * @param childIndices the indices of the removed elements
 529      * @param children the removed elements
 530      * @see EventListenerList
 531      */
 532     protected void fireTreeNodesRemoved(Object source, Object[] path,
 533                                         int[] childIndices,
 534                                         Object[] children) {
 535         // Guaranteed to return a non-null array
 536         Object[] listeners = listenerList.getListenerList();
 537         TreeModelEvent e = null;
 538         // Process the listeners last to first, notifying
 539         // those that are interested in this event
 540         for (int i = listeners.length-2; i>=0; i-=2) {
 541             if (listeners[i]==TreeModelListener.class) {
 542                 // Lazily create the event:
 543                 if (e == null)
 544                     e = new TreeModelEvent(source, path,
 545                                            childIndices, children);
 546                 ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
 547             }
 548         }
 549     }
 550 
 551     /**
 552      * Notifies all listeners that have registered interest for
 553      * notification on this event type.  The event instance
 554      * is lazily created using the parameters passed into
 555      * the fire method.
 556      *
 557      * @param source the node where the tree model has changed
 558      * @param path the path to the root node
 559      * @param childIndices the indices of the affected elements
 560      * @param children the affected elements
 561      * @see EventListenerList
 562      */
 563     protected void fireTreeStructureChanged(Object source, Object[] path,
 564                                         int[] childIndices,
 565                                         Object[] children) {
 566         // Guaranteed to return a non-null array
 567         Object[] listeners = listenerList.getListenerList();
 568         TreeModelEvent e = null;
 569         // Process the listeners last to first, notifying
 570         // those that are interested in this event
 571         for (int i = listeners.length-2; i>=0; i-=2) {
 572             if (listeners[i]==TreeModelListener.class) {
 573                 // Lazily create the event:
 574                 if (e == null)
 575                     e = new TreeModelEvent(source, path,
 576                                            childIndices, children);
 577                 ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
 578             }
 579         }
 580     }
 581 
 582     /*
 583      * Notifies all listeners that have registered interest for
 584      * notification on this event type.  The event instance
 585      * is lazily created using the parameters passed into
 586      * the fire method.
 587      *
 588      * @param source the node where the tree model has changed
 589      * @param path the path to the root node
 590      * @see EventListenerList
 591      */
 592     private void fireTreeStructureChanged(Object source, TreePath path) {
 593         // Guaranteed to return a non-null array
 594         Object[] listeners = listenerList.getListenerList();
 595         TreeModelEvent e = null;
 596         // Process the listeners last to first, notifying
 597         // those that are interested in this event
 598         for (int i = listeners.length-2; i>=0; i-=2) {
 599             if (listeners[i]==TreeModelListener.class) {
 600                 // Lazily create the event:
 601                 if (e == null)
 602                     e = new TreeModelEvent(source, path);
 603                 ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
 604             }
 605         }
 606     }
 607 
 608     /**
 609      * Returns an array of all the objects currently registered
 610      * as <code><em>Foo</em>Listener</code>s
 611      * upon this model.
 612      * <code><em>Foo</em>Listener</code>s are registered using the
 613      * <code>add<em>Foo</em>Listener</code> method.
 614      *
 615      * <p>
 616      *
 617      * You can specify the <code>listenerType</code> argument
 618      * with a class literal,
 619      * such as
 620      * <code><em>Foo</em>Listener.class</code>.
 621      * For example, you can query a
 622      * <code>DefaultTreeModel</code> <code>m</code>
 623      * for its tree model listeners with the following code:
 624      *
 625      * <pre>TreeModelListener[] tmls = (TreeModelListener[])(m.getListeners(TreeModelListener.class));</pre>
 626      *
 627      * If no such listeners exist, this method returns an empty array.
 628      *
 629      * @param listenerType the type of listeners requested; this parameter
 630      *          should specify an interface that descends from
 631      *          <code>java.util.EventListener</code>
 632      * @return an array of all objects registered as
 633      *          <code><em>Foo</em>Listener</code>s on this component,
 634      *          or an empty array if no such
 635      *          listeners have been added
 636      * @exception ClassCastException if <code>listenerType</code>
 637      *          doesn't specify a class or interface that implements
 638      *          <code>java.util.EventListener</code>
 639      *
 640      * @see #getTreeModelListeners
 641      *
 642      * @since 1.3
 643      */
 644     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 645         return listenerList.getListeners(listenerType);
 646     }
 647 
 648     // Serialization support.
 649     private void writeObject(ObjectOutputStream s) throws IOException {
 650         Vector      values = new Vector();
 651 
 652         s.defaultWriteObject();
 653         // Save the root, if its Serializable.
 654         if(root != null && root instanceof Serializable) {
 655             values.addElement("root");
 656             values.addElement(root);
 657         }
 658         s.writeObject(values);
 659     }
 660 
 661     private void readObject(ObjectInputStream s)
 662         throws IOException, ClassNotFoundException {
 663         s.defaultReadObject();
 664 
 665         Vector          values = (Vector)s.readObject();
 666         int             indexCounter = 0;
 667         int             maxCounter = values.size();
 668 
 669         if(indexCounter < maxCounter && values.elementAt(indexCounter).
 670            equals("root")) {
 671             root = (TreeNode)values.elementAt(++indexCounter);
 672             indexCounter++;
 673         }
 674     }
 675 
 676 
 677 } // End of class DefaultTreeModel