summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java')
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java1217
1 files changed, 1217 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
new file mode 100644
index 000000000..260c385aa
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
@@ -0,0 +1,1217 @@
+/* DefaultMutableTreeNode.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.tree;
+
+import gnu.java.util.EmptyEnumeration;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+import java.util.Vector;
+
+
+/**
+ * A default implementation of the {@link MutableTreeNode} interface.
+ *
+ * @author Andrew Selkirk
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ */
+public class DefaultMutableTreeNode
+ implements Cloneable, MutableTreeNode, Serializable
+{
+ private static final long serialVersionUID = -4298474751201349152L;
+
+ /**
+ * An empty enumeration, returned by {@link #children()} if a node has no
+ * children.
+ */
+ public static final Enumeration<TreeNode> EMPTY_ENUMERATION =
+ new EmptyEnumeration<TreeNode>();
+
+ /**
+ * The parent of this node (possibly <code>null</code>).
+ */
+ protected MutableTreeNode parent;
+
+ /**
+ * The child nodes for this node (may be empty).
+ */
+ protected Vector<MutableTreeNode> children = new Vector<MutableTreeNode>();
+
+ /**
+ * userObject
+ */
+ protected transient Object userObject;
+
+ /**
+ * allowsChildren
+ */
+ protected boolean allowsChildren;
+
+ /**
+ * Creates a <code>DefaultMutableTreeNode</code> object.
+ * This is equivalent to <code>DefaultMutableTreeNode(null, true)</code>.
+ */
+ public DefaultMutableTreeNode()
+ {
+ this(null, true);
+ }
+
+ /**
+ * Creates a <code>DefaultMutableTreeNode</code> object with the given
+ * user object attached to it. This is equivalent to
+ * <code>DefaultMutableTreeNode(userObject, true)</code>.
+ *
+ * @param userObject the user object (<code>null</code> permitted).
+ */
+ public DefaultMutableTreeNode(Object userObject)
+ {
+ this(userObject, true);
+ }
+
+ /**
+ * Creates a <code>DefaultMutableTreeNode</code> object with the given
+ * user object attached to it.
+ *
+ * @param userObject the user object (<code>null</code> permitted).
+ * @param allowsChildren <code>true</code> if the code allows to add child
+ * nodes, <code>false</code> otherwise
+ */
+ public DefaultMutableTreeNode(Object userObject, boolean allowsChildren)
+ {
+ this.userObject = userObject;
+ this.allowsChildren = allowsChildren;
+ }
+
+ /**
+ * Returns a clone of the node. The clone contains a shallow copy of the
+ * user object, and does not copy the parent node or the child nodes.
+ *
+ * @return A clone of the node.
+ */
+ public Object clone()
+ {
+ return new DefaultMutableTreeNode(this.userObject, this.allowsChildren);
+ }
+
+ /**
+ * Returns a string representation of the node. This implementation returns
+ * <code>getUserObject().toString()</code>, or <code>null</code> if there
+ * is no user object.
+ *
+ * @return A string representation of the node (possibly <code>null</code>).
+ */
+ public String toString()
+ {
+ if (userObject == null)
+ return null;
+
+ return userObject.toString();
+ }
+
+ /**
+ * Adds a new child node to this node and sets this node as the parent of
+ * the child node. The child node must not be an ancestor of this node.
+ * If the tree uses the {@link DefaultTreeModel}, you must subsequently
+ * call {@link DefaultTreeModel#reload(TreeNode)}.
+ *
+ * @param child the child node (<code>null</code> not permitted).
+ *
+ * @throws IllegalStateException if {@link #getAllowsChildren()} returns
+ * <code>false</code>.
+ * @throws IllegalArgumentException if {@link #isNodeAncestor} returns
+ * <code>true</code>.
+ * @throws IllegalArgumentException if <code>child</code> is
+ * <code>null</code>.
+ */
+ public void add(MutableTreeNode child)
+ {
+ if (! allowsChildren)
+ throw new IllegalStateException();
+
+ if (child == null)
+ throw new IllegalArgumentException();
+
+ if (isNodeAncestor(child))
+ throw new IllegalArgumentException("Cannot add ancestor node.");
+
+ children.add(child);
+ child.setParent(this);
+ }
+
+ /**
+ * Returns the parent node of this node.
+ *
+ * @return The parent node (possibly <code>null</code>).
+ */
+ public TreeNode getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Removes the child with the given index from this node.
+ *
+ * @param index the index (in the range <code>0</code> to
+ * <code>getChildCount() - 1</code>).
+ *
+ * @throws ArrayIndexOutOfBoundsException if <code>index</code> is outside
+ * the valid range.
+ */
+ public void remove(int index)
+ {
+ MutableTreeNode child = children.remove(index);
+ child.setParent(null);
+ }
+
+ /**
+ * Removes the given child from this node and sets its parent to
+ * <code>null</code>.
+ *
+ * @param node the child node (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>node</code> is not a child of
+ * this node.
+ * @throws IllegalArgumentException if <code>node</code> is null.
+ */
+ public void remove(MutableTreeNode node)
+ {
+ if (node == null)
+ throw new IllegalArgumentException("Null 'node' argument.");
+ if (node.getParent() != this)
+ throw new IllegalArgumentException(
+ "The given 'node' is not a child of this node.");
+ children.remove(node);
+ node.setParent(null);
+ }
+
+ /**
+ * writeObject
+ *
+ * @param stream the output stream
+ *
+ * @exception IOException If an error occurs
+ */
+ private void writeObject(ObjectOutputStream stream)
+ throws IOException
+ {
+ // TODO: Implement me.
+ }
+
+ /**
+ * readObject
+ *
+ * @param stream the input stream
+ *
+ * @exception IOException If an error occurs
+ * @exception ClassNotFoundException TODO
+ */
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException
+ {
+ // TODO: Implement me.
+ }
+
+ /**
+ * Inserts given child node at the given index.
+ *
+ * @param node the child node (<code>null</code> not permitted).
+ * @param index the index.
+ *
+ * @throws IllegalArgumentException if <code>node</code> is
+ * </code>null</code>.
+ */
+ public void insert(MutableTreeNode node, int index)
+ {
+ if (! allowsChildren)
+ throw new IllegalStateException();
+
+ if (node == null)
+ throw new IllegalArgumentException("Null 'node' argument.");
+
+ if (isNodeAncestor(node))
+ throw new IllegalArgumentException("Cannot insert ancestor node.");
+
+ children.insertElementAt(node, index);
+ }
+
+ /**
+ * Returns a path to this node from the root.
+ *
+ * @return an array of tree nodes
+ */
+ public TreeNode[] getPath()
+ {
+ return getPathToRoot(this, 0);
+ }
+
+ /**
+ * Returns an enumeration containing all children of this node.
+ * <code>EMPTY_ENUMERATION</code> is returned if this node has no children.
+ *
+ * @return an enumeration of tree nodes
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration children()
+ {
+ if (children.size() == 0)
+ return EMPTY_ENUMERATION;
+
+ return children.elements();
+ }
+
+ /**
+ * Set the parent node for this node.
+ *
+ * @param node the parent node
+ */
+ public void setParent(MutableTreeNode node)
+ {
+ parent = node;
+ }
+
+ /**
+ * Returns the child node at a given index.
+ *
+ * @param index the index
+ *
+ * @return the child node
+ */
+ public TreeNode getChildAt(int index)
+ {
+ return children.elementAt(index);
+ }
+
+ /**
+ * Returns the number of children of this node.
+ *
+ * @return the number of children
+ */
+ public int getChildCount()
+ {
+ return children.size();
+ }
+
+ /**
+ * Returns the index of the specified child node, or -1 if the node is not
+ * in fact a child of this node.
+ *
+ * @param node the node (<code>null</code> not permitted).
+ *
+ * @return The index of the specified child node, or -1.
+ *
+ * @throws IllegalArgumentException if <code>node</code> is <code>null</code>.
+ */
+ public int getIndex(TreeNode node)
+ {
+ if (node == null)
+ throw new IllegalArgumentException("Null 'node' argument.");
+ return children.indexOf(node);
+ }
+
+ /**
+ * Sets the flag that controls whether or not this node allows the addition /
+ * insertion of child nodes. If the flag is set to <code>false</code>, any
+ * existing children are removed.
+ *
+ * @param allowsChildren the flag.
+ */
+ public void setAllowsChildren(boolean allowsChildren)
+ {
+ if (!allowsChildren)
+ removeAllChildren();
+ this.allowsChildren = allowsChildren;
+ }
+
+ /**
+ * getAllowsChildren
+ *
+ * @return boolean
+ */
+ public boolean getAllowsChildren()
+ {
+ return allowsChildren;
+ }
+
+ /**
+ * Sets the user object for this node
+ *
+ * @param userObject the user object
+ */
+ public void setUserObject(Object userObject)
+ {
+ this.userObject = userObject;
+ }
+
+ /**
+ * Returns the user object attached to this node. <code>null</code> is
+ * returned when no user object is set.
+ *
+ * @return the user object
+ */
+ public Object getUserObject()
+ {
+ return userObject;
+ }
+
+ /**
+ * Removes this node from its parent.
+ */
+ public void removeFromParent()
+ {
+ parent.remove(this);
+ parent = null;
+ }
+
+ /**
+ * Removes all child nodes from this node.
+ */
+ public void removeAllChildren()
+ {
+ for (int i = getChildCount() - 1; i >= 0; i--)
+ remove(i);
+ }
+
+ /**
+ * Returns <code>true</code> if <code>node</code> is an ancestor of this
+ * tree node, and <code>false</code> otherwise. An ancestor node is any of:
+ * <ul>
+ * <li>this tree node;</li>
+ * <li>the parent node (if there is one);</li>
+ * <li>any ancestor of the parent node;</li>
+ * </ul>
+ * If <code>node</code> is <code>null</code>, this method returns
+ * <code>false</code>.
+ *
+ * @param node the node (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean isNodeAncestor(TreeNode node)
+ {
+ if (node == null)
+ return false;
+
+ TreeNode current = this;
+
+ while (current != null && current != node)
+ current = current.getParent();
+
+ return current == node;
+ }
+
+ /**
+ * Returns <code>true</code> if <code>node</code> is a descendant of this
+ * tree node, and <code>false</code> otherwise. A descendant node is any of:
+ * <ul>
+ * <li>this tree node;</li>
+ * <li>the child nodes belonging to this tree node, if there are any;</li>
+ * <li>any descendants of the child nodes;</li>
+ * </ul>
+ * If <code>node</code> is <code>null</code>, this method returns
+ * <code>false</code>.
+ *
+ * @param node the node (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean isNodeDescendant(DefaultMutableTreeNode node)
+ {
+ if (node == null)
+ return false;
+
+ TreeNode current = node;
+
+ while (current != null
+ && current != this)
+ current = current.getParent();
+
+ return current == this;
+ }
+
+ /**
+ * getSharedAncestor
+ *
+ * @param node TODO
+ *
+ * @return TreeNode
+ */
+ public TreeNode getSharedAncestor(DefaultMutableTreeNode node)
+ {
+ TreeNode current = this;
+ ArrayList<TreeNode> list = new ArrayList<TreeNode>();
+
+ while (current != null)
+ {
+ list.add(current);
+ current = current.getParent();
+ }
+
+ current = node;
+
+ while (current != null)
+ {
+ if (list.contains(current))
+ return current;
+
+ current = current.getParent();
+ }
+
+ return null;
+ }
+
+ /**
+ * isNodeRelated
+ *
+ * @param node TODO
+ *
+ * @return boolean
+ */
+ public boolean isNodeRelated(DefaultMutableTreeNode node)
+ {
+ if (node == null)
+ return false;
+
+ return node.getRoot() == getRoot();
+ }
+
+ /**
+ * getDepth
+ *
+ * @return int
+ */
+ public int getDepth()
+ {
+ if ((! allowsChildren)
+ || children.size() == 0)
+ return 0;
+
+ Stack<Integer> stack = new Stack<Integer>();
+ stack.push(new Integer(0));
+ TreeNode node = getChildAt(0);
+ int depth = 0;
+ int current = 1;
+
+ while (! stack.empty())
+ {
+ if (node.getChildCount() != 0)
+ {
+ node = node.getChildAt(0);
+ stack.push(new Integer(0));
+ current++;
+ }
+ else
+ {
+ if (current > depth)
+ depth = current;
+
+ int size;
+ int index;
+
+ do
+ {
+ node = node.getParent();
+ size = node.getChildCount();
+ index = stack.pop().intValue() + 1;
+ current--;
+ }
+ while (index >= size
+ && node != this);
+
+ if (index < size)
+ {
+ node = node.getChildAt(index);
+ stack.push(new Integer(index));
+ current++;
+ }
+ }
+ }
+
+ return depth;
+ }
+
+ /**
+ * getLevel
+ *
+ * @return int
+ */
+ public int getLevel()
+ {
+ int count = -1;
+ TreeNode current = this;
+
+ do
+ {
+ current = current.getParent();
+ count++;
+ }
+ while (current != null);
+
+ return count;
+ }
+
+ /**
+ * getPathToRoot
+ *
+ * @param node TODO
+ * @param depth TODO
+ *
+ * @return TreeNode[]
+ */
+ protected TreeNode[] getPathToRoot(TreeNode node, int depth)
+ {
+ if (node == null)
+ {
+ if (depth == 0)
+ return null;
+
+ return new TreeNode[depth];
+ }
+
+ TreeNode[] path = getPathToRoot(node.getParent(), depth + 1);
+ path[path.length - depth - 1] = node;
+ return path;
+ }
+
+ /**
+ * getUserObjectPath
+ *
+ * @return Object[]
+ */
+ public Object[] getUserObjectPath()
+ {
+ TreeNode[] path = getPathToRoot(this, 0);
+ Object[] object = new Object[path.length];
+
+ for (int index = 0; index < path.length; ++index)
+ object[index] = ((DefaultMutableTreeNode) path[index]).getUserObject();
+
+ return object;
+ }
+
+ /**
+ * Returns the root node by iterating the parents of this node.
+ *
+ * @return the root node
+ */
+ public TreeNode getRoot()
+ {
+ TreeNode current = this;
+ TreeNode check = current.getParent();
+
+ while (check != null)
+ {
+ current = check;
+ check = current.getParent();
+ }
+
+ return current;
+ }
+
+ /**
+ * Tells whether this node is the root node or not.
+ *
+ * @return <code>true</code> if this is the root node,
+ * <code>false</code>otherwise
+ */
+ public boolean isRoot()
+ {
+ return parent == null;
+ }
+
+ /**
+ * getNextNode
+ *
+ * @return DefaultMutableTreeNode
+ */
+ public DefaultMutableTreeNode getNextNode()
+ {
+ // Return first child.
+ if (getChildCount() != 0)
+ return (DefaultMutableTreeNode) getChildAt(0);
+
+ // Return next sibling (if needed the sibling of some parent).
+ DefaultMutableTreeNode node = this;
+ DefaultMutableTreeNode sibling;
+
+ do
+ {
+ sibling = node.getNextSibling();
+ node = (DefaultMutableTreeNode) node.getParent();
+ }
+ while (sibling == null &&
+ node != null);
+
+ // Return sibling.
+ return sibling;
+ }
+
+ /**
+ * getPreviousNode
+ *
+ * @return DefaultMutableTreeNode
+ */
+ public DefaultMutableTreeNode getPreviousNode()
+ {
+ // Return null if no parent.
+ if (parent == null)
+ return null;
+
+ DefaultMutableTreeNode sibling = getPreviousSibling();
+
+ // Return parent if no sibling.
+ if (sibling == null)
+ return (DefaultMutableTreeNode) parent;
+
+ // Return last leaf of sibling.
+ if (sibling.getChildCount() != 0)
+ return sibling.getLastLeaf();
+
+ // Return sibling.
+ return sibling;
+ }
+
+ /**
+ * preorderEnumeration
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration preorderEnumeration()
+ {
+ return new PreorderEnumeration(this);
+ }
+
+ /**
+ * postorderEnumeration
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration postorderEnumeration()
+ {
+ return new PostorderEnumeration(this);
+ }
+
+ /**
+ * breadthFirstEnumeration
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration breadthFirstEnumeration()
+ {
+ return new BreadthFirstEnumeration(this);
+ }
+
+ /**
+ * depthFirstEnumeration
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration depthFirstEnumeration()
+ {
+ return postorderEnumeration();
+ }
+
+ /**
+ * pathFromAncestorEnumeration
+ *
+ * @param node TODO
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration pathFromAncestorEnumeration(TreeNode node)
+ {
+ if (node == null)
+ throw new IllegalArgumentException();
+
+ TreeNode parent = this;
+ Vector<TreeNode> nodes = new Vector<TreeNode>();
+ nodes.add(this);
+
+ while (parent != node && parent != null)
+ {
+ parent = parent.getParent();
+ nodes.add(0, parent);
+ }
+
+ if (parent != node)
+ throw new IllegalArgumentException();
+
+ return nodes.elements();
+ }
+
+ /**
+ * Returns <code>true</code> if <code>node</code> is a child of this tree
+ * node, and <code>false</code> otherwise. If <code>node</code> is
+ * <code>null</code>, this method returns <code>false</code>.
+ *
+ * @param node the node (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean isNodeChild(TreeNode node)
+ {
+ if (node == null)
+ return false;
+
+ return node.getParent() == this;
+ }
+
+ /**
+ * Returns the first child node belonging to this tree node.
+ *
+ * @return The first child node.
+ *
+ * @throws NoSuchElementException if this tree node has no children.
+ */
+ public TreeNode getFirstChild()
+ {
+ return children.firstElement();
+ }
+
+ /**
+ * Returns the last child node belonging to this tree node.
+ *
+ * @return The last child node.
+ *
+ * @throws NoSuchElementException if this tree node has no children.
+ */
+ public TreeNode getLastChild()
+ {
+ return children.lastElement();
+ }
+
+ /**
+ * Returns the next child after the specified <code>node</code>, or
+ * <code>null</code> if there is no child after the specified
+ * <code>node</code>.
+ *
+ * @param node a child of this node (<code>null</code> not permitted).
+ *
+ * @return The next child, or <code>null</code>.
+ *
+ * @throws IllegalArgumentException if <code>node</code> is not a child of
+ * this node, or is <code>null</code>.
+ */
+ public TreeNode getChildAfter(TreeNode node)
+ {
+ if (node == null || node.getParent() != this)
+ throw new IllegalArgumentException();
+
+ int index = getIndex(node) + 1;
+
+ if (index == getChildCount())
+ return null;
+
+ return getChildAt(index);
+ }
+
+ /**
+ * Returns the previous child before the specified <code>node</code>, or
+ * <code>null</code> if there is no child before the specified
+ * <code>node</code>.
+ *
+ * @param node a child of this node (<code>null</code> not permitted).
+ *
+ * @return The previous child, or <code>null</code>.
+ *
+ * @throws IllegalArgumentException if <code>node</code> is not a child of
+ * this node, or is <code>null</code>.
+ */
+ public TreeNode getChildBefore(TreeNode node)
+ {
+ if (node == null || node.getParent() != this)
+ throw new IllegalArgumentException();
+
+ int index = getIndex(node) - 1;
+
+ if (index < 0)
+ return null;
+
+ return getChildAt(index);
+ }
+
+ /**
+ * Returns <code>true</code> if this tree node and <code>node</code> share
+ * the same parent. If <code>node</code> is this tree node, the method
+ * returns <code>true</code> and if <code>node</code> is <code>null</code>
+ * this method returns <code>false</code>.
+ *
+ * @param node the node (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean isNodeSibling(TreeNode node)
+ {
+ if (node == null)
+ return false;
+ if (node == this)
+ return true;
+ return node.getParent() == getParent() && getParent() != null;
+ }
+
+ /**
+ * Returns the number of siblings for this tree node. If the tree node has
+ * a parent, this method returns the child count for the parent, otherwise
+ * it returns <code>1</code>.
+ *
+ * @return The sibling count.
+ */
+ public int getSiblingCount()
+ {
+ if (parent == null)
+ return 1;
+
+ return parent.getChildCount();
+ }
+
+ /**
+ * Returns the next sibling for this tree node. If this node has no parent,
+ * or this node is the last child of its parent, this method returns
+ * <code>null</code>.
+ *
+ * @return The next sibling, or <code>null</code>.
+ */
+ public DefaultMutableTreeNode getNextSibling()
+ {
+ if (parent == null)
+ return null;
+
+ int index = parent.getIndex(this) + 1;
+
+ if (index == parent.getChildCount())
+ return null;
+
+ return (DefaultMutableTreeNode) parent.getChildAt(index);
+ }
+
+ /**
+ * Returns the previous sibling for this tree node. If this node has no
+ * parent, or this node is the first child of its parent, this method returns
+ * <code>null</code>.
+ *
+ * @return The previous sibling, or <code>null</code>.
+ */
+ public DefaultMutableTreeNode getPreviousSibling()
+ {
+ if (parent == null)
+ return null;
+
+ int index = parent.getIndex(this) - 1;
+
+ if (index < 0)
+ return null;
+
+ return (DefaultMutableTreeNode) parent.getChildAt(index);
+ }
+
+ /**
+ * Returns <code>true</code> if this tree node is a lead node (that is, it
+ * has no children), and <code>false</otherwise>.
+ *
+ * @return A boolean.
+ */
+ public boolean isLeaf()
+ {
+ return children.size() == 0;
+ }
+
+ /**
+ * Returns the first leaf node that is a descendant of this node. Recall
+ * that a node is its own descendant, so if this node has no children then
+ * it is returned as the first leaf.
+ *
+ * @return The first leaf node.
+ */
+ public DefaultMutableTreeNode getFirstLeaf()
+ {
+ TreeNode current = this;
+
+ while (current.getChildCount() > 0)
+ current = current.getChildAt(0);
+
+ return (DefaultMutableTreeNode) current;
+ }
+
+ /**
+ * Returns the last leaf node that is a descendant of this node. Recall
+ * that a node is its own descendant, so if this node has no children then
+ * it is returned as the last leaf.
+ *
+ * @return The first leaf node.
+ */
+ public DefaultMutableTreeNode getLastLeaf()
+ {
+ TreeNode current = this;
+ int size = current.getChildCount();
+
+ while (size > 0)
+ {
+ current = current.getChildAt(size - 1);
+ size = current.getChildCount();
+ }
+
+ return (DefaultMutableTreeNode) current;
+ }
+
+ /**
+ * Returns the next leaf node after this tree node.
+ *
+ * @return The next leaf node, or <code>null</code>.
+ */
+ public DefaultMutableTreeNode getNextLeaf()
+ {
+ // if there is a next sibling, return its first leaf
+ DefaultMutableTreeNode sibling = getNextSibling();
+ if (sibling != null)
+ return sibling.getFirstLeaf();
+ // otherwise move up one level and try again...
+ if (parent != null)
+ return ((DefaultMutableTreeNode) parent).getNextLeaf();
+ return null;
+ }
+
+ /**
+ * Returns the previous leaf node before this tree node.
+ *
+ * @return The previous leaf node, or <code>null</code>.
+ */
+ public DefaultMutableTreeNode getPreviousLeaf()
+ {
+ // if there is a previous sibling, return its last leaf
+ DefaultMutableTreeNode sibling = getPreviousSibling();
+ if (sibling != null)
+ return sibling.getLastLeaf();
+ // otherwise move up one level and try again...
+ if (parent != null)
+ return ((DefaultMutableTreeNode) parent).getPreviousLeaf();
+ return null;
+ }
+
+ /**
+ * getLeafCount
+ *
+ * @return int
+ */
+ public int getLeafCount()
+ {
+ int count = 0;
+ Enumeration<?> e = depthFirstEnumeration();
+
+ while (e.hasMoreElements())
+ {
+ TreeNode current = (TreeNode) e.nextElement();
+
+ if (current.isLeaf())
+ count++;
+ }
+
+ return count;
+ }
+
+ /** Provides an enumeration of a tree in breadth-first traversal
+ * order.
+ */
+ static class BreadthFirstEnumeration implements Enumeration<TreeNode>
+ {
+
+ LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
+
+ BreadthFirstEnumeration(TreeNode node)
+ {
+ queue.add(node);
+ }
+
+ public boolean hasMoreElements()
+ {
+ return !queue.isEmpty();
+ }
+
+ @SuppressWarnings("unchecked")
+ public TreeNode nextElement()
+ {
+ if (queue.isEmpty())
+ throw new NoSuchElementException("No more elements left.");
+
+ TreeNode node = queue.removeFirst();
+
+ Enumeration<TreeNode> children =
+ (Enumeration<TreeNode>) node.children();
+ while (children.hasMoreElements())
+ queue.add(children.nextElement());
+
+ return node;
+ }
+ }
+
+ /** Provides an enumeration of a tree traversing it
+ * preordered.
+ */
+ static class PreorderEnumeration implements Enumeration<TreeNode>
+ {
+ TreeNode next;
+
+ Stack<Enumeration<TreeNode>> childrenEnums =
+ new Stack<Enumeration<TreeNode>>();
+
+ @SuppressWarnings("unchecked")
+ PreorderEnumeration(TreeNode node)
+ {
+ next = node;
+ childrenEnums.push((Enumeration<TreeNode>) node.children());
+ }
+
+ public boolean hasMoreElements()
+ {
+ return next != null;
+ }
+
+ public TreeNode nextElement()
+ {
+ if (next == null)
+ throw new NoSuchElementException("No more elements left.");
+
+ TreeNode current = next;
+
+ Enumeration<TreeNode> children = childrenEnums.peek();
+
+ // Retrieves the next element.
+ next = traverse(children);
+
+ return current;
+ }
+
+ @SuppressWarnings("unchecked")
+ private TreeNode traverse(Enumeration<TreeNode> children)
+ {
+ // If more children are available step down.
+ if (children.hasMoreElements())
+ {
+ TreeNode child = children.nextElement();
+ childrenEnums.push((Enumeration<TreeNode>) child.children());
+
+ return child;
+ }
+
+ // If no children are left, we return to a higher level.
+ childrenEnums.pop();
+
+ // If there are no more levels left, there is no next
+ // element to return.
+ if (childrenEnums.isEmpty())
+ return null;
+ else
+ {
+ return traverse(childrenEnums.peek());
+ }
+ }
+ }
+
+ /** Provides an enumeration of a tree traversing it
+ * postordered (= depth-first).
+ */
+ static class PostorderEnumeration implements Enumeration<TreeNode>
+ {
+
+ Stack<TreeNode> nodes = new Stack<TreeNode>();
+ Stack<Enumeration<TreeNode>> childrenEnums =
+ new Stack<Enumeration<TreeNode>>();
+
+ @SuppressWarnings("unchecked")
+ PostorderEnumeration(TreeNode node)
+ {
+ nodes.push(node);
+ childrenEnums.push((Enumeration<TreeNode>) node.children());
+ }
+
+ public boolean hasMoreElements()
+ {
+ return !nodes.isEmpty();
+ }
+
+ public TreeNode nextElement()
+ {
+ if (nodes.isEmpty())
+ throw new NoSuchElementException("No more elements left!");
+
+ Enumeration<TreeNode> children = childrenEnums.peek();
+
+ return traverse(children);
+ }
+
+ @SuppressWarnings("unchecked")
+ private TreeNode traverse(Enumeration<TreeNode> children)
+ {
+ if (children.hasMoreElements())
+ {
+ TreeNode node = children.nextElement();
+ nodes.push(node);
+
+ Enumeration<TreeNode> newChildren =
+ (Enumeration<TreeNode>) node.children();
+ childrenEnums.push(newChildren);
+
+ return traverse(newChildren);
+ }
+ else
+ {
+ childrenEnums.pop();
+
+ // Returns the node whose children
+ // have all been visited. (= postorder)
+ TreeNode next = nodes.peek();
+ nodes.pop();
+
+ return next;
+ }
+ }
+
+ }
+
+}