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