001 /*
002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.swing.tree;
027
028 import java.io.*;
029 import java.beans.ConstructorProperties;
030
031 /**
032 * {@code TreePath} represents an array of objects that uniquely
033 * identify the path to a node in a tree. The elements of the array
034 * are ordered with the root as the first element of the array. For
035 * example, a file on the file system is uniquely identified based on
036 * the array of parent directories and the name of the file. The path
037 * {@code /tmp/foo/bar} could be represented by a {@code TreePath} as
038 * {@code new TreePath(new Object[] {"tmp", "foo", "bar"})}.
039 * <p>
040 * {@code TreePath} is used extensively by {@code JTree} and related classes.
041 * For example, {@code JTree} represents the selection as an array of
042 * {@code TreePath}s. When used with {@code JTree}, the elements of the
043 * path are the objects returned from the {@code TreeModel}. When {@code JTree}
044 * is paired with {@code DefaultTreeModel}, the elements of the
045 * path are {@code TreeNode}s. The following example illustrates extracting
046 * the user object from the selection of a {@code JTree}:
047 * <pre>
048 * DefaultMutableTreeNode root = ...;
049 * DefaultTreeModel model = new DefaultTreeModel(root);
050 * JTree tree = new JTree(model);
051 * ...
052 * TreePath selectedPath = tree.getSelectionPath();
053 * DefaultMutableTreeNode selectedNode =
054 * ((DefaultMutableTreeNode)selectedPath.getLastPathComponent()).
055 * getUserObject();
056 * </pre>
057 * Subclasses typically need override only {@code
058 * getLastPathComponent}, and {@code getParentPath}. As {@code JTree}
059 * internally creates {@code TreePath}s at various points, it's
060 * generally not useful to subclass {@code TreePath} and use with
061 * {@code JTree}.
062 * <p>
063 * While {@code TreePath} is serializable, a {@code
064 * NotSerializableException} is thrown if any elements of the path are
065 * not serializable.
066 * <p>
067 * For further information and examples of using tree paths,
068 * see <a
069 href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
070 * in <em>The Java Tutorial.</em>
071 * <p>
072 * <strong>Warning:</strong>
073 * Serialized objects of this class will not be compatible with
074 * future Swing releases. The current serialization support is
075 * appropriate for short term storage or RMI between applications running
076 * the same version of Swing. As of 1.4, support for long term storage
077 * of all JavaBeans<sup><font size="-2">TM</font></sup>
078 * has been added to the <code>java.beans</code> package.
079 * Please see {@link java.beans.XMLEncoder}.
080 *
081 * @version 1.37 05/05/07
082 * @author Scott Violet
083 * @author Philip Milne
084 */
085 public class TreePath extends Object implements Serializable {
086 /** Path representing the parent, null if lastPathComponent represents
087 * the root. */
088 private TreePath parentPath;
089 /** Last path component. */
090 private Object lastPathComponent;
091
092 /**
093 * Creates a {@code TreePath} from an array. The array uniquely
094 * identifies the path to a node.
095 *
096 * @param path an array of objects representing the path to a node
097 * @throws IllegalArgumentException if {@code path} is {@code null},
098 * empty, or contains a {@code null} value
099 */
100 @ConstructorProperties({"path"})
101 public TreePath(Object[] path) {
102 if (path == null || path.length == 0)
103 throw new IllegalArgumentException(
104 "path in TreePath must be non null and not empty.");
105 lastPathComponent = path[path.length - 1];
106 if (lastPathComponent == null) {
107 throw new IllegalArgumentException(
108 "Last path component must be non-null");
109 }
110 if (path.length > 1)
111 parentPath = new TreePath(path, path.length - 1);
112 }
113
114 /**
115 * Creates a {@code TreePath} containing a single element. This is
116 * used to construct a {@code TreePath} identifying the root.
117 *
118 * @param lastPathComponent the root
119 * @see #TreePath(Object[])
120 * @throws IllegalArgumentException if {@code lastPathComponent} is
121 * {@code null}
122 */
123 public TreePath(Object lastPathComponent) {
124 if (lastPathComponent == null)
125 throw new IllegalArgumentException(
126 "path in TreePath must be non null.");
127 this .lastPathComponent = lastPathComponent;
128 parentPath = null;
129 }
130
131 /**
132 * Creates a {@code TreePath} with the specified parent and element.
133 *
134 * @param parent the path to the parent, or {@code null} to indicate
135 * the root
136 * @param lastPathComponent the last path element
137 * @throws IllegalArgumentException if {@code lastPathComponent} is
138 * {@code null}
139 */
140 protected TreePath(TreePath parent, Object lastPathComponent) {
141 if (lastPathComponent == null)
142 throw new IllegalArgumentException(
143 "path in TreePath must be non null.");
144 parentPath = parent;
145 this .lastPathComponent = lastPathComponent;
146 }
147
148 /**
149 * Creates a {@code TreePath} from an array. The returned
150 * {@code TreePath} represents the elements of the array from
151 * {@code 0} to {@code length - 1}.
152 * <p>
153 * This constructor is used internally, and generally not useful outside
154 * of subclasses.
155 *
156 * @param path the array to create the {@code TreePath} from
157 * @param length identifies the number of elements in {@code path} to
158 * create the {@code TreePath} from
159 * @throws NullPointerException if {@code path} is {@code null}
160 * @throws ArrayIndexOutOfBoundsException if {@code length - 1} is
161 * outside the range of the array
162 * @throws IllegalArgumentException if any of the elements from
163 * {@code 0} to {@code length - 1} are {@code null}
164 */
165 protected TreePath(Object[] path, int length) {
166 lastPathComponent = path[length - 1];
167 if (lastPathComponent == null) {
168 throw new IllegalArgumentException(
169 "Path elements must be non-null");
170 }
171 if (length > 1)
172 parentPath = new TreePath(path, length - 1);
173 }
174
175 /**
176 * Creates an empty {@code TreePath}. This is provided for
177 * subclasses that represent paths in a different
178 * manner. Subclasses that use this constructor must override
179 * {@code getLastPathComponent}, and {@code getParentPath}.
180 */
181 protected TreePath() {
182 }
183
184 /**
185 * Returns an ordered array of the elements of this {@code TreePath}.
186 * The first element is the root.
187 *
188 * @return an array of the elements in this {@code TreePath}
189 */
190 public Object[] getPath() {
191 int i = getPathCount();
192 Object[] result = new Object[i--];
193
194 for (TreePath path = this ; path != null; path = path
195 .getParentPath()) {
196 result[i--] = path.getLastPathComponent();
197 }
198 return result;
199 }
200
201 /**
202 * Returns the last element of this path.
203 *
204 * @return the last element in the path
205 */
206 public Object getLastPathComponent() {
207 return lastPathComponent;
208 }
209
210 /**
211 * Returns the number of elements in the path.
212 *
213 * @return the number of elements in the path
214 */
215 public int getPathCount() {
216 int result = 0;
217 for (TreePath path = this ; path != null; path = path
218 .getParentPath()) {
219 result++;
220 }
221 return result;
222 }
223
224 /**
225 * Returns the path element at the specified index.
226 *
227 * @param index the index of the element requested
228 * @return the element at the specified index
229 * @throws IllegalArgumentException if the index is outside the
230 * range of this path
231 */
232 public Object getPathComponent(int index) {
233 int pathLength = getPathCount();
234
235 if (index < 0 || index >= pathLength)
236 throw new IllegalArgumentException("Index " + index
237 + " is out of the specified range");
238
239 TreePath path = this ;
240
241 for (int i = pathLength - 1; i != index; i--) {
242 path = path.getParentPath();
243 }
244 return path.getLastPathComponent();
245 }
246
247 /**
248 * Compares this {@code TreePath} to the specified object. This returns
249 * {@code true} if {@code o} is a {@code TreePath} with the exact
250 * same elements (as determined by using {@code equals} on each
251 * element of the path).
252 *
253 * @param o the object to compare
254 */
255 public boolean equals(Object o) {
256 if (o == this )
257 return true;
258 if (o instanceof TreePath) {
259 TreePath oTreePath = (TreePath) o;
260
261 if (getPathCount() != oTreePath.getPathCount())
262 return false;
263 for (TreePath path = this ; path != null; path = path
264 .getParentPath()) {
265 if (!(path.getLastPathComponent().equals(oTreePath
266 .getLastPathComponent()))) {
267 return false;
268 }
269 oTreePath = oTreePath.getParentPath();
270 }
271 return true;
272 }
273 return false;
274 }
275
276 /**
277 * Returns the hash code of this {@code TreePath}. The hash code of a
278 * {@code TreePath} is the hash code of the last element in the path.
279 *
280 * @return the hashCode for the object
281 */
282 public int hashCode() {
283 return getLastPathComponent().hashCode();
284 }
285
286 /**
287 * Returns true if <code>aTreePath</code> is a
288 * descendant of this
289 * {@code TreePath}. A {@code TreePath} {@code P1} is a descendant of a
290 * {@code TreePath} {@code P2}
291 * if {@code P1} contains all of the elements that make up
292 * {@code P2's} path.
293 * For example, if this object has the path {@code [a, b]},
294 * and <code>aTreePath</code> has the path {@code [a, b, c]},
295 * then <code>aTreePath</code> is a descendant of this object.
296 * However, if <code>aTreePath</code> has the path {@code [a]},
297 * then it is not a descendant of this object. By this definition
298 * a {@code TreePath} is always considered a descendant of itself.
299 * That is, <code>aTreePath.isDescendant(aTreePath)</code> returns
300 * {@code true}.
301 *
302 * @param aTreePath the {@code TreePath} to check
303 * @return true if <code>aTreePath</code> is a descendant of this path
304 */
305 public boolean isDescendant(TreePath aTreePath) {
306 if (aTreePath == this )
307 return true;
308
309 if (aTreePath != null) {
310 int pathLength = getPathCount();
311 int oPathLength = aTreePath.getPathCount();
312
313 if (oPathLength < pathLength)
314 // Can't be a descendant, has fewer components in the path.
315 return false;
316 while (oPathLength-- > pathLength)
317 aTreePath = aTreePath.getParentPath();
318 return equals(aTreePath);
319 }
320 return false;
321 }
322
323 /**
324 * Returns a new path containing all the elements of this path
325 * plus <code>child</code>. <code>child</code> is the last element
326 * of the newly created {@code TreePath}.
327 *
328 * @param child the path element to add
329 * @throws NullPointerException if {@code child} is {@code null}
330 */
331 public TreePath pathByAddingChild(Object child) {
332 if (child == null)
333 throw new NullPointerException("Null child not allowed");
334
335 return new TreePath(this , child);
336 }
337
338 /**
339 * Returns the {@code TreePath} of the parent. A return value of
340 * {@code null} indicates this is the root node.
341 *
342 * @return the parent path
343 */
344 public TreePath getParentPath() {
345 return parentPath;
346 }
347
348 /**
349 * Returns a string that displays and identifies this
350 * object's properties.
351 *
352 * @return a String representation of this object
353 */
354 public String toString() {
355 StringBuffer tempSpot = new StringBuffer("[");
356
357 for (int counter = 0, maxCounter = getPathCount(); counter < maxCounter; counter++) {
358 if (counter > 0)
359 tempSpot.append(", ");
360 tempSpot.append(getPathComponent(counter));
361 }
362 tempSpot.append("]");
363 return tempSpot.toString();
364 }
365 }
|