jdk/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeModel.java
changeset 25859 3317bb8137f4
parent 25568 b906a74c6882
child 26037 508779ce6619
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     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