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