Source for javax.swing.tree.DefaultTreeModel

   1: /* DefaultTreeModel.java --
   2:    Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING. If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library. Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module. An independent module is a module which is not derived from
  33: or based on this library. If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so. If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package javax.swing.tree;
  39: 
  40: import java.io.IOException;
  41: import java.io.ObjectInputStream;
  42: import java.io.ObjectOutputStream;
  43: import java.io.Serializable;
  44: import java.util.EventListener;
  45: 
  46: import javax.swing.event.EventListenerList;
  47: import javax.swing.event.TreeModelEvent;
  48: import javax.swing.event.TreeModelListener;
  49: 
  50: /**
  51:  * DefaultTreeModel
  52:  *
  53:  * @author Andrew Selkirk
  54:  */
  55: public class DefaultTreeModel
  56:     implements Serializable, TreeModel
  57: {
  58:   static final long serialVersionUID = -2621068368932566998L;
  59: 
  60:   /**
  61:    * root
  62:    */
  63:   protected TreeNode root;
  64: 
  65:   /**
  66:    * listenerList
  67:    */
  68:   protected EventListenerList listenerList = new EventListenerList();
  69: 
  70:   /**
  71:    * asksAllowsChildren
  72:    */
  73:   protected boolean asksAllowsChildren;
  74: 
  75:   /**
  76:    * Constructor DefaultTreeModel where any node can have children.
  77:    *
  78:    * @param root the tree root.
  79:    */
  80:   public DefaultTreeModel(TreeNode root)
  81:   {
  82:     this (root, false);
  83:   }
  84: 
  85:   /**
  86:    * Create the DefaultTreeModel that may check if the nodes can have
  87:    * children or not.
  88:    *
  89:    * @param aRoot the tree root.
  90:    * @param asksAllowsChildren if true, each node is asked if it can have
  91:    * children. If false, the model does not care about this, supposing, that
  92:    * any node can have children.
  93:    */
  94:   public DefaultTreeModel(TreeNode aRoot, boolean asksAllowsChildren)
  95:   {
  96:     if (aRoot == null)
  97:       aRoot = new DefaultMutableTreeNode();
  98:     this.root = aRoot;
  99:     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: }