/* Copyright (C) 2006 Christian Schneider
*
* This file is part of Nomad.
*
* Nomad 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 of the License, or
* (at your option) any later version.
*
* Nomad 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 Nomad; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Created on Oct 29, 2006
*/
package net.sf.nmedit.nomad.core.swing.explorer;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
public class RootNode extends DefaultMutableTreeNode implements TreeNode
{
/** array of children, may be null if this node has no children */
protected Vector<TreeNode> children;
public RootNode()
{
super();
}
public TreeNode getParent()
{
return null;
}
//
// Primitives
//
/**
* Removes <code>newChild</code> from its present parent (if it has a
* parent), sets the child's parent to this node, and then adds the child
* to this node's child array at index <code>childIndex</code>.
* <code>newChild</code> must not be null and must not be an ancestor of
* this node.
*
* @param newChild the MutableTreeNode to insert under this node
* @param childIndex the index in this node's child array
* where this node is to be inserted
* @exception ArrayIndexOutOfBoundsException if
* <code>childIndex</code> is out of bounds
* @exception IllegalArgumentException if
* <code>newChild</code> is null or is an
* ancestor of this node
* @exception IllegalStateException if this node does not allow
* children
* @see #isNodeDescendant
*/
public void insert(TreeNode newChild, int childIndex) {
if (newChild == null) {
throw new IllegalArgumentException("new child is null");
} else if (isNodeAncestor(newChild)) {
throw new IllegalArgumentException("new child is an ancestor");
}
else if (newChild.getParent()!=this) {
if (newChild.getParent()==null) {
throw new IllegalArgumentException("parent not set");
}
else
throw new IllegalArgumentException("parent must be the root node");
}
if (children == null) {
children = new Vector<TreeNode>();
}
children.insertElementAt(newChild, childIndex);
}
/**
* Removes the child at the specified index from this node's children
* and sets that node's parent to null. The child node to remove
* must be a <code>MutableTreeNode</code>.
*
* @param childIndex the index in this node's child array
* of the child to remove
* @exception ArrayIndexOutOfBoundsException if
* <code>childIndex</code> is out of bounds
*/
public void remove(int childIndex) {
if (childIndex < 0)
return;
TreeNode child = getChildAt(childIndex);
children.removeElementAt(childIndex);
if (child instanceof MutableTreeNode)
((MutableTreeNode) child).setParent(null);
}
/**
* Returns the child at the specified index in this node's child array.
*
* @param index an index into this node's child array
* @exception ArrayIndexOutOfBoundsException if <code>index</code>
* is out of bounds
* @return the TreeNode in this node's child array at the specified index
*/
public TreeNode getChildAt(int index) {
if (children == null) {
throw new ArrayIndexOutOfBoundsException("node has no children");
}
return (TreeNode)children.elementAt(index);
}
/**
* Returns the number of children of this node.
*
* @return an int giving the number of children of this node
*/
public int getChildCount() {
if (children == null) {
return 0;
} else {
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
* <code>-1</code>. 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
* @exception IllegalArgumentException if <code>aChild</code>
* is null
* @return an int giving the index of the node in this node's child
* array, or <code>-1</code> if the specified node is a not
* a child of this node
*/
public int getIndex(TreeNode aChild) {
if (aChild == null) {
throw new IllegalArgumentException("argument is null");
}
if (!isNodeChild(aChild)) {
return -1;
}
return children.indexOf(aChild); // linear search
}
/**
* 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.
*
* @return an Enumeration of this node's children
*/
public Enumeration<TreeNode> children() {
if (children == null) {
return DefaultMutableTreeNode.EMPTY_ENUMERATION;
} else {
return children.elements();
}
}
public boolean getAllowsChildren() {
return true;
}
//
// Derived methods
//
/**
* Removes <code>aChild</code> from this node's child array, giving it a
* null parent.
*
* @param aChild a child of this node to remove
* @exception IllegalArgumentException if <code>aChild</code>
* is null or is not a child of this node
*/
public void remove(TreeNode aChild) {
if (aChild == null) {
throw new IllegalArgumentException("argument is null");
}
if (!isNodeChild(aChild)) {
throw new IllegalArgumentException("argument is not a child");
}
remove(getIndex(aChild)); // linear search
}
/**
* 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() {
for (int i = getChildCount()-1; i >= 0; i--) {
remove(i);
}
}
/**
* Removes <code>newChild</code> from its parent and makes it a child of
* this node by adding it to the end of this node's child array.
*
* @see #insert
* @param newChild node to add as a child of this node
* @exception IllegalArgumentException if <code>newChild</code>
* is null
* @exception IllegalStateException if this node does not allow
* children
*/
public void add(TreeNode newChild) {
insert(newChild, getChildCount());
}
public void add(TreeNode newChild, int i) {
insert(newChild, i);
}
//
// Tree Queries
//
/**
* Returns true if <code>anotherNode</code> 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 <code>anotherNode</code> is null, this method returns false. This
* operation is at worst O(h) where h is the distance from the root to
* this node.
*
* @see #isNodeDescendant
* @see #getSharedAncestor
* @param anotherNode node to test as an ancestor of this node
* @return true if this node is a descendant of <code>anotherNode</code>
*/
public boolean isNodeAncestor(TreeNode anotherNode) {
if (anotherNode == null) {
return false;
}
TreeNode ancestor = this;
do {
if (ancestor == anotherNode) {
return true;
}
} while((ancestor = ancestor.getParent()) != null);
return false;
}
//
// Child Queries
//
/**
* Returns true if <code>aNode</code> is a child of this node. If
* <code>aNode</code> is null, this method returns false.
*
* @return true if <code>aNode</code> is a child of this node; false if
* <code>aNode</code> is null
*/
public boolean isNodeChild(TreeNode aNode) {
boolean retval;
if (aNode == null) {
retval = false;
} else {
if (getChildCount() == 0) {
retval = false;
} else {
retval = (aNode.getParent() == this);
}
}
return retval;
}
//
// Leaf Queries
//
/**
* Returns true if this node has no children. To distinguish between
* nodes that have no children and nodes that <i>cannot</i> have
* children (e.g. to distinguish files from empty directories), use this
* method in conjunction with <code>getAllowsChildren</code>
*
* @see #getAllowsChildren
* @return true if this node has no children
*/
public boolean isLeaf() {
return (getChildCount() == 0);
}
}