1. /*
  2. * @(#)TreePath.java 1.21 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.io.*;
  9. import java.util.Vector;
  10. /**
  11. * Represents a path to a node. TreePath is Serializable, but if any
  12. * components of the path are not serializable, it will not be written
  13. * out.
  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.21 11/29/01
  23. * @author Scott Violet
  24. * @author Philip Milne
  25. */
  26. public class TreePath extends Object implements Serializable {
  27. /** Path representing the parent, null if lastPathComponent represents
  28. * the root. */
  29. private TreePath parentPath;
  30. /** Last path component. */
  31. transient private Object lastPathComponent;
  32. /**
  33. * Constructs a path from an array of Objects, uniquely identifying
  34. * the path from the root of the tree to a specific node, as returned
  35. * by the tree's data model.
  36. * <p>
  37. * The model is free to return an array of any Objects it needs to
  38. * represent the path. The DefaultTreeModel returns an array of
  39. * TreeNode objects. The first TreeNode in the path is the root of the
  40. * tree, the last TreeNode is the node identified by the path.
  41. *
  42. * @param path an array of Objects representing the path to a node
  43. */
  44. public TreePath(Object[] path) {
  45. if(path == null || path.length == 0)
  46. throw new IllegalArgumentException("path in TreePath must be non null and not empty.");
  47. lastPathComponent = path[path.length - 1];
  48. if(path.length > 1)
  49. parentPath = new TreePath(path, path.length - 1);
  50. }
  51. /**
  52. * Constructs a TreePath when there is only item in the path.
  53. * <p>
  54. * @param singlePath an Object representing the path to a node
  55. * @see #TreePath(Object[])
  56. */
  57. public TreePath(Object singlePath) {
  58. if(singlePath == null)
  59. throw new IllegalArgumentException("path in TreePath must be non null.");
  60. lastPathComponent = singlePath;
  61. parentPath = null;
  62. }
  63. /**
  64. * Constructs a TreePath this is the combination of all the path elements
  65. * in <code>parent</code> with a last path component of
  66. * <code>lastElement</code>.
  67. */
  68. protected TreePath(TreePath parent, Object lastElement) {
  69. if(lastElement == null)
  70. throw new IllegalArgumentException("path in TreePath must be non null.");
  71. parentPath = parent;
  72. lastPathComponent = lastElement;
  73. }
  74. protected TreePath(Object[] path, int length) {
  75. lastPathComponent = path[length - 1];
  76. if(length > 1)
  77. parentPath = new TreePath(path, length - 1);
  78. }
  79. /**
  80. * Primarily provided for subclasses that don't wish to use the path ivar.
  81. * If a subclass uses this, it should also subclass getPath(),
  82. * getPathCount(), getPathComponent() and possibly equals.
  83. */
  84. protected TreePath() {
  85. }
  86. /**
  87. * Returns an array of Objects containing the components of this
  88. * TreePath.
  89. *
  90. * @return an array of Objects representing the TreePath
  91. * @see #TreePath(Object[])
  92. */
  93. public Object[] getPath() {
  94. int i = getPathCount();
  95. Object[] result = new Object[i--];
  96. for(TreePath path = this; path != null; path = path.parentPath) {
  97. result[i--] = path.lastPathComponent;
  98. }
  99. return result;
  100. }
  101. /**
  102. * Returns the last component of this path. For a path
  103. * returned by the DefaultTreeModel, that is the TreeNode object
  104. * for the node specified by the path.
  105. *
  106. * @return the Object at the end of the path
  107. * @see #TreePath(Object[])
  108. */
  109. public Object getLastPathComponent() {
  110. return lastPathComponent;
  111. }
  112. /**
  113. * Returns the number of elements in the path.
  114. *
  115. * @return an int giving a count of items the path
  116. */
  117. public int getPathCount() {
  118. int result = 0;
  119. for(TreePath path = this; path != null; path = path.parentPath) {
  120. result++;
  121. }
  122. return result;
  123. }
  124. /**
  125. * Returns the path component at the specified index.
  126. *
  127. * @param element an int specifying an element in the path, where
  128. * 0 is the first element in the path
  129. * @return the Object at that index location
  130. * @throws IllegalArgumentException if the index is beyond the length
  131. * of the path
  132. * @see #TreePath(Object[])
  133. */
  134. public Object getPathComponent(int element) {
  135. int pathLength = getPathCount();
  136. if(element < 0 || element >= pathLength)
  137. throw new IllegalArgumentException("Index " + element + " is out of the specified range");
  138. TreePath path = this;
  139. for(int i = pathLength-1; i != element; i--) {
  140. path = path.parentPath;
  141. }
  142. return path.lastPathComponent;
  143. }
  144. /**
  145. * Tests two TreePaths for equality by checking each element of the
  146. * paths for equality.
  147. *
  148. * @param o the Object to compare
  149. */
  150. public boolean equals(Object o) {
  151. if(o == this)
  152. return true;
  153. if(o instanceof TreePath) {
  154. TreePath oTreePath = (TreePath)o;
  155. if(getPathCount() != oTreePath.getPathCount())
  156. return false;
  157. for(TreePath path = this; path != null; path = path.parentPath) {
  158. if (!(path.lastPathComponent.equals
  159. (oTreePath.lastPathComponent))) {
  160. return false;
  161. }
  162. oTreePath = oTreePath.parentPath;
  163. }
  164. return true;
  165. }
  166. return false;
  167. }
  168. /**
  169. * Returns the hashCode for the object. The hash code of a TreePath
  170. * is defined to be the hash code of the last component in the path.
  171. *
  172. * @return the hashCode for the object
  173. */
  174. public int hashCode() {
  175. return lastPathComponent.hashCode();
  176. }
  177. /**
  178. * Returns true if the specified node is a descendant of this
  179. * TreePath. A TreePath, child, is a descendent of another TreePath,
  180. * parent, if child contains all of the components that make up
  181. * parent's path.
  182. *
  183. * @return true if aTreePath is a descendant of the receiver.
  184. */
  185. public boolean isDescendant(TreePath aTreePath) {
  186. if(aTreePath == this)
  187. return true;
  188. if(aTreePath != null) {
  189. int pathLength = getPathCount();
  190. int oPathLength = aTreePath.getPathCount();
  191. if(oPathLength < pathLength)
  192. // Can't be a descendant, has fewer components in the path.
  193. return false;
  194. while(oPathLength-- > pathLength)
  195. aTreePath = aTreePath.getParentPath();
  196. return equals(aTreePath);
  197. }
  198. return false;
  199. }
  200. /**
  201. * Returns a new path containing all the elements of this receiver
  202. * plus <code>child</code>. This will throw a NullPointerException
  203. * if child is null.
  204. */
  205. public TreePath pathByAddingChild(Object child) {
  206. if(child == null)
  207. throw new NullPointerException("Null child not allowed");
  208. return new TreePath(this, child);
  209. }
  210. /**
  211. * Returns a path containing all the elements of the receiver, accept
  212. * the last path component.
  213. */
  214. public TreePath getParentPath() {
  215. return parentPath;
  216. }
  217. /**
  218. * Returns a string that displays and identifies this
  219. * object's properties.
  220. *
  221. * @return a String representation of this object
  222. */
  223. public String toString() {
  224. StringBuffer tempSpot = new StringBuffer("[");
  225. for(int counter = 0, maxCounter = getPathCount();counter < maxCounter;
  226. counter++) {
  227. if(counter > 0)
  228. tempSpot.append(", ");
  229. tempSpot.append(getPathComponent(counter));
  230. }
  231. tempSpot.append("]");
  232. return tempSpot.toString();
  233. }
  234. // Serialization support.
  235. private void writeObject(ObjectOutputStream s) throws IOException {
  236. s.defaultWriteObject();
  237. Vector values = new Vector();
  238. boolean writePath = true;
  239. if(lastPathComponent != null &&
  240. (lastPathComponent instanceof Serializable)) {
  241. values.addElement("lastPathComponent");
  242. values.addElement(lastPathComponent);
  243. }
  244. s.writeObject(values);
  245. }
  246. private void readObject(ObjectInputStream s)
  247. throws IOException, ClassNotFoundException {
  248. s.defaultReadObject();
  249. Vector values = (Vector)s.readObject();
  250. int indexCounter = 0;
  251. int maxCounter = values.size();
  252. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  253. equals("lastPathComponent")) {
  254. lastPathComponent = values.elementAt(++indexCounter);
  255. indexCounter++;
  256. }
  257. }
  258. }