1 /*
2  * Copyright (c) 1997, 2015, 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="https://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
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"})
DefaultTreeModel(TreeNode 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       */
DefaultTreeModel(TreeNode root, boolean asksAllowsChildren)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       */
setAsksAllowsChildren(boolean newValue)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       */
asksAllowsChildren()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      */
setRoot(TreeNode root)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      */
getRoot()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      */
getIndexOfChild(Object parent, Object child)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      */
getChild(Object parent, int index)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      */
getChildCount(Object parent)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      */
isLeaf(Object node)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      */
reload()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       */
valueForPathChanged(TreePath path, Object newValue)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      */
insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index)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      */
removeNodeFromParent(MutableTreeNode node)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       */
nodeChanged(TreeNode node)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      */
reload(TreeNode node)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       */
nodesWereInserted(TreeNode node, int[] childIndices)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       */
nodesWereRemoved(TreeNode node, int[] childIndices, Object[] removedChildren)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       */
nodesChanged(TreeNode node, int[] childIndices)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       */
nodeStructureChanged(TreeNode node)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      */
getPathToRoot(TreeNode aNode)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      */
getPathToRoot(TreeNode aNode, int depth)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      */
addTreeModelListener(TreeModelListener l)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      */
removeTreeModelListener(TreeModelListener l)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      */
getTreeModelListeners()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      */
fireTreeNodesChanged(Object source, Object[] path, int[] childIndices, Object[] children)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      */
fireTreeNodesInserted(Object source, Object[] path, int[] childIndices, Object[] children)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      */
fireTreeNodesRemoved(Object source, Object[] path, int[] childIndices, Object[] children)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      */
fireTreeStructureChanged(Object source, Object[] path, int[] childIndices, Object[] children)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      */
fireTreeStructureChanged(Object source, TreePath path)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 <T> the listener type
661      * @param listenerType the type of listeners requested
662      * @return an array of all objects registered as
663      *          <code><em>Foo</em>Listener</code>s on this component,
664      *          or an empty array if no such
665      *          listeners have been added
666      * @exception ClassCastException if <code>listenerType</code>
667      *          doesn't specify a class or interface that implements
668      *          <code>java.util.EventListener</code>
669      *
670      * @see #getTreeModelListeners
671      *
672      * @since 1.3
673      */
getListeners(Class<T> listenerType)674     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
675         return listenerList.getListeners(listenerType);
676     }
677 
678     // Serialization support.
writeObject(ObjectOutputStream s)679     private void writeObject(ObjectOutputStream s) throws IOException {
680         Vector<Object> values = new Vector<Object>();
681 
682         s.defaultWriteObject();
683         // Save the root, if its Serializable.
684         if(root != null && root instanceof Serializable) {
685             values.addElement("root");
686             values.addElement(root);
687         }
688         s.writeObject(values);
689     }
690 
readObject(ObjectInputStream s)691     private void readObject(ObjectInputStream s)
692         throws IOException, ClassNotFoundException {
693         ObjectInputStream.GetField f = s.readFields();
694         EventListenerList newListenerList = (EventListenerList) f.get("listenerList", null);
695         if (newListenerList == null) {
696             throw new InvalidObjectException("Null listenerList");
697         }
698         listenerList = newListenerList;
699         asksAllowsChildren = f.get("asksAllowsChildren", false);
700 
701         Vector<?>       values = (Vector)s.readObject();
702         int             indexCounter = 0;
703         int             maxCounter = values.size();
704 
705         if(indexCounter < maxCounter && values.elementAt(indexCounter).
706            equals("root")) {
707             TreeNode newRoot  = (TreeNode)values.elementAt(++indexCounter);
708             if (newRoot == null) {
709                 throw new InvalidObjectException("Null root");
710             }
711             root = newRoot;
712             indexCounter++;
713         }
714     }
715 
716 
717 } // End of class DefaultTreeModel
718