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