1. /*
  2. * @(#)TreePath.java 1.29 03/12/19
  3. *
  4. * Copyright 2004 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.io.*;
  9. import java.util.Vector;
  10. /**
  11. * Represents a path to a node. A TreePath is an array of Objects that are
  12. * vended from a TreeModel. The elements of the array are ordered such
  13. * that the root is always the first element (index 0) of the array.
  14. * TreePath is Serializable, but if any
  15. * components of the path are not serializable, it will not be written
  16. * out.
  17. * <p>
  18. * For further information and examples of using tree paths,
  19. * see <a
  20. href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
  21. * in <em>The Java Tutorial.</em>
  22. * <p>
  23. * <strong>Warning:</strong>
  24. * Serialized objects of this class will not be compatible with
  25. * future Swing releases. The current serialization support is
  26. * appropriate for short term storage or RMI between applications running
  27. * the same version of Swing. As of 1.4, support for long term storage
  28. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  29. * has been added to the <code>java.beans</code> package.
  30. * Please see {@link java.beans.XMLEncoder}.
  31. *
  32. * @version 1.29 12/19/03
  33. * @author Scott Violet
  34. * @author Philip Milne
  35. */
  36. public class TreePath extends Object implements Serializable {
  37. /** Path representing the parent, null if lastPathComponent represents
  38. * the root. */
  39. private TreePath parentPath;
  40. /** Last path component. */
  41. transient private Object lastPathComponent;
  42. /**
  43. * Constructs a path from an array of Objects, uniquely identifying
  44. * the path from the root of the tree to a specific node, as returned
  45. * by the tree's data model.
  46. * <p>
  47. * The model is free to return an array of any Objects it needs to
  48. * represent the path. The DefaultTreeModel returns an array of
  49. * TreeNode objects. The first TreeNode in the path is the root of the
  50. * tree, the last TreeNode is the node identified by the path.
  51. *
  52. * @param path an array of Objects representing the path to a node
  53. */
  54. public TreePath(Object[] path) {
  55. if(path == null || path.length == 0)
  56. throw new IllegalArgumentException("path in TreePath must be non null and not empty.");
  57. lastPathComponent = path[path.length - 1];
  58. if(path.length > 1)
  59. parentPath = new TreePath(path, path.length - 1);
  60. }
  61. /**
  62. * Constructs a TreePath containing only a single element. This is
  63. * usually used to construct a TreePath for the the root of the TreeModel.
  64. * <p>
  65. * @param singlePath an Object representing the path to a node
  66. * @see #TreePath(Object[])
  67. */
  68. public TreePath(Object singlePath) {
  69. if(singlePath == null)
  70. throw new IllegalArgumentException("path in TreePath must be non null.");
  71. lastPathComponent = singlePath;
  72. parentPath = null;
  73. }
  74. /**
  75. * Constructs a new TreePath, which is the path identified by
  76. * <code>parent</code> ending in <code>lastElement</code>.
  77. */
  78. protected TreePath(TreePath parent, Object lastElement) {
  79. if(lastElement == null)
  80. throw new IllegalArgumentException("path in TreePath must be non null.");
  81. parentPath = parent;
  82. lastPathComponent = lastElement;
  83. }
  84. /**
  85. * Constructs a new TreePath with the identified path components of
  86. * length <code>length</code>.
  87. */
  88. protected TreePath(Object[] path, int length) {
  89. lastPathComponent = path[length - 1];
  90. if(length > 1)
  91. parentPath = new TreePath(path, length - 1);
  92. }
  93. /**
  94. * Primarily provided for subclasses
  95. * that represent paths in a different manner.
  96. * If a subclass uses this constructor, it should also override
  97. * the <code>getPath</code>,
  98. * <code>getPathCount</code>, and
  99. * <code>getPathComponent</code> methods,
  100. * and possibly the <code>equals</code> method.
  101. */
  102. protected TreePath() {
  103. }
  104. /**
  105. * Returns an ordered array of Objects containing the components of this
  106. * TreePath. The first element (index 0) is the root.
  107. *
  108. * @return an array of Objects representing the TreePath
  109. * @see #TreePath(Object[])
  110. */
  111. public Object[] getPath() {
  112. int i = getPathCount();
  113. Object[] result = new Object[i--];
  114. for(TreePath path = this; path != null; path = path.parentPath) {
  115. result[i--] = path.lastPathComponent;
  116. }
  117. return result;
  118. }
  119. /**
  120. * Returns the last component of this path. For a path returned by
  121. * DefaultTreeModel this will return an instance of TreeNode.
  122. *
  123. * @return the Object at the end of the path
  124. * @see #TreePath(Object[])
  125. */
  126. public Object getLastPathComponent() {
  127. return lastPathComponent;
  128. }
  129. /**
  130. * Returns the number of elements in the path.
  131. *
  132. * @return an int giving a count of items the path
  133. */
  134. public int getPathCount() {
  135. int result = 0;
  136. for(TreePath path = this; path != null; path = path.parentPath) {
  137. result++;
  138. }
  139. return result;
  140. }
  141. /**
  142. * Returns the path component at the specified index.
  143. *
  144. * @param element an int specifying an element in the path, where
  145. * 0 is the first element in the path
  146. * @return the Object at that index location
  147. * @throws IllegalArgumentException if the index is beyond the length
  148. * of the path
  149. * @see #TreePath(Object[])
  150. */
  151. public Object getPathComponent(int element) {
  152. int pathLength = getPathCount();
  153. if(element < 0 || element >= pathLength)
  154. throw new IllegalArgumentException("Index " + element + " is out of the specified range");
  155. TreePath path = this;
  156. for(int i = pathLength-1; i != element; i--) {
  157. path = path.parentPath;
  158. }
  159. return path.lastPathComponent;
  160. }
  161. /**
  162. * Tests two TreePaths for equality by checking each element of the
  163. * paths for equality. Two paths are considered equal if they are of
  164. * the same length, and contain
  165. * the same elements (<code>.equals</code>).
  166. *
  167. * @param o the Object to compare
  168. */
  169. public boolean equals(Object o) {
  170. if(o == this)
  171. return true;
  172. if(o instanceof TreePath) {
  173. TreePath oTreePath = (TreePath)o;
  174. if(getPathCount() != oTreePath.getPathCount())
  175. return false;
  176. for(TreePath path = this; path != null; path = path.parentPath) {
  177. if (!(path.lastPathComponent.equals
  178. (oTreePath.lastPathComponent))) {
  179. return false;
  180. }
  181. oTreePath = oTreePath.parentPath;
  182. }
  183. return true;
  184. }
  185. return false;
  186. }
  187. /**
  188. * Returns the hashCode for the object. The hash code of a TreePath
  189. * is defined to be the hash code of the last component in the path.
  190. *
  191. * @return the hashCode for the object
  192. */
  193. public int hashCode() {
  194. return lastPathComponent.hashCode();
  195. }
  196. /**
  197. * Returns true if <code>aTreePath</code> is a
  198. * descendant of this
  199. * TreePath. A TreePath P1 is a descendent of a TreePath P2
  200. * if P1 contains all of the components that make up
  201. * P2's path.
  202. * For example, if this object has the path [a, b],
  203. * and <code>aTreePath</code> has the path [a, b, c],
  204. * then <code>aTreePath</code> is a descendant of this object.
  205. * However, if <code>aTreePath</code> has the path [a],
  206. * then it is not a descendant of this object.
  207. *
  208. * @return true if <code>aTreePath</code> is a descendant of this path
  209. */
  210. public boolean isDescendant(TreePath aTreePath) {
  211. if(aTreePath == this)
  212. return true;
  213. if(aTreePath != null) {
  214. int pathLength = getPathCount();
  215. int oPathLength = aTreePath.getPathCount();
  216. if(oPathLength < pathLength)
  217. // Can't be a descendant, has fewer components in the path.
  218. return false;
  219. while(oPathLength-- > pathLength)
  220. aTreePath = aTreePath.getParentPath();
  221. return equals(aTreePath);
  222. }
  223. return false;
  224. }
  225. /**
  226. * Returns a new path containing all the elements of this object
  227. * plus <code>child</code>. <code>child</code> will be the last element
  228. * of the newly created TreePath.
  229. * This will throw a NullPointerException
  230. * if child is null.
  231. */
  232. public TreePath pathByAddingChild(Object child) {
  233. if(child == null)
  234. throw new NullPointerException("Null child not allowed");
  235. return new TreePath(this, child);
  236. }
  237. /**
  238. * Returns a path containing all the elements of this object, except
  239. * the last path component.
  240. */
  241. public TreePath getParentPath() {
  242. return parentPath;
  243. }
  244. /**
  245. * Returns a string that displays and identifies this
  246. * object's properties.
  247. *
  248. * @return a String representation of this object
  249. */
  250. public String toString() {
  251. StringBuffer tempSpot = new StringBuffer("[");
  252. for(int counter = 0, maxCounter = getPathCount();counter < maxCounter;
  253. counter++) {
  254. if(counter > 0)
  255. tempSpot.append(", ");
  256. tempSpot.append(getPathComponent(counter));
  257. }
  258. tempSpot.append("]");
  259. return tempSpot.toString();
  260. }
  261. // Serialization support.
  262. private void writeObject(ObjectOutputStream s) throws IOException {
  263. s.defaultWriteObject();
  264. Vector values = new Vector();
  265. boolean writePath = true;
  266. if(lastPathComponent != null &&
  267. (lastPathComponent instanceof Serializable)) {
  268. values.addElement("lastPathComponent");
  269. values.addElement(lastPathComponent);
  270. }
  271. s.writeObject(values);
  272. }
  273. private void readObject(ObjectInputStream s)
  274. throws IOException, ClassNotFoundException {
  275. s.defaultReadObject();
  276. Vector values = (Vector)s.readObject();
  277. int indexCounter = 0;
  278. int maxCounter = values.size();
  279. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  280. equals("lastPathComponent")) {
  281. lastPathComponent = values.elementAt(++indexCounter);
  282. indexCounter++;
  283. }
  284. }
  285. }