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 }