home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / DefaultTreeModel.java < prev    next >
Text File  |  1998-02-26  |  17KB  |  463 lines

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