1 /* 2 * Copyright (c) 1997, 2007, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * 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 }