001    /* DefaultTreeModel.java --
002       Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING. If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library. Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module. An independent module is a module which is not derived from
033    or based on this library. If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so. If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.swing.tree;
039    
040    import java.io.IOException;
041    import java.io.ObjectInputStream;
042    import java.io.ObjectOutputStream;
043    import java.io.Serializable;
044    import java.util.EventListener;
045    
046    import javax.swing.event.EventListenerList;
047    import javax.swing.event.TreeModelEvent;
048    import javax.swing.event.TreeModelListener;
049    
050    /**
051     * DefaultTreeModel
052     *
053     * @author Andrew Selkirk
054     */
055    public class DefaultTreeModel
056        implements Serializable, TreeModel
057    {
058      static final long serialVersionUID = -2621068368932566998L;
059    
060      /**
061       * root
062       */
063      protected TreeNode root;
064    
065      /**
066       * listenerList
067       */
068      protected EventListenerList listenerList = new EventListenerList();
069    
070      /**
071       * asksAllowsChildren
072       */
073      protected boolean asksAllowsChildren;
074    
075      /**
076       * Constructor DefaultTreeModel where any node can have children.
077       *
078       * @param root the tree root.
079       */
080      public DefaultTreeModel(TreeNode root)
081      {
082        this (root, false);
083      }
084    
085      /**
086       * Create the DefaultTreeModel that may check if the nodes can have
087       * children or not.
088       *
089       * @param aRoot the tree root.
090       * @param asksAllowsChildren if true, each node is asked if it can have
091       * children. If false, the model does not care about this, supposing, that
092       * any node can have children.
093       */
094      public DefaultTreeModel(TreeNode aRoot, boolean asksAllowsChildren)
095      {
096        if (aRoot == null)
097          aRoot = new DefaultMutableTreeNode();
098        this.root = aRoot;
099        this.asksAllowsChildren = asksAllowsChildren;
100      }
101    
102      /**
103       * writeObject
104       *
105       * @param obj the object.
106       * @exception IOException TODO
107       */
108      private void writeObject(ObjectOutputStream obj) throws IOException
109      {
110        // TODO
111      }
112    
113      /**
114       * readObject
115       *
116       * @param value0 TODO
117       * @exception IOException TODO
118       * @exception ClassNotFoundException TODO
119       */
120      private void readObject(ObjectInputStream value0) throws IOException,
121          ClassNotFoundException
122      {
123        // TODO
124      }
125    
126      /**
127       * asksAllowsChildren
128       *
129       * @return boolean
130       */
131      public boolean asksAllowsChildren()
132      {
133        return asksAllowsChildren;
134      }
135    
136      /**
137       * setAsksAllowsChildren
138       *
139       * @param value TODO
140       */
141      public void setAsksAllowsChildren(boolean value)
142      {
143        asksAllowsChildren = value;
144      }
145    
146      /**
147       * setRoot
148       *
149       * @param root the root node.
150       */
151      public void setRoot(TreeNode root)
152      {
153        this.root = root;
154      }
155    
156      /**
157       * getRoot
158       *
159       * @return Object
160       */
161      public Object getRoot()
162      {
163        return root;
164      }
165    
166      /**
167       * getIndexOfChild
168       *
169       * @param parent TODO
170       * @param child TODO
171       * @return int
172       */
173      public int getIndexOfChild(Object parent, Object child)
174      {
175        for (int i = 0; i < getChildCount(parent); i++)
176          {
177            if (getChild(parent, i).equals(child))
178              return i;
179          }
180        return -1;
181      }
182    
183      /**
184       * getChild
185       *
186       * @param node TODO
187       * @param idx TODO
188       * @return Object
189       */
190      public Object getChild(Object node, int idx)
191      {
192        if (node instanceof TreeNode)
193          return ((TreeNode) node).getChildAt(idx);
194        else
195          return null;
196      }
197    
198      /**
199       * getChildCount
200       *
201       * @param node TODO
202       * @return int
203       */
204      public int getChildCount(Object node)
205      {
206        if (node instanceof TreeNode)
207          return ((TreeNode) node).getChildCount();
208        else
209          return 0;
210      }
211    
212      /**
213       * Returns if the specified node is a leaf or not. When
214       * {@link #asksAllowsChildren} is true, then this checks if the TreeNode
215       * allows children, otherwise it returns the TreeNode's <code>leaf</code>
216       * property.
217       *
218       * @param node the node to check
219       *
220       * @return boolean <code>true</code> if the node is a leaf node,
221       *         <code>false</code> otherwise
222       *
223       * @throws ClassCastException if the specified node is not a
224       *         <code>TreeNode</code> instance
225       *
226       * @see TreeNode#getAllowsChildren()
227       * @see TreeNode#isLeaf()
228       */
229      public boolean isLeaf(Object node)
230      {
231        // The RI throws a ClassCastException when node isn't a TreeNode, so do we.
232        TreeNode treeNode = (TreeNode) node;
233        boolean leaf;
234        if (asksAllowsChildren)
235          leaf = ! treeNode.getAllowsChildren();
236        else
237          leaf = treeNode.isLeaf();
238        return leaf;
239      }
240    
241      /**
242       * <p>
243       * Invoke this method if you've modified the TreeNodes upon which this model
244       * depends. The model will notify all of its listeners that the model has
245       * changed. It will fire the events, necessary to update the layout caches and
246       * repaint the tree. The tree will <i>not</i> be properly refreshed if you
247       * call the JTree.repaint instead.
248       * </p>
249       * <p>
250       * This method will refresh the information about whole tree from the root. If
251       * only part of the tree should be refreshed, it is more effective to call
252       * {@link #reload(TreeNode)}.
253       * </p>
254       */
255      public void reload()
256      {
257        // Need to duplicate the code because the root can formally be
258        // no an instance of the TreeNode.
259        int n = getChildCount(root);
260        int[] childIdx = new int[n];
261        Object[] children = new Object[n];
262    
263        for (int i = 0; i < n; i++)
264          {
265            childIdx[i] = i;
266            children[i] = getChild(root, i);
267          }
268    
269        fireTreeStructureChanged(this, new Object[] { root }, childIdx, children);
270      }
271    
272      /**
273       * Invoke this method if you've modified the TreeNodes upon which this model
274       * depends. The model will notify all of its listeners that the model has
275       * changed. It will fire the events, necessary to update the layout caches and
276       * repaint the tree. The tree will <i>not</i> be properly refreshed if you
277       * call the JTree.repaint instead.
278       *
279       * @param node - the tree node, from which the tree nodes have changed
280       *          (inclusive). If you do not know this node, call {@link #reload()}
281       *          instead.
282       */
283      public void reload(TreeNode node)
284      {
285        int n = getChildCount(node);
286        int[] childIdx = new int[n];
287        Object[] children = new Object[n];
288    
289        for (int i = 0; i < n; i++)
290          {
291            childIdx[i] = i;
292            children[i] = getChild(node, i);
293          }
294    
295        fireTreeStructureChanged(this, getPathToRoot(node), childIdx, children);
296      }
297    
298      /**
299       * Messaged when the user has altered the value for the item
300       * identified by path to newValue. If newValue signifies a truly new
301       * value the model should post a treeNodesChanged event.
302       * This sets the user object of the TreeNode identified by
303       * path and posts a node changed. If you use custom user objects
304       * in the TreeModel you're going to need to subclass this and set
305       * the user object of the changed node to something meaningful.
306       *
307       * @param path - path to the node that the user has altered
308       * @param newValue - the new value from the TreeCellEditor
309       */
310      public void valueForPathChanged(TreePath path, Object newValue)
311      {
312        Object node = path.getLastPathComponent();
313        if (node instanceof MutableTreeNode)
314          {
315            ((MutableTreeNode) node).setUserObject(newValue);
316            int[] ci = null;
317            Object[] c = null;
318            Object[] parentPath = path.getPath();
319            if (path.getPathCount() > 1)
320              {
321                Object parent = ((TreeNode) node).getParent();
322                ci = new int[1];
323                ci[0] = getIndexOfChild(parent, node);
324                node = newValue;
325                path = path.getParentPath().pathByAddingChild(node);
326                c = new Object[1];
327                c[0] = node;
328                parentPath = path.getParentPath().getPath();
329              }
330    
331            fireTreeNodesChanged(this, parentPath, ci, c);
332          }
333        }
334    
335      /**
336       * Invoked this to insert newChild at location index in parents children.
337       * This will then message nodesWereInserted to create the appropriate event.
338       * This is the preferred way to add children as it will create the
339       * appropriate event.
340       *
341       * @param newChild is the node to add to the parent's children
342       * @param parent is the parent of the newChild
343       * @param index is the index of the newChild
344       */
345      public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent,
346                                 int index)
347      {
348        newChild.setParent(parent);
349        parent.insert(newChild, index);
350        int[] childIndices = new int[1];
351        childIndices[0] = index;
352        nodesWereInserted(parent, childIndices);
353      }
354    
355      /**
356       * Message this to remove node from its parent. This will message
357       * nodesWereRemoved to create the appropriate event. This is the preferred
358       * way to remove a node as it handles the event creation for you.
359       *
360       * @param node to be removed
361       */
362      public void removeNodeFromParent(MutableTreeNode node)
363      {
364        TreeNode parent = node.getParent();
365        Object[] children = new Object[1];
366        children[0] = node;
367        int[] childIndices = new int[1];
368        childIndices[0] = getIndexOfChild(parent, node);
369        node.removeFromParent();
370        nodesWereRemoved(parent, childIndices, children);
371      }
372    
373      /**
374       * Invoke this method after you've changed how node is to be represented
375       * in the tree.
376       *
377       * @param node that was changed
378       */
379      public void nodeChanged(TreeNode node)
380      {
381        TreeNode parent = node.getParent();
382        int[] childIndices = new int[1];
383        childIndices[0] = getIndexOfChild(parent, node);
384        Object[] children = new Object[1];
385        children[0] = node;
386        fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
387      }
388    
389      /**
390       * Invoke this method after you've inserted some TreeNodes
391       * into node. childIndices should be the index of the new elements and must
392       * be sorted in ascending order.
393       *
394       * @param parent that had a child added to
395       * @param childIndices of the children added
396       */
397      public void nodesWereInserted(TreeNode parent, int[] childIndices)
398      {
399        Object[] children = new Object[childIndices.length];
400        for (int i = 0; i < children.length; i++)
401          children[i] = getChild(parent, childIndices[i]);
402        fireTreeNodesInserted(this, getPathToRoot(parent), childIndices, children);
403      }
404    
405      /**
406       * Invoke this method after you've removed some TreeNodes from node.
407       * childIndices should be the index of the removed elements and
408       * must be sorted in ascending order. And removedChildren should be the
409       * array of the children objects that were removed.
410       *
411       * @param parent that had a child added to
412       * @param childIndices of the children added
413       * @param removedChildren are all the children removed from parent.
414       */
415      public void nodesWereRemoved(TreeNode parent, int[] childIndices,
416                                   Object[] removedChildren)
417      {
418        fireTreeNodesRemoved(this, getPathToRoot(parent), childIndices,
419                             removedChildren);
420      }
421    
422      /**
423       * Invoke this method after you've changed how the children identified by
424       * childIndices are to be represented in the tree.
425       *
426       * @param node that is the parent of the children that changed in a tree.
427       * @param childIndices are the child nodes that changed.
428       */
429      public void nodesChanged(TreeNode node, int[] childIndices)
430      {
431        Object[] children = new Object[childIndices.length];
432        for (int i = 0; i < children.length; i++)
433          children[i] = getChild(node, childIndices[i]);
434        fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
435      }
436    
437      /**
438       * Invoke this method if you've totally changed the children of node and
439       * its childrens children. This will post a treeStructureChanged event.
440       *
441       * @param node that had its children and grandchildren changed.
442       */
443      public void nodeStructureChanged(TreeNode node)
444      {
445        int n = getChildCount(root);
446        int[] childIdx = new int[n];
447        Object[] children = new Object[n];
448    
449        for (int i = 0; i < n; i++)
450          {
451            childIdx[i] = i;
452            children[i] = getChild(root, i);
453          }
454    
455        fireTreeStructureChanged(this, new Object[] { root }, childIdx, children);
456      }
457    
458      /**
459       * Builds the parents of node up to and including the root node, where
460       * the original node is the last element in the returned array. The
461       * length of the returned array gives the node's depth in the tree.
462       *
463       * @param node - the TreeNode to get the path for
464       * @return TreeNode[] - the path from node to the root
465       */
466      public TreeNode[] getPathToRoot(TreeNode node)
467      {
468        return getPathToRoot(node, 0);
469      }
470    
471      /**
472       * Builds the parents of node up to and including the root node, where
473       * the original node is the last element in the returned array. The
474       * length of the returned array gives the node's depth in the tree.
475       *
476       * @param node - the TreeNode to get the path for
477       * @param depth - an int giving the number of steps already taken
478       * towards the root (on recursive calls), used to size the returned array
479       * @return an array of TreeNodes giving the path from the root to the
480       * specified node
481       */
482      protected TreeNode[] getPathToRoot(TreeNode node, int depth)
483      {
484        if (node == null)
485          {
486            if (depth == 0)
487              return null;
488    
489            return new TreeNode[depth];
490          }
491    
492        TreeNode[] path = getPathToRoot(node.getParent(), depth + 1);
493        path[path.length - depth - 1] = node;
494        return path;
495      }
496    
497      /**
498       * Registers a listere to the model.
499       *
500       * @param listener the listener to add
501       */
502      public void addTreeModelListener(TreeModelListener listener)
503      {
504        listenerList.add(TreeModelListener.class, listener);
505      }
506    
507      /**
508       * Removes a listener from the model.
509       *
510       * @param listener the listener to remove
511       */
512      public void removeTreeModelListener(TreeModelListener listener)
513      {
514        listenerList.remove(TreeModelListener.class, listener);
515      }
516    
517      /**
518       * Returns all registered <code>TreeModelListener</code> listeners.
519       *
520       * @return an array of listeners.
521       *
522       * @since 1.4
523       */
524      public TreeModelListener[] getTreeModelListeners()
525      {
526        return (TreeModelListener[]) listenerList
527            .getListeners(TreeModelListener.class);
528      }
529    
530      /**
531       * Notifies all listeners that have registered interest for notification
532       * on this event type. The event instance is lazily created using the parameters
533       * passed into the fire method.
534       *
535       * @param source the node being changed
536       * @param path the path to the root node
537       * @param childIndices the indices of the changed elements
538       * @param children the changed elements
539       */
540      protected void fireTreeNodesChanged(Object source, Object[] path,
541          int[] childIndices, Object[] children)
542      {
543        TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
544            children);
545    
546        TreeModelListener[] listeners = getTreeModelListeners();
547    
548        for (int i = listeners.length - 1; i >= 0; --i)
549          listeners[i].treeNodesChanged(event);
550      }
551    
552      /**
553       * fireTreeNodesInserted
554       *
555       * @param source the node where new nodes got inserted
556       * @param path the path to the root node
557       * @param childIndices the indices of the new elements
558       * @param children the new elements
559       */
560      protected void fireTreeNodesInserted(Object source, Object[] path,
561          int[] childIndices, Object[] children)
562      {
563        TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
564            children);
565        TreeModelListener[] listeners = getTreeModelListeners();
566    
567        for (int i = listeners.length - 1; i >= 0; --i)
568          listeners[i].treeNodesInserted(event);
569      }
570    
571      /**
572       * fireTreeNodesRemoved
573       *
574       * @param source the node where nodes got removed-
575       * @param path the path to the root node
576       * @param childIndices the indices of the removed elements
577       * @param children the removed elements
578       */
579      protected void fireTreeNodesRemoved(Object source, Object[] path,
580          int[] childIndices, Object[] children)
581      {
582        TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
583            children);
584        TreeModelListener[] listeners = getTreeModelListeners();
585    
586        for (int i = listeners.length - 1; i >= 0; --i)
587          listeners[i].treeNodesRemoved(event);
588      }
589    
590      /**
591       * fireTreeStructureChanged
592       *
593       * @param source the node where the model has changed
594       * @param path the path to the root node
595       * @param childIndices the indices of the affected elements
596       * @param children the affected elements
597       */
598      protected void fireTreeStructureChanged(Object source, Object[] path,
599          int[] childIndices, Object[] children)
600      {
601        TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
602            children);
603        TreeModelListener[] listeners = getTreeModelListeners();
604    
605        for (int i = listeners.length - 1; i >= 0; --i)
606          listeners[i].treeStructureChanged(event);
607      }
608    
609      /**
610       * Returns the registered listeners of a given type.
611       *
612       * @param listenerType the listener type to return
613       *
614       * @return an array of listeners
615       *
616       * @since 1.3
617       */
618      public <T extends EventListener> T[] getListeners(Class<T> listenerType)
619      {
620        return listenerList.getListeners(listenerType);
621      }
622    }