/*
* 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.util.*;
import java.beans.
ConstructorProperties;
import java.io.*;
import javax.swing.event.*;
/**
* A simple tree data model that uses TreeNodes.
* For further information and examples that use DefaultTreeModel,
* 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 Rob Davis
* @author Ray Ryan
* @author Scott Violet
*/
public class
DefaultTreeModel implements
Serializable,
TreeModel {
/** Root of the tree. */
protected
TreeNode root;
/** Listeners. */
protected
EventListenerList listenerList = new
EventListenerList();
/**
* Determines how the <code>isLeaf</code> method figures
* out if a node is a leaf node. If true, a node is a leaf
* node if it does not allow children. (If it allows
* children, it is not a leaf node, even if no children
* are present.) That lets you distinguish between <i>folder</i>
* nodes and <i>file</i> nodes in a file system, for example.
* <p>
* If this value is false, then any node which has no
* children is a leaf node, and any node may acquire
* children.
*
* @see TreeNode#getAllowsChildren
* @see TreeModel#isLeaf
* @see #setAsksAllowsChildren
*/
protected boolean
asksAllowsChildren;
/**
* Creates a tree in which any node can have children.
*
* @param root a TreeNode object that is the root of the tree
* @see #DefaultTreeModel(TreeNode, boolean)
*/
@
ConstructorProperties({"root"})
public
DefaultTreeModel(
TreeNode root) {
this(
root, false);
}
/**
* Creates a tree specifying whether any node can have children,
* or whether only certain nodes can have children.
*
* @param root a TreeNode object that is the root of the tree
* @param asksAllowsChildren a boolean, false if any node can
* have children, true if each node is asked to see if
* it can have children
* @see #asksAllowsChildren
*/
public
DefaultTreeModel(
TreeNode root, boolean
asksAllowsChildren) {
super();
this.
root =
root;
this.
asksAllowsChildren =
asksAllowsChildren;
}
/**
* Sets whether or not to test leafness by asking getAllowsChildren()
* or isLeaf() to the TreeNodes. If newvalue is true, getAllowsChildren()
* is messaged, otherwise isLeaf() is messaged.
*/
public void
setAsksAllowsChildren(boolean
newValue) {
asksAllowsChildren =
newValue;
}
/**
* Tells how leaf nodes are determined.
*
* @return true if only nodes which do not allow children are
* leaf nodes, false if nodes which have no children
* (even if allowed) are leaf nodes
* @see #asksAllowsChildren
*/
public boolean
asksAllowsChildren() {
return
asksAllowsChildren;
}
/**
* Sets the root to <code>root</code>. A null <code>root</code> implies
* the tree is to display nothing, and is legal.
*/
public void
setRoot(
TreeNode root) {
Object oldRoot = this.
root;
this.
root =
root;
if (
root == null &&
oldRoot != null) {
fireTreeStructureChanged(this, null);
}
else {
nodeStructureChanged(
root);
}
}
/**
* Returns the root of the tree. Returns null only if the tree has
* no nodes.
*
* @return the root of the tree
*/
public
Object getRoot() {
return
root;
}
/**
* Returns the index of child in parent.
* If either the parent or child is <code>null</code>, returns -1.
* @param parent a note in the tree, obtained from this data source
* @param child the node we are interested in
* @return the index of the child in the parent, or -1
* if either the parent or the child is <code>null</code>
*/
public int
getIndexOfChild(
Object parent,
Object child) {
if(
parent == null ||
child == null)
return -1;
return ((
TreeNode)
parent).
getIndex((
TreeNode)
child);
}
/**
* Returns the child of <I>parent</I> at index <I>index</I> in the parent's
* child array. <I>parent</I> must be a node previously obtained from
* this data source. This should not return null if <i>index</i>
* is a valid index for <i>parent</i> (that is <i>index</i> >= 0 &&
* <i>index</i> < getChildCount(<i>parent</i>)).
*
* @param parent a node in the tree, obtained from this data source
* @return the child of <I>parent</I> at index <I>index</I>
*/
public
Object getChild(
Object parent, int
index) {
return ((
TreeNode)
parent).
getChildAt(
index);
}
/**
* Returns the number of children of <I>parent</I>. Returns 0 if the node
* is a leaf or if it has no children. <I>parent</I> must be a node
* previously obtained from this data source.
*
* @param parent a node in the tree, obtained from this data source
* @return the number of children of the node <I>parent</I>
*/
public int
getChildCount(
Object parent) {
return ((
TreeNode)
parent).
getChildCount();
}
/**
* Returns whether the specified node is a leaf node.
* The way the test is performed depends on the
* <code>askAllowsChildren</code> setting.
*
* @param node the node to check
* @return true if the node is a leaf node
*
* @see #asksAllowsChildren
* @see TreeModel#isLeaf
*/
public boolean
isLeaf(
Object node) {
if(
asksAllowsChildren)
return !((
TreeNode)
node).
getAllowsChildren();
return ((
TreeNode)
node).
isLeaf();
}
/**
* Invoke this method if you've modified the {@code TreeNode}s upon which
* this model depends. The model will notify all of its listeners that the
* model has changed.
*/
public void
reload() {
reload(
root);
}
/**
* This sets the user object of the TreeNode identified by path
* and posts a node changed. If you use custom user objects in
* the TreeModel you're going to need to subclass this and
* set the user object of the changed node to something meaningful.
*/
public void
valueForPathChanged(
TreePath path,
Object newValue) {
MutableTreeNode aNode = (
MutableTreeNode)
path.
getLastPathComponent();
aNode.
setUserObject(
newValue);
nodeChanged(
aNode);
}
/**
* Invoked this to insert newChild at location index in parents children.
* This will then message nodesWereInserted to create the appropriate
* event. This is the preferred way to add children as it will create
* the appropriate event.
*/
public void
insertNodeInto(
MutableTreeNode newChild,
MutableTreeNode parent, int
index){
parent.
insert(
newChild,
index);
int[]
newIndexs = new int[1];
newIndexs[0] =
index;
nodesWereInserted(
parent,
newIndexs);
}
/**
* Message this to remove node from its parent. This will message
* nodesWereRemoved to create the appropriate event. This is the
* preferred way to remove a node as it handles the event creation
* for you.
*/
public void
removeNodeFromParent(
MutableTreeNode node) {
MutableTreeNode parent = (
MutableTreeNode)
node.
getParent();
if(
parent == null)
throw new
IllegalArgumentException("node does not have a parent.");
int[]
childIndex = new int[1];
Object[]
removedArray = new
Object[1];
childIndex[0] =
parent.
getIndex(
node);
parent.
remove(
childIndex[0]);
removedArray[0] =
node;
nodesWereRemoved(
parent,
childIndex,
removedArray);
}
/**
* Invoke this method after you've changed how node is to be
* represented in the tree.
*/
public void
nodeChanged(
TreeNode node) {
if(
listenerList != null &&
node != null) {
TreeNode parent =
node.
getParent();
if(
parent != null) {
int
anIndex =
parent.
getIndex(
node);
if(
anIndex != -1) {
int[]
cIndexs = new int[1];
cIndexs[0] =
anIndex;
nodesChanged(
parent,
cIndexs);
}
}
else if (
node ==
getRoot()) {
nodesChanged(
node, null);
}
}
}
/**
* Invoke this method if you've modified the {@code TreeNode}s upon which
* this model depends. The model will notify all of its listeners that the
* model has changed below the given node.
*
* @param node the node below which the model has changed
*/
public void
reload(
TreeNode node) {
if(
node != null) {
fireTreeStructureChanged(this,
getPathToRoot(
node), null, null);
}
}
/**
* Invoke this method after you've inserted some TreeNodes into
* node. childIndices should be the index of the new elements and
* must be sorted in ascending order.
*/
public void
nodesWereInserted(
TreeNode node, int[]
childIndices) {
if(
listenerList != null &&
node != null &&
childIndices != null
&&
childIndices.length > 0) {
int
cCount =
childIndices.length;
Object[]
newChildren = new
Object[
cCount];
for(int
counter = 0;
counter <
cCount;
counter++)
newChildren[
counter] =
node.
getChildAt(
childIndices[
counter]);
fireTreeNodesInserted(this,
getPathToRoot(
node),
childIndices,
newChildren);
}
}
/**
* Invoke this method after you've removed some TreeNodes from
* node. childIndices should be the index of the removed elements and
* must be sorted in ascending order. And removedChildren should be
* the array of the children objects that were removed.
*/
public void
nodesWereRemoved(
TreeNode node, int[]
childIndices,
Object[]
removedChildren) {
if(
node != null &&
childIndices != null) {
fireTreeNodesRemoved(this,
getPathToRoot(
node),
childIndices,
removedChildren);
}
}
/**
* Invoke this method after you've changed how the children identified by
* childIndicies are to be represented in the tree.
*/
public void
nodesChanged(
TreeNode node, int[]
childIndices) {
if(
node != null) {
if (
childIndices != null) {
int
cCount =
childIndices.length;
if(
cCount > 0) {
Object[]
cChildren = new
Object[
cCount];
for(int
counter = 0;
counter <
cCount;
counter++)
cChildren[
counter] =
node.
getChildAt
(
childIndices[
counter]);
fireTreeNodesChanged(this,
getPathToRoot(
node),
childIndices,
cChildren);
}
}
else if (
node ==
getRoot()) {
fireTreeNodesChanged(this,
getPathToRoot(
node), null, null);
}
}
}
/**
* Invoke this method if you've totally changed the children of
* node and its children's children... This will post a
* treeStructureChanged event.
*/
public void
nodeStructureChanged(
TreeNode node) {
if(
node != null) {
fireTreeStructureChanged(this,
getPathToRoot(
node), null, null);
}
}
/**
* Builds the parents of node up to and including the root node,
* where the original node is the last element in the returned array.
* The length of the returned array gives the node's depth in the
* tree.
*
* @param aNode the TreeNode to get the path for
*/
public
TreeNode[]
getPathToRoot(
TreeNode aNode) {
return
getPathToRoot(
aNode, 0);
}
/**
* Builds the parents of node up to and including the root node,
* where the original node is the last element in the returned array.
* The length of the returned array gives the node's depth in the
* tree.
*
* @param aNode the TreeNode to get the path for
* @param depth an int giving the number of steps already taken towards
* the root (on recursive calls), used to size the returned array
* @return an array of TreeNodes giving the path from the root to the
* specified node
*/
protected
TreeNode[]
getPathToRoot(
TreeNode aNode, int
depth) {
TreeNode[]
retNodes;
// This method recurses, traversing towards the root in order
// size the array. On the way back, it fills in the nodes,
// starting from the root and working back to the original node.
/* Check for null, in case someone passed in a null node, or
they passed in an element that isn't rooted at root. */
if(
aNode == null) {
if(
depth == 0)
return null;
else
retNodes = new
TreeNode[
depth];
}
else {
depth++;
if(
aNode ==
root)
retNodes = new
TreeNode[
depth];
else
retNodes =
getPathToRoot(
aNode.
getParent(),
depth);
retNodes[
retNodes.length -
depth] =
aNode;
}
return
retNodes;
}
//
// Events
//
/**
* Adds a listener for the TreeModelEvent posted after the tree changes.
*
* @see #removeTreeModelListener
* @param l the listener to add
*/
public void
addTreeModelListener(
TreeModelListener l) {
listenerList.
add(
TreeModelListener.class,
l);
}
/**
* Removes a listener previously added with <B>addTreeModelListener()</B>.
*
* @see #addTreeModelListener
* @param l the listener to remove
*/
public void
removeTreeModelListener(
TreeModelListener l) {
listenerList.
remove(
TreeModelListener.class,
l);
}
/**
* Returns an array of all the tree model listeners
* registered on this model.
*
* @return all of this model's <code>TreeModelListener</code>s
* or an empty
* array if no tree model listeners are currently registered
*
* @see #addTreeModelListener
* @see #removeTreeModelListener
*
* @since 1.4
*/
public
TreeModelListener[]
getTreeModelListeners() {
return
listenerList.
getListeners(
TreeModelListener.class);
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
*
* @param source the source of the {@code TreeModelEvent};
* typically {@code this}
* @param path the path to the parent of the nodes that changed; use
* {@code null} to identify the root has changed
* @param childIndices the indices of the changed elements
* @param children the changed elements
*/
protected void
fireTreeNodesChanged(
Object source,
Object[]
path,
int[]
childIndices,
Object[]
children) {
// Guaranteed to return a non-null array
Object[]
listeners =
listenerList.
getListenerList();
TreeModelEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int
i =
listeners.length-2;
i>=0;
i-=2) {
if (
listeners[
i]==
TreeModelListener.class) {
// Lazily create the event:
if (
e == null)
e = new
TreeModelEvent(
source,
path,
childIndices,
children);
((
TreeModelListener)
listeners[
i+1]).
treeNodesChanged(
e);
}
}
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
*
* @param source the source of the {@code TreeModelEvent};
* typically {@code this}
* @param path the path to the parent the nodes were added to
* @param childIndices the indices of the new elements
* @param children the new elements
*/
protected void
fireTreeNodesInserted(
Object source,
Object[]
path,
int[]
childIndices,
Object[]
children) {
// Guaranteed to return a non-null array
Object[]
listeners =
listenerList.
getListenerList();
TreeModelEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int
i =
listeners.length-2;
i>=0;
i-=2) {
if (
listeners[
i]==
TreeModelListener.class) {
// Lazily create the event:
if (
e == null)
e = new
TreeModelEvent(
source,
path,
childIndices,
children);
((
TreeModelListener)
listeners[
i+1]).
treeNodesInserted(
e);
}
}
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
*
* @param source the source of the {@code TreeModelEvent};
* typically {@code this}
* @param path the path to the parent the nodes were removed from
* @param childIndices the indices of the removed elements
* @param children the removed elements
*/
protected void
fireTreeNodesRemoved(
Object source,
Object[]
path,
int[]
childIndices,
Object[]
children) {
// Guaranteed to return a non-null array
Object[]
listeners =
listenerList.
getListenerList();
TreeModelEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int
i =
listeners.length-2;
i>=0;
i-=2) {
if (
listeners[
i]==
TreeModelListener.class) {
// Lazily create the event:
if (
e == null)
e = new
TreeModelEvent(
source,
path,
childIndices,
children);
((
TreeModelListener)
listeners[
i+1]).
treeNodesRemoved(
e);
}
}
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
*
* @param source the source of the {@code TreeModelEvent};
* typically {@code this}
* @param path the path to the parent of the structure that has changed;
* use {@code null} to identify the root has changed
* @param childIndices the indices of the affected elements
* @param children the affected elements
*/
protected void
fireTreeStructureChanged(
Object source,
Object[]
path,
int[]
childIndices,
Object[]
children) {
// Guaranteed to return a non-null array
Object[]
listeners =
listenerList.
getListenerList();
TreeModelEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int
i =
listeners.length-2;
i>=0;
i-=2) {
if (
listeners[
i]==
TreeModelListener.class) {
// Lazily create the event:
if (
e == null)
e = new
TreeModelEvent(
source,
path,
childIndices,
children);
((
TreeModelListener)
listeners[
i+1]).
treeStructureChanged(
e);
}
}
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
*
* @param source the source of the {@code TreeModelEvent};
* typically {@code this}
* @param path the path to the parent of the structure that has changed;
* use {@code null} to identify the root has changed
*/
private void
fireTreeStructureChanged(
Object source,
TreePath path) {
// Guaranteed to return a non-null array
Object[]
listeners =
listenerList.
getListenerList();
TreeModelEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int
i =
listeners.length-2;
i>=0;
i-=2) {
if (
listeners[
i]==
TreeModelListener.class) {
// Lazily create the event:
if (
e == null)
e = new
TreeModelEvent(
source,
path);
((
TreeModelListener)
listeners[
i+1]).
treeStructureChanged(
e);
}
}
}
/**
* Returns an array of all the objects currently registered
* as <code><em>Foo</em>Listener</code>s
* upon this model.
* <code><em>Foo</em>Listener</code>s are registered using the
* <code>add<em>Foo</em>Listener</code> method.
*
* <p>
*
* You can specify the <code>listenerType</code> argument
* with a class literal,
* such as
* <code><em>Foo</em>Listener.class</code>.
* For example, you can query a
* <code>DefaultTreeModel</code> <code>m</code>
* for its tree model listeners with the following code:
*
* <pre>TreeModelListener[] tmls = (TreeModelListener[])(m.getListeners(TreeModelListener.class));</pre>
*
* If no such listeners exist, this method returns an empty array.
*
* @param listenerType the type of listeners requested; this parameter
* should specify an interface that descends from
* <code>java.util.EventListener</code>
* @return an array of all objects registered as
* <code><em>Foo</em>Listener</code>s on this component,
* or an empty array if no such
* listeners have been added
* @exception ClassCastException if <code>listenerType</code>
* doesn't specify a class or interface that implements
* <code>java.util.EventListener</code>
*
* @see #getTreeModelListeners
*
* @since 1.3
*/
public <T extends
EventListener> T[]
getListeners(
Class<T>
listenerType) {
return
listenerList.
getListeners(
listenerType);
}
// Serialization support.
private void
writeObject(
ObjectOutputStream s) throws
IOException {
Vector<
Object>
values = new
Vector<
Object>();
s.
defaultWriteObject();
// Save the root, if its Serializable.
if(
root != null &&
root instanceof
Serializable) {
values.
addElement("root");
values.
addElement(
root);
}
s.
writeObject(
values);
}
private void
readObject(
ObjectInputStream s)
throws
IOException,
ClassNotFoundException {
s.
defaultReadObject();
Vector values = (
Vector)
s.
readObject();
int
indexCounter = 0;
int
maxCounter =
values.
size();
if(
indexCounter <
maxCounter &&
values.
elementAt(
indexCounter).
equals("root")) {
root = (
TreeNode)
values.
elementAt(++
indexCounter);
indexCounter++;
}
}
} // End of class DefaultTreeModel