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