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