001/* DefaultTreeModel.java -- 002 Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038package javax.swing.tree; 039 040import java.io.IOException; 041import java.io.ObjectInputStream; 042import java.io.ObjectOutputStream; 043import java.io.Serializable; 044import java.util.EventListener; 045 046import javax.swing.event.EventListenerList; 047import javax.swing.event.TreeModelEvent; 048import javax.swing.event.TreeModelListener; 049 050/** 051 * DefaultTreeModel 052 * 053 * @author Andrew Selkirk 054 */ 055public class DefaultTreeModel 056 implements Serializable, TreeModel 057{ 058 static final long serialVersionUID = -2621068368932566998L; 059 060 /** 061 * root 062 */ 063 protected TreeNode root; 064 065 /** 066 * listenerList 067 */ 068 protected EventListenerList listenerList = new EventListenerList(); 069 070 /** 071 * asksAllowsChildren 072 */ 073 protected boolean asksAllowsChildren; 074 075 /** 076 * Constructor DefaultTreeModel where any node can have children. 077 * 078 * @param root the tree root. 079 */ 080 public DefaultTreeModel(TreeNode root) 081 { 082 this (root, false); 083 } 084 085 /** 086 * Create the DefaultTreeModel that may check if the nodes can have 087 * children or not. 088 * 089 * @param aRoot the tree root. 090 * @param asksAllowsChildren if true, each node is asked if it can have 091 * children. If false, the model does not care about this, supposing, that 092 * any node can have children. 093 */ 094 public DefaultTreeModel(TreeNode aRoot, boolean asksAllowsChildren) 095 { 096 if (aRoot == null) 097 aRoot = new DefaultMutableTreeNode(); 098 this.root = aRoot; 099 this.asksAllowsChildren = asksAllowsChildren; 100 } 101 102 /** 103 * writeObject 104 * 105 * @param obj the object. 106 * @exception IOException TODO 107 */ 108 private void writeObject(ObjectOutputStream obj) throws IOException 109 { 110 // TODO 111 } 112 113 /** 114 * readObject 115 * 116 * @param value0 TODO 117 * @exception IOException TODO 118 * @exception ClassNotFoundException TODO 119 */ 120 private void readObject(ObjectInputStream value0) throws IOException, 121 ClassNotFoundException 122 { 123 // TODO 124 } 125 126 /** 127 * asksAllowsChildren 128 * 129 * @return boolean 130 */ 131 public boolean asksAllowsChildren() 132 { 133 return asksAllowsChildren; 134 } 135 136 /** 137 * setAsksAllowsChildren 138 * 139 * @param value TODO 140 */ 141 public void setAsksAllowsChildren(boolean value) 142 { 143 asksAllowsChildren = value; 144 } 145 146 /** 147 * setRoot 148 * 149 * @param root the root node. 150 */ 151 public void setRoot(TreeNode root) 152 { 153 this.root = root; 154 } 155 156 /** 157 * getRoot 158 * 159 * @return Object 160 */ 161 public Object getRoot() 162 { 163 return root; 164 } 165 166 /** 167 * getIndexOfChild 168 * 169 * @param parent TODO 170 * @param child TODO 171 * @return int 172 */ 173 public int getIndexOfChild(Object parent, Object child) 174 { 175 for (int i = 0; i < getChildCount(parent); i++) 176 { 177 if (getChild(parent, i).equals(child)) 178 return i; 179 } 180 return -1; 181 } 182 183 /** 184 * getChild 185 * 186 * @param node TODO 187 * @param idx TODO 188 * @return Object 189 */ 190 public Object getChild(Object node, int idx) 191 { 192 if (node instanceof TreeNode) 193 return ((TreeNode) node).getChildAt(idx); 194 else 195 return null; 196 } 197 198 /** 199 * getChildCount 200 * 201 * @param node TODO 202 * @return int 203 */ 204 public int getChildCount(Object node) 205 { 206 if (node instanceof TreeNode) 207 return ((TreeNode) node).getChildCount(); 208 else 209 return 0; 210 } 211 212 /** 213 * Returns if the specified node is a leaf or not. When 214 * {@link #asksAllowsChildren} is true, then this checks if the TreeNode 215 * allows children, otherwise it returns the TreeNode's <code>leaf</code> 216 * property. 217 * 218 * @param node the node to check 219 * 220 * @return boolean <code>true</code> if the node is a leaf node, 221 * <code>false</code> otherwise 222 * 223 * @throws ClassCastException if the specified node is not a 224 * <code>TreeNode</code> instance 225 * 226 * @see TreeNode#getAllowsChildren() 227 * @see TreeNode#isLeaf() 228 */ 229 public boolean isLeaf(Object node) 230 { 231 // The RI throws a ClassCastException when node isn't a TreeNode, so do we. 232 TreeNode treeNode = (TreeNode) node; 233 boolean leaf; 234 if (asksAllowsChildren) 235 leaf = ! treeNode.getAllowsChildren(); 236 else 237 leaf = treeNode.isLeaf(); 238 return leaf; 239 } 240 241 /** 242 * <p> 243 * Invoke this method if you've modified the TreeNodes upon which this model 244 * depends. The model will notify all of its listeners that the model has 245 * changed. It will fire the events, necessary to update the layout caches and 246 * repaint the tree. The tree will <i>not</i> be properly refreshed if you 247 * call the JTree.repaint instead. 248 * </p> 249 * <p> 250 * This method will refresh the information about whole tree from the root. If 251 * only part of the tree should be refreshed, it is more effective to call 252 * {@link #reload(TreeNode)}. 253 * </p> 254 */ 255 public void reload() 256 { 257 // Need to duplicate the code because the root can formally be 258 // no an instance of the TreeNode. 259 int n = getChildCount(root); 260 int[] childIdx = new int[n]; 261 Object[] children = new Object[n]; 262 263 for (int i = 0; i < n; i++) 264 { 265 childIdx[i] = i; 266 children[i] = getChild(root, i); 267 } 268 269 fireTreeStructureChanged(this, new Object[] { root }, childIdx, children); 270 } 271 272 /** 273 * Invoke this method if you've modified the TreeNodes upon which this model 274 * depends. The model will notify all of its listeners that the model has 275 * changed. It will fire the events, necessary to update the layout caches and 276 * repaint the tree. The tree will <i>not</i> be properly refreshed if you 277 * call the JTree.repaint instead. 278 * 279 * @param node - the tree node, from which the tree nodes have changed 280 * (inclusive). If you do not know this node, call {@link #reload()} 281 * instead. 282 */ 283 public void reload(TreeNode node) 284 { 285 int n = getChildCount(node); 286 int[] childIdx = new int[n]; 287 Object[] children = new Object[n]; 288 289 for (int i = 0; i < n; i++) 290 { 291 childIdx[i] = i; 292 children[i] = getChild(node, i); 293 } 294 295 fireTreeStructureChanged(this, getPathToRoot(node), childIdx, children); 296 } 297 298 /** 299 * Messaged when the user has altered the value for the item 300 * identified by path to newValue. If newValue signifies a truly new 301 * value the model should post a treeNodesChanged event. 302 * This sets the user object of the TreeNode identified by 303 * path and posts a node changed. If you use custom user objects 304 * in the TreeModel you're going to need to subclass this and set 305 * the user object of the changed node to something meaningful. 306 * 307 * @param path - path to the node that the user has altered 308 * @param newValue - the new value from the TreeCellEditor 309 */ 310 public void valueForPathChanged(TreePath path, Object newValue) 311 { 312 Object node = path.getLastPathComponent(); 313 if (node instanceof MutableTreeNode) 314 { 315 ((MutableTreeNode) node).setUserObject(newValue); 316 int[] ci = null; 317 Object[] c = null; 318 Object[] parentPath = path.getPath(); 319 if (path.getPathCount() > 1) 320 { 321 Object parent = ((TreeNode) node).getParent(); 322 ci = new int[1]; 323 ci[0] = getIndexOfChild(parent, node); 324 node = newValue; 325 path = path.getParentPath().pathByAddingChild(node); 326 c = new Object[1]; 327 c[0] = node; 328 parentPath = path.getParentPath().getPath(); 329 } 330 331 fireTreeNodesChanged(this, parentPath, ci, c); 332 } 333 } 334 335 /** 336 * Invoked this to insert newChild at location index in parents children. 337 * This will then message nodesWereInserted to create the appropriate event. 338 * This is the preferred way to add children as it will create the 339 * appropriate event. 340 * 341 * @param newChild is the node to add to the parent's children 342 * @param parent is the parent of the newChild 343 * @param index is the index of the newChild 344 */ 345 public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, 346 int index) 347 { 348 newChild.setParent(parent); 349 parent.insert(newChild, index); 350 int[] childIndices = new int[1]; 351 childIndices[0] = index; 352 nodesWereInserted(parent, childIndices); 353 } 354 355 /** 356 * Message this to remove node from its parent. This will message 357 * nodesWereRemoved to create the appropriate event. This is the preferred 358 * way to remove a node as it handles the event creation for you. 359 * 360 * @param node to be removed 361 */ 362 public void removeNodeFromParent(MutableTreeNode node) 363 { 364 TreeNode parent = node.getParent(); 365 Object[] children = new Object[1]; 366 children[0] = node; 367 int[] childIndices = new int[1]; 368 childIndices[0] = getIndexOfChild(parent, node); 369 node.removeFromParent(); 370 nodesWereRemoved(parent, childIndices, children); 371 } 372 373 /** 374 * Invoke this method after you've changed how node is to be represented 375 * in the tree. 376 * 377 * @param node that was changed 378 */ 379 public void nodeChanged(TreeNode node) 380 { 381 TreeNode parent = node.getParent(); 382 int[] childIndices = new int[1]; 383 childIndices[0] = getIndexOfChild(parent, node); 384 Object[] children = new Object[1]; 385 children[0] = node; 386 fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children); 387 } 388 389 /** 390 * Invoke this method after you've inserted some TreeNodes 391 * into node. childIndices should be the index of the new elements and must 392 * be sorted in ascending order. 393 * 394 * @param parent that had a child added to 395 * @param childIndices of the children added 396 */ 397 public void nodesWereInserted(TreeNode parent, int[] childIndices) 398 { 399 Object[] children = new Object[childIndices.length]; 400 for (int i = 0; i < children.length; i++) 401 children[i] = getChild(parent, childIndices[i]); 402 fireTreeNodesInserted(this, getPathToRoot(parent), childIndices, children); 403 } 404 405 /** 406 * Invoke this method after you've removed some TreeNodes from node. 407 * childIndices should be the index of the removed elements and 408 * must be sorted in ascending order. And removedChildren should be the 409 * array of the children objects that were removed. 410 * 411 * @param parent that had a child added to 412 * @param childIndices of the children added 413 * @param removedChildren are all the children removed from parent. 414 */ 415 public void nodesWereRemoved(TreeNode parent, int[] childIndices, 416 Object[] removedChildren) 417 { 418 fireTreeNodesRemoved(this, getPathToRoot(parent), childIndices, 419 removedChildren); 420 } 421 422 /** 423 * Invoke this method after you've changed how the children identified by 424 * childIndices are to be represented in the tree. 425 * 426 * @param node that is the parent of the children that changed in a tree. 427 * @param childIndices are the child nodes that changed. 428 */ 429 public void nodesChanged(TreeNode node, int[] childIndices) 430 { 431 Object[] children = new Object[childIndices.length]; 432 for (int i = 0; i < children.length; i++) 433 children[i] = getChild(node, childIndices[i]); 434 fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children); 435 } 436 437 /** 438 * Invoke this method if you've totally changed the children of node and 439 * its childrens children. This will post a treeStructureChanged event. 440 * 441 * @param node that had its children and grandchildren changed. 442 */ 443 public void nodeStructureChanged(TreeNode node) 444 { 445 int n = getChildCount(root); 446 int[] childIdx = new int[n]; 447 Object[] children = new Object[n]; 448 449 for (int i = 0; i < n; i++) 450 { 451 childIdx[i] = i; 452 children[i] = getChild(root, i); 453 } 454 455 fireTreeStructureChanged(this, new Object[] { root }, childIdx, children); 456 } 457 458 /** 459 * Builds the parents of node up to and including the root node, where 460 * the original node is the last element in the returned array. The 461 * length of the returned array gives the node's depth in the tree. 462 * 463 * @param node - the TreeNode to get the path for 464 * @return TreeNode[] - the path from node to the root 465 */ 466 public TreeNode[] getPathToRoot(TreeNode node) 467 { 468 return getPathToRoot(node, 0); 469 } 470 471 /** 472 * Builds the parents of node up to and including the root node, where 473 * the original node is the last element in the returned array. The 474 * length of the returned array gives the node's depth in the tree. 475 * 476 * @param node - the TreeNode to get the path for 477 * @param depth - an int giving the number of steps already taken 478 * towards the root (on recursive calls), used to size the returned array 479 * @return an array of TreeNodes giving the path from the root to the 480 * specified node 481 */ 482 protected TreeNode[] getPathToRoot(TreeNode node, int depth) 483 { 484 if (node == null) 485 { 486 if (depth == 0) 487 return null; 488 489 return new TreeNode[depth]; 490 } 491 492 TreeNode[] path = getPathToRoot(node.getParent(), depth + 1); 493 path[path.length - depth - 1] = node; 494 return path; 495 } 496 497 /** 498 * Registers a listere to the model. 499 * 500 * @param listener the listener to add 501 */ 502 public void addTreeModelListener(TreeModelListener listener) 503 { 504 listenerList.add(TreeModelListener.class, listener); 505 } 506 507 /** 508 * Removes a listener from the model. 509 * 510 * @param listener the listener to remove 511 */ 512 public void removeTreeModelListener(TreeModelListener listener) 513 { 514 listenerList.remove(TreeModelListener.class, listener); 515 } 516 517 /** 518 * Returns all registered <code>TreeModelListener</code> listeners. 519 * 520 * @return an array of listeners. 521 * 522 * @since 1.4 523 */ 524 public TreeModelListener[] getTreeModelListeners() 525 { 526 return (TreeModelListener[]) listenerList 527 .getListeners(TreeModelListener.class); 528 } 529 530 /** 531 * Notifies all listeners that have registered interest for notification 532 * on this event type. The event instance is lazily created using the parameters 533 * passed into the fire method. 534 * 535 * @param source the node being changed 536 * @param path the path to the root node 537 * @param childIndices the indices of the changed elements 538 * @param children the changed elements 539 */ 540 protected void fireTreeNodesChanged(Object source, Object[] path, 541 int[] childIndices, Object[] children) 542 { 543 TreeModelEvent event = new TreeModelEvent(source, path, childIndices, 544 children); 545 546 TreeModelListener[] listeners = getTreeModelListeners(); 547 548 for (int i = listeners.length - 1; i >= 0; --i) 549 listeners[i].treeNodesChanged(event); 550 } 551 552 /** 553 * fireTreeNodesInserted 554 * 555 * @param source the node where new nodes got inserted 556 * @param path the path to the root node 557 * @param childIndices the indices of the new elements 558 * @param children the new elements 559 */ 560 protected void fireTreeNodesInserted(Object source, Object[] path, 561 int[] childIndices, Object[] children) 562 { 563 TreeModelEvent event = new TreeModelEvent(source, path, childIndices, 564 children); 565 TreeModelListener[] listeners = getTreeModelListeners(); 566 567 for (int i = listeners.length - 1; i >= 0; --i) 568 listeners[i].treeNodesInserted(event); 569 } 570 571 /** 572 * fireTreeNodesRemoved 573 * 574 * @param source the node where nodes got removed- 575 * @param path the path to the root node 576 * @param childIndices the indices of the removed elements 577 * @param children the removed elements 578 */ 579 protected void fireTreeNodesRemoved(Object source, Object[] path, 580 int[] childIndices, Object[] children) 581 { 582 TreeModelEvent event = new TreeModelEvent(source, path, childIndices, 583 children); 584 TreeModelListener[] listeners = getTreeModelListeners(); 585 586 for (int i = listeners.length - 1; i >= 0; --i) 587 listeners[i].treeNodesRemoved(event); 588 } 589 590 /** 591 * fireTreeStructureChanged 592 * 593 * @param source the node where the model has changed 594 * @param path the path to the root node 595 * @param childIndices the indices of the affected elements 596 * @param children the affected elements 597 */ 598 protected void fireTreeStructureChanged(Object source, Object[] path, 599 int[] childIndices, Object[] children) 600 { 601 TreeModelEvent event = new TreeModelEvent(source, path, childIndices, 602 children); 603 TreeModelListener[] listeners = getTreeModelListeners(); 604 605 for (int i = listeners.length - 1; i >= 0; --i) 606 listeners[i].treeStructureChanged(event); 607 } 608 609 /** 610 * Returns the registered listeners of a given type. 611 * 612 * @param listenerType the listener type to return 613 * 614 * @return an array of listeners 615 * 616 * @since 1.3 617 */ 618 public <T extends EventListener> T[] getListeners(Class<T> listenerType) 619 { 620 return listenerList.getListeners(listenerType); 621 } 622}