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:    * isLeaf
 214:    * 
 215:    * @param node TODO
 216:    * @return boolean
 217:    */
 218:   public boolean isLeaf(Object node)
 219:   {
 220:     if (node instanceof TreeNode)
 221:       return ((TreeNode) node).isLeaf();
 222:     else
 223:       return true;
 224:   }
 225: 
 226:   /**
 227:    * <p>
 228:    * Invoke this method if you've modified the TreeNodes upon which this model
 229:    * depends. The model will notify all of its listeners that the model has
 230:    * changed. It will fire the events, necessary to update the layout caches and
 231:    * repaint the tree. The tree will <i>not</i> be properly refreshed if you
 232:    * call the JTree.repaint instead.
 233:    * </p>
 234:    * <p>
 235:    * This method will refresh the information about whole tree from the root. If
 236:    * only part of the tree should be refreshed, it is more effective to call
 237:    * {@link #reload(TreeNode)}.
 238:    * </p>
 239:    */
 240:   public void reload()
 241:   {
 242:     // Need to duplicate the code because the root can formally be
 243:     // no an instance of the TreeNode.
 244:     int n = getChildCount(root);
 245:     int[] childIdx = new int[n];
 246:     Object[] children = new Object[n];
 247: 
 248:     for (int i = 0; i < n; i++)
 249:       {
 250:         childIdx[i] = i;
 251:         children[i] = getChild(root, i);
 252:       }
 253: 
 254:     fireTreeStructureChanged(this, new Object[] { root }, childIdx, children);
 255:   }
 256: 
 257:   /**
 258:    * Invoke this method if you've modified the TreeNodes upon which this model
 259:    * depends. The model will notify all of its listeners that the model has
 260:    * changed. It will fire the events, necessary to update the layout caches and
 261:    * repaint the tree. The tree will <i>not</i> be properly refreshed if you
 262:    * call the JTree.repaint instead.
 263:    * 
 264:    * @param node - the tree node, from which the tree nodes have changed
 265:    *          (inclusive). If you do not know this node, call {@link #reload()}
 266:    *          instead.
 267:    */
 268:   public void reload(TreeNode node)
 269:   {
 270:     int n = getChildCount(node);
 271:     int[] childIdx = new int[n];
 272:     Object[] children = new Object[n];
 273: 
 274:     for (int i = 0; i < n; i++)
 275:       {
 276:         childIdx[i] = i;
 277:         children[i] = getChild(node, i);
 278:       }
 279: 
 280:     fireTreeStructureChanged(this, getPathToRoot(node), childIdx, children);
 281:   }
 282: 
 283:   /**
 284:    * Messaged when the user has altered the value for the item 
 285:    * identified by path to newValue. If newValue signifies a truly new 
 286:    * value the model should post a treeNodesChanged event.
 287:    * This sets the user object of the TreeNode identified by 
 288:    * path and posts a node changed. If you use custom user objects 
 289:    * in the TreeModel you're going to need to subclass this and set 
 290:    * the user object of the changed node to something meaningful.
 291:    * 
 292:    * @param path - path to the node that the user has altered
 293:    * @param newValue - the new value from the TreeCellEditor
 294:    */
 295:   public void valueForPathChanged(TreePath path, Object newValue)
 296:   {
 297:     Object node = path.getLastPathComponent();
 298:     if (node instanceof MutableTreeNode)
 299:       {
 300:         ((MutableTreeNode) node).setUserObject(newValue);
 301:         int[] ci = null;
 302:         Object[] c = null; 
 303:         Object[] parentPath = path.getPath();
 304:         if (path.getPathCount() > 1)
 305:           {
 306:             Object parent = ((TreeNode) node).getParent();
 307:             ci = new int[1];
 308:             ci[0] = getIndexOfChild(parent, node);
 309:             node = newValue;
 310:             path = path.getParentPath().pathByAddingChild(node);
 311:             c = new Object[1];
 312:             c[0] = node;
 313:             parentPath = path.getParentPath().getPath();
 314:           }
 315:         
 316:         fireTreeNodesChanged(this, parentPath, ci, c);
 317:       }
 318:     }
 319: 
 320:   /**
 321:    * Invoked this to insert newChild at location index in parents children.
 322:    * This will then message nodesWereInserted to create the appropriate event. 
 323:    * This is the preferred way to add children as it will create the 
 324:    * appropriate event.
 325:    * 
 326:    * @param newChild is the node to add to the parent's children
 327:    * @param parent is the parent of the newChild
 328:    * @param index is the index of the newChild
 329:    */
 330:   public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent,
 331:                              int index)
 332:   {
 333:     newChild.setParent(parent);
 334:     parent.insert(newChild, index);
 335:     int[] childIndices = new int[1];
 336:     childIndices[0] = index;
 337:     nodesWereInserted(parent, childIndices);
 338:   }
 339: 
 340:   /**
 341:    * Message this to remove node from its parent. This will message 
 342:    * nodesWereRemoved to create the appropriate event. This is the preferred 
 343:    * way to remove a node as it handles the event creation for you.
 344:    * 
 345:    * @param node to be removed
 346:    */
 347:   public void removeNodeFromParent(MutableTreeNode node)
 348:   {
 349:     TreeNode parent = node.getParent();
 350:     Object[] children = new Object[1];
 351:     children[0] = node;
 352:     int[] childIndices = new int[1];
 353:     childIndices[0] = getIndexOfChild(parent, node);
 354:     node.removeFromParent();
 355:     nodesWereRemoved(parent, childIndices, children);
 356:   }
 357: 
 358:   /**
 359:    * Invoke this method after you've changed how node is to be represented
 360:    * in the tree.
 361:    * 
 362:    * @param node that was changed
 363:    */
 364:   public void nodeChanged(TreeNode node)
 365:   {
 366:     TreeNode parent = node.getParent();
 367:     int[] childIndices = new int[1];
 368:     childIndices[0] = getIndexOfChild(parent, node);
 369:     Object[] children = new Object[1];
 370:     children[0] = node;
 371:     fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
 372:   }
 373: 
 374:   /**
 375:    * Invoke this method after you've inserted some TreeNodes 
 376:    * into node. childIndices should be the index of the new elements and must 
 377:    * be sorted in ascending order.
 378:    * 
 379:    * @param parent that had a child added to
 380:    * @param childIndices of the children added
 381:    */
 382:   public void nodesWereInserted(TreeNode parent, int[] childIndices)
 383:   {
 384:     Object[] children = new Object[childIndices.length];
 385:     for (int i = 0; i < children.length; i++)
 386:       children[i] = getChild(parent, childIndices[i]);
 387:     fireTreeNodesInserted(this, getPathToRoot(parent), childIndices, children);
 388:   }
 389: 
 390:   /**
 391:    * Invoke this method after you've removed some TreeNodes from node. 
 392:    * childIndices should be the index of the removed elements and 
 393:    * must be sorted in ascending order. And removedChildren should be the 
 394:    * array of the children objects that were removed.
 395:    * 
 396:    * @param parent that had a child added to
 397:    * @param childIndices of the children added
 398:    * @param removedChildren are all the children removed from parent.
 399:    */
 400:   public void nodesWereRemoved(TreeNode parent, int[] childIndices, 
 401:                                Object[] removedChildren)
 402:   {
 403:     fireTreeNodesRemoved(this, getPathToRoot(parent), childIndices, 
 404:                          removedChildren);
 405:   }
 406: 
 407:   /**
 408:    * Invoke this method after you've changed how the children identified by 
 409:    * childIndices are to be represented in the tree.
 410:    * 
 411:    * @param node that is the parent of the children that changed in a tree.
 412:    * @param childIndices are the child nodes that changed.
 413:    */
 414:   public void nodesChanged(TreeNode node, int[] childIndices)
 415:   {
 416:     Object[] children = new Object[childIndices.length];
 417:     for (int i = 0; i < children.length; i++)
 418:       children[i] = getChild(node, childIndices[i]);
 419:     fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
 420:   }
 421: 
 422:   /**
 423:    * Invoke this method if you've totally changed the children of node and 
 424:    * its childrens children. This will post a treeStructureChanged event.
 425:    * 
 426:    * @param node that had its children and grandchildren changed.
 427:    */
 428:   public void nodeStructureChanged(TreeNode node)
 429:   {
 430:     int n = getChildCount(root);
 431:     int[] childIdx = new int[n];
 432:     Object[] children = new Object[n];
 433: 
 434:     for (int i = 0; i < n; i++)
 435:       {
 436:         childIdx[i] = i;
 437:         children[i] = getChild(root, i);
 438:       }
 439: 
 440:     fireTreeStructureChanged(this, new Object[] { root }, childIdx, children);
 441:   }
 442: 
 443:   /**
 444:    * Builds the parents of node up to and including the root node, where 
 445:    * the original node is the last element in the returned array. The 
 446:    * length of the returned array gives the node's depth in the tree.
 447:    * 
 448:    * @param node - the TreeNode to get the path for
 449:    * @return TreeNode[] - the path from node to the root
 450:    */
 451:   public TreeNode[] getPathToRoot(TreeNode node)
 452:   {
 453:     return getPathToRoot(node, 0);
 454:   }
 455: 
 456:   /**
 457:    * Builds the parents of node up to and including the root node, where 
 458:    * the original node is the last element in the returned array. The 
 459:    * length of the returned array gives the node's depth in the tree.
 460:    * 
 461:    * @param node - the TreeNode to get the path for
 462:    * @param depth - an int giving the number of steps already taken 
 463:    * towards the root (on recursive calls), used to size the returned array
 464:    * @return an array of TreeNodes giving the path from the root to the 
 465:    * specified node
 466:    */
 467:   protected TreeNode[] getPathToRoot(TreeNode node, int depth)
 468:   {
 469:     if (node == null)
 470:       {
 471:         if (depth == 0)
 472:           return null;
 473:         
 474:         return new TreeNode[depth];
 475:       }
 476: 
 477:     TreeNode[] path = getPathToRoot(node.getParent(), depth + 1);
 478:     path[path.length - depth - 1] = node;
 479:     return path;
 480:   }
 481: 
 482:   /**
 483:    * Registers a listere to the model.
 484:    * 
 485:    * @param listener the listener to add
 486:    */
 487:   public void addTreeModelListener(TreeModelListener listener)
 488:   {
 489:     listenerList.add(TreeModelListener.class, listener);
 490:   }
 491: 
 492:   /**
 493:    * Removes a listener from the model.
 494:    * 
 495:    * @param listener the listener to remove
 496:    */
 497:   public void removeTreeModelListener(TreeModelListener listener)
 498:   {
 499:     listenerList.remove(TreeModelListener.class, listener);
 500:   }
 501: 
 502:   /**
 503:    * Returns all registered <code>TreeModelListener</code> listeners.
 504:    * 
 505:    * @return an array of listeners.
 506:    * 
 507:    * @since 1.4
 508:    */
 509:   public TreeModelListener[] getTreeModelListeners()
 510:   {
 511:     return (TreeModelListener[]) listenerList
 512:         .getListeners(TreeModelListener.class);
 513:   }
 514: 
 515:   /**
 516:    * Notifies all listeners that have registered interest for notification 
 517:    * on this event type. The event instance is lazily created using the parameters 
 518:    * passed into the fire method.
 519:    * 
 520:    * @param source the node being changed
 521:    * @param path the path to the root node
 522:    * @param childIndices the indices of the changed elements
 523:    * @param children the changed elements
 524:    */
 525:   protected void fireTreeNodesChanged(Object source, Object[] path,
 526:       int[] childIndices, Object[] children)
 527:   {
 528:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 529:         children);
 530: 
 531:     TreeModelListener[] listeners = getTreeModelListeners();
 532: 
 533:     for (int i = listeners.length - 1; i >= 0; --i)
 534:       listeners[i].treeNodesChanged(event);
 535:   }
 536: 
 537:   /**
 538:    * fireTreeNodesInserted
 539:    * 
 540:    * @param source the node where new nodes got inserted
 541:    * @param path the path to the root node
 542:    * @param childIndices the indices of the new elements
 543:    * @param children the new elements
 544:    */
 545:   protected void fireTreeNodesInserted(Object source, Object[] path,
 546:       int[] childIndices, Object[] children)
 547:   {
 548:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 549:         children);
 550:     TreeModelListener[] listeners = getTreeModelListeners();
 551: 
 552:     for (int i = listeners.length - 1; i >= 0; --i)
 553:       listeners[i].treeNodesInserted(event);
 554:   }
 555: 
 556:   /**
 557:    * fireTreeNodesRemoved
 558:    * 
 559:    * @param source the node where nodes got removed-
 560:    * @param path the path to the root node
 561:    * @param childIndices the indices of the removed elements
 562:    * @param children the removed elements
 563:    */
 564:   protected void fireTreeNodesRemoved(Object source, Object[] path,
 565:       int[] childIndices, Object[] children)
 566:   {
 567:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 568:         children);
 569:     TreeModelListener[] listeners = getTreeModelListeners();
 570: 
 571:     for (int i = listeners.length - 1; i >= 0; --i)
 572:       listeners[i].treeNodesRemoved(event);
 573:   }
 574: 
 575:   /**
 576:    * fireTreeStructureChanged
 577:    * 
 578:    * @param source the node where the model has changed
 579:    * @param path the path to the root node
 580:    * @param childIndices the indices of the affected elements
 581:    * @param children the affected elements
 582:    */
 583:   protected void fireTreeStructureChanged(Object source, Object[] path,
 584:       int[] childIndices, Object[] children)
 585:   {
 586:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 587:         children);
 588:     TreeModelListener[] listeners = getTreeModelListeners();
 589: 
 590:     for (int i = listeners.length - 1; i >= 0; --i)
 591:       listeners[i].treeStructureChanged(event);
 592:   }
 593: 
 594:   /**
 595:    * Returns the registered listeners of a given type.
 596:    *
 597:    * @param listenerType the listener type to return
 598:    *
 599:    * @return an array of listeners
 600:    *
 601:    * @since 1.3
 602:    */
 603:   public EventListener[] getListeners(Class listenerType)
 604:   {
 605:     return listenerList.getListeners(listenerType);
 606:   }
 607: }