/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.swing.tree;
import java.io.*;
import java.beans.
ConstructorProperties;
/**
* {@code TreePath} represents an array of objects that uniquely
* identify the path to a node in a tree. The elements of the array
* are ordered with the root as the first element of the array. For
* example, a file on the file system is uniquely identified based on
* the array of parent directories and the name of the file. The path
* {@code /tmp/foo/bar} could be represented by a {@code TreePath} as
* {@code new TreePath(new Object[] {"tmp", "foo", "bar"})}.
* <p>
* {@code TreePath} is used extensively by {@code JTree} and related classes.
* For example, {@code JTree} represents the selection as an array of
* {@code TreePath}s. When used with {@code JTree}, the elements of the
* path are the objects returned from the {@code TreeModel}. When {@code JTree}
* is paired with {@code DefaultTreeModel}, the elements of the
* path are {@code TreeNode}s. The following example illustrates extracting
* the user object from the selection of a {@code JTree}:
* <pre>
* DefaultMutableTreeNode root = ...;
* DefaultTreeModel model = new DefaultTreeModel(root);
* JTree tree = new JTree(model);
* ...
* TreePath selectedPath = tree.getSelectionPath();
* DefaultMutableTreeNode selectedNode =
* ((DefaultMutableTreeNode)selectedPath.getLastPathComponent()).
* getUserObject();
* </pre>
* Subclasses typically need override only {@code
* getLastPathComponent}, and {@code getParentPath}. As {@code JTree}
* internally creates {@code TreePath}s at various points, it's
* generally not useful to subclass {@code TreePath} and use with
* {@code JTree}.
* <p>
* While {@code TreePath} is serializable, a {@code
* NotSerializableException} is thrown if any elements of the path are
* not serializable.
* <p>
* For further information and examples of using tree paths,
* see <a
href="https://docs.oracle.com/javase/tutorial/uiswing/components/tree.html">How to Use Trees</a>
* in <em>The Java Tutorial.</em>
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans™
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @author Scott Violet
* @author Philip Milne
*/
public class
TreePath extends
Object implements
Serializable {
/** Path representing the parent, null if lastPathComponent represents
* the root. */
private
TreePath parentPath;
/** Last path component. */
private
Object lastPathComponent;
/**
* Creates a {@code TreePath} from an array. The array uniquely
* identifies the path to a node.
*
* @param path an array of objects representing the path to a node
* @throws IllegalArgumentException if {@code path} is {@code null},
* empty, or contains a {@code null} value
*/
@
ConstructorProperties({"path"})
public
TreePath(
Object[]
path) {
if(
path == null ||
path.length == 0)
throw new
IllegalArgumentException("path in TreePath must be non null and not empty.");
lastPathComponent =
path[
path.length - 1];
if (
lastPathComponent == null) {
throw new
IllegalArgumentException(
"Last path component must be non-null");
}
if(
path.length > 1)
parentPath = new
TreePath(
path,
path.length - 1);
}
/**
* Creates a {@code TreePath} containing a single element. This is
* used to construct a {@code TreePath} identifying the root.
*
* @param lastPathComponent the root
* @see #TreePath(Object[])
* @throws IllegalArgumentException if {@code lastPathComponent} is
* {@code null}
*/
public
TreePath(
Object lastPathComponent) {
if(
lastPathComponent == null)
throw new
IllegalArgumentException("path in TreePath must be non null.");
this.
lastPathComponent =
lastPathComponent;
parentPath = null;
}
/**
* Creates a {@code TreePath} with the specified parent and element.
*
* @param parent the path to the parent, or {@code null} to indicate
* the root
* @param lastPathComponent the last path element
* @throws IllegalArgumentException if {@code lastPathComponent} is
* {@code null}
*/
protected
TreePath(
TreePath parent,
Object lastPathComponent) {
if(
lastPathComponent == null)
throw new
IllegalArgumentException("path in TreePath must be non null.");
parentPath =
parent;
this.
lastPathComponent =
lastPathComponent;
}
/**
* Creates a {@code TreePath} from an array. The returned
* {@code TreePath} represents the elements of the array from
* {@code 0} to {@code length - 1}.
* <p>
* This constructor is used internally, and generally not useful outside
* of subclasses.
*
* @param path the array to create the {@code TreePath} from
* @param length identifies the number of elements in {@code path} to
* create the {@code TreePath} from
* @throws NullPointerException if {@code path} is {@code null}
* @throws ArrayIndexOutOfBoundsException if {@code length - 1} is
* outside the range of the array
* @throws IllegalArgumentException if any of the elements from
* {@code 0} to {@code length - 1} are {@code null}
*/
protected
TreePath(
Object[]
path, int
length) {
lastPathComponent =
path[
length - 1];
if (
lastPathComponent == null) {
throw new
IllegalArgumentException(
"Path elements must be non-null");
}
if(
length > 1)
parentPath = new
TreePath(
path,
length - 1);
}
/**
* Creates an empty {@code TreePath}. This is provided for
* subclasses that represent paths in a different
* manner. Subclasses that use this constructor must override
* {@code getLastPathComponent}, and {@code getParentPath}.
*/
protected
TreePath() {
}
/**
* Returns an ordered array of the elements of this {@code TreePath}.
* The first element is the root.
*
* @return an array of the elements in this {@code TreePath}
*/
public
Object[]
getPath() {
int
i =
getPathCount();
Object[]
result = new
Object[
i--];
for(
TreePath path = this;
path != null;
path =
path.
getParentPath()) {
result[
i--] =
path.
getLastPathComponent();
}
return
result;
}
/**
* Returns the last element of this path.
*
* @return the last element in the path
*/
public
Object getLastPathComponent() {
return
lastPathComponent;
}
/**
* Returns the number of elements in the path.
*
* @return the number of elements in the path
*/
public int
getPathCount() {
int
result = 0;
for(
TreePath path = this;
path != null;
path =
path.
getParentPath()) {
result++;
}
return
result;
}
/**
* Returns the path element at the specified index.
*
* @param index the index of the element requested
* @return the element at the specified index
* @throws IllegalArgumentException if the index is outside the
* range of this path
*/
public
Object getPathComponent(int
index) {
int
pathLength =
getPathCount();
if(
index < 0 ||
index >=
pathLength)
throw new
IllegalArgumentException("Index " +
index +
" is out of the specified range");
TreePath path = this;
for(int
i =
pathLength-1;
i !=
index;
i--) {
path =
path.
getParentPath();
}
return
path.
getLastPathComponent();
}
/**
* Compares this {@code TreePath} to the specified object. This returns
* {@code true} if {@code o} is a {@code TreePath} with the exact
* same elements (as determined by using {@code equals} on each
* element of the path).
*
* @param o the object to compare
*/
public boolean
equals(
Object o) {
if(
o == this)
return true;
if(
o instanceof
TreePath) {
TreePath oTreePath = (
TreePath)
o;
if(
getPathCount() !=
oTreePath.
getPathCount())
return false;
for(
TreePath path = this;
path != null;
path =
path.
getParentPath()) {
if (!(
path.
getLastPathComponent().
equals
(
oTreePath.
getLastPathComponent()))) {
return false;
}
oTreePath =
oTreePath.
getParentPath();
}
return true;
}
return false;
}
/**
* Returns the hash code of this {@code TreePath}. The hash code of a
* {@code TreePath} is the hash code of the last element in the path.
*
* @return the hashCode for the object
*/
public int
hashCode() {
return
getLastPathComponent().
hashCode();
}
/**
* Returns true if <code>aTreePath</code> is a
* descendant of this
* {@code TreePath}. A {@code TreePath} {@code P1} is a descendant of a
* {@code TreePath} {@code P2}
* if {@code P1} contains all of the elements that make up
* {@code P2's} path.
* For example, if this object has the path {@code [a, b]},
* and <code>aTreePath</code> has the path {@code [a, b, c]},
* then <code>aTreePath</code> is a descendant of this object.
* However, if <code>aTreePath</code> has the path {@code [a]},
* then it is not a descendant of this object. By this definition
* a {@code TreePath} is always considered a descendant of itself.
* That is, <code>aTreePath.isDescendant(aTreePath)</code> returns
* {@code true}.
*
* @param aTreePath the {@code TreePath} to check
* @return true if <code>aTreePath</code> is a descendant of this path
*/
public boolean
isDescendant(
TreePath aTreePath) {
if(
aTreePath == this)
return true;
if(
aTreePath != null) {
int
pathLength =
getPathCount();
int
oPathLength =
aTreePath.
getPathCount();
if(
oPathLength <
pathLength)
// Can't be a descendant, has fewer components in the path.
return false;
while(
oPathLength-- >
pathLength)
aTreePath =
aTreePath.
getParentPath();
return
equals(
aTreePath);
}
return false;
}
/**
* Returns a new path containing all the elements of this path
* plus <code>child</code>. <code>child</code> is the last element
* of the newly created {@code TreePath}.
*
* @param child the path element to add
* @throws NullPointerException if {@code child} is {@code null}
*/
public
TreePath pathByAddingChild(
Object child) {
if(
child == null)
throw new
NullPointerException("Null child not allowed");
return new
TreePath(this,
child);
}
/**
* Returns the {@code TreePath} of the parent. A return value of
* {@code null} indicates this is the root node.
*
* @return the parent path
*/
public
TreePath getParentPath() {
return
parentPath;
}
/**
* Returns a string that displays and identifies this
* object's properties.
*
* @return a String representation of this object
*/
public
String toString() {
StringBuffer tempSpot = new
StringBuffer("[");
for(int
counter = 0,
maxCounter =
getPathCount();
counter <
maxCounter;
counter++) {
if(
counter > 0)
tempSpot.
append(", ");
tempSpot.
append(
getPathComponent(
counter));
}
tempSpot.
append("]");
return
tempSpot.
toString();
}
}