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> >= 0 && 168 * <i>index</i> < 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