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 }