1 /* 2 * Copyright (c) 1997, 2017, 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="https://docs.oracle.com/javase/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 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 @SuppressWarnings("serial") // Same-version serialization only 85 public class TreePath implements Serializable { 86 /** Path representing the parent, null if lastPathComponent represents 87 * the root. */ 88 private TreePath parentPath; 89 /** Last path component. */ 90 private Object lastPathComponent; 91 92 /** 93 * Creates a {@code TreePath} from an array. The array uniquely 94 * identifies the path to a node. 95 * 96 * @param path an array of objects representing the path to a node 97 * @throws IllegalArgumentException if {@code path} is {@code null}, 98 * empty, or contains a {@code null} value 99 */ 100 @ConstructorProperties({"path"}) 101 public TreePath(Object[] path) { 102 if(path == null || path.length == 0) 103 throw new IllegalArgumentException("path in TreePath must be non null and not empty."); 104 lastPathComponent = path[path.length - 1]; 105 if (lastPathComponent == null) { 106 throw new IllegalArgumentException( 107 "Last path component must be non-null"); 108 } 109 if(path.length > 1) 110 parentPath = new TreePath(path, path.length - 1); 111 } 112 113 /** 114 * Creates a {@code TreePath} containing a single element. This is 115 * used to construct a {@code TreePath} identifying the root. 116 * 117 * @param lastPathComponent the root 118 * @see #TreePath(Object[]) 119 * @throws IllegalArgumentException if {@code lastPathComponent} is 120 * {@code null} 121 */ 122 public TreePath(Object lastPathComponent) { 123 if(lastPathComponent == null) 124 throw new IllegalArgumentException("path in TreePath must be non null."); 125 this.lastPathComponent = lastPathComponent; 126 parentPath = null; 127 } 128 129 /** 130 * Creates a {@code TreePath} with the specified parent and element. 131 * 132 * @param parent the path to the parent, or {@code null} to indicate 133 * the root 134 * @param lastPathComponent the last path element 135 * @throws IllegalArgumentException if {@code lastPathComponent} is 136 * {@code null} 137 */ 138 protected TreePath(TreePath parent, Object lastPathComponent) { 139 if(lastPathComponent == null) 140 throw new IllegalArgumentException("path in TreePath must be non null."); 141 parentPath = parent; 142 this.lastPathComponent = lastPathComponent; 143 } 144 145 /** 146 * Creates a {@code TreePath} from an array. The returned 147 * {@code TreePath} represents the elements of the array from 148 * {@code 0} to {@code length - 1}. 149 * <p> 150 * This constructor is used internally, and generally not useful outside 151 * of subclasses. 152 * 153 * @param path the array to create the {@code TreePath} from 154 * @param length identifies the number of elements in {@code path} to 155 * create the {@code TreePath} from 156 * @throws NullPointerException if {@code path} is {@code null} 157 * @throws ArrayIndexOutOfBoundsException if {@code length - 1} is 158 * outside the range of the array 159 * @throws IllegalArgumentException if any of the elements from 160 * {@code 0} to {@code length - 1} are {@code null} 161 */ 162 protected TreePath(Object[] path, int length) { 163 lastPathComponent = path[length - 1]; 164 if (lastPathComponent == null) { 165 throw new IllegalArgumentException( 166 "Path elements must be non-null"); 167 } 168 if(length > 1) 169 parentPath = new TreePath(path, length - 1); 170 } 171 172 /** 173 * Creates an empty {@code TreePath}. This is provided for 174 * subclasses that represent paths in a different 175 * manner. Subclasses that use this constructor must override 176 * {@code getLastPathComponent}, and {@code getParentPath}. 177 */ 178 protected TreePath() { 179 } 180 181 /** 182 * Returns an ordered array of the elements of this {@code TreePath}. 183 * The first element is the root. 184 * 185 * @return an array of the elements in this {@code TreePath} 186 */ 187 public Object[] getPath() { 188 int i = getPathCount(); 189 Object[] result = new Object[i--]; 190 191 for(TreePath path = this; path != null; path = path.getParentPath()) { 192 result[i--] = path.getLastPathComponent(); 193 } 194 return result; 195 } 196 197 /** 198 * Returns the last element of this path. 199 * 200 * @return the last element in the path 201 */ 202 public Object getLastPathComponent() { 203 return lastPathComponent; 204 } 205 206 /** 207 * Returns the number of elements in the path. 208 * 209 * @return the number of elements in the path 210 */ 211 public int getPathCount() { 212 int result = 0; 213 for(TreePath path = this; path != null; path = path.getParentPath()) { 214 result++; 215 } 216 return result; 217 } 218 219 /** 220 * Returns the path element at the specified index. 221 * 222 * @param index the index of the element requested 223 * @return the element at the specified index 224 * @throws IllegalArgumentException if the index is outside the 225 * range of this path 226 */ 227 public Object getPathComponent(int index) { 228 int pathLength = getPathCount(); 229 230 if(index < 0 || index >= pathLength) 231 throw new IllegalArgumentException("Index " + index + 232 " is out of the specified range"); 233 234 TreePath path = this; 235 236 for(int i = pathLength-1; i != index; i--) { 237 path = path.getParentPath(); 238 } 239 return path.getLastPathComponent(); 240 } 241 242 /** 243 * Compares this {@code TreePath} to the specified object. This returns 244 * {@code true} if {@code o} is a {@code TreePath} with the exact 245 * same elements (as determined by using {@code equals} on each 246 * element of the path). 247 * 248 * @param o the object to compare 249 */ 250 public boolean equals(Object o) { 251 if(o == this) 252 return true; 253 if(o instanceof TreePath) { 254 TreePath oTreePath = (TreePath)o; 255 256 if(getPathCount() != oTreePath.getPathCount()) 257 return false; 258 for(TreePath path = this; path != null; 259 path = path.getParentPath()) { 260 if (!(path.getLastPathComponent().equals 261 (oTreePath.getLastPathComponent()))) { 262 return false; 263 } 264 oTreePath = oTreePath.getParentPath(); 265 } 266 return true; 267 } 268 return false; 269 } 270 271 /** 272 * Returns the hash code of this {@code TreePath}. The hash code of a 273 * {@code TreePath} is the hash code of the last element in the path. 274 * 275 * @return the hashCode for the object 276 */ 277 public int hashCode() { 278 return getLastPathComponent().hashCode(); 279 } 280 281 /** 282 * Returns true if <code>aTreePath</code> is a 283 * descendant of this 284 * {@code TreePath}. A {@code TreePath} {@code P1} is a descendant of a 285 * {@code TreePath} {@code P2} 286 * if {@code P1} contains all of the elements that make up 287 * {@code P2's} path. 288 * For example, if this object has the path {@code [a, b]}, 289 * and <code>aTreePath</code> has the path {@code [a, b, c]}, 290 * then <code>aTreePath</code> is a descendant of this object. 291 * However, if <code>aTreePath</code> has the path {@code [a]}, 292 * then it is not a descendant of this object. By this definition 293 * a {@code TreePath} is always considered a descendant of itself. 294 * That is, <code>aTreePath.isDescendant(aTreePath)</code> returns 295 * {@code true}. 296 * 297 * @param aTreePath the {@code TreePath} to check 298 * @return true if <code>aTreePath</code> is a descendant of this path 299 */ 300 public boolean isDescendant(TreePath aTreePath) { 301 if(aTreePath == this) 302 return true; 303 304 if(aTreePath != null) { 305 int pathLength = getPathCount(); 306 int oPathLength = aTreePath.getPathCount(); 307 308 if(oPathLength < pathLength) 309 // Can't be a descendant, has fewer components in the path. 310 return false; 311 while(oPathLength-- > pathLength) 312 aTreePath = aTreePath.getParentPath(); 313 return equals(aTreePath); 314 } 315 return false; 316 } 317 318 /** 319 * Returns a new path containing all the elements of this path 320 * plus <code>child</code>. <code>child</code> is the last element 321 * of the newly created {@code TreePath}. 322 * 323 * @param child the path element to add 324 * @throws NullPointerException if {@code child} is {@code null} 325 * @return a new path containing all the elements of this path 326 * plus {@code child} 327 */ 328 public TreePath pathByAddingChild(Object child) { 329 if(child == null) 330 throw new NullPointerException("Null child not allowed"); 331 332 return new TreePath(this, child); 333 } 334 335 /** 336 * Returns the {@code TreePath} of the parent. A return value of 337 * {@code null} indicates this is the root node. 338 * 339 * @return the parent path 340 */ 341 public TreePath getParentPath() { 342 return parentPath; 343 } 344 345 /** 346 * Returns a string that displays and identifies this 347 * object's properties. 348 * 349 * @return a String representation of this object 350 */ 351 public String toString() { 352 StringBuilder tempSpot = new StringBuilder("["); 353 354 for(int counter = 0, maxCounter = getPathCount();counter < maxCounter; 355 counter++) { 356 if(counter > 0) 357 tempSpot.append(", "); 358 tempSpot.append(getPathComponent(counter)); 359 } 360 tempSpot.append("]"); 361 return tempSpot.toString(); 362 } 363 }