home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / VCAFE.3.0A / Main.bin / DefaultTreeModel.java < prev    next >
Text File  |  1998-11-09  |  18KB  |  486 lines

  1. /*
  2.  * @(#)DefaultTreeModel.java    1.29 98/04/02
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package javax.awt.swing.tree;
  22.  
  23. import java.util.*;
  24. import java.awt.*;
  25. import java.io.*;
  26. import javax.awt.swing.event.TreeModelEvent;
  27. import javax.awt.swing.event.TreeModelListener;
  28. import javax.awt.swing.event.EventListenerList;
  29.  
  30. /**
  31.  * A simple tree data model that uses TreeNodes.
  32.  * <p>
  33.  * Warning: serialized objects of this class will not be compatible with
  34.  * future swing releases.  The current serialization support is appropriate
  35.  * for short term storage or RMI between Swing1.0 applications.  It will
  36.  * not be possible to load serialized Swing1.0 objects with future releases
  37.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  38.  * baseline for the serialized form of Swing objects.
  39.  *
  40.  * @version 1.29 04/02/98
  41.  * @author Rob Davis
  42.  * @author Ray Ryan
  43.  * @author Scott Violet
  44.  */
  45. public class DefaultTreeModel implements Serializable, TreeModel {
  46.     /** Root of the tree. */
  47.     protected TreeNode root;
  48.     /** Listeners. */
  49.     protected EventListenerList listenerList = new EventListenerList();
  50.     /**
  51.       * Determines how the <code>isLeaf</code> determines if
  52.       * a node is a leaf node. If true, a node is a leaf 
  53.       * node if it does not allow children. (If it allows 
  54.       * children, it is not a leaf node, even if no children
  55.       * are present.) If this value is false, then any node 
  56.       * which has no children is a leaf node. 
  57.       *
  58.       * @see TreeNode#getAllowsChildren
  59.       * @see TreeModel#isLeaf
  60.       * @see #setAsksAllowsChildren
  61.       */
  62.     protected boolean asksAllowsChildren;
  63.  
  64.     public DefaultTreeModel(TreeNode root) {
  65.         this(root, false);
  66.     }
  67.  
  68.     public DefaultTreeModel(TreeNode root, boolean asksAllowsChildren) {
  69.         super();
  70.         if (root == null) {
  71.             throw new IllegalArgumentException("root is null");
  72.         }
  73.         this.root = root;
  74.         this.asksAllowsChildren = asksAllowsChildren;
  75.     }
  76.  
  77.     /**
  78.       * Sets whether or not to test leafness by asking getAllowsChildren()
  79.       * or isLeaf() to the TreeNodes.  If newvalue is true, getAllowsChildren()
  80.       * is messaged, otherwise isLeaf() is messaged.
  81.       */
  82.     public void setAsksAllowsChildren(boolean newValue) {
  83.         asksAllowsChildren = newValue;
  84.     }
  85.  
  86.     /**
  87.       * Tells how leaf nodes are determined.
  88.       *
  89.       * @return true if only nodes which do not allow children are
  90.       *         leaf nodes, false if nodes which have no children
  91.       *         (even if allowed) are leaf nodes
  92.       * @see #asksAllowsChildren
  93.       */
  94.     public boolean asksAllowsChildren() {
  95.         return asksAllowsChildren;
  96.     }
  97.  
  98.     public Object getRoot() {
  99.         return root;
  100.     }
  101.  
  102.     public int getIndexOfChild(Object parent, Object child) {
  103.         if(parent == null || child == null)
  104.             return 0;
  105.         return ((TreeNode)parent).getIndex((TreeNode)child);
  106.     }
  107.  
  108.     public Object getChild(Object parent, int index) {
  109.         return ((TreeNode)parent).getChildAt(index);
  110.     }
  111.  
  112.     public int getChildCount(Object parent) {
  113.         return ((TreeNode)parent).getChildCount();
  114.     }
  115.  
  116.     /** 
  117.      * Returns whether the specified node is a leaf node.
  118.      * The way the test is performed depends on the
  119.      * <code>askAllowsChildren</code> setting.
  120.      *
  121.      * @param node the node to check
  122.      * @return true if the node is a leaf node
  123.      *
  124.      * @see #asksAllowsChildren
  125.      * @see TreeModel#isLeaf
  126.      */
  127.     public boolean isLeaf(Object node) {
  128.         if(asksAllowsChildren)
  129.             return !((TreeNode)node).getAllowsChildren();
  130.         return ((TreeNode)node).isLeaf();
  131.     }
  132.  
  133.     /**
  134.      * Invoke this method if you've modified the TreeNodes upon which this
  135.      * model depends.  The model will notify all of its listeners that the
  136.      * model has changed.
  137.      */
  138.     public void reload() {
  139.         reload(root);
  140.     }
  141.  
  142.     /**
  143.       * This sets the user object of the TreeNode identified by path
  144.       * and posts a node changed.  If you use custom user objects in
  145.       * the TreeModel you're going to need to subclass this and
  146.       * set the user object of the changed node to something meaningful.
  147.       */
  148.     public void valueForPathChanged(TreePath path, Object newValue) {
  149.         Object[]             aPath = path.getPath();
  150.         MutableTreeNode      aNode = (MutableTreeNode)aPath[aPath.length - 1];
  151.  
  152.         aNode.setUserObject(newValue);
  153.         nodeChanged(aNode);
  154.     }
  155.  
  156.     /**
  157.      * Invoked this to insert newChild at location index in parents children.
  158.      * This will then message nodesWereInserted to create the appropriate
  159.      * event. This is the preferred way to add children as it will create
  160.      * the appropriate event.
  161.      */
  162.     public void insertNodeInto(MutableTreeNode newChild,
  163.                                MutableTreeNode parent, int index){
  164.         parent.insert(newChild, index);
  165.  
  166.         int[]           newIndexs = new int[1];
  167.  
  168.         newIndexs[0] = index;
  169.         nodesWereInserted(parent, newIndexs);
  170.     }
  171.  
  172.     /**
  173.      * Message this to remove node from its parent. This will message
  174.      * nodesWereRemoved to create the appropriate event. This is the
  175.      * preferred way to remove a node as it handles the event creation
  176.      * for you.
  177.      */
  178.     public void removeNodeFromParent(MutableTreeNode node) {
  179.         MutableTreeNode         parent = (MutableTreeNode)node.getParent();
  180.  
  181.         if(parent == null)
  182.             throw new IllegalArgumentException("node does not have a parent.");
  183.  
  184.         int[]            childIndex = new int[1];
  185.         Object[]         removedArray = new Object[1];
  186.  
  187.         childIndex[0] = parent.getIndex(node);
  188.         parent.remove(childIndex[0]);
  189.         removedArray[0] = node;
  190.         nodesWereRemoved(parent, childIndex, removedArray);
  191.     }
  192.  
  193.     /**
  194.       * Invoke this method after you've changed how node is to be
  195.       * represented in the tree.
  196.       */
  197.     public void nodeChanged(TreeNode node) {
  198.         if(listenerList != null && node != null) {
  199.             TreeNode         parent = node.getParent();
  200.  
  201.             if(parent != null) {
  202.                 int        anIndex = parent.getIndex(node);
  203.                 if(anIndex != -1) {
  204.                     int[]        cIndexs = new int[1];
  205.  
  206.                     cIndexs[0] = anIndex;
  207.                     nodesChanged(parent, cIndexs);
  208.                 }
  209.             }
  210.         }
  211.     }
  212.  
  213.     /**
  214.      * Invoke this method if you've modified the TreeNodes upon which this
  215.      * model depends.  The model will notify all of its listeners that the
  216.      * model has changed below the node <code>node</code> (PENDING).
  217.      */
  218.     public void reload(TreeNode node) {
  219.         if(node != null) {
  220.             fireTreeStructureChanged(this, getPathToRoot(node), null, null);
  221.         }
  222.     }
  223.  
  224.     /**
  225.       * Invoke this method after you've inserted some TreeNodes into
  226.       * node.  childIndices should be the index of the new elements and
  227.       * must be sorted in ascending order.
  228.       */
  229.     public void nodesWereInserted(TreeNode node, int[] childIndices) {
  230.         if(listenerList != null && node != null && childIndices != null
  231.            && childIndices.length > 0) {
  232.             int               cCount = childIndices.length;
  233.             Object[]          newChildren = new Object[cCount];
  234.  
  235.             for(int counter = 0; counter < cCount; counter++)
  236.                 newChildren[counter] = node.getChildAt(childIndices[counter]);
  237.             fireTreeNodesInserted(this, getPathToRoot(node), childIndices, 
  238.                                   newChildren);
  239.         }
  240.     }
  241.     
  242.     /**
  243.       * Invoke this method after you've removed some TreeNodes from
  244.       * node.  childIndices should be the index of the removed elements and
  245.       * must be sorted in ascending order. And removedChildren should be
  246.       * the array of the children objects that were removed.
  247.       */
  248.     public void nodesWereRemoved(TreeNode node, int[] childIndices,
  249.                                  Object[] removedChildren) {
  250.         if(node != null && childIndices != null) {
  251.             fireTreeNodesRemoved(this, getPathToRoot(node), childIndices, 
  252.                                  removedChildren);
  253.         }
  254.     }
  255.  
  256.     /**
  257.       * Invoke this method after you've changed how the children identified by
  258.       * childIndicies are to be represented in the tree.
  259.       */
  260.     public void nodesChanged(TreeNode node, int[] childIndices) {
  261.         if(node != null && childIndices != null) {
  262.             int            cCount = childIndices.length;
  263.  
  264.             if(cCount > 0) {
  265.                 Object[]       cChildren = new Object[cCount];
  266.  
  267.                 for(int counter = 0; counter < cCount; counter++)
  268.                     cChildren[counter] = node.getChildAt
  269.                         (childIndices[counter]);
  270.                 fireTreeNodesChanged(this, getPathToRoot(node), childIndices, 
  271.                                      cChildren);
  272.             }
  273.         }
  274.     }
  275.  
  276.     /**
  277.       * Invoke this method if you've totally changed the children of
  278.       * node and its childrens children...  This will post a
  279.       * treeStructureChanged event.
  280.       */
  281.     public void nodeStructureChanged(TreeNode node) {
  282.         if(node != null) {
  283.            fireTreeStructureChanged(this, getPathToRoot(node), null, null);
  284.         }
  285.     }
  286.  
  287.     /**
  288.      * Builds the parents of node up to and including the root node,
  289.      * where the original node is the last element in the returned array.
  290.      * The length of the returned array gives the node's depth in the
  291.      * tree.
  292.      * 
  293.      * @param aNode the TreeNode to get the path for
  294.      * @param an array of TreeNodes giving the path from the root to the
  295.      *        specified node. 
  296.      */
  297.     public TreeNode[] getPathToRoot(TreeNode aNode) {
  298.         return getPathToRoot(aNode, 0);
  299.     }
  300.  
  301.     /**
  302.      * Builds the parents of node up to and including the root node,
  303.      * where the original node is the last element in the returned array.
  304.      * The length of the returned array gives the node's depth in the
  305.      * tree.
  306.      * 
  307.      * @param aNode  the TreeNode to get the path for
  308.      * @param depth  an int giving the number of steps already taken towards
  309.      *        the root (on recursive calls), used to size the returned array
  310.      * @return an array of TreeNodes giving the path from the root to the
  311.      *         specified node 
  312.      */
  313.     protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
  314.         TreeNode[]              retNodes;
  315.     // This method recurses, traversing towards the root in order
  316.     // size the array. On the way back, it fills in the nodes,
  317.     // starting from the root and working back to the original node.
  318.  
  319.         /* Check for null, in case someone passed in a null node, or
  320.            they passed in an element that isn't rooted at root. */
  321.         if(aNode == null) {
  322.             if(depth == 0)
  323.                 return null;
  324.             else
  325.                 retNodes = new TreeNode[depth];
  326.         }
  327.         else {
  328.             depth++;
  329.             if(aNode == root)
  330.                 retNodes = new TreeNode[depth];
  331.             else
  332.                 retNodes = getPathToRoot(aNode.getParent(), depth);
  333.             retNodes[retNodes.length - depth] = aNode;
  334.         }
  335.         return retNodes;
  336.     }
  337.  
  338.     //
  339.     //  Events
  340.     //
  341.  
  342.     public void addTreeModelListener(TreeModelListener l) {
  343.         listenerList.add(TreeModelListener.class, l);
  344.     }
  345.  
  346.     public void removeTreeModelListener(TreeModelListener l) {
  347.         listenerList.remove(TreeModelListener.class, l);
  348.     }
  349.  
  350.     /*
  351.      * Notify all listeners that have registered interest for
  352.      * notification on this event type.  The event instance 
  353.      * is lazily created using the parameters passed into 
  354.      * the fire method.
  355.      * @see EventListenerList
  356.      */
  357.     protected void fireTreeNodesChanged(Object source, Object[] path, 
  358.                                         int[] childIndices, 
  359.                                         Object[] children) {
  360.         // Guaranteed to return a non-null array
  361.         Object[] listeners = listenerList.getListenerList();
  362.         TreeModelEvent e = null;
  363.         // Process the listeners last to first, notifying
  364.         // those that are interested in this event
  365.         for (int i = listeners.length-2; i>=0; i-=2) {
  366.             if (listeners[i]==TreeModelListener.class) {
  367.                 // Lazily create the event:
  368.                 if (e == null)
  369.                     e = new TreeModelEvent(source, path, 
  370.                                            childIndices, children);
  371.                 ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
  372.             }          
  373.         }
  374.     }
  375.  
  376.     /*
  377.      * Notify all listeners that have registered interest for
  378.      * notification on this event type.  The event instance 
  379.      * is lazily created using the parameters passed into 
  380.      * the fire method.
  381.      * @see EventListenerList
  382.      */
  383.     protected void fireTreeNodesInserted(Object source, Object[] path, 
  384.                                         int[] childIndices, 
  385.                                         Object[] children) {
  386.         // Guaranteed to return a non-null array
  387.         Object[] listeners = listenerList.getListenerList();
  388.         TreeModelEvent e = null;
  389.         // Process the listeners last to first, notifying
  390.         // those that are interested in this event
  391.         for (int i = listeners.length-2; i>=0; i-=2) {
  392.             if (listeners[i]==TreeModelListener.class) {
  393.                 // Lazily create the event:
  394.                 if (e == null)
  395.                     e = new TreeModelEvent(source, path, 
  396.                                            childIndices, children);
  397.                 ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
  398.             }          
  399.         }
  400.     }
  401.  
  402.     /*
  403.      * Notify all listeners that have registered interest for
  404.      * notification on this event type.  The event instance 
  405.      * is lazily created using the parameters passed into 
  406.      * the fire method.
  407.      * @see EventListenerList
  408.      */
  409.     protected void fireTreeNodesRemoved(Object source, Object[] path, 
  410.                                         int[] childIndices, 
  411.                                         Object[] children) {
  412.         // Guaranteed to return a non-null array
  413.         Object[] listeners = listenerList.getListenerList();
  414.         TreeModelEvent e = null;
  415.         // Process the listeners last to first, notifying
  416.         // those that are interested in this event
  417.         for (int i = listeners.length-2; i>=0; i-=2) {
  418.             if (listeners[i]==TreeModelListener.class) {
  419.                 // Lazily create the event:
  420.                 if (e == null)
  421.                     e = new TreeModelEvent(source, path, 
  422.                                            childIndices, children);
  423.                 ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
  424.             }          
  425.         }
  426.     }
  427.  
  428.     /*
  429.      * Notify all listeners that have registered interest for
  430.      * notification on this event type.  The event instance 
  431.      * is lazily created using the parameters passed into 
  432.      * the fire method.
  433.      * @see EventListenerList
  434.      */
  435.     protected void fireTreeStructureChanged(Object source, Object[] path, 
  436.                                         int[] childIndices, 
  437.                                         Object[] children) {
  438.         // Guaranteed to return a non-null array
  439.         Object[] listeners = listenerList.getListenerList();
  440.         TreeModelEvent e = null;
  441.         // Process the listeners last to first, notifying
  442.         // those that are interested in this event
  443.         for (int i = listeners.length-2; i>=0; i-=2) {
  444.             if (listeners[i]==TreeModelListener.class) {
  445.                 // Lazily create the event:
  446.                 if (e == null)
  447.                     e = new TreeModelEvent(source, path, 
  448.                                            childIndices, children);
  449.                 ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
  450.             }          
  451.         }
  452.     }
  453.  
  454.     // Serialization support.  
  455.     private void writeObject(ObjectOutputStream s) throws IOException {
  456.         Vector      values = new Vector();
  457.  
  458.         s.defaultWriteObject();
  459.         // Save the cellRenderer, if its Serializable.
  460.         if(root != null && root instanceof Serializable) {
  461.             values.addElement("root");
  462.             values.addElement(root);
  463.         }
  464.         s.writeObject(values);
  465.     }
  466.  
  467.     private void readObject(ObjectInputStream s) 
  468.         throws IOException, ClassNotFoundException {
  469.         s.defaultReadObject();
  470.  
  471.         Vector          values = (Vector)s.readObject();
  472.         int             indexCounter = 0;
  473.         int             maxCounter = values.size();
  474.  
  475.         if(indexCounter < maxCounter && values.elementAt(indexCounter).
  476.            equals("root")) {
  477.             root = (TreeNode)values.elementAt(++indexCounter);
  478.             indexCounter++;
  479.         }
  480.     }
  481.  
  482.  
  483. } // End of class DefaultTreeModel
  484.  
  485.  
  486.