001/* DefaultTreeModel.java -- 
002   Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
003 
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING. If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library. Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module. An independent module is a module which is not derived from
033or based on this library. If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so. If you do not wish to do so, delete this
036exception statement from your version. */
037
038package javax.swing.tree;
039
040import java.io.IOException;
041import java.io.ObjectInputStream;
042import java.io.ObjectOutputStream;
043import java.io.Serializable;
044import java.util.EventListener;
045
046import javax.swing.event.EventListenerList;
047import javax.swing.event.TreeModelEvent;
048import javax.swing.event.TreeModelListener;
049
050/**
051 * DefaultTreeModel
052 * 
053 * @author Andrew Selkirk
054 */
055public 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}