1. /*
  2. * @(#)DefaultTreeModel.java 1.53 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.tree;
  8. import java.util.*;
  9. import java.awt.*;
  10. import java.io.*;
  11. import javax.swing.event.*;
  12. /**
  13. * A simple tree data model that uses TreeNodes.
  14. * For further information and examples that use DefaultTreeModel,
  15. * see <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
  16. * in <em>The Java Tutorial.</em>
  17. * <p>
  18. * <strong>Warning:</strong>
  19. * Serialized objects of this class will not be compatible with
  20. * future Swing releases. The current serialization support is
  21. * appropriate for short term storage or RMI between applications running
  22. * the same version of Swing. As of 1.4, support for long term storage
  23. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  24. * has been added to the <code>java.beans</code> package.
  25. * Please see {@link java.beans.XMLEncoder}.
  26. *
  27. * @version 1.53 01/23/03
  28. * @author Rob Davis
  29. * @author Ray Ryan
  30. * @author Scott Violet
  31. */
  32. public class DefaultTreeModel implements Serializable, TreeModel {
  33. /** Root of the tree. */
  34. protected TreeNode root;
  35. /** Listeners. */
  36. protected EventListenerList listenerList = new EventListenerList();
  37. /**
  38. * Determines how the <code>isLeaf</code> method figures
  39. * out if a node is a leaf node. If true, a node is a leaf
  40. * node if it does not allow children. (If it allows
  41. * children, it is not a leaf node, even if no children
  42. * are present.) That lets you distinguish between <i>folder</i>
  43. * nodes and <i>file</i> nodes in a file system, for example.
  44. * <p>
  45. * If this value is false, then any node which has no
  46. * children is a leaf node, and any node may acquire
  47. * children.
  48. *
  49. * @see TreeNode#getAllowsChildren
  50. * @see TreeModel#isLeaf
  51. * @see #setAsksAllowsChildren
  52. */
  53. protected boolean asksAllowsChildren;
  54. /**
  55. * Creates a tree in which any node can have children.
  56. *
  57. * @param root a TreeNode object that is the root of the tree
  58. * @see #DefaultTreeModel(TreeNode, boolean)
  59. */
  60. public DefaultTreeModel(TreeNode root) {
  61. this(root, false);
  62. }
  63. /**
  64. * Creates a tree specifying whether any node can have children,
  65. * or whether only certain nodes can have children.
  66. *
  67. * @param root a TreeNode object that is the root of the tree
  68. * @param asksAllowsChildren a boolean, false if any node can
  69. * have children, true if each node is asked to see if
  70. * it can have children
  71. * @see #asksAllowsChildren
  72. */
  73. public DefaultTreeModel(TreeNode root, boolean asksAllowsChildren) {
  74. super();
  75. this.root = root;
  76. this.asksAllowsChildren = asksAllowsChildren;
  77. }
  78. /**
  79. * Sets whether or not to test leafness by asking getAllowsChildren()
  80. * or isLeaf() to the TreeNodes. If newvalue is true, getAllowsChildren()
  81. * is messaged, otherwise isLeaf() is messaged.
  82. */
  83. public void setAsksAllowsChildren(boolean newValue) {
  84. asksAllowsChildren = newValue;
  85. }
  86. /**
  87. * Tells how leaf nodes are determined.
  88. *
  89. * @return true if only nodes which do not allow children are
  90. * leaf nodes, false if nodes which have no children
  91. * (even if allowed) are leaf nodes
  92. * @see #asksAllowsChildren
  93. */
  94. public boolean asksAllowsChildren() {
  95. return asksAllowsChildren;
  96. }
  97. /**
  98. * Sets the root to <code>root</code>. A null <code>root</code> implies
  99. * the tree is to display nothing, and is legal.
  100. */
  101. public void setRoot(TreeNode root) {
  102. Object oldRoot = this.root;
  103. this.root = root;
  104. if (root == null && oldRoot != null) {
  105. fireTreeStructureChanged(this, null);
  106. }
  107. else {
  108. nodeStructureChanged(root);
  109. }
  110. }
  111. /**
  112. * Returns the root of the tree. Returns null only if the tree has
  113. * no nodes.
  114. *
  115. * @return the root of the tree
  116. */
  117. public Object getRoot() {
  118. return root;
  119. }
  120. /**
  121. * Returns the index of child in parent.
  122. * If either the parent or child is <code>null</code>, returns -1.
  123. * @param parent a note in the tree, obtained from this data source
  124. * @param child the node we are interested in
  125. * @return the index of the child in the parent, or -1
  126. * if either the parent or the child is <code>null</code>
  127. */
  128. public int getIndexOfChild(Object parent, Object child) {
  129. if(parent == null || child == null)
  130. return -1;
  131. return ((TreeNode)parent).getIndex((TreeNode)child);
  132. }
  133. /**
  134. * Returns the child of <I>parent</I> at index <I>index</I> in the parent's
  135. * child array. <I>parent</I> must be a node previously obtained from
  136. * this data source. This should not return null if <i>index</i>
  137. * is a valid index for <i>parent</i> (that is <i>index</i> >= 0 &&
  138. * <i>index</i> < getChildCount(<i>parent</i>)).
  139. *
  140. * @param parent a node in the tree, obtained from this data source
  141. * @return the child of <I>parent</I> at index <I>index</I>
  142. */
  143. public Object getChild(Object parent, int index) {
  144. return ((TreeNode)parent).getChildAt(index);
  145. }
  146. /**
  147. * Returns the number of children of <I>parent</I>. Returns 0 if the node
  148. * is a leaf or if it has no children. <I>parent</I> must be a node
  149. * previously obtained from this data source.
  150. *
  151. * @param parent a node in the tree, obtained from this data source
  152. * @return the number of children of the node <I>parent</I>
  153. */
  154. public int getChildCount(Object parent) {
  155. return ((TreeNode)parent).getChildCount();
  156. }
  157. /**
  158. * Returns whether the specified node is a leaf node.
  159. * The way the test is performed depends on the
  160. * <code>askAllowsChildren</code> setting.
  161. *
  162. * @param node the node to check
  163. * @return true if the node is a leaf node
  164. *
  165. * @see #asksAllowsChildren
  166. * @see TreeModel#isLeaf
  167. */
  168. public boolean isLeaf(Object node) {
  169. if(asksAllowsChildren)
  170. return !((TreeNode)node).getAllowsChildren();
  171. return ((TreeNode)node).isLeaf();
  172. }
  173. /**
  174. * Invoke this method if you've modified the TreeNodes upon which this
  175. * model depends. The model will notify all of its listeners that the
  176. * model has changed.
  177. */
  178. public void reload() {
  179. reload(root);
  180. }
  181. /**
  182. * This sets the user object of the TreeNode identified by path
  183. * and posts a node changed. If you use custom user objects in
  184. * the TreeModel you're going to need to subclass this and
  185. * set the user object of the changed node to something meaningful.
  186. */
  187. public void valueForPathChanged(TreePath path, Object newValue) {
  188. MutableTreeNode aNode = (MutableTreeNode)path.getLastPathComponent();
  189. aNode.setUserObject(newValue);
  190. nodeChanged(aNode);
  191. }
  192. /**
  193. * Invoked this to insert newChild at location index in parents children.
  194. * This will then message nodesWereInserted to create the appropriate
  195. * event. This is the preferred way to add children as it will create
  196. * the appropriate event.
  197. */
  198. public void insertNodeInto(MutableTreeNode newChild,
  199. MutableTreeNode parent, int index){
  200. parent.insert(newChild, index);
  201. int[] newIndexs = new int[1];
  202. newIndexs[0] = index;
  203. nodesWereInserted(parent, newIndexs);
  204. }
  205. /**
  206. * Message this to remove node from its parent. This will message
  207. * nodesWereRemoved to create the appropriate event. This is the
  208. * preferred way to remove a node as it handles the event creation
  209. * for you.
  210. */
  211. public void removeNodeFromParent(MutableTreeNode node) {
  212. MutableTreeNode parent = (MutableTreeNode)node.getParent();
  213. if(parent == null)
  214. throw new IllegalArgumentException("node does not have a parent.");
  215. int[] childIndex = new int[1];
  216. Object[] removedArray = new Object[1];
  217. childIndex[0] = parent.getIndex(node);
  218. parent.remove(childIndex[0]);
  219. removedArray[0] = node;
  220. nodesWereRemoved(parent, childIndex, removedArray);
  221. }
  222. /**
  223. * Invoke this method after you've changed how node is to be
  224. * represented in the tree.
  225. */
  226. public void nodeChanged(TreeNode node) {
  227. if(listenerList != null && node != null) {
  228. TreeNode parent = node.getParent();
  229. if(parent != null) {
  230. int anIndex = parent.getIndex(node);
  231. if(anIndex != -1) {
  232. int[] cIndexs = new int[1];
  233. cIndexs[0] = anIndex;
  234. nodesChanged(parent, cIndexs);
  235. }
  236. }
  237. else if (node == getRoot()) {
  238. nodesChanged(node, null);
  239. }
  240. }
  241. }
  242. /**
  243. * Invoke this method if you've modified the TreeNodes upon which this
  244. * model depends. The model will notify all of its listeners that the
  245. * model has changed below the node <code>node</code> (PENDING).
  246. */
  247. public void reload(TreeNode node) {
  248. if(node != null) {
  249. fireTreeStructureChanged(this, getPathToRoot(node), null, null);
  250. }
  251. }
  252. /**
  253. * Invoke this method after you've inserted some TreeNodes into
  254. * node. childIndices should be the index of the new elements and
  255. * must be sorted in ascending order.
  256. */
  257. public void nodesWereInserted(TreeNode node, int[] childIndices) {
  258. if(listenerList != null && node != null && childIndices != null
  259. && childIndices.length > 0) {
  260. int cCount = childIndices.length;
  261. Object[] newChildren = new Object[cCount];
  262. for(int counter = 0; counter < cCount; counter++)
  263. newChildren[counter] = node.getChildAt(childIndices[counter]);
  264. fireTreeNodesInserted(this, getPathToRoot(node), childIndices,
  265. newChildren);
  266. }
  267. }
  268. /**
  269. * Invoke this method after you've removed some TreeNodes from
  270. * node. childIndices should be the index of the removed elements and
  271. * must be sorted in ascending order. And removedChildren should be
  272. * the array of the children objects that were removed.
  273. */
  274. public void nodesWereRemoved(TreeNode node, int[] childIndices,
  275. Object[] removedChildren) {
  276. if(node != null && childIndices != null) {
  277. fireTreeNodesRemoved(this, getPathToRoot(node), childIndices,
  278. removedChildren);
  279. }
  280. }
  281. /**
  282. * Invoke this method after you've changed how the children identified by
  283. * childIndicies are to be represented in the tree.
  284. */
  285. public void nodesChanged(TreeNode node, int[] childIndices) {
  286. if(node != null) {
  287. if (childIndices != null) {
  288. int cCount = childIndices.length;
  289. if(cCount > 0) {
  290. Object[] cChildren = new Object[cCount];
  291. for(int counter = 0; counter < cCount; counter++)
  292. cChildren[counter] = node.getChildAt
  293. (childIndices[counter]);
  294. fireTreeNodesChanged(this, getPathToRoot(node),
  295. childIndices, cChildren);
  296. }
  297. }
  298. else if (node == getRoot()) {
  299. fireTreeNodesChanged(this, getPathToRoot(node), null, null);
  300. }
  301. }
  302. }
  303. /**
  304. * Invoke this method if you've totally changed the children of
  305. * node and its childrens children... This will post a
  306. * treeStructureChanged event.
  307. */
  308. public void nodeStructureChanged(TreeNode node) {
  309. if(node != null) {
  310. fireTreeStructureChanged(this, getPathToRoot(node), null, null);
  311. }
  312. }
  313. /**
  314. * Builds the parents of node up to and including the root node,
  315. * where the original node is the last element in the returned array.
  316. * The length of the returned array gives the node's depth in the
  317. * tree.
  318. *
  319. * @param aNode the TreeNode to get the path for
  320. */
  321. public TreeNode[] getPathToRoot(TreeNode aNode) {
  322. return getPathToRoot(aNode, 0);
  323. }
  324. /**
  325. * Builds the parents of node up to and including the root node,
  326. * where the original node is the last element in the returned array.
  327. * The length of the returned array gives the node's depth in the
  328. * tree.
  329. *
  330. * @param aNode the TreeNode to get the path for
  331. * @param depth an int giving the number of steps already taken towards
  332. * the root (on recursive calls), used to size the returned array
  333. * @return an array of TreeNodes giving the path from the root to the
  334. * specified node
  335. */
  336. protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
  337. TreeNode[] retNodes;
  338. // This method recurses, traversing towards the root in order
  339. // size the array. On the way back, it fills in the nodes,
  340. // starting from the root and working back to the original node.
  341. /* Check for null, in case someone passed in a null node, or
  342. they passed in an element that isn't rooted at root. */
  343. if(aNode == null) {
  344. if(depth == 0)
  345. return null;
  346. else
  347. retNodes = new TreeNode[depth];
  348. }
  349. else {
  350. depth++;
  351. if(aNode == root)
  352. retNodes = new TreeNode[depth];
  353. else
  354. retNodes = getPathToRoot(aNode.getParent(), depth);
  355. retNodes[retNodes.length - depth] = aNode;
  356. }
  357. return retNodes;
  358. }
  359. //
  360. // Events
  361. //
  362. /**
  363. * Adds a listener for the TreeModelEvent posted after the tree changes.
  364. *
  365. * @see #removeTreeModelListener
  366. * @param l the listener to add
  367. */
  368. public void addTreeModelListener(TreeModelListener l) {
  369. listenerList.add(TreeModelListener.class, l);
  370. }
  371. /**
  372. * Removes a listener previously added with <B>addTreeModelListener()</B>.
  373. *
  374. * @see #addTreeModelListener
  375. * @param l the listener to remove
  376. */
  377. public void removeTreeModelListener(TreeModelListener l) {
  378. listenerList.remove(TreeModelListener.class, l);
  379. }
  380. /**
  381. * Returns an array of all the tree model listeners
  382. * registered on this model.
  383. *
  384. * @return all of this model's <code>TreeModelListener</code>s
  385. * or an empty
  386. * array if no tree model listeners are currently registered
  387. *
  388. * @see #addTreeModelListener
  389. * @see #removeTreeModelListener
  390. *
  391. * @since 1.4
  392. */
  393. public TreeModelListener[] getTreeModelListeners() {
  394. return (TreeModelListener[])listenerList.getListeners(
  395. TreeModelListener.class);
  396. }
  397. /**
  398. * Notifies all listeners that have registered interest for
  399. * notification on this event type. The event instance
  400. * is lazily created using the parameters passed into
  401. * the fire method.
  402. *
  403. * @param source the node being changed
  404. * @param path the path to the root node
  405. * @param childIndices the indices of the changed elements
  406. * @param children the changed elements
  407. * @see EventListenerList
  408. */
  409. protected void fireTreeNodesChanged(Object source, Object[] path,
  410. int[] childIndices,
  411. Object[] children) {
  412. // Guaranteed to return a non-null array
  413. Object[] listeners = listenerList.getListenerList();
  414. TreeModelEvent e = null;
  415. // Process the listeners last to first, notifying
  416. // those that are interested in this event
  417. for (int i = listeners.length-2; i>=0; i-=2) {
  418. if (listeners[i]==TreeModelListener.class) {
  419. // Lazily create the event:
  420. if (e == null)
  421. e = new TreeModelEvent(source, path,
  422. childIndices, children);
  423. ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
  424. }
  425. }
  426. }
  427. /**
  428. * Notifies all listeners that have registered interest for
  429. * notification on this event type. The event instance
  430. * is lazily created using the parameters passed into
  431. * the fire method.
  432. *
  433. * @param source the node where new elements are being inserted
  434. * @param path the path to the root node
  435. * @param childIndices the indices of the new elements
  436. * @param children the new elements
  437. * @see EventListenerList
  438. */
  439. protected void fireTreeNodesInserted(Object source, Object[] path,
  440. int[] childIndices,
  441. Object[] children) {
  442. // Guaranteed to return a non-null array
  443. Object[] listeners = listenerList.getListenerList();
  444. TreeModelEvent e = null;
  445. // Process the listeners last to first, notifying
  446. // those that are interested in this event
  447. for (int i = listeners.length-2; i>=0; i-=2) {
  448. if (listeners[i]==TreeModelListener.class) {
  449. // Lazily create the event:
  450. if (e == null)
  451. e = new TreeModelEvent(source, path,
  452. childIndices, children);
  453. ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
  454. }
  455. }
  456. }
  457. /**
  458. * Notifies all listeners that have registered interest for
  459. * notification on this event type. The event instance
  460. * is lazily created using the parameters passed into
  461. * the fire method.
  462. *
  463. * @param source the node where elements are being removed
  464. * @param path the path to the root node
  465. * @param childIndices the indices of the removed elements
  466. * @param children the removed elements
  467. * @see EventListenerList
  468. */
  469. protected void fireTreeNodesRemoved(Object source, Object[] path,
  470. int[] childIndices,
  471. Object[] children) {
  472. // Guaranteed to return a non-null array
  473. Object[] listeners = listenerList.getListenerList();
  474. TreeModelEvent e = null;
  475. // Process the listeners last to first, notifying
  476. // those that are interested in this event
  477. for (int i = listeners.length-2; i>=0; i-=2) {
  478. if (listeners[i]==TreeModelListener.class) {
  479. // Lazily create the event:
  480. if (e == null)
  481. e = new TreeModelEvent(source, path,
  482. childIndices, children);
  483. ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
  484. }
  485. }
  486. }
  487. /**
  488. * Notifies all listeners that have registered interest for
  489. * notification on this event type. The event instance
  490. * is lazily created using the parameters passed into
  491. * the fire method.
  492. *
  493. * @param source the node where the tree model has changed
  494. * @param path the path to the root node
  495. * @param childIndices the indices of the affected elements
  496. * @param children the affected elements
  497. * @see EventListenerList
  498. */
  499. protected void fireTreeStructureChanged(Object source, Object[] path,
  500. int[] childIndices,
  501. Object[] children) {
  502. // Guaranteed to return a non-null array
  503. Object[] listeners = listenerList.getListenerList();
  504. TreeModelEvent e = null;
  505. // Process the listeners last to first, notifying
  506. // those that are interested in this event
  507. for (int i = listeners.length-2; i>=0; i-=2) {
  508. if (listeners[i]==TreeModelListener.class) {
  509. // Lazily create the event:
  510. if (e == null)
  511. e = new TreeModelEvent(source, path,
  512. childIndices, children);
  513. ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
  514. }
  515. }
  516. }
  517. /*
  518. * Notifies all listeners that have registered interest for
  519. * notification on this event type. The event instance
  520. * is lazily created using the parameters passed into
  521. * the fire method.
  522. *
  523. * @param source the node where the tree model has changed
  524. * @param path the path to the root node
  525. * @see EventListenerList
  526. */
  527. private void fireTreeStructureChanged(Object source, TreePath path) {
  528. // Guaranteed to return a non-null array
  529. Object[] listeners = listenerList.getListenerList();
  530. TreeModelEvent e = null;
  531. // Process the listeners last to first, notifying
  532. // those that are interested in this event
  533. for (int i = listeners.length-2; i>=0; i-=2) {
  534. if (listeners[i]==TreeModelListener.class) {
  535. // Lazily create the event:
  536. if (e == null)
  537. e = new TreeModelEvent(source, path);
  538. ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
  539. }
  540. }
  541. }
  542. /**
  543. * Returns an array of all the objects currently registered
  544. * as <code><em>Foo</em>Listener</code>s
  545. * upon this model.
  546. * <code><em>Foo</em>Listener</code>s are registered using the
  547. * <code>add<em>Foo</em>Listener</code> method.
  548. *
  549. * <p>
  550. *
  551. * You can specify the <code>listenerType</code> argument
  552. * with a class literal,
  553. * such as
  554. * <code><em>Foo</em>Listener.class</code>.
  555. * For example, you can query a
  556. * <code>DefaultTreeModel</code> <code>m</code>
  557. * for its tree model listeners with the following code:
  558. *
  559. * <pre>TreeModelListener[] tmls = (TreeModelListener[])(m.getListeners(TreeModelListener.class));</pre>
  560. *
  561. * If no such listeners exist, this method returns an empty array.
  562. *
  563. * @param listenerType the type of listeners requested; this parameter
  564. * should specify an interface that descends from
  565. * <code>java.util.EventListener</code>
  566. * @return an array of all objects registered as
  567. * <code><em>Foo</em>Listener</code>s on this component,
  568. * or an empty array if no such
  569. * listeners have been added
  570. * @exception ClassCastException if <code>listenerType</code>
  571. * doesn't specify a class or interface that implements
  572. * <code>java.util.EventListener</code>
  573. *
  574. * @see #getTreeModelListeners
  575. *
  576. * @since 1.3
  577. */
  578. public EventListener[] getListeners(Class listenerType) {
  579. return listenerList.getListeners(listenerType);
  580. }
  581. // Serialization support.
  582. private void writeObject(ObjectOutputStream s) throws IOException {
  583. Vector values = new Vector();
  584. s.defaultWriteObject();
  585. // Save the root, if its Serializable.
  586. if(root != null && root instanceof Serializable) {
  587. values.addElement("root");
  588. values.addElement(root);
  589. }
  590. s.writeObject(values);
  591. }
  592. private void readObject(ObjectInputStream s)
  593. throws IOException, ClassNotFoundException {
  594. s.defaultReadObject();
  595. Vector values = (Vector)s.readObject();
  596. int indexCounter = 0;
  597. int maxCounter = values.size();
  598. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  599. equals("root")) {
  600. root = (TreeNode)values.elementAt(++indexCounter);
  601. indexCounter++;
  602. }
  603. }
  604. } // End of class DefaultTreeModel