1/*
2 * Copyright (c) 1997, 2014, 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
26package javax.swing.tree;
27
28import java.io.*;
29import 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://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&trade;
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
85public class TreePath extends Object 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}
364