Source for javax.swing.tree.DefaultTreeModel

   1: /* DefaultTreeModel.java -- 
   2:    Copyright (C) 2002, 2004, 2005 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 gnu.classpath.NotImplementedException;
  41: 
  42: import java.io.IOException;
  43: import java.io.ObjectInputStream;
  44: import java.io.ObjectOutputStream;
  45: import java.io.Serializable;
  46: import java.util.EventListener;
  47: 
  48: import javax.swing.event.EventListenerList;
  49: import javax.swing.event.TreeModelEvent;
  50: import javax.swing.event.TreeModelListener;
  51: 
  52: /**
  53:  * DefaultTreeModel
  54:  * 
  55:  * @author Andrew Selkirk
  56:  */
  57: public class DefaultTreeModel
  58:     implements Serializable, TreeModel
  59: {
  60:   static final long serialVersionUID = -2621068368932566998L;
  61: 
  62:   /**
  63:    * root
  64:    */
  65:   protected TreeNode root = null;
  66: 
  67:   /**
  68:    * listenerList
  69:    */
  70:   protected EventListenerList listenerList = new EventListenerList();
  71: 
  72:   /**
  73:    * asksAllowsChildren
  74:    */
  75:   protected boolean asksAllowsChildren;
  76: 
  77:   /**
  78:    * Constructor DefaultTreeModel where any node can have children.
  79:    * 
  80:    * @param root the tree root.
  81:    */
  82:   public DefaultTreeModel(TreeNode root)
  83:   {
  84:     this (root, false);
  85:   }
  86: 
  87:   /**
  88:    * Create the DefaultTreeModel that may check if the nodes can have
  89:    * children or not.
  90:    * 
  91:    * @param aRoot the tree root.
  92:    * @param asksAllowsChildren if true, each node is asked if it can have 
  93:    * children. If false, the model does not care about this, supposing, that
  94:    * any node can have children.
  95:    */
  96:   public DefaultTreeModel(TreeNode aRoot, boolean asksAllowsChildren)
  97:   {
  98:     if (aRoot == null)
  99:       aRoot = new DefaultMutableTreeNode();
 100:     this.root = aRoot;
 101:     this.asksAllowsChildren = asksAllowsChildren;
 102:   }
 103: 
 104:   /**
 105:    * writeObject
 106:    * 
 107:    * @param obj the object.
 108:    * @exception IOException TODO
 109:    */
 110:   private void writeObject(ObjectOutputStream obj) throws IOException
 111:   {
 112:     // TODO
 113:   }
 114: 
 115:   /**
 116:    * readObject
 117:    * 
 118:    * @param value0 TODO
 119:    * @exception IOException TODO
 120:    * @exception ClassNotFoundException TODO
 121:    */
 122:   private void readObject(ObjectInputStream value0) throws IOException,
 123:       ClassNotFoundException
 124:   {
 125:     // TODO
 126:   }
 127: 
 128:   /**
 129:    * asksAllowsChildren
 130:    * 
 131:    * @return boolean
 132:    */
 133:   public boolean asksAllowsChildren()
 134:   {
 135:     return asksAllowsChildren;
 136:   }
 137: 
 138:   /**
 139:    * setAsksAllowsChildren
 140:    * 
 141:    * @param value TODO
 142:    */
 143:   public void setAsksAllowsChildren(boolean value)
 144:   {
 145:     asksAllowsChildren = value;
 146:   }
 147: 
 148:   /**
 149:    * setRoot
 150:    * 
 151:    * @param root the root node.
 152:    */
 153:   public void setRoot(TreeNode root)
 154:   {
 155:     this.root = root;
 156:   }
 157: 
 158:   /**
 159:    * getRoot
 160:    * 
 161:    * @return Object
 162:    */
 163:   public Object getRoot()
 164:   {
 165:     return root;
 166:   }
 167: 
 168:   /**
 169:    * getIndexOfChild
 170:    * 
 171:    * @param parent TODO
 172:    * @param child TODO
 173:    * @return int
 174:    */
 175:   public int getIndexOfChild(Object parent, Object child)
 176:   {
 177:     for (int i = 0; i < getChildCount(parent); i++)
 178:       {
 179:         if (getChild(parent, i).equals(child))
 180:           return i;
 181:       }
 182:     return -1;
 183:   }
 184: 
 185:   /**
 186:    * getChild
 187:    * 
 188:    * @param node TODO
 189:    * @param idx TODO
 190:    * @return Object
 191:    */
 192:   public Object getChild(Object node, int idx)
 193:   {
 194:     if (node instanceof TreeNode)
 195:       return ((TreeNode) node).getChildAt(idx);
 196:     else
 197:       return null;
 198:   }
 199: 
 200:   /**
 201:    * getChildCount
 202:    * 
 203:    * @param node TODO
 204:    * @return int
 205:    */
 206:   public int getChildCount(Object node)
 207:   {
 208:     if (node instanceof TreeNode)
 209:       return ((TreeNode) node).getChildCount();
 210:     else
 211:       return 0;
 212:   }
 213: 
 214:   /**
 215:    * isLeaf
 216:    * 
 217:    * @param node TODO
 218:    * @return boolean
 219:    */
 220:   public boolean isLeaf(Object node)
 221:   {
 222:     if (node instanceof TreeNode)
 223:       return ((TreeNode) node).isLeaf();
 224:     else
 225:       return true;
 226:   }
 227: 
 228:   /**
 229:    * <p>
 230:    * Invoke this method if you've modified the TreeNodes upon which this model
 231:    * depends. The model will notify all of its listeners that the model has
 232:    * changed. It will fire the events, necessary to update the layout caches and
 233:    * repaint the tree. The tree will <i>not</i> be properly refreshed if you
 234:    * call the JTree.repaint instead.
 235:    * </p>
 236:    * <p>
 237:    * This method will refresh the information about whole tree from the root. If
 238:    * only part of the tree should be refreshed, it is more effective to call
 239:    * {@link #reload(TreeNode)}.
 240:    * </p>
 241:    */
 242:   public void reload()
 243:   {
 244:     // Need to duplicate the code because the root can formally be
 245:     // no an instance of the TreeNode.
 246:     int n = getChildCount(root);
 247:     int[] childIdx = new int[n];
 248:     Object[] children = new Object[n];
 249: 
 250:     for (int i = 0; i < n; i++)
 251:       {
 252:         childIdx[i] = i;
 253:         children[i] = getChild(root, i);
 254:       }
 255: 
 256:     fireTreeStructureChanged(this, new Object[] { root }, childIdx, children);
 257:   }
 258: 
 259:   /**
 260:    * Invoke this method if you've modified the TreeNodes upon which this model
 261:    * depends. The model will notify all of its listeners that the model has
 262:    * changed. It will fire the events, necessary to update the layout caches and
 263:    * repaint the tree. The tree will <i>not</i> be properly refreshed if you
 264:    * call the JTree.repaint instead.
 265:    * 
 266:    * @param node - the tree node, from which the tree nodes have changed
 267:    *          (inclusive). If you do not know this node, call {@link #reload()}
 268:    *          instead.
 269:    */
 270:   public void reload(TreeNode node)
 271:   {
 272:     int n = getChildCount(node);
 273:     int[] childIdx = new int[n];
 274:     Object[] children = new Object[n];
 275: 
 276:     for (int i = 0; i < n; i++)
 277:       {
 278:         childIdx[i] = i;
 279:         children[i] = getChild(node, i);
 280:       }
 281: 
 282:     fireTreeStructureChanged(this, getPathToRoot(node), childIdx, children);
 283:   }
 284: 
 285:   /**
 286:    * Messaged when the user has altered the value for the item 
 287:    * identified by path to newValue. If newValue signifies a truly new 
 288:    * value the model should post a treeNodesChanged event.
 289:    * This sets the user object of the TreeNode identified by 
 290:    * path and posts a node changed. If you use custom user objects 
 291:    * in the TreeModel you're going to need to subclass this and set 
 292:    * the user object of the changed node to something meaningful.
 293:    * 
 294:    * @param path - path to the node that the user has altered
 295:    * @param newValue - the new value from the TreeCellEditor
 296:    */
 297:   public void valueForPathChanged(TreePath path, Object newValue)
 298:   {
 299:     Object node = path.getLastPathComponent();
 300:     if (node instanceof MutableTreeNode)
 301:       {
 302:         ((MutableTreeNode) node).setUserObject(newValue);
 303:         int[] ci = null;
 304:         Object[] c = null; 
 305:         Object[] parentPath = path.getPath();
 306:         if (path.getPathCount() > 1)
 307:           {
 308:             Object parent = ((TreeNode) node).getParent();
 309:             ci = new int[1];
 310:             ci[0] = getIndexOfChild(parent, node);
 311:             node = newValue;
 312:             path = path.getParentPath().pathByAddingChild(node);
 313:             c = new Object[1];
 314:             c[0] = node;
 315:             parentPath = path.getParentPath().getPath();
 316:           }
 317:         
 318:         fireTreeNodesChanged(this, parentPath, ci, c);
 319:       }
 320:     }
 321: 
 322:   /**
 323:    * Invoked this to insert newChild at location index in parents children.
 324:    * This will then message nodesWereInserted to create the appropriate event. 
 325:    * This is the preferred way to add children as it will create the 
 326:    * appropriate event.
 327:    * 
 328:    * @param newChild is the node to add to the parent's children
 329:    * @param parent is the parent of the newChild
 330:    * @param index is the index of the newChild
 331:    */
 332:   public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent,
 333:                              int index)
 334:   {
 335:     newChild.setParent(parent);
 336:     parent.insert(newChild, index);
 337:     int[] childIndices = new int[1];
 338:     childIndices[0] = index;
 339:     nodesWereInserted(parent, childIndices);
 340:   }
 341: 
 342:   /**
 343:    * Message this to remove node from its parent. This will message 
 344:    * nodesWereRemoved to create the appropriate event. This is the preferred 
 345:    * way to remove a node as it handles the event creation for you.
 346:    * 
 347:    * @param node to be removed
 348:    */
 349:   public void removeNodeFromParent(MutableTreeNode node)
 350:   {
 351:     TreeNode parent = node.getParent();
 352:     Object[] children = new Object[1];
 353:     children[0] = node;
 354:     int[] childIndices = new int[1];
 355:     childIndices[0] = getIndexOfChild(parent, node);
 356:     node.removeFromParent();
 357:     nodesWereRemoved(parent, childIndices, children);
 358:   }
 359: 
 360:   /**
 361:    * Invoke this method after you've changed how node is to be represented
 362:    * in the tree.
 363:    * 
 364:    * @param node that was changed
 365:    */
 366:   public void nodeChanged(TreeNode node)
 367:   {
 368:     TreeNode parent = node.getParent();
 369:     int[] childIndices = new int[1];
 370:     childIndices[0] = getIndexOfChild(parent, node);
 371:     Object[] children = new Object[1];
 372:     children[0] = node;
 373:     fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
 374:   }
 375: 
 376:   /**
 377:    * Invoke this method after you've inserted some TreeNodes 
 378:    * into node. childIndices should be the index of the new elements and must 
 379:    * be sorted in ascending order.
 380:    * 
 381:    * @param parent that had a child added to
 382:    * @param childIndices of the children added
 383:    */
 384:   public void nodesWereInserted(TreeNode parent, int[] childIndices)
 385:   {
 386:     Object[] children = new Object[childIndices.length];
 387:     for (int i = 0; i < children.length; i++)
 388:       children[i] = getChild(parent, childIndices[i]);
 389:     fireTreeNodesInserted(this, getPathToRoot(parent), childIndices, children);
 390:   }
 391: 
 392:   /**
 393:    * Invoke this method after you've removed some TreeNodes from node. 
 394:    * childIndices should be the index of the removed elements and 
 395:    * must be sorted in ascending order. And removedChildren should be the 
 396:    * array of the children objects that were removed.
 397:    * 
 398:    * @param parent that had a child added to
 399:    * @param childIndices of the children added
 400:    * @param removedChildren are all the children removed from parent.
 401:    */
 402:   public void nodesWereRemoved(TreeNode parent, int[] childIndices, 
 403:                                Object[] removedChildren)
 404:   {
 405:     fireTreeNodesRemoved(this, getPathToRoot(parent), childIndices, 
 406:                          removedChildren);
 407:   }
 408: 
 409:   /**
 410:    * Invoke this method after you've changed how the children identified by 
 411:    * childIndices are to be represented in the tree.
 412:    * 
 413:    * @param node that is the parent of the children that changed in a tree.
 414:    * @param childIndices are the child nodes that changed.
 415:    */
 416:   public void nodesChanged(TreeNode node, int[] childIndices)
 417:   {
 418:     Object[] children = new Object[childIndices.length];
 419:     for (int i = 0; i < children.length; i++)
 420:       children[i] = getChild(node, childIndices[i]);
 421:     fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
 422:   }
 423: 
 424:   /**
 425:    * Invoke this method if you've totally changed the children of node and 
 426:    * its childrens children. This will post a treeStructureChanged event.
 427:    * 
 428:    * @param node that had its children and grandchildren changed.
 429:    */
 430:   public void nodeStructureChanged(TreeNode node)
 431:   {
 432:     int n = getChildCount(root);
 433:     int[] childIdx = new int[n];
 434:     Object[] children = new Object[n];
 435: 
 436:     for (int i = 0; i < n; i++)
 437:       {
 438:         childIdx[i] = i;
 439:         children[i] = getChild(root, i);
 440:       }
 441: 
 442:     fireTreeStructureChanged(this, new Object[] { root }, childIdx, children);
 443:   }
 444: 
 445:   /**
 446:    * Builds the parents of node up to and including the root node, where 
 447:    * the original node is the last element in the returned array. The 
 448:    * length of the returned array gives the node's depth in the tree.
 449:    * 
 450:    * @param node - the TreeNode to get the path for
 451:    * @return TreeNode[] - the path from node to the root
 452:    */
 453:   public TreeNode[] getPathToRoot(TreeNode node)
 454:   {
 455:     return getPathToRoot(node, 0);
 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:    * @param depth - an int giving the number of steps already taken 
 465:    * towards the root (on recursive calls), used to size the returned array
 466:    * @return an array of TreeNodes giving the path from the root to the 
 467:    * specified node
 468:    */
 469:   protected TreeNode[] getPathToRoot(TreeNode node, int depth)
 470:   {
 471:     if (node == null)
 472:       {
 473:         if (depth == 0)
 474:           return null;
 475:         
 476:         return new TreeNode[depth];
 477:       }
 478: 
 479:     TreeNode[] path = getPathToRoot(node.getParent(), depth + 1);
 480:     path[path.length - depth - 1] = node;
 481:     return path;
 482:   }
 483: 
 484:   /**
 485:    * Registers a listere to the model.
 486:    * 
 487:    * @param listener the listener to add
 488:    */
 489:   public void addTreeModelListener(TreeModelListener listener)
 490:   {
 491:     listenerList.add(TreeModelListener.class, listener);
 492:   }
 493: 
 494:   /**
 495:    * Removes a listener from the model.
 496:    * 
 497:    * @param listener the listener to remove
 498:    */
 499:   public void removeTreeModelListener(TreeModelListener listener)
 500:   {
 501:     listenerList.remove(TreeModelListener.class, listener);
 502:   }
 503: 
 504:   /**
 505:    * Returns all registered <code>TreeModelListener</code> listeners.
 506:    * 
 507:    * @return an array of listeners.
 508:    * 
 509:    * @since 1.4
 510:    */
 511:   public TreeModelListener[] getTreeModelListeners()
 512:   {
 513:     return (TreeModelListener[]) listenerList
 514:         .getListeners(TreeModelListener.class);
 515:   }
 516: 
 517:   /**
 518:    * Notifies all listeners that have registered interest for notification 
 519:    * on this event type. The event instance is lazily created using the parameters 
 520:    * passed into the fire method.
 521:    * 
 522:    * @param source the node being changed
 523:    * @param path the path to the root node
 524:    * @param childIndices the indices of the changed elements
 525:    * @param children the changed elements
 526:    */
 527:   protected void fireTreeNodesChanged(Object source, Object[] path,
 528:       int[] childIndices, Object[] children)
 529:   {
 530:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 531:         children);
 532: 
 533:     TreeModelListener[] listeners = getTreeModelListeners();
 534: 
 535:     for (int i = listeners.length - 1; i >= 0; --i)
 536:       listeners[i].treeNodesChanged(event);
 537:   }
 538: 
 539:   /**
 540:    * fireTreeNodesInserted
 541:    * 
 542:    * @param source the node where new nodes got inserted
 543:    * @param path the path to the root node
 544:    * @param childIndices the indices of the new elements
 545:    * @param children the new elements
 546:    */
 547:   protected void fireTreeNodesInserted(Object source, Object[] path,
 548:       int[] childIndices, Object[] children)
 549:   {
 550:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 551:         children);
 552:     TreeModelListener[] listeners = getTreeModelListeners();
 553: 
 554:     for (int i = listeners.length - 1; i >= 0; --i)
 555:       listeners[i].treeNodesInserted(event);
 556:   }
 557: 
 558:   /**
 559:    * fireTreeNodesRemoved
 560:    * 
 561:    * @param source the node where nodes got removed-
 562:    * @param path the path to the root node
 563:    * @param childIndices the indices of the removed elements
 564:    * @param children the removed elements
 565:    */
 566:   protected void fireTreeNodesRemoved(Object source, Object[] path,
 567:       int[] childIndices, Object[] children)
 568:   {
 569:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 570:         children);
 571:     TreeModelListener[] listeners = getTreeModelListeners();
 572: 
 573:     for (int i = listeners.length - 1; i >= 0; --i)
 574:       listeners[i].treeNodesRemoved(event);
 575:   }
 576: 
 577:   /**
 578:    * fireTreeStructureChanged
 579:    * 
 580:    * @param source the node where the model has changed
 581:    * @param path the path to the root node
 582:    * @param childIndices the indices of the affected elements
 583:    * @param children the affected elements
 584:    */
 585:   protected void fireTreeStructureChanged(Object source, Object[] path,
 586:       int[] childIndices, Object[] children)
 587:   {
 588:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 589:         children);
 590:     TreeModelListener[] listeners = getTreeModelListeners();
 591: 
 592:     for (int i = listeners.length - 1; i >= 0; --i)
 593:       listeners[i].treeStructureChanged(event);
 594:   }
 595: 
 596:   /**
 597:    * Returns the registered listeners of a given type.
 598:    *
 599:    * @param listenerType the listener type to return
 600:    *
 601:    * @return an array of listeners
 602:    *
 603:    * @since 1.3
 604:    */
 605:   public EventListener[] getListeners(Class listenerType)
 606:   {
 607:     return listenerList.getListeners(listenerType);
 608:   }
 609: }