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:     parent.insert(newChild, index);
 303:     int[] childIndices = new int[1];
 304:     childIndices[0] = index;
 305:     nodesWereInserted(parent, childIndices);
 306:   }
 307: 
 308:   /**
 309:    * Message this to remove node from its parent. This will message 
 310:    * nodesWereRemoved to create the appropriate event. This is the preferred 
 311:    * way to remove a node as it handles the event creation for you.
 312:    * 
 313:    * @param node to be removed
 314:    */
 315:   public void removeNodeFromParent(MutableTreeNode node)
 316:   {
 317:     TreeNode parent = node.getParent();
 318:     Object[] children = new Object[1];
 319:     children[0] = node;
 320:     int[] childIndices = new int[1];
 321:     childIndices[0] = getIndexOfChild(parent, node);
 322:     node.removeFromParent();
 323:     nodesWereRemoved(parent, childIndices, children);
 324:   }
 325: 
 326:   /**
 327:    * Invoke this method after you've changed how node is to be represented
 328:    * in the tree.
 329:    * 
 330:    * @param node that was changed
 331:    */
 332:   public void nodeChanged(TreeNode node)
 333:   {
 334:     TreeNode parent = node.getParent();
 335:     int[] childIndices = new int[1];
 336:     childIndices[0] = getIndexOfChild(parent, node);
 337:     Object[] children = new Object[1];
 338:     children[0] = node;
 339:     fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
 340:   }
 341: 
 342:   /**
 343:    * Invoke this method after you've inserted some TreeNodes 
 344:    * into node. childIndices should be the index of the new elements and must 
 345:    * be sorted in ascending order.
 346:    * 
 347:    * @param parent that had a child added to
 348:    * @param childIndices of the children added
 349:    */
 350:   public void nodesWereInserted(TreeNode parent, int[] childIndices)
 351:   {
 352:     Object[] children = new Object[childIndices.length];
 353:     for (int i = 0; i < children.length; i++)
 354:       children[i] = getChild(parent, childIndices[i]);
 355:     fireTreeNodesInserted(this, getPathToRoot(parent), childIndices, children);
 356:   }
 357: 
 358:   /**
 359:    * Invoke this method after you've removed some TreeNodes from node. 
 360:    * childIndices should be the index of the removed elements and 
 361:    * must be sorted in ascending order. And removedChildren should be the 
 362:    * array of the children objects that were removed.
 363:    * 
 364:    * @param parent that had a child added to
 365:    * @param childIndices of the children added
 366:    * @param removedChildren are all the children removed from parent.
 367:    */
 368:   public void nodesWereRemoved(TreeNode parent, int[] childIndices, 
 369:                                Object[] removedChildren)
 370:   {
 371:     fireTreeNodesRemoved(this, getPathToRoot(parent), childIndices, 
 372:                          removedChildren);
 373:   }
 374: 
 375:   /**
 376:    * Invoke this method after you've changed how the children identified by 
 377:    * childIndices are to be represented in the tree.
 378:    * 
 379:    * @param node that is the parent of the children that changed in a tree.
 380:    * @param childIndices are the child nodes that changed.
 381:    */
 382:   public void nodesChanged(TreeNode node, int[] childIndices)
 383:   {
 384:     Object[] children = new Object[childIndices.length];
 385:     for (int i = 0; i < children.length; i++)
 386:       children[i] = getChild(node, childIndices[i]);
 387:     fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
 388:   }
 389: 
 390:   /**
 391:    * Invoke this method if you've totally changed the children of node and 
 392:    * its childrens children. This will post a treeStructureChanged event.
 393:    * 
 394:    * @param node that had its children and grandchildren changed.
 395:    */
 396:   public void nodeStructureChanged(TreeNode node)
 397:   {
 398:     // TODO
 399:   }
 400: 
 401:   /**
 402:    * Builds the parents of node up to and including the root node, where 
 403:    * the original node is the last element in the returned array. The 
 404:    * length of the returned array gives the node's depth in the tree.
 405:    * 
 406:    * @param node - the TreeNode to get the path for
 407:    * @return TreeNode[] - the path from node to the root
 408:    */
 409:   public TreeNode[] getPathToRoot(TreeNode node)
 410:   {
 411:     return getPathToRoot(node, 0);
 412:   }
 413: 
 414:   /**
 415:    * Builds the parents of node up to and including the root node, where 
 416:    * the original node is the last element in the returned array. The 
 417:    * length of the returned array gives the node's depth in the tree.
 418:    * 
 419:    * @param node - the TreeNode to get the path for
 420:    * @param depth - an int giving the number of steps already taken 
 421:    * towards the root (on recursive calls), used to size the returned array
 422:    * @return an array of TreeNodes giving the path from the root to the 
 423:    * specified node
 424:    */
 425:   protected TreeNode[] getPathToRoot(TreeNode node, int depth)
 426:   {
 427:     if (node == null)
 428:       {
 429:         if (depth == 0)
 430:           return null;
 431:         
 432:         return new TreeNode[depth];
 433:       }
 434: 
 435:     TreeNode[] path = getPathToRoot(node.getParent(), depth + 1);
 436:     path[path.length - depth - 1] = node;
 437:     return path;
 438:   }
 439: 
 440:   /**
 441:    * Registers a listere to the model.
 442:    * 
 443:    * @param listener the listener to add
 444:    */
 445:   public void addTreeModelListener(TreeModelListener listener)
 446:   {
 447:     listenerList.add(TreeModelListener.class, listener);
 448:   }
 449: 
 450:   /**
 451:    * Removes a listener from the model.
 452:    * 
 453:    * @param listener the listener to remove
 454:    */
 455:   public void removeTreeModelListener(TreeModelListener listener)
 456:   {
 457:     listenerList.remove(TreeModelListener.class, listener);
 458:   }
 459: 
 460:   /**
 461:    * Returns all registered <code>TreeModelListener</code> listeners.
 462:    * 
 463:    * @return an array of listeners.
 464:    * 
 465:    * @since 1.4
 466:    */
 467:   public TreeModelListener[] getTreeModelListeners()
 468:   {
 469:     return (TreeModelListener[]) listenerList
 470:         .getListeners(TreeModelListener.class);
 471:   }
 472: 
 473:   /**
 474:    * Notifies all listeners that have registered interest for notification 
 475:    * on this event type. The event instance is lazily created using the parameters 
 476:    * passed into the fire method.
 477:    * 
 478:    * @param source the node being changed
 479:    * @param path the path to the root node
 480:    * @param childIndices the indices of the changed elements
 481:    * @param children the changed elements
 482:    */
 483:   protected void fireTreeNodesChanged(Object source, Object[] path,
 484:       int[] childIndices, Object[] children)
 485:   {
 486:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 487:         children);
 488: 
 489:     TreeModelListener[] listeners = getTreeModelListeners();
 490: 
 491:     for (int i = listeners.length - 1; i >= 0; --i)
 492:       listeners[i].treeNodesChanged(event);
 493:   }
 494: 
 495:   /**
 496:    * fireTreeNodesInserted
 497:    * 
 498:    * @param source the node where new nodes got inserted
 499:    * @param path the path to the root node
 500:    * @param childIndices the indices of the new elements
 501:    * @param children the new elements
 502:    */
 503:   protected void fireTreeNodesInserted(Object source, Object[] path,
 504:       int[] childIndices, Object[] children)
 505:   {
 506:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 507:         children);
 508:     TreeModelListener[] listeners = getTreeModelListeners();
 509: 
 510:     for (int i = listeners.length - 1; i >= 0; --i)
 511:       listeners[i].treeNodesInserted(event);
 512:   }
 513: 
 514:   /**
 515:    * fireTreeNodesRemoved
 516:    * 
 517:    * @param source the node where nodes got removed-
 518:    * @param path the path to the root node
 519:    * @param childIndices the indices of the removed elements
 520:    * @param children the removed elements
 521:    */
 522:   protected void fireTreeNodesRemoved(Object source, Object[] path,
 523:       int[] childIndices, Object[] children)
 524:   {
 525:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 526:         children);
 527:     TreeModelListener[] listeners = getTreeModelListeners();
 528: 
 529:     for (int i = listeners.length - 1; i >= 0; --i)
 530:       listeners[i].treeNodesRemoved(event);
 531:   }
 532: 
 533:   /**
 534:    * fireTreeStructureChanged
 535:    * 
 536:    * @param source the node where the model has changed
 537:    * @param path the path to the root node
 538:    * @param childIndices the indices of the affected elements
 539:    * @param children the affected elements
 540:    */
 541:   protected void fireTreeStructureChanged(Object source, Object[] path,
 542:       int[] childIndices, Object[] children)
 543:   {
 544:     TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
 545:         children);
 546:     TreeModelListener[] listeners = getTreeModelListeners();
 547: 
 548:     for (int i = listeners.length - 1; i >= 0; --i)
 549:       listeners[i].treeStructureChanged(event);
 550:   }
 551: 
 552:   /**
 553:    * Returns the registered listeners of a given type.
 554:    *
 555:    * @param listenerType the listener type to return
 556:    *
 557:    * @return an array of listeners
 558:    *
 559:    * @since 1.3
 560:    */
 561:   public EventListener[] getListeners(Class listenerType)
 562:   {
 563:     return listenerList.getListeners(listenerType);
 564:   }
 565: }