1. /*
  2. * @(#)DefaultTreeModel.java 1.38 01/11/29
  3. *
  4. * Copyright 2002 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. * <p>
  15. * <strong>Warning:</strong>
  16. * Serialized objects of this class will not be compatible with
  17. * future Swing releases. The current serialization support is appropriate
  18. * for short term storage or RMI between applications running the same
  19. * version of Swing. A future release of Swing will provide support for
  20. * long term persistence.
  21. *
  22. * @version 1.38 11/29/01
  23. * @author Rob Davis
  24. * @author Ray Ryan
  25. * @author Scott Violet
  26. */
  27. public class DefaultTreeModel implements Serializable, TreeModel {
  28. /** Root of the tree. */
  29. protected TreeNode root;
  30. /** Listeners. */
  31. protected EventListenerList listenerList = new EventListenerList();
  32. /**
  33. * Determines how the <code>isLeaf</code> method figures
  34. * out if a node is a leaf node. If true, a node is a leaf
  35. * node if it does not allow children. (If it allows
  36. * children, it is not a leaf node, even if no children
  37. * are present.) That lets you distinguish between <i>folder</i>
  38. * nodes and <i>file</i> nodes in a file system, for example.
  39. * <p>
  40. * If this value is false, then any node which has no
  41. * children is a leaf node, and any node may acquire
  42. * children.
  43. *
  44. * @see TreeNode#getAllowsChildren
  45. * @see TreeModel#isLeaf
  46. * @see #setAsksAllowsChildren
  47. */
  48. protected boolean asksAllowsChildren;
  49. /**
  50. * Creates a tree in which any node can have children.
  51. *
  52. * @param root a TreeNode object that is the root of the tree
  53. * @see #DefaultTreeModel(TreeNode, boolean)
  54. */
  55. public DefaultTreeModel(TreeNode root) {
  56. this(root, false);
  57. }
  58. /**
  59. * Creates a tree specifying whether any node can have children,
  60. * or whether only certain nodes can have children.
  61. *
  62. * @param root a TreeNode object that is the root of the tree
  63. * @param askAllowsChildren a boolean, false if any node can
  64. * have children, true if each node is asked to see if
  65. * it can have children
  66. * @see #asksAllowsChildren
  67. */
  68. public DefaultTreeModel(TreeNode root, boolean asksAllowsChildren) {
  69. super();
  70. if (root == null) {
  71. throw new IllegalArgumentException("root is null");
  72. }
  73. this.root = root;
  74. this.asksAllowsChildren = asksAllowsChildren;
  75. }
  76. /**
  77. * Sets whether or not to test leafness by asking getAllowsChildren()
  78. * or isLeaf() to the TreeNodes. If newvalue is true, getAllowsChildren()
  79. * is messaged, otherwise isLeaf() is messaged.
  80. */
  81. public void setAsksAllowsChildren(boolean newValue) {
  82. asksAllowsChildren = newValue;
  83. }
  84. /**
  85. * Tells how leaf nodes are determined.
  86. *
  87. * @return true if only nodes which do not allow children are
  88. * leaf nodes, false if nodes which have no children
  89. * (even if allowed) are leaf nodes
  90. * @see #asksAllowsChildren
  91. */
  92. public boolean asksAllowsChildren() {
  93. return asksAllowsChildren;
  94. }
  95. /**
  96. * Sets the root to <code>root</code>. This will throw an
  97. * IllegalArgumentException if <code>root</code> is null.
  98. */
  99. public void setRoot(TreeNode root) {
  100. if(root == null)
  101. throw new IllegalArgumentException("Root of tree is not allowed to be null");
  102. this.root = root;
  103. nodeStructureChanged(root);
  104. }
  105. /**
  106. * Returns the root of the tree. Returns null only if the tree has
  107. * no nodes.
  108. *
  109. * @return the root of the tree
  110. */
  111. public Object getRoot() {
  112. return root;
  113. }
  114. /**
  115. * Returns the index of child in parent.
  116. */
  117. public int getIndexOfChild(Object parent, Object child) {
  118. if(parent == null || child == null)
  119. return 0;
  120. return ((TreeNode)parent).getIndex((TreeNode)child);
  121. }
  122. /**
  123. * Returns the child of <I>parent</I> at index <I>index</I> in the parent's
  124. * child array. <I>parent</I> must be a node previously obtained from
  125. * this data source. This should not return null if <i>index</i>
  126. * is a valid index for <i>parent</i> (that is <i>index</i> >= 0 &&
  127. * <i>index</i> < getChildCount(<i>parent</i>)).
  128. *
  129. * @param parent a node in the tree, obtained from this data source
  130. * @return the child of <I>parent</I> at index <I>index</I>
  131. */
  132. public Object getChild(Object parent, int index) {
  133. return ((TreeNode)parent).getChildAt(index);
  134. }
  135. /**
  136. * Returns the number of children of <I>parent</I>. Returns 0 if the node
  137. * is a leaf or if it has no children. <I>parent</I> must be a node
  138. * previously obtained from this data source.
  139. *
  140. * @param parent a node in the tree, obtained from this data source
  141. * @return the number of children of the node <I>parent</I>
  142. */
  143. public int getChildCount(Object parent) {
  144. return ((TreeNode)parent).getChildCount();
  145. }
  146. /**
  147. * Returns whether the specified node is a leaf node.
  148. * The way the test is performed depends on the
  149. * <code>askAllowsChildren</code> setting.
  150. *
  151. * @param node the node to check
  152. * @return true if the node is a leaf node
  153. *
  154. * @see #asksAllowsChildren
  155. * @see TreeModel#isLeaf
  156. */
  157. public boolean isLeaf(Object node) {
  158. if(asksAllowsChildren)
  159. return !((TreeNode)node).getAllowsChildren();
  160. return ((TreeNode)node).isLeaf();
  161. }
  162. /**
  163. * Invoke this method if you've modified the TreeNodes upon which this
  164. * model depends. The model will notify all of its listeners that the
  165. * model has changed.
  166. */
  167. public void reload() {
  168. reload(root);
  169. }
  170. /**
  171. * This sets the user object of the TreeNode identified by path
  172. * and posts a node changed. If you use custom user objects in
  173. * the TreeModel you're going to need to subclass this and
  174. * set the user object of the changed node to something meaningful.
  175. */
  176. public void valueForPathChanged(TreePath path, Object newValue) {
  177. MutableTreeNode aNode = (MutableTreeNode)path.getLastPathComponent();
  178. aNode.setUserObject(newValue);
  179. nodeChanged(aNode);
  180. }
  181. /**
  182. * Invoked this to insert newChild at location index in parents children.
  183. * This will then message nodesWereInserted to create the appropriate
  184. * event. This is the preferred way to add children as it will create
  185. * the appropriate event.
  186. */
  187. public void insertNodeInto(MutableTreeNode newChild,
  188. MutableTreeNode parent, int index){
  189. parent.insert(newChild, index);
  190. int[] newIndexs = new int[1];
  191. newIndexs[0] = index;
  192. nodesWereInserted(parent, newIndexs);
  193. }
  194. /**
  195. * Message this to remove node from its parent. This will message
  196. * nodesWereRemoved to create the appropriate event. This is the
  197. * preferred way to remove a node as it handles the event creation
  198. * for you.
  199. */
  200. public void removeNodeFromParent(MutableTreeNode node) {
  201. MutableTreeNode parent = (MutableTreeNode)node.getParent();
  202. if(parent == null)
  203. throw new IllegalArgumentException("node does not have a parent.");
  204. int[] childIndex = new int[1];
  205. Object[] removedArray = new Object[1];
  206. childIndex[0] = parent.getIndex(node);
  207. parent.remove(childIndex[0]);
  208. removedArray[0] = node;
  209. nodesWereRemoved(parent, childIndex, removedArray);
  210. }
  211. /**
  212. * Invoke this method after you've changed how node is to be
  213. * represented in the tree.
  214. */
  215. public void nodeChanged(TreeNode node) {
  216. if(listenerList != null && node != null) {
  217. TreeNode parent = node.getParent();
  218. if(parent != null) {
  219. int anIndex = parent.getIndex(node);
  220. if(anIndex != -1) {
  221. int[] cIndexs = new int[1];
  222. cIndexs[0] = anIndex;
  223. nodesChanged(parent, cIndexs);
  224. }
  225. }
  226. else if (node == getRoot()) {
  227. nodesChanged(node, null);
  228. }
  229. }
  230. }
  231. /**
  232. * Invoke this method if you've modified the TreeNodes upon which this
  233. * model depends. The model will notify all of its listeners that the
  234. * model has changed below the node <code>node</code> (PENDING).
  235. */
  236. public void reload(TreeNode node) {
  237. if(node != null) {
  238. fireTreeStructureChanged(this, getPathToRoot(node), null, null);
  239. }
  240. }
  241. /**
  242. * Invoke this method after you've inserted some TreeNodes into
  243. * node. childIndices should be the index of the new elements and
  244. * must be sorted in ascending order.
  245. */
  246. public void nodesWereInserted(TreeNode node, int[] childIndices) {
  247. if(listenerList != null && node != null && childIndices != null
  248. && childIndices.length > 0) {
  249. int cCount = childIndices.length;
  250. Object[] newChildren = new Object[cCount];
  251. for(int counter = 0; counter < cCount; counter++)
  252. newChildren[counter] = node.getChildAt(childIndices[counter]);
  253. fireTreeNodesInserted(this, getPathToRoot(node), childIndices,
  254. newChildren);
  255. }
  256. }
  257. /**
  258. * Invoke this method after you've removed some TreeNodes from
  259. * node. childIndices should be the index of the removed elements and
  260. * must be sorted in ascending order. And removedChildren should be
  261. * the array of the children objects that were removed.
  262. */
  263. public void nodesWereRemoved(TreeNode node, int[] childIndices,
  264. Object[] removedChildren) {
  265. if(node != null && childIndices != null) {
  266. fireTreeNodesRemoved(this, getPathToRoot(node), childIndices,
  267. removedChildren);
  268. }
  269. }
  270. /**
  271. * Invoke this method after you've changed how the children identified by
  272. * childIndicies are to be represented in the tree.
  273. */
  274. public void nodesChanged(TreeNode node, int[] childIndices) {
  275. if(node != null) {
  276. if (childIndices != null) {
  277. int cCount = childIndices.length;
  278. if(cCount > 0) {
  279. Object[] cChildren = new Object[cCount];
  280. for(int counter = 0; counter < cCount; counter++)
  281. cChildren[counter] = node.getChildAt
  282. (childIndices[counter]);
  283. fireTreeNodesChanged(this, getPathToRoot(node),
  284. childIndices, cChildren);
  285. }
  286. }
  287. else if (node == getRoot()) {
  288. fireTreeNodesChanged(this, getPathToRoot(node), null, null);
  289. }
  290. }
  291. }
  292. /**
  293. * Invoke this method if you've totally changed the children of
  294. * node and its childrens children... This will post a
  295. * treeStructureChanged event.
  296. */
  297. public void nodeStructureChanged(TreeNode node) {
  298. if(node != null) {
  299. fireTreeStructureChanged(this, getPathToRoot(node), null, null);
  300. }
  301. }
  302. /**
  303. * Builds the parents of node up to and including the root node,
  304. * where the original node is the last element in the returned array.
  305. * The length of the returned array gives the node's depth in the
  306. * tree.
  307. *
  308. * @param aNode the TreeNode to get the path for
  309. * @param an array of TreeNodes giving the path from the root to the
  310. * specified node.
  311. */
  312. public TreeNode[] getPathToRoot(TreeNode aNode) {
  313. return getPathToRoot(aNode, 0);
  314. }
  315. /**
  316. * Builds the parents of node up to and including the root node,
  317. * where the original node is the last element in the returned array.
  318. * The length of the returned array gives the node's depth in the
  319. * tree.
  320. *
  321. * @param aNode the TreeNode to get the path for
  322. * @param depth an int giving the number of steps already taken towards
  323. * the root (on recursive calls), used to size the returned array
  324. * @return an array of TreeNodes giving the path from the root to the
  325. * specified node
  326. */
  327. protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
  328. TreeNode[] retNodes;
  329. // This method recurses, traversing towards the root in order
  330. // size the array. On the way back, it fills in the nodes,
  331. // starting from the root and working back to the original node.
  332. /* Check for null, in case someone passed in a null node, or
  333. they passed in an element that isn't rooted at root. */
  334. if(aNode == null) {
  335. if(depth == 0)
  336. return null;
  337. else
  338. retNodes = new TreeNode[depth];
  339. }
  340. else {
  341. depth++;
  342. if(aNode == root)
  343. retNodes = new TreeNode[depth];
  344. else
  345. retNodes = getPathToRoot(aNode.getParent(), depth);
  346. retNodes[retNodes.length - depth] = aNode;
  347. }
  348. return retNodes;
  349. }
  350. //
  351. // Events
  352. //
  353. /**
  354. * Adds a listener for the TreeModelEvent posted after the tree changes.
  355. *
  356. * @see #removeTreeModelListener
  357. * @param l the listener to add
  358. */
  359. public void addTreeModelListener(TreeModelListener l) {
  360. listenerList.add(TreeModelListener.class, l);
  361. }
  362. /**
  363. * Removes a listener previously added with <B>addTreeModelListener()</B>.
  364. *
  365. * @see #addTreeModelListener
  366. * @param l the listener to remove
  367. */
  368. public void removeTreeModelListener(TreeModelListener l) {
  369. listenerList.remove(TreeModelListener.class, l);
  370. }
  371. /*
  372. * Notify all listeners that have registered interest for
  373. * notification on this event type. The event instance
  374. * is lazily created using the parameters passed into
  375. * the fire method.
  376. * @see EventListenerList
  377. */
  378. protected void fireTreeNodesChanged(Object source, Object[] path,
  379. int[] childIndices,
  380. Object[] children) {
  381. // Guaranteed to return a non-null array
  382. Object[] listeners = listenerList.getListenerList();
  383. TreeModelEvent e = null;
  384. // Process the listeners last to first, notifying
  385. // those that are interested in this event
  386. for (int i = listeners.length-2; i>=0; i-=2) {
  387. if (listeners[i]==TreeModelListener.class) {
  388. // Lazily create the event:
  389. if (e == null)
  390. e = new TreeModelEvent(source, path,
  391. childIndices, children);
  392. ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
  393. }
  394. }
  395. }
  396. /*
  397. * Notify all listeners that have registered interest for
  398. * notification on this event type. The event instance
  399. * is lazily created using the parameters passed into
  400. * the fire method.
  401. * @see EventListenerList
  402. */
  403. protected void fireTreeNodesInserted(Object source, Object[] path,
  404. int[] childIndices,
  405. Object[] children) {
  406. // Guaranteed to return a non-null array
  407. Object[] listeners = listenerList.getListenerList();
  408. TreeModelEvent e = null;
  409. // Process the listeners last to first, notifying
  410. // those that are interested in this event
  411. for (int i = listeners.length-2; i>=0; i-=2) {
  412. if (listeners[i]==TreeModelListener.class) {
  413. // Lazily create the event:
  414. if (e == null)
  415. e = new TreeModelEvent(source, path,
  416. childIndices, children);
  417. ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
  418. }
  419. }
  420. }
  421. /*
  422. * Notify all listeners that have registered interest for
  423. * notification on this event type. The event instance
  424. * is lazily created using the parameters passed into
  425. * the fire method.
  426. * @see EventListenerList
  427. */
  428. protected void fireTreeNodesRemoved(Object source, Object[] path,
  429. int[] childIndices,
  430. Object[] children) {
  431. // Guaranteed to return a non-null array
  432. Object[] listeners = listenerList.getListenerList();
  433. TreeModelEvent e = null;
  434. // Process the listeners last to first, notifying
  435. // those that are interested in this event
  436. for (int i = listeners.length-2; i>=0; i-=2) {
  437. if (listeners[i]==TreeModelListener.class) {
  438. // Lazily create the event:
  439. if (e == null)
  440. e = new TreeModelEvent(source, path,
  441. childIndices, children);
  442. ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
  443. }
  444. }
  445. }
  446. /*
  447. * Notify all listeners that have registered interest for
  448. * notification on this event type. The event instance
  449. * is lazily created using the parameters passed into
  450. * the fire method.
  451. * @see EventListenerList
  452. */
  453. protected void fireTreeStructureChanged(Object source, Object[] path,
  454. int[] childIndices,
  455. Object[] children) {
  456. // Guaranteed to return a non-null array
  457. Object[] listeners = listenerList.getListenerList();
  458. TreeModelEvent e = null;
  459. // Process the listeners last to first, notifying
  460. // those that are interested in this event
  461. for (int i = listeners.length-2; i>=0; i-=2) {
  462. if (listeners[i]==TreeModelListener.class) {
  463. // Lazily create the event:
  464. if (e == null)
  465. e = new TreeModelEvent(source, path,
  466. childIndices, children);
  467. ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
  468. }
  469. }
  470. }
  471. // Serialization support.
  472. private void writeObject(ObjectOutputStream s) throws IOException {
  473. Vector values = new Vector();
  474. s.defaultWriteObject();
  475. // Save the root, if its Serializable.
  476. if(root != null && root instanceof Serializable) {
  477. values.addElement("root");
  478. values.addElement(root);
  479. }
  480. s.writeObject(values);
  481. }
  482. private void readObject(ObjectInputStream s)
  483. throws IOException, ClassNotFoundException {
  484. s.defaultReadObject();
  485. Vector values = (Vector)s.readObject();
  486. int indexCounter = 0;
  487. int maxCounter = values.size();
  488. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  489. equals("root")) {
  490. root = (TreeNode)values.elementAt(++indexCounter);
  491. indexCounter++;
  492. }
  493. }
  494. } // End of class DefaultTreeModel