| /* 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("rawtypes") // 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("rawtypes") // Required for API compatibility | |
| public Enumeration preorderEnumeration() | |
| { | |
| return new PreorderEnumeration(this); | |
| } | |
| /** | |
| * postorderEnumeration | |
| * | |
| * @return Enumeration | |
| */ | |
| @SuppressWarnings("rawtypes") // Required for API compatibility | |
| public Enumeration postorderEnumeration() | |
| { | |
| return new PostorderEnumeration(this); | |
| } | |
| /** | |
| * breadthFirstEnumeration | |
| * | |
| * @return Enumeration | |
| */ | |
| @SuppressWarnings("rawtypes") // Required for API compatibility | |
| public Enumeration breadthFirstEnumeration() | |
| { | |
| return new BreadthFirstEnumeration(this); | |
| } | |
| /** | |
| * depthFirstEnumeration | |
| * | |
| * @return Enumeration | |
| */ | |
| @SuppressWarnings("rawtypes") // Required for API compatibility | |
| public Enumeration depthFirstEnumeration() | |
| { | |
| return postorderEnumeration(); | |
| } | |
| /** | |
| * pathFromAncestorEnumeration | |
| * | |
| * @param node TODO | |
| * | |
| * @return Enumeration | |
| */ | |
| @SuppressWarnings("rawtypes") // 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(); | |
| } | |
| public TreeNode nextElement() | |
| { | |
| if (queue.isEmpty()) | |
| throw new NoSuchElementException("No more elements left."); | |
| TreeNode node = queue.removeFirst(); | |
| @SuppressWarnings("unchecked") | |
| 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>>(); | |
| PreorderEnumeration(TreeNode node) | |
| { | |
| next = node; | |
| @SuppressWarnings("unchecked") | |
| Enumeration<TreeNode> children = | |
| (Enumeration<TreeNode>) node.children(); | |
| childrenEnums.push(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; | |
| } | |
| private TreeNode traverse(Enumeration<TreeNode> children) | |
| { | |
| // If more children are available step down. | |
| if (children.hasMoreElements()) | |
| { | |
| TreeNode child = children.nextElement(); | |
| @SuppressWarnings("unchecked") | |
| Enumeration<TreeNode> grandchildren = | |
| (Enumeration<TreeNode>) child.children(); | |
| childrenEnums.push(grandchildren); | |
| 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>>(); | |
| PostorderEnumeration(TreeNode node) | |
| { | |
| nodes.push(node); | |
| @SuppressWarnings("unchecked") | |
| Enumeration<TreeNode> children = | |
| (Enumeration<TreeNode>) node.children(); | |
| childrenEnums.push(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); | |
| } | |
| private TreeNode traverse(Enumeration<TreeNode> children) | |
| { | |
| if (children.hasMoreElements()) | |
| { | |
| TreeNode node = children.nextElement(); | |
| nodes.push(node); | |
| @SuppressWarnings("unchecked") | |
| 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; | |
| } | |
| } | |
| } | |
| } |