1 /*
2 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package javax.swing.tree;
27
28 import java.io;
29 import java.beans.ConstructorProperties;
30
31 /**
32 * {@code TreePath} represents an array of objects that uniquely
33 * identify the path to a node in a tree. The elements of the array
34 * are ordered with the root as the first element of the array. For
35 * example, a file on the file system is uniquely identified based on
36 * the array of parent directories and the name of the file. The path
37 * {@code /tmp/foo/bar} could be represented by a {@code TreePath} as
38 * {@code new TreePath(new Object[] {"tmp", "foo", "bar"})}.
39 * <p>
40 * {@code TreePath} is used extensively by {@code JTree} and related classes.
41 * For example, {@code JTree} represents the selection as an array of
42 * {@code TreePath}s. When used with {@code JTree}, the elements of the
43 * path are the objects returned from the {@code TreeModel}. When {@code JTree}
44 * is paired with {@code DefaultTreeModel}, the elements of the
45 * path are {@code TreeNode}s. The following example illustrates extracting
46 * the user object from the selection of a {@code JTree}:
47 * <pre>
48 * DefaultMutableTreeNode root = ...;
49 * DefaultTreeModel model = new DefaultTreeModel(root);
50 * JTree tree = new JTree(model);
51 * ...
52 * TreePath selectedPath = tree.getSelectionPath();
53 * DefaultMutableTreeNode selectedNode =
54 * ((DefaultMutableTreeNode)selectedPath.getLastPathComponent()).
55 * getUserObject();
56 * </pre>
57 * Subclasses typically need override only {@code
58 * getLastPathComponent}, and {@code getParentPath}. As {@code JTree}
59 * internally creates {@code TreePath}s at various points, it's
60 * generally not useful to subclass {@code TreePath} and use with
61 * {@code JTree}.
62 * <p>
63 * While {@code TreePath} is serializable, a {@code
64 * NotSerializableException} is thrown if any elements of the path are
65 * not serializable.
66 * <p>
67 * For further information and examples of using tree paths,
68 * see <a
69 href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
70 * in <em>The Java Tutorial.</em>
71 * <p>
72 * <strong>Warning:</strong>
73 * Serialized objects of this class will not be compatible with
74 * future Swing releases. The current serialization support is
75 * appropriate for short term storage or RMI between applications running
76 * the same version of Swing. As of 1.4, support for long term storage
77 * of all JavaBeans<sup><font size="-2">TM</font></sup>
78 * has been added to the <code>java.beans</code> package.
79 * Please see {@link java.beans.XMLEncoder}.
80 *
81 * @author Scott Violet
82 * @author Philip Milne
83 */
84 public class TreePath extends Object implements Serializable {
85 /** Path representing the parent, null if lastPathComponent represents
86 * the root. */
87 private TreePath parentPath;
88 /** Last path component. */
89 private Object lastPathComponent;
90
91 /**
92 * Creates a {@code TreePath} from an array. The array uniquely
93 * identifies the path to a node.
94 *
95 * @param path an array of objects representing the path to a node
96 * @throws IllegalArgumentException if {@code path} is {@code null},
97 * empty, or contains a {@code null} value
98 */
99 @ConstructorProperties({"path"})
100 public TreePath(Object[] path) {
101 if(path == null || path.length == 0)
102 throw new IllegalArgumentException("path in TreePath must be non null and not empty.");
103 lastPathComponent = path[path.length - 1];
104 if (lastPathComponent == null) {
105 throw new IllegalArgumentException(
106 "Last path component must be non-null");
107 }
108 if(path.length > 1)
109 parentPath = new TreePath(path, path.length - 1);
110 }
111
112 /**
113 * Creates a {@code TreePath} containing a single element. This is
114 * used to construct a {@code TreePath} identifying the root.
115 *
116 * @param lastPathComponent the root
117 * @see #TreePath(Object[])
118 * @throws IllegalArgumentException if {@code lastPathComponent} is
119 * {@code null}
120 */
121 public TreePath(Object lastPathComponent) {
122 if(lastPathComponent == null)
123 throw new IllegalArgumentException("path in TreePath must be non null.");
124 this.lastPathComponent = lastPathComponent;
125 parentPath = null;
126 }
127
128 /**
129 * Creates a {@code TreePath} with the specified parent and element.
130 *
131 * @param parent the path to the parent, or {@code null} to indicate
132 * the root
133 * @param lastPathComponent the last path element
134 * @throws IllegalArgumentException if {@code lastPathComponent} is
135 * {@code null}
136 */
137 protected TreePath(TreePath parent, Object lastPathComponent) {
138 if(lastPathComponent == null)
139 throw new IllegalArgumentException("path in TreePath must be non null.");
140 parentPath = parent;
141 this.lastPathComponent = lastPathComponent;
142 }
143
144 /**
145 * Creates a {@code TreePath} from an array. The returned
146 * {@code TreePath} represents the elements of the array from
147 * {@code 0} to {@code length - 1}.
148 * <p>
149 * This constructor is used internally, and generally not useful outside
150 * of subclasses.
151 *
152 * @param path the array to create the {@code TreePath} from
153 * @param length identifies the number of elements in {@code path} to
154 * create the {@code TreePath} from
155 * @throws NullPointerException if {@code path} is {@code null}
156 * @throws ArrayIndexOutOfBoundsException if {@code length - 1} is
157 * outside the range of the array
158 * @throws IllegalArgumentException if any of the elements from
159 * {@code 0} to {@code length - 1} are {@code null}
160 */
161 protected TreePath(Object[] path, int length) {
162 lastPathComponent = path[length - 1];
163 if (lastPathComponent == null) {
164 throw new IllegalArgumentException(
165 "Path elements must be non-null");
166 }
167 if(length > 1)
168 parentPath = new TreePath(path, length - 1);
169 }
170
171 /**
172 * Creates an empty {@code TreePath}. This is provided for
173 * subclasses that represent paths in a different
174 * manner. Subclasses that use this constructor must override
175 * {@code getLastPathComponent}, and {@code getParentPath}.
176 */
177 protected TreePath() {
178 }
179
180 /**
181 * Returns an ordered array of the elements of this {@code TreePath}.
182 * The first element is the root.
183 *
184 * @return an array of the elements in this {@code TreePath}
185 */
186 public Object[] getPath() {
187 int i = getPathCount();
188 Object[] result = new Object[i--];
189
190 for(TreePath path = this; path != null; path = path.getParentPath()) {
191 result[i--] = path.getLastPathComponent();
192 }
193 return result;
194 }
195
196 /**
197 * Returns the last element of this path.
198 *
199 * @return the last element in the path
200 */
201 public Object getLastPathComponent() {
202 return lastPathComponent;
203 }
204
205 /**
206 * Returns the number of elements in the path.
207 *
208 * @return the number of elements in the path
209 */
210 public int getPathCount() {
211 int result = 0;
212 for(TreePath path = this; path != null; path = path.getParentPath()) {
213 result++;
214 }
215 return result;
216 }
217
218 /**
219 * Returns the path element at the specified index.
220 *
221 * @param index the index of the element requested
222 * @return the element at the specified index
223 * @throws IllegalArgumentException if the index is outside the
224 * range of this path
225 */
226 public Object getPathComponent(int index) {
227 int pathLength = getPathCount();
228
229 if(index < 0 || index >= pathLength)
230 throw new IllegalArgumentException("Index " + index +
231 " is out of the specified range");
232
233 TreePath path = this;
234
235 for(int i = pathLength-1; i != index; i--) {
236 path = path.getParentPath();
237 }
238 return path.getLastPathComponent();
239 }
240
241 /**
242 * Compares this {@code TreePath} to the specified object. This returns
243 * {@code true} if {@code o} is a {@code TreePath} with the exact
244 * same elements (as determined by using {@code equals} on each
245 * element of the path).
246 *
247 * @param o the object to compare
248 */
249 public boolean equals(Object o) {
250 if(o == this)
251 return true;
252 if(o instanceof TreePath) {
253 TreePath oTreePath = (TreePath)o;
254
255 if(getPathCount() != oTreePath.getPathCount())
256 return false;
257 for(TreePath path = this; path != null;
258 path = path.getParentPath()) {
259 if (!(path.getLastPathComponent().equals
260 (oTreePath.getLastPathComponent()))) {
261 return false;
262 }
263 oTreePath = oTreePath.getParentPath();
264 }
265 return true;
266 }
267 return false;
268 }
269
270 /**
271 * Returns the hash code of this {@code TreePath}. The hash code of a
272 * {@code TreePath} is the hash code of the last element in the path.
273 *
274 * @return the hashCode for the object
275 */
276 public int hashCode() {
277 return getLastPathComponent().hashCode();
278 }
279
280 /**
281 * Returns true if <code>aTreePath</code> is a
282 * descendant of this
283 * {@code TreePath}. A {@code TreePath} {@code P1} is a descendant of a
284 * {@code TreePath} {@code P2}
285 * if {@code P1} contains all of the elements that make up
286 * {@code P2's} path.
287 * For example, if this object has the path {@code [a, b]},
288 * and <code>aTreePath</code> has the path {@code [a, b, c]},
289 * then <code>aTreePath</code> is a descendant of this object.
290 * However, if <code>aTreePath</code> has the path {@code [a]},
291 * then it is not a descendant of this object. By this definition
292 * a {@code TreePath} is always considered a descendant of itself.
293 * That is, <code>aTreePath.isDescendant(aTreePath)</code> returns
294 * {@code true}.
295 *
296 * @param aTreePath the {@code TreePath} to check
297 * @return true if <code>aTreePath</code> is a descendant of this path
298 */
299 public boolean isDescendant(TreePath aTreePath) {
300 if(aTreePath == this)
301 return true;
302
303 if(aTreePath != null) {
304 int pathLength = getPathCount();
305 int oPathLength = aTreePath.getPathCount();
306
307 if(oPathLength < pathLength)
308 // Can't be a descendant, has fewer components in the path.
309 return false;
310 while(oPathLength-- > pathLength)
311 aTreePath = aTreePath.getParentPath();
312 return equals(aTreePath);
313 }
314 return false;
315 }
316
317 /**
318 * Returns a new path containing all the elements of this path
319 * plus <code>child</code>. <code>child</code> is the last element
320 * of the newly created {@code TreePath}.
321 *
322 * @param child the path element to add
323 * @throws NullPointerException if {@code child} is {@code null}
324 */
325 public TreePath pathByAddingChild(Object child) {
326 if(child == null)
327 throw new NullPointerException("Null child not allowed");
328
329 return new TreePath(this, child);
330 }
331
332 /**
333 * Returns the {@code TreePath} of the parent. A return value of
334 * {@code null} indicates this is the root node.
335 *
336 * @return the parent path
337 */
338 public TreePath getParentPath() {
339 return parentPath;
340 }
341
342 /**
343 * Returns a string that displays and identifies this
344 * object's properties.
345 *
346 * @return a String representation of this object
347 */
348 public String toString() {
349 StringBuffer tempSpot = new StringBuffer("[");
350
351 for(int counter = 0, maxCounter = getPathCount();counter < maxCounter;
352 counter++) {
353 if(counter > 0)
354 tempSpot.append(", ");
355 tempSpot.append(getPathComponent(counter));
356 }
357 tempSpot.append("]");
358 return tempSpot.toString();
359 }
360 }