/* class DefaultMutableTreeNode
*
* Copyright (C) 2003 R M Pitman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package charvax.swing.tree;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Vector;
/**
* A DefaultMutableTreeNode is a general-purpose node in a tree data structure.
* <p>
*
* A tree node may have at most one parent and 0 or more children.
* DefaultMutableTreeNode provides operations for examining and modifying a
* node's parent and children and also operations for examining the tree that
* the node is a part of. A node's tree is the set of all nodes that can be
* reached by starting at the node and following all the possible links to
* parents and children. A node with no parent is the root of its tree; a node
* with no children is a leaf. A tree may consist of many subtrees, each node
* acting as the root for its own subtree.
* <p>
*
* This class provides enumerations for efficiently traversing a tree or
* subtree in various orders or for following the path between two nodes. A
* DefaultMutableTreeNode may also hold a reference to a user object, the use
* of which is left to the user. Asking a DefaultMutableTreeNode for its string
* representation with toString() returns the string representation of its user
* object.
*/
public class DefaultMutableTreeNode implements MutableTreeNode {
/**
* Creates a tree node that has no parent and no children, but which allows
* children.
*/
DefaultMutableTreeNode() {
this(null, true);
}
/**
* Creates a tree node with no parent, no children, but which allows
* children, and initializes it with the specified user object.
*/
DefaultMutableTreeNode(Object userObject) {
this(userObject, true);
}
/**
* Creates a tree node with no parent, no children, initialized with the
* specified user object, and that allows children only if specified.
*/
DefaultMutableTreeNode(Object userObject, boolean allowsChildren) {
_userObject = userObject;
_allowsChildren = allowsChildren;
}
/**
* Adds child to this MutableTreeNode at index. The setParent() method of
* child will be called.
*/
public void insert(MutableTreeNode child, int index) {
if (!_allowsChildren)
throw new IllegalStateException(
"TreeNode does not allow children");
if (_children == null) _children = new Vector<TreeNode>();
_children.insertElementAt(child, index);
child.setParent(this);
}
/**
* Removes the child at index from this MutableTreeNode.
*/
public void remove(int index) {
MutableTreeNode node = (MutableTreeNode) _children.elementAt(index);
if (node != null) this.remove(node);
}
/**
* Removes node from this MutableTreeNode, giving it a null parent. The
* setParent() method of "node" will be called.
*/
public void remove(MutableTreeNode node) {
_children.remove(node);
node.setParent(null);
}
/**
* Resets the user object of this MutableTreeNode to object.
*/
public void setUserObject(Object object) {
_userObject = object;
}
/**
* Removes the subtree rooted at this node from the tree, giving this node
* a null parent. Does nothing if this node is the root of its tree.
*/
public void removeFromParent() {
if (_parent == null) return;
_parent.remove(this);
}
/**
* Sets this node's parent to newParent but does not change the parent's
* child array. This method is called from insert() and remove() to
* reassign a child's parent, it should not be called from anywhere else.
*/
public void setParent(MutableTreeNode newParent) {
// This cast seems reasonable.
_parent = (DefaultMutableTreeNode) newParent;
}
/**
* Returns this node's parent or null if this node has no parent.
*/
public TreeNode getParent() {
return _parent;
}
/**
* Returns the child at the specified index in this node's child array.
*
* @param index
* an index into this node's child array.
* @return the TreeNode in this node's child array at the specified index
*/
public TreeNode getChildAt(int index) {
return (TreeNode) _children.elementAt(index);
}
/**
* Returns the number of children of this node.
*/
public int getChildCount() {
if (_children == null) return 0;
return _children.size();
}
/**
* Returns the index of the specified child in this node's child array. If
* the specified node is not a child of this node, returns -1. This method
* performs a linear search and is O(n) where n is the number of children.
*
* @param aChild
* the TreeNode to search for among this node's children.
* @return an int giving the index of the node in this node's child array,
* or -1 if the specified node is a not a child of this node
*/
public int getIndex(TreeNode aChild) {
if (_children == null) return -1;
return _children.indexOf(aChild);
}
/**
* Creates and returns a forward-order enumeration of this node's children.
* Modifying this node's child array invalidates any child enumerations
* created before the modification.
*/
public Enumeration<TreeNode> children() {
if (_children == null) return null;
return _children.elements();
}
/**
* Determines whether or not this node is allowed to have children. If
* allows is false, all of this node's children are removed.
*
* @param allows
* true if this node is allowed to have children.
*/
public void setAllowsChildren(boolean allows) {
_allowsChildren = allows;
}
/**
* Returns true if this node is allowed to have children.
*/
public boolean getAllowsChildren() {
return _allowsChildren;
}
/**
* Returns this node's user object.
*/
public Object getUserObject() {
return _userObject;
}
/**
* Removes all of this node's children, setting their parents to null. If
* this node has no children, this method does nothing.
*/
public void removeAllChildren() {
if (_children == null) return;
Enumeration<TreeNode> e = _children.elements();
while (e.hasMoreElements()) {
MutableTreeNode node = (MutableTreeNode) e.nextElement();
node.setParent(null);
}
_children.removeAllElements();
}
/**
* Removes newChild from its parent and makes it a child of this node by
* adding it to the end of this node's child array.
*
* @param newChild
* node to add as a child of this node.
* @exception IllegalArgumentException
* if newChild is null.
* @exception IllegalStateException
* if this node does not allow children.
*/
public void add(MutableTreeNode newChild) {
if (newChild == null)
throw new IllegalArgumentException("child node is null");
if (!_allowsChildren)
throw new IllegalStateException(
"TreeNode does not allow children");
newChild.setParent(this);
if (_children == null) _children = new Vector<TreeNode>();
_children.add(newChild);
}
/**
* Returns true if anotherNode is an ancestor of this node -- if it is this
* node, this node's parent, or an ancestor of this node's parent. (Note
* that a node is considered an ancestor of itself.) If anotherNode is
* null, this method returns false. This operation is at worst O(h) where h
* is the distance from the root to this node.
*
* @param anotherNode
* node to test as an ancestor of this node
* @return true if this node is a descendant of anotherNode.
*/
public boolean isNodeAncestor(TreeNode anotherNode) {
if (anotherNode == null) return false;
if (anotherNode == this) return true;
if (_parent == null) return false;
TreeNode parent;
for (parent = _parent; parent != null; parent.getParent()) {
if (parent == anotherNode) return true;
}
return false;
}
/**
* Returns true if anotherNode is a descendant of this node -- if it is
* this node, one of this node's children, or a descendant of one of this
* node's children. Note that a node is considered a descendant of itself.
* If anotherNode is null, returns false. This operation is at worst O(h)
* where h is the distance from the root to anotherNode.
*
* @param anotherNode
* node to test as a descendant of this node
* @return true if this node is an ancestor of anotherNode.
*/
public boolean isNodeDescendant(TreeNode anotherNode) {
if (anotherNode == null) return false;
if (anotherNode == this) return true;
if (_children == null || _children.size() == 0) return false;
TreeNode parent;
for (parent = anotherNode.getParent(); parent != null; parent
.getParent()) {
if (parent == this) return true;
}
return false;
}
/**
* Returns the depth of the tree rooted at this node -- the longest
* distance from this node to a leaf. If this node has no children, returns 0.
* This operation is much more expensive than getLevel() because it must
* effectively traverse the entire tree rooted at this node.
*
* @return the depth of the tree whose root is this node.
*/
public int getDepth() {
return this._depth(this, 0);
}
/**
* Returns the number of levels above this node -- the distance from the
* root to this node. If this node is the root, returns 0.
*
* @return the number of levels above this node.
*/
public int getLevel() {
TreeNode parent = _parent;
int i;
for (i = 0; parent != null; i++) {
// do nothink
}
return i;
}
/**
* Returns the path from the root, to get to this node. The last element in
* the path is this node.
*
* @return an array of TreeNode objects giving the path, where the first
* element in the path is the root and the last element is this
* node.
*/
public TreeNode[] getPath() {
TreeNode[] path = new TreeNode[ this.getLevel() + 1];
TreeNode node = this;
for (int i = path.length - 1; i >= 0; i--) {
path[ i] = node;
node = node.getParent();
}
return path;
}
/**
* Returns the user object path, from the root, to get to this node. If
* some of the TreeNodes in the path have null user objects, the returned
* path will contain nulls.
*/
public Object[] getUserObjectPath() {
Object[] objectPath = new Object[ this.getLevel() + 1];
MutableTreeNode node = this;
for (int i = objectPath.length - 1; i >= 0; i--) {
objectPath[ i] = node.getUserObject();
node = (MutableTreeNode) node.getParent();
}
return objectPath;
}
/**
* Returns the root of the tree that contains this node. The root is the
* ancestor with a null parent.
*
* @return the root of the tree that contains this node
*/
public TreeNode getRoot() {
TreeNode parent = this.getParent();
while (parent != null) {
if (parent.getParent() == null) return parent;
}
return null;
}
/**
* Returns true if this node is the root of the tree. The root is the only
* node in the tree with a null parent; every tree has exactly one root.
*
* @return true if this node is the root of its tree
*/
public boolean isRoot() {
return (_parent == null);
}
/**
* Creates and returns an enumeration that traverses the subtree rooted at
* this node in preorder. The first node returned by the enumeration's
* nextElement() method is this node.
* <p>
*
* Modifying the tree by inserting, removing, or moving a node invalidates
* any enumerations created before the modification.
*
* @return an enumeration for traversing the tree in preorder
*/
public Enumeration<TreeNode> preorderEnumeration() {
return null; // not implemented yet
}
/**
* Creates and returns an enumeration that traverses the subtree rooted at
* this node in postorder. The first node returned by the enumeration's
* nextElement() method is the leftmost leaf. This is the same as a
* depth-first traversal.
* <p>
*
* Modifying the tree by inserting, removing, or moving a node invalidates
* any enumerations created before the modification.
*/
public Enumeration<TreeNode> postorderEnumeration() {
return null; // not implemented
}
/**
* Returns true if aNode is a child of this node. If aNode is null, this
* method returns false.
*
* @return true if aNode is a child of this node; false if aNode is null
*/
public boolean isNodeChild(TreeNode aNode) {
if (aNode == null) return false;
return (aNode.getParent() == this);
}
/**
* Returns this node's first child. If this node has no children, throws
* NoSuchElementException.
*
* @return the first child of this node
* @exception NoSuchElementException
* if this node has no children
*/
public TreeNode getFirstChild() {
if (_children == null)
throw new NoSuchElementException("Node has no child");
return (TreeNode) _children.firstElement();
}
/**
* Returns this node's last child. If this node has no children, throws
* NoSuchElementException.
*
* @return the last child of this node
* @exception NoSuchElementException
* if this node has no children
*/
public TreeNode getLastChild() {
if (_children == null)
throw new NoSuchElementException("Node has no child");
return (TreeNode) _children.lastElement();
}
/**
* Returns the child in this node's child array that immediately follows
* aChild, which must be a child of this node. If aChild is the last child,
* returns null. This method performs a linear search of this node's
* children for aChild and is O(n) where n is the number of children; to
* traverse the entire array of children, use an enumeration instead.
*
* @return the child of this node that immediately follows aChild
* @exception IllegalArgumentException -
* if aChild is null or is not a child of this node
*/
public TreeNode getChildAfter(TreeNode aChild) {
if (!this.isNodeChild(aChild))
throw new IllegalArgumentException("not a child of this node");
int index = _children.indexOf(aChild);
if (index + 1 >= _children.size()) return null;
return (TreeNode) _children.elementAt(index + 1);
}
/**
* Returns the child in this node's child array that immediately precedes
* aChild, which must be a child of this node. If aChild is the first
* child, returns null. This method performs a linear search of this node's
* children for aChild and is O(n) where n is the number of children.
*
* @return the child of this node that immediately precedes aChild.
* @exception IllegalArgumentException -
* if aChild is null or is not a child of this node
*/
public TreeNode getChildBefore(TreeNode aChild) {
if (!this.isNodeChild(aChild))
throw new IllegalArgumentException("not a child of this node");
int index = _children.indexOf(aChild);
if (index == 0) return null;
return (TreeNode) _children.elementAt(index - 1);
}
/**
* Returns true if this node has no children. To distinguish between nodes
* that have no children and nodes that cannot have children (e.g. to
* distinguish files from empty directories), use this method in
* conjunction with getAllowsChildren
*
* @return true if this node has no children.
*/
public boolean isLeaf() {
return (_children != null && _children.size() > 0);
}
/**
* Finds and returns the first leaf that is a descendant of this node --
* either this node or its first child's first leaf. Returns this node if
* it is a leaf.
*
* @return the first leaf in the subtree rooted at this node.
*/
public DefaultMutableTreeNode getFirstLeaf() {
DefaultMutableTreeNode node = this;
while (!node.isLeaf()) {
node = (DefaultMutableTreeNode) node.getChildAt(0);
}
return node;
}
/**
* Finds and returns the last leaf that is a descendant of this node --
* either this node or its last child's last leaf. Returns this node if it
* is a leaf.
*
* @return the last leaf in the subtree rooted at this node
*/
public DefaultMutableTreeNode getLastLeaf() {
DefaultMutableTreeNode node = this;
while (!node.isLeaf()) {
node = (DefaultMutableTreeNode) node.getChildAt(node
.getChildCount() - 1);
}
return node;
}
/**
* Returns the leaf after this node or null if this node is the last leaf
* in the tree.
* <p>
*
* In this implementation of the MutableNode interface, this operation is
* very inefficient. In order to determine the next node, this method first
* performs a linear search in the parent's child-list in order to find the
* current node.
* <p>
*
* That implementation makes the operation suitable for short traversals
* from a known position. But to traverse all of the leaves in the tree,
* you should use depthFirstEnumeration to enumerate the nodes in the tree
* and use isLeaf on each node to determine which are leaves.
*
* @return returns the next leaf past this node
*/
public DefaultMutableTreeNode getNextLeaf() {
if (_parent == null) return null;
// This cast seems reasonable.
DefaultMutableTreeNode nextNode = (DefaultMutableTreeNode) _parent
.getChildAfter(this);
if (nextNode == null) return null;
return nextNode.getFirstLeaf();
}
/**
* Returns the leaf before this node or null if this node is the first leaf
* in the tree.
*
* In this implementation of the MutableNode interface, this operation is
* very inefficient. In order to determine the previous node, this method
* first performs a linear search in the parent's child-list in order to
* find the current node.
*
* That implementation makes the operation suitable for short traversals
* from a known position. But to traverse all of the leaves in the tree,
* you should use depthFirstEnumeration to enumerate the nodes in the tree
* and use isLeaf on each node to determine which are leaves.
*/
public DefaultMutableTreeNode getPreviousLeaf() {
if (_parent == null) return null;
// This cast seems reasonable.
DefaultMutableTreeNode nextNode = (DefaultMutableTreeNode) _parent
.getChildBefore(this);
if (nextNode == null) return null;
return nextNode.getLastLeaf();
}
private int _depth(TreeNode node, int start) {
if (node.isLeaf()) return start;
int depth = start;
for (Enumeration<TreeNode> e = node.children(); e.hasMoreElements();) {
TreeNode child = (TreeNode) e.nextElement();
int child_depth = this._depth(child, start);
depth = (child_depth > depth) ? child_depth : depth;
}
return depth + 1;
}
//====================================================================
// INSTANCE VARIABLES
/** Array of children, may be null if this node has no children. */
protected Vector<TreeNode> _children;
/**
* This node's parent, or null if this node has no parent. Note that in
* javax.swing.tree.DefaultMutableTreeNode, this member is a
* MutableTreeNode - which makes life difficult.
*/
protected DefaultMutableTreeNode _parent;
/** True if this node can have children. */
protected boolean _allowsChildren = true;
/** Optional user object. */
protected Object _userObject;
}