/*
 * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javax.swing.tree;

import java.util.*;
import java.io.*;
import javax.swing.event.*;

A simple tree data model that uses TreeNodes. For further information and examples that use DefaultTreeModel, see How to Use Trees in The Java Tutorial.

Warning: 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 JavaBeansTM has been added to the java.beans package. Please see XMLEncoder.

Author:Rob Davis, Ray Ryan, Scott Violet
/** * A simple tree data model that uses TreeNodes. * For further information and examples that use DefaultTreeModel, * see <a href="http://java.sun.com/docs/books/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<sup><font size="-2">TM</font></sup> * 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.
/** Root of the tree. */
protected TreeNode root;
Listeners.
/** Listeners. */
protected EventListenerList listenerList = new EventListenerList();
Determines how the isLeaf 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 folder nodes and file nodes in a file system, for example.

If this value is false, then any node which has no children is a leaf node, and any node may acquire children.

See Also:
/** * 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.
Params:
  • root – a TreeNode object that is the root of the tree
See Also:
/** * 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) */
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.
Params:
  • root – a TreeNode object that is the root of the tree
  • asksAllowsChildren – a boolean, false if any node can have children, true if each node is asked to see if it can have children
See Also:
/** * 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.
/** * 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.
See Also:
Returns: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
/** * 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 root. A null root implies the tree is to display nothing, and is legal.
/** * 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.
Returns: the root of the tree
/** * 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 null, returns -1.
Params:
  • parent – a note in the tree, obtained from this data source
  • child – the node we are interested in
Returns:the index of the child in the parent, or -1 if either the parent or the child is null
/** * 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 parent at index index in the parent's child array. parent must be a node previously obtained from this data source. This should not return null if index is a valid index for parent (that is index >= 0 && index < getChildCount(parent)).
Params:
  • parent – a node in the tree, obtained from this data source
Returns: the child of parent at index index
/** * 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 parent. Returns 0 if the node is a leaf or if it has no children. parent must be a node previously obtained from this data source.
Params:
  • parent – a node in the tree, obtained from this data source
Returns: the number of children of the node parent
/** * 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 askAllowsChildren setting.
Params:
  • node – the node to check
See Also:
Returns:true if the node is a leaf node
/** * 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 TreeNodes upon which this model depends. The model will notify all of its listeners that the model has changed.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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 TreeNodes upon which this model depends. The model will notify all of its listeners that the model has changed below the given node.
Params:
  • node – the node below which the model has changed
/** * 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.
/** * 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.
/** * 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.
/** * 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 childrens children... This will post a treeStructureChanged event.
/** * Invoke this method if you've totally changed the children of * node and its childrens 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.
Params:
  • aNode – the TreeNode to get the path for
/** * 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.
Params:
  • aNode – the TreeNode to get the path for
  • depth – an int giving the number of steps already taken towards the root (on recursive calls), used to size the returned array
Returns:an array of TreeNodes giving the path from the root to the specified node
/** * 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.
Params:
  • l – the listener to add
See Also:
  • removeTreeModelListener
/** * 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 addTreeModelListener().
Params:
  • l – the listener to remove
See Also:
  • addTreeModelListener
/** * 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.
See Also:
Returns:all of this model's TreeModelListeners or an empty array if no tree model listeners are currently registered
Since:1.4
/** * 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.
Params:
  • source – the node being changed
  • path – the path to the root node
  • childIndices – the indices of the changed elements
  • children – the changed elements
See Also:
/** * 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 node being changed * @param path the path to the root node * @param childIndices the indices of the changed elements * @param children the changed elements * @see EventListenerList */
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.
Params:
  • source – the node where new elements are being inserted
  • path – the path to the root node
  • childIndices – the indices of the new elements
  • children – the new elements
See Also:
/** * 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 node where new elements are being inserted * @param path the path to the root node * @param childIndices the indices of the new elements * @param children the new elements * @see EventListenerList */
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.
Params:
  • source – the node where elements are being removed
  • path – the path to the root node
  • childIndices – the indices of the removed elements
  • children – the removed elements
See Also:
/** * 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 node where elements are being removed * @param path the path to the root node * @param childIndices the indices of the removed elements * @param children the removed elements * @see EventListenerList */
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.
Params:
  • source – the node where the tree model has changed
  • path – the path to the root node
  • childIndices – the indices of the affected elements
  • children – the affected elements
See Also:
/** * 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 node where the tree model has changed * @param path the path to the root node * @param childIndices the indices of the affected elements * @param children the affected elements * @see EventListenerList */
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 node where the tree model has changed * @param path the path to the root node * @see EventListenerList */ 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 FooListeners upon this model. FooListeners are registered using the addFooListener method.

You can specify the listenerType argument with a class literal, such as FooListener.class. For example, you can query a DefaultTreeModel m for its tree model listeners with the following code:

TreeModelListener[] tmls = (TreeModelListener[])(m.getListeners(TreeModelListener.class));
If no such listeners exist, this method returns an empty array.
Params:
  • listenerType – the type of listeners requested; this parameter should specify an interface that descends from java.util.EventListener
Throws:
  • ClassCastException – if listenerType doesn't specify a class or interface that implements java.util.EventListener
See Also:
Returns:an array of all objects registered as FooListeners on this component, or an empty array if no such listeners have been added
Since:1.3
/** * 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