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