KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > tree > DefaultTreeModel


1 /*
2  * @(#)DefaultTreeModel.java 1.55 04/05/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing.tree;
9
10 import java.util.*;
11 import java.awt.*;
12 import java.io.*;
13 import javax.swing.event.*;
14
15 /**
16  * A simple tree data model that uses TreeNodes.
17  * For further information and examples that use DefaultTreeModel,
18  * see <a HREF="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
19  * in <em>The Java Tutorial.</em>
20  * <p>
21  * <strong>Warning:</strong>
22  * Serialized objects of this class will not be compatible with
23  * future Swing releases. The current serialization support is
24  * appropriate for short term storage or RMI between applications running
25  * the same version of Swing. As of 1.4, support for long term storage
26  * of all JavaBeans<sup><font size="-2">TM</font></sup>
27  * has been added to the <code>java.beans</code> package.
28  * Please see {@link java.beans.XMLEncoder}.
29  *
30  * @version 1.55 05/05/04
31  * @author Rob Davis
32  * @author Ray Ryan
33  * @author Scott Violet
34  */

35 public class DefaultTreeModel implements Serializable, TreeModel JavaDoc {
36     /** Root of the tree. */
37     protected TreeNode JavaDoc root;
38     /** Listeners. */
39     protected EventListenerList listenerList = new EventListenerList();
40     /**
41       * Determines how the <code>isLeaf</code> method figures
42       * out if a node is a leaf node. If true, a node is a leaf
43       * node if it does not allow children. (If it allows
44       * children, it is not a leaf node, even if no children
45       * are present.) That lets you distinguish between <i>folder</i>
46       * nodes and <i>file</i> nodes in a file system, for example.
47       * <p>
48       * If this value is false, then any node which has no
49       * children is a leaf node, and any node may acquire
50       * children.
51       *
52       * @see TreeNode#getAllowsChildren
53       * @see TreeModel#isLeaf
54       * @see #setAsksAllowsChildren
55       */

56     protected boolean asksAllowsChildren;
57
58
59     /**
60       * Creates a tree in which any node can have children.
61       *
62       * @param root a TreeNode object that is the root of the tree
63       * @see #DefaultTreeModel(TreeNode, boolean)
64       */

65      public DefaultTreeModel(TreeNode JavaDoc root) {
66         this(root, false);
67     }
68
69     /**
70       * Creates a tree specifying whether any node can have children,
71       * or whether only certain nodes can have children.
72       *
73       * @param root a TreeNode object that is the root of the tree
74       * @param asksAllowsChildren a boolean, false if any node can
75       * have children, true if each node is asked to see if
76       * it can have children
77       * @see #asksAllowsChildren
78       */

79     public DefaultTreeModel(TreeNode JavaDoc root, boolean asksAllowsChildren) {
80         super();
81         this.root = root;
82         this.asksAllowsChildren = asksAllowsChildren;
83     }
84
85     /**
86       * Sets whether or not to test leafness by asking getAllowsChildren()
87       * or isLeaf() to the TreeNodes. If newvalue is true, getAllowsChildren()
88       * is messaged, otherwise isLeaf() is messaged.
89       */

90     public void setAsksAllowsChildren(boolean newValue) {
91         asksAllowsChildren = newValue;
92     }
93
94     /**
95       * Tells how leaf nodes are determined.
96       *
97       * @return true if only nodes which do not allow children are
98       * leaf nodes, false if nodes which have no children
99       * (even if allowed) are leaf nodes
100       * @see #asksAllowsChildren
101       */

102     public boolean asksAllowsChildren() {
103         return asksAllowsChildren;
104     }
105
106     /**
107      * Sets the root to <code>root</code>. A null <code>root</code> implies
108      * the tree is to display nothing, and is legal.
109      */

110     public void setRoot(TreeNode JavaDoc root) {
111         Object JavaDoc oldRoot = this.root;
112     this.root = root;
113         if (root == null && oldRoot != null) {
114             fireTreeStructureChanged(this, null);
115         }
116         else {
117             nodeStructureChanged(root);
118         }
119     }
120
121     /**
122      * Returns the root of the tree. Returns null only if the tree has
123      * no nodes.
124      *
125      * @return the root of the tree
126      */

127     public Object JavaDoc getRoot() {
128         return root;
129     }
130
131     /**
132      * Returns the index of child in parent.
133      * If either the parent or child is <code>null</code>, returns -1.
134      * @param parent a note in the tree, obtained from this data source
135      * @param child the node we are interested in
136      * @return the index of the child in the parent, or -1
137      * if either the parent or the child is <code>null</code>
138      */

139     public int getIndexOfChild(Object JavaDoc parent, Object JavaDoc child) {
140         if(parent == null || child == null)
141             return -1;
142         return ((TreeNode JavaDoc)parent).getIndex((TreeNode JavaDoc)child);
143     }
144
145     /**
146      * Returns the child of <I>parent</I> at index <I>index</I> in the parent's
147      * child array. <I>parent</I> must be a node previously obtained from
148      * this data source. This should not return null if <i>index</i>
149      * is a valid index for <i>parent</i> (that is <i>index</i> >= 0 &&
150      * <i>index</i> < getChildCount(<i>parent</i>)).
151      *
152      * @param parent a node in the tree, obtained from this data source
153      * @return the child of <I>parent</I> at index <I>index</I>
154      */

155     public Object JavaDoc getChild(Object JavaDoc parent, int index) {
156         return ((TreeNode JavaDoc)parent).getChildAt(index);
157     }
158
159     /**
160      * Returns the number of children of <I>parent</I>. Returns 0 if the node
161      * is a leaf or if it has no children. <I>parent</I> must be a node
162      * previously obtained from this data source.
163      *
164      * @param parent a node in the tree, obtained from this data source
165      * @return the number of children of the node <I>parent</I>
166      */

167     public int getChildCount(Object JavaDoc parent) {
168         return ((TreeNode JavaDoc)parent).getChildCount();
169     }
170
171     /**
172      * Returns whether the specified node is a leaf node.
173      * The way the test is performed depends on the
174      * <code>askAllowsChildren</code> setting.
175      *
176      * @param node the node to check
177      * @return true if the node is a leaf node
178      *
179      * @see #asksAllowsChildren
180      * @see TreeModel#isLeaf
181      */

182     public boolean isLeaf(Object JavaDoc node) {
183         if(asksAllowsChildren)
184             return !((TreeNode JavaDoc)node).getAllowsChildren();
185         return ((TreeNode JavaDoc)node).isLeaf();
186     }
187
188     /**
189      * Invoke this method if you've modified the TreeNodes upon which this
190      * model depends. The model will notify all of its listeners that the
191      * model has changed.
192      */

193     public void reload() {
194         reload(root);
195     }
196
197     /**
198       * This sets the user object of the TreeNode identified by path
199       * and posts a node changed. If you use custom user objects in
200       * the TreeModel you're going to need to subclass this and
201       * set the user object of the changed node to something meaningful.
202       */

203     public void valueForPathChanged(TreePath JavaDoc path, Object JavaDoc newValue) {
204     MutableTreeNode JavaDoc aNode = (MutableTreeNode JavaDoc)path.getLastPathComponent();
205
206         aNode.setUserObject(newValue);
207         nodeChanged(aNode);
208     }
209
210     /**
211      * Invoked this to insert newChild at location index in parents children.
212      * This will then message nodesWereInserted to create the appropriate
213      * event. This is the preferred way to add children as it will create
214      * the appropriate event.
215      */

216     public void insertNodeInto(MutableTreeNode JavaDoc newChild,
217                                MutableTreeNode JavaDoc parent, int index){
218         parent.insert(newChild, index);
219
220         int[] newIndexs = new int[1];
221
222         newIndexs[0] = index;
223         nodesWereInserted(parent, newIndexs);
224     }
225
226     /**
227      * Message this to remove node from its parent. This will message
228      * nodesWereRemoved to create the appropriate event. This is the
229      * preferred way to remove a node as it handles the event creation
230      * for you.
231      */

232     public void removeNodeFromParent(MutableTreeNode JavaDoc node) {
233         MutableTreeNode JavaDoc parent = (MutableTreeNode JavaDoc)node.getParent();
234
235         if(parent == null)
236             throw new IllegalArgumentException JavaDoc("node does not have a parent.");
237
238         int[] childIndex = new int[1];
239         Object JavaDoc[] removedArray = new Object JavaDoc[1];
240
241         childIndex[0] = parent.getIndex(node);
242         parent.remove(childIndex[0]);
243         removedArray[0] = node;
244         nodesWereRemoved(parent, childIndex, removedArray);
245     }
246
247     /**
248       * Invoke this method after you've changed how node is to be
249       * represented in the tree.
250       */

251     public void nodeChanged(TreeNode JavaDoc node) {
252         if(listenerList != null && node != null) {
253             TreeNode JavaDoc parent = node.getParent();
254
255             if(parent != null) {
256                 int anIndex = parent.getIndex(node);
257                 if(anIndex != -1) {
258                     int[] cIndexs = new int[1];
259
260                     cIndexs[0] = anIndex;
261                     nodesChanged(parent, cIndexs);
262                 }
263             }
264         else if (node == getRoot()) {
265         nodesChanged(node, null);
266         }
267         }
268     }
269
270     /**
271      * Invoke this method if you've modified the TreeNodes upon which this
272      * model depends. The model will notify all of its listeners that the
273      * model has changed below the node <code>node</code> (PENDING).
274      */

275     public void reload(TreeNode JavaDoc node) {
276         if(node != null) {
277             fireTreeStructureChanged(this, getPathToRoot(node), null, null);
278         }
279     }
280
281     /**
282       * Invoke this method after you've inserted some TreeNodes into
283       * node. childIndices should be the index of the new elements and
284       * must be sorted in ascending order.
285       */

286     public void nodesWereInserted(TreeNode JavaDoc node, int[] childIndices) {
287         if(listenerList != null && node != null && childIndices != null
288            && childIndices.length > 0) {
289             int cCount = childIndices.length;
290             Object JavaDoc[] newChildren = new Object JavaDoc[cCount];
291
292             for(int counter = 0; counter < cCount; counter++)
293                 newChildren[counter] = node.getChildAt(childIndices[counter]);
294             fireTreeNodesInserted(this, getPathToRoot(node), childIndices,
295                                   newChildren);
296         }
297     }
298     
299     /**
300       * Invoke this method after you've removed some TreeNodes from
301       * node. childIndices should be the index of the removed elements and
302       * must be sorted in ascending order. And removedChildren should be
303       * the array of the children objects that were removed.
304       */

305     public void nodesWereRemoved(TreeNode JavaDoc node, int[] childIndices,
306                                  Object JavaDoc[] removedChildren) {
307         if(node != null && childIndices != null) {
308             fireTreeNodesRemoved(this, getPathToRoot(node), childIndices,
309                                  removedChildren);
310         }
311     }
312
313     /**
314       * Invoke this method after you've changed how the children identified by
315       * childIndicies are to be represented in the tree.
316       */

317     public void nodesChanged(TreeNode JavaDoc node, int[] childIndices) {
318         if(node != null) {
319         if (childIndices != null) {
320         int cCount = childIndices.length;
321
322         if(cCount > 0) {
323             Object JavaDoc[] cChildren = new Object JavaDoc[cCount];
324
325             for(int counter = 0; counter < cCount; counter++)
326             cChildren[counter] = node.getChildAt
327                 (childIndices[counter]);
328             fireTreeNodesChanged(this, getPathToRoot(node),
329                      childIndices, cChildren);
330         }
331         }
332         else if (node == getRoot()) {
333         fireTreeNodesChanged(this, getPathToRoot(node), null, null);
334         }
335         }
336     }
337
338     /**
339       * Invoke this method if you've totally changed the children of
340       * node and its childrens children... This will post a
341       * treeStructureChanged event.
342       */

343     public void nodeStructureChanged(TreeNode JavaDoc node) {
344         if(node != null) {
345            fireTreeStructureChanged(this, getPathToRoot(node), null, null);
346         }
347     }
348
349     /**
350      * Builds the parents of node up to and including the root node,
351      * where the original node is the last element in the returned array.
352      * The length of the returned array gives the node's depth in the
353      * tree.
354      *
355      * @param aNode the TreeNode to get the path for
356      */

357     public TreeNode JavaDoc[] getPathToRoot(TreeNode JavaDoc aNode) {
358         return getPathToRoot(aNode, 0);
359     }
360
361     /**
362      * Builds the parents of node up to and including the root node,
363      * where the original node is the last element in the returned array.
364      * The length of the returned array gives the node's depth in the
365      * tree.
366      *
367      * @param aNode the TreeNode to get the path for
368      * @param depth an int giving the number of steps already taken towards
369      * the root (on recursive calls), used to size the returned array
370      * @return an array of TreeNodes giving the path from the root to the
371      * specified node
372      */

373     protected TreeNode JavaDoc[] getPathToRoot(TreeNode JavaDoc aNode, int depth) {
374         TreeNode JavaDoc[] retNodes;
375     // This method recurses, traversing towards the root in order
376
// size the array. On the way back, it fills in the nodes,
377
// starting from the root and working back to the original node.
378

379         /* Check for null, in case someone passed in a null node, or
380            they passed in an element that isn't rooted at root. */

381         if(aNode == null) {
382             if(depth == 0)
383                 return null;
384             else
385                 retNodes = new TreeNode JavaDoc[depth];
386         }
387         else {
388             depth++;
389             if(aNode == root)
390                 retNodes = new TreeNode JavaDoc[depth];
391             else
392                 retNodes = getPathToRoot(aNode.getParent(), depth);
393             retNodes[retNodes.length - depth] = aNode;
394         }
395         return retNodes;
396     }
397
398     //
399
// Events
400
//
401

402     /**
403      * Adds a listener for the TreeModelEvent posted after the tree changes.
404      *
405      * @see #removeTreeModelListener
406      * @param l the listener to add
407      */

408     public void addTreeModelListener(TreeModelListener l) {
409         listenerList.add(TreeModelListener.class, l);
410     }
411
412     /**
413      * Removes a listener previously added with <B>addTreeModelListener()</B>.
414      *
415      * @see #addTreeModelListener
416      * @param l the listener to remove
417      */

418     public void removeTreeModelListener(TreeModelListener l) {
419         listenerList.remove(TreeModelListener.class, l);
420     }
421
422     /**
423      * Returns an array of all the tree model listeners
424      * registered on this model.
425      *
426      * @return all of this model's <code>TreeModelListener</code>s
427      * or an empty
428      * array if no tree model listeners are currently registered
429      *
430      * @see #addTreeModelListener
431      * @see #removeTreeModelListener
432      *
433      * @since 1.4
434      */

435     public TreeModelListener[] getTreeModelListeners() {
436         return (TreeModelListener[])listenerList.getListeners(
437                 TreeModelListener.class);
438     }
439
440     /**
441      * Notifies all listeners that have registered interest for
442      * notification on this event type. The event instance
443      * is lazily created using the parameters passed into
444      * the fire method.
445      *
446      * @param source the node being changed
447      * @param path the path to the root node
448      * @param childIndices the indices of the changed elements
449      * @param children the changed elements
450      * @see EventListenerList
451      */

452     protected void fireTreeNodesChanged(Object JavaDoc source, Object JavaDoc[] path,
453                                         int[] childIndices,
454                                         Object JavaDoc[] children) {
455         // Guaranteed to return a non-null array
456
Object JavaDoc[] listeners = listenerList.getListenerList();
457         TreeModelEvent e = null;
458         // Process the listeners last to first, notifying
459
// those that are interested in this event
460
for (int i = listeners.length-2; i>=0; i-=2) {
461             if (listeners[i]==TreeModelListener.class) {
462                 // Lazily create the event:
463
if (e == null)
464                     e = new TreeModelEvent(source, path,
465                                            childIndices, children);
466                 ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
467             }
468         }
469     }
470
471     /**
472      * Notifies all listeners that have registered interest for
473      * notification on this event type. The event instance
474      * is lazily created using the parameters passed into
475      * the fire method.
476      *
477      * @param source the node where new elements are being inserted
478      * @param path the path to the root node
479      * @param childIndices the indices of the new elements
480      * @param children the new elements
481      * @see EventListenerList
482      */

483     protected void fireTreeNodesInserted(Object JavaDoc source, Object JavaDoc[] path,
484                                         int[] childIndices,
485                                         Object JavaDoc[] children) {
486         // Guaranteed to return a non-null array
487
Object JavaDoc[] listeners = listenerList.getListenerList();
488         TreeModelEvent e = null;
489         // Process the listeners last to first, notifying
490
// those that are interested in this event
491
for (int i = listeners.length-2; i>=0; i-=2) {
492             if (listeners[i]==TreeModelListener.class) {
493                 // Lazily create the event:
494
if (e == null)
495                     e = new TreeModelEvent(source, path,
496                                            childIndices, children);
497                 ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
498             }
499         }
500     }
501
502     /**
503      * Notifies all listeners that have registered interest for
504      * notification on this event type. The event instance
505      * is lazily created using the parameters passed into
506      * the fire method.
507      *
508      * @param source the node where elements are being removed
509      * @param path the path to the root node
510      * @param childIndices the indices of the removed elements
511      * @param children the removed elements
512      * @see EventListenerList
513      */

514     protected void fireTreeNodesRemoved(Object JavaDoc source, Object JavaDoc[] path,
515                                         int[] childIndices,
516                                         Object JavaDoc[] children) {
517         // Guaranteed to return a non-null array
518
Object JavaDoc[] listeners = listenerList.getListenerList();
519         TreeModelEvent e = null;
520         // Process the listeners last to first, notifying
521
// those that are interested in this event
522
for (int i = listeners.length-2; i>=0; i-=2) {
523             if (listeners[i]==TreeModelListener.class) {
524                 // Lazily create the event:
525
if (e == null)
526                     e = new TreeModelEvent(source, path,
527                                            childIndices, children);
528                 ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
529             }
530         }
531     }
532
533     /**
534      * Notifies all listeners that have registered interest for
535      * notification on this event type. The event instance
536      * is lazily created using the parameters passed into
537      * the fire method.
538      *
539      * @param source the node where the tree model has changed
540      * @param path the path to the root node
541      * @param childIndices the indices of the affected elements
542      * @param children the affected elements
543      * @see EventListenerList
544      */

545     protected void fireTreeStructureChanged(Object JavaDoc source, Object JavaDoc[] path,
546                                         int[] childIndices,
547                                         Object JavaDoc[] children) {
548         // Guaranteed to return a non-null array
549
Object JavaDoc[] listeners = listenerList.getListenerList();
550         TreeModelEvent e = null;
551         // Process the listeners last to first, notifying
552
// those that are interested in this event
553
for (int i = listeners.length-2; i>=0; i-=2) {
554             if (listeners[i]==TreeModelListener.class) {
555                 // Lazily create the event:
556
if (e == null)
557                     e = new TreeModelEvent(source, path,
558                                            childIndices, children);
559                 ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
560             }
561         }
562     }
563
564     /*
565      * Notifies all listeners that have registered interest for
566      * notification on this event type. The event instance
567      * is lazily created using the parameters passed into
568      * the fire method.
569      *
570      * @param source the node where the tree model has changed
571      * @param path the path to the root node
572      * @see EventListenerList
573      */

574     private void fireTreeStructureChanged(Object JavaDoc source, TreePath JavaDoc path) {
575         // Guaranteed to return a non-null array
576
Object JavaDoc[] listeners = listenerList.getListenerList();
577         TreeModelEvent e = null;
578         // Process the listeners last to first, notifying
579
// those that are interested in this event
580
for (int i = listeners.length-2; i>=0; i-=2) {
581             if (listeners[i]==TreeModelListener.class) {
582                 // Lazily create the event:
583
if (e == null)
584                     e = new TreeModelEvent(source, path);
585                 ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
586             }
587         }
588     }
589
590     /**
591      * Returns an array of all the objects currently registered
592      * as <code><em>Foo</em>Listener</code>s
593      * upon this model.
594      * <code><em>Foo</em>Listener</code>s are registered using the
595      * <code>add<em>Foo</em>Listener</code> method.
596      *
597      * <p>
598      *
599      * You can specify the <code>listenerType</code> argument
600      * with a class literal,
601      * such as
602      * <code><em>Foo</em>Listener.class</code>.
603      * For example, you can query a
604      * <code>DefaultTreeModel</code> <code>m</code>
605      * for its tree model listeners with the following code:
606      *
607      * <pre>TreeModelListener[] tmls = (TreeModelListener[])(m.getListeners(TreeModelListener.class));</pre>
608      *
609      * If no such listeners exist, this method returns an empty array.
610      *
611      * @param listenerType the type of listeners requested; this parameter
612      * should specify an interface that descends from
613      * <code>java.util.EventListener</code>
614      * @return an array of all objects registered as
615      * <code><em>Foo</em>Listener</code>s on this component,
616      * or an empty array if no such
617      * listeners have been added
618      * @exception ClassCastException if <code>listenerType</code>
619      * doesn't specify a class or interface that implements
620      * <code>java.util.EventListener</code>
621      *
622      * @see #getTreeModelListeners
623      *
624      * @since 1.3
625      */

626     public <T extends EventListener> T[] getListeners(Class JavaDoc<T> listenerType) {
627     return listenerList.getListeners(listenerType);
628     }
629
630     // Serialization support.
631
private void writeObject(ObjectOutputStream s) throws IOException {
632         Vector values = new Vector();
633
634         s.defaultWriteObject();
635         // Save the root, if its Serializable.
636
if(root != null && root instanceof Serializable) {
637             values.addElement("root");
638             values.addElement(root);
639         }
640         s.writeObject(values);
641     }
642
643     private void readObject(ObjectInputStream s)
644         throws IOException, ClassNotFoundException JavaDoc {
645         s.defaultReadObject();
646
647         Vector values = (Vector)s.readObject();
648         int indexCounter = 0;
649         int maxCounter = values.size();
650
651         if(indexCounter < maxCounter && values.elementAt(indexCounter).
652            equals("root")) {
653             root = (TreeNode JavaDoc)values.elementAt(++indexCounter);
654             indexCounter++;
655         }
656     }
657
658
659 } // End of class DefaultTreeModel
660

661
662
Popular Tags