View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.custom.tree;
20  
21  import java.io.IOException;
22  import java.io.Serializable;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.LinkedList;
26  import java.util.List;
27  
28  import javax.faces.component.UIViewRoot;
29  import javax.faces.component.html.HtmlPanelGroup;
30  import javax.faces.context.FacesContext;
31  import javax.faces.el.ValueBinding;
32  
33  import org.apache.myfaces.custom.tree.event.TreeSelectionEvent;
34  import org.apache.myfaces.custom.tree.event.TreeSelectionListener;
35  import org.apache.myfaces.custom.tree.model.TreeModel;
36  import org.apache.myfaces.custom.tree.model.TreeModelEvent;
37  import org.apache.myfaces.custom.tree.model.TreeModelListener;
38  import org.apache.myfaces.custom.tree.model.TreePath;
39  
40  
41  /**
42   * A tree data component. 
43   * Unless otherwise specified, all attributes accept static values or EL expressions.
44   * <p>
45   * Tree implementation based on javax.swing.JTree.
46   * </p>
47   * <p>
48   * The tree model is assigned by using a value binding named <code>model</code>
49   * and is not stored in view state.
50   * </p>
51   * <p>
52   * A hierarchy of {@link HtmlTreeNode}objects is used to represent the current
53   * expanded state of the tree. The root node is held as a faces named * 
54   * <code>rootNode</code>.
55   * </p>
56   *
57   * @JSFComponent
58   *   name = "t:tree"
59   *   tagClass = "org.apache.myfaces.custom.tree.taglib.TreeTag"
60   *   tagSuperclass = "org.apache.myfaces.custom.tree.taglib.AbstractTreeTag"
61   *   type = "org.apache.myfaces.HtmlTree"
62   *   tagHandler = "org.apache.myfaces.custom.tree.taglib.TreeTagHandler"
63   *
64   * @JSFJspProperty name = "headerClass" returnType = "java.lang.String"
65   * @JSFJspProperty name = "footerClass" returnType = "java.lang.String"
66   * @JSFJspProperty name = "expandRoot" returnType = "boolean" literalOnly="true" inheritedTag="true"
67   * @JSFJspProperty name = "style" tagExcluded = "true"
68   * @author <a href="mailto:oliver@rossmueller.com">Oliver Rossmueller </a>
69   * @version $Revision: 672986 $ $Date: 2008-06-30 23:13:55 -0500 (lun, 30 jun 2008) $
70   */
71  public class HtmlTree extends HtmlPanelGroup implements TreeModelListener
72  {
73      public static final String COMPONENT_TYPE = "org.apache.myfaces.HtmlTree";
74      public static final String COMPONENT_FAMILY = "org.apache.myfaces.HtmlTree";
75      private static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.HtmlTree";
76      
77      public static final long DEFAULT_EXPIRE_LISTENERS = 8 * 60 * 60 * 1000; // 8 hours
78      private static final String FACET_ROOTNODE = "rootNode";
79      private static final String PREVIOUS_VIEW_ROOT = HtmlTree.class.getName() + ".PREVIOUS_VIEW_ROOT";
80      private static final int EVENT_CHANGED = 0;
81      private static final int EVENT_INSERTED = 1;
82      private static final int EVENT_REMOVED = 2;
83      private static final int EVENT_STRUCTURE_CHANGED = 3;
84      private static int counter = 0;
85  
86      private IconProvider iconProvider;
87      private boolean itemStatesRestored = false;
88      private String var;
89      private String nodeClass;
90      private String rowClasses;
91      private String columnClasses;
92      private String selectedNodeClass;
93      private String iconClass;
94      private String iconLine;
95      private String iconNoline;
96      private String iconChildFirst;
97      private String iconChildMiddle;
98      private String iconChildLast;
99      private String iconNodeOpen;
100     private String iconNodeOpenFirst;
101     private String iconNodeOpenMiddle;
102     private String iconNodeOpenLast;
103     private String iconNodeClose;
104     private String iconNodeCloseFirst;
105     private String iconNodeCloseMiddle;
106     private String iconNodeCloseLast;
107     private int uniqueIdCounter = 0;
108     private int[] selectedPath;
109     private int internalId;
110     private Long expireListeners;
111 
112 
113     /**
114      * <p/>
115      * Default constructor.
116      * </p>
117      */
118     public HtmlTree()
119     {
120         internalId = counter++;
121     }
122 
123 
124     /**
125      * @JSFProperty
126      *   jspName = "value"
127      *   required = "true"
128      *   inheritedTag = "true"
129      */
130     public TreeModel getModel(FacesContext context)
131     {
132         ValueBinding binding = getValueBinding("model");
133 
134         if (binding != null)
135         {
136             TreeModel model = (TreeModel) binding.getValue(context);
137             if (model != null)
138             {
139                 return model;
140             }
141         }
142         return null;
143     }
144 
145 
146     public String createUniqueId(FacesContext context)
147     {
148         return getClientId(context).replaceAll(":", "_") + "_node_" + uniqueIdCounter++;
149     }
150 
151 
152     public void addTreeSelectionListener(TreeSelectionListener listener)
153     {
154         addFacesListener(listener);
155     }
156 
157 
158     public IconProvider getIconProvider()
159     {
160         return iconProvider;
161     }
162 
163 
164     public void setIconProvider(IconProvider iconProvider)
165     {
166         this.iconProvider = iconProvider;
167     }
168 
169 
170     /**
171      * @JSFProperty
172      * @return Returns the var.
173      */
174     public String getVar()
175     {
176         return getStringValue(var, "var");
177     }
178 
179 
180     /**
181      * @param var The var to set.
182      */
183     public void setVar(String var)
184     {
185         this.var = var;
186     }
187 
188     protected String getStringValue(String value, String vbName)
189     {
190         if(value != null)
191         {
192             return value;
193         }
194         ValueBinding vb = getValueBinding(vbName);
195         if(vb != null)
196         {
197             Object obj = vb.getValue(getFacesContext());
198             if(obj != null)
199             {
200                 return obj.toString();
201             }
202         }
203         return null;
204     }
205 
206     /**
207      * @JSFProperty
208      */
209     public String getIconLine()
210     {
211         return getStringValue(iconLine, "iconLine");
212     }
213 
214     public void setIconLine(String iconLine)
215     {
216         this.iconLine = iconLine;
217     }
218 
219     /**
220      * @JSFProperty
221      */
222     public String getIconNoline()
223     {
224         return getStringValue(iconNoline, "iconNoline");
225     }
226 
227 
228     public void setIconNoline(String iconNoline)
229     {
230         this.iconNoline = iconNoline;
231     }
232 
233     /**
234      * @JSFProperty
235      */
236     public String getIconChildFirst()
237     {
238         return getStringValue(iconChildFirst, "iconChildFirst");
239     }
240 
241 
242     public void setIconChildFirst(String iconChildFirst)
243     {
244         this.iconChildFirst = iconChildFirst;
245     }
246 
247 
248     /**
249      * @JSFProperty
250      */
251     public String getIconChildMiddle()
252     {
253         return getStringValue(iconChildMiddle, "iconChildMiddle");
254     }
255 
256 
257     public void setIconChildMiddle(String iconChildMiddle)
258     {
259         this.iconChildMiddle = iconChildMiddle;
260     }
261 
262 
263     /**
264      * @JSFProperty
265      */
266     public String getIconChildLast()
267     {
268         return getStringValue(iconChildLast, "iconChildLast");
269     }
270 
271 
272     public void setIconChildLast(String iconChildLast)
273     {
274         this.iconChildLast = iconChildLast;
275     }
276 
277 
278     /**
279      * @JSFProperty
280      */
281     public String getIconNodeOpen()
282     {
283         return getStringValue(iconNodeOpen, "iconNodeOpen");
284     }
285 
286 
287     public void setIconNodeOpen(String iconNodeOpen)
288     {
289         this.iconNodeOpen = iconNodeOpen;
290     }
291 
292 
293     /**
294      * @JSFProperty
295      */
296     public String getIconNodeOpenFirst()
297     {
298         return getStringValue(iconNodeOpenFirst, "iconNodeOpenFirst");
299     }
300 
301 
302     public void setIconNodeOpenFirst(String iconNodeOpenFirst)
303     {
304         this.iconNodeOpenFirst = iconNodeOpenFirst;
305     }
306 
307 
308     /**
309      * @JSFProperty
310      */
311     public String getIconNodeOpenMiddle()
312     {
313         return getStringValue(iconNodeOpenMiddle, "iconNodeOpenMiddle");
314     }
315 
316 
317     public void setIconNodeOpenMiddle(String iconNodeOpenMiddle)
318     {
319         this.iconNodeOpenMiddle = iconNodeOpenMiddle;
320     }
321 
322 
323     /**
324      * @JSFProperty
325      */
326     public String getIconNodeOpenLast()
327     {
328         return getStringValue(iconNodeOpenLast, "iconNodeOpenLast");
329     }
330 
331 
332     public void setIconNodeOpenLast(String iconNodeOpenLast)
333     {
334         this.iconNodeOpenLast = iconNodeOpenLast;
335     }
336 
337 
338     /**
339      * @JSFProperty
340      */
341     public String getIconNodeClose()
342     {
343         return getStringValue(iconNodeClose, "iconNodeClose");
344     }
345 
346 
347     public void setIconNodeClose(String iconNodeClose)
348     {
349         this.iconNodeClose = iconNodeClose;
350     }
351 
352 
353     /**
354      * @JSFProperty
355      */
356     public String getIconNodeCloseFirst()
357     {
358         return getStringValue(iconNodeCloseFirst, "iconNodeCloseFirst");
359     }
360 
361 
362     public void setIconNodeCloseFirst(String iconNodeCloseFirst)
363     {
364         this.iconNodeCloseFirst = iconNodeCloseFirst;
365     }
366 
367 
368     /**
369      * @JSFProperty
370      */
371     public String getIconNodeCloseMiddle()
372     {
373         return getStringValue(iconNodeCloseMiddle, "iconNodeCloseMiddle");
374     }
375 
376 
377     public void setIconNodeCloseMiddle(String iconNodeCloseMiddle)
378     {
379         this.iconNodeCloseMiddle = iconNodeCloseMiddle;
380     }
381 
382 
383     /**
384      * @JSFProperty
385      */
386     public String getIconNodeCloseLast()
387     {
388         return getStringValue(iconNodeCloseLast, "iconNodeCloseLast");
389     }
390 
391 
392     public void setIconNodeCloseLast(String iconNodeCloseLast)
393     {
394         this.iconNodeCloseLast = iconNodeCloseLast;
395     }
396 
397 
398     /**
399      * @JSFProperty
400      */
401     public String getNodeClass()
402     {
403         return getStringValue(nodeClass, "nodeClass");
404     }
405 
406 
407     public void setNodeClass(String nodeClass)
408     {
409         this.nodeClass = nodeClass;
410     }
411 
412 
413     /**
414      * @JSFProperty
415      * @return Returns the rowClasses.
416      */
417     public String getRowClasses()
418     {
419         return getStringValue(rowClasses, "rowClasses");
420     }
421 
422 
423     /**
424      * @param rowClasses The rowClasses to set.
425      */
426     public void setRowClasses(String rowClasses)
427     {
428         this.rowClasses = rowClasses;
429     }
430 
431 
432     /**
433      * @JSFProperty
434      * @return Returns the columnClasses.
435      */
436     public String getColumnClasses()
437     {
438         return getStringValue(columnClasses, "columnClasses");
439     }
440 
441 
442     /**
443      * @param columnClasses The columnClasses to set.
444      */
445     public void setColumnClasses(String columnClasses)
446     {
447         this.columnClasses = columnClasses;
448     }
449 
450 
451     /**
452      * @JSFProperty
453      * @return Returns the selectedNodeClass.
454      */
455     public String getSelectedNodeClass()
456     {
457         return getStringValue(selectedNodeClass, "selectedNodeClass");
458     }
459 
460 
461     /**
462      * @param selectedNodeClass The selectedNodeClass to set.
463      */
464     public void setSelectedNodeClass(String selectedNodeClass)
465     {
466         this.selectedNodeClass = selectedNodeClass;
467     }
468 
469 
470     /**
471      * @JSFProperty
472      */
473     public String getIconClass()
474     {
475         return getStringValue(iconClass, "iconClass");
476     }
477 
478 
479     public void setIconClass(String iconClass)
480     {
481         this.iconClass = iconClass;
482     }
483 
484 
485     /**
486      * Time interval the tree will remain registered as a TreeModelListener 
487      * without being accessed
488      * 
489      * @JSFProperty
490      */
491     public long getExpireListeners()
492     {
493         if(expireListeners != null)
494         {
495             return expireListeners.longValue();
496         }
497         ValueBinding vb = getValueBinding("expireListeners");
498         if(vb != null)
499         {
500             Object obj = vb.getValue(getFacesContext());
501             if(obj instanceof java.lang.Number)
502             {
503                 return ((java.lang.Number)obj).longValue();
504             }
505         }
506         return DEFAULT_EXPIRE_LISTENERS;
507     }
508 
509 
510     public void setExpireListeners(long expireListeners)
511     {
512         this.expireListeners = new Long(expireListeners);
513     }
514 
515 
516     public String getFamily()
517     {
518         return "org.apache.myfaces.HtmlTree";
519     }
520 
521 
522     /**
523      * Ensures that the node identified by the specified path is expanded and
524      * viewable. If the last item in the path is a leaf, this will have no
525      * effect.
526      *
527      * @param path the <code>TreePath</code> identifying a node
528      */
529     public void expandPath(TreePath path, FacesContext context)
530     {
531         // Only expand if not leaf!
532         TreeModel model = getModel(context);
533 
534         if (path != null && model != null && !model.isLeaf(path.getLastPathComponent()))
535         {
536             int[] translatedPath = HtmlTreeNode.translatePath(path, getModel(context));
537             HtmlTreeNode rootNode = getRootNode();
538             if (rootNode == null)
539             {
540                 createRootNode(context);
541                 rootNode = getRootNode();
542             }
543             if (!rootNode.isExpanded())
544             {
545                 rootNode.setExpanded(true);
546             }
547             rootNode.expandPath(translatedPath, 0);
548 
549         }
550     }
551 
552 
553     /**
554      * Ensures that the node identified by the specified path is collapsed and
555      * viewable.
556      *
557      * @param path the <code>TreePath</code> identifying a node
558      */
559     public void collapsePath(TreePath path, FacesContext context)
560     {
561         HtmlTreeNode node = findNode(path, context);
562 
563         if (node != null)
564         {
565             node.setExpanded(false);
566         }
567     }
568 
569 
570     public boolean isExpanded(TreePath path, FacesContext context)
571     {
572         if (path == null)
573         {
574             return false;
575         }
576 
577         return findNode(path, context) != null;
578     }
579 
580 
581     private HtmlTreeNode findNode(TreePath path, FacesContext context)
582     {
583         HtmlTreeNode node = getRootNode();
584         int[] translatedPath = HtmlTreeNode.translatePath(path, getModel(context));
585 
586         for (int i = 0; i < translatedPath.length; i++)
587         {
588             if (!node.isExpanded())
589             {
590                 return null;
591             }
592             int index = translatedPath[i];
593             node = (HtmlTreeNode) node.getChildren().get(index);
594         }
595         return node;
596     }
597 
598 
599     public TreePath getSelectionPath()
600     {
601         if (selectedPath == null)
602         {
603             return null;
604         }
605         return HtmlTreeNode.translatePath(selectedPath, getModel(FacesContext.getCurrentInstance()));
606     }
607 
608 
609     public void selectionChanged(HtmlTreeNode node)
610     {
611         TreePath oldPath = null;
612 
613         if (selectedPath != null)
614         {
615             oldPath = HtmlTreeNode.translatePath(selectedPath, getModel(FacesContext.getCurrentInstance()));
616         }
617         selectedPath = node.getTranslatedPath();
618         if (node.isSelected())
619         {
620             queueEvent(new TreeSelectionEvent(this, oldPath, node.getPath()));
621         } else
622         {
623             queueEvent(new TreeSelectionEvent(this, oldPath, null));
624         }
625     }
626 
627 
628     private void createRootNode(FacesContext context)
629     {
630         HtmlTreeNode node;
631         TreeModel model = getModel(context);
632         Object root = model.getRoot();
633         node = (HtmlTreeNode) context.getApplication().createComponent(HtmlTreeNode.COMPONENT_TYPE);
634         String id = createUniqueId(context);
635         node.setId(id);
636 
637         node.setPath(new TreePath(new Object[]{root}));
638         node.setUserObject(root);
639         node.setLayout(new int[]{HtmlTreeNode.CLOSED_SINGLE});
640         getFacets().put(FACET_ROOTNODE, node);
641     }
642 
643 
644     public HtmlTreeNode getRootNode()
645     {
646         return (HtmlTreeNode) getFacet(FACET_ROOTNODE);
647     }
648 
649 
650     public Object saveState(FacesContext context)
651     {
652         Object values[] = new Object[24];
653         values[0] = super.saveState(context);
654         values[1] = iconChildFirst;
655         values[2] = iconChildMiddle;
656         values[3] = iconChildLast;
657         values[4] = iconLine;
658         values[5] = iconNodeClose;
659         values[6] = iconNodeCloseFirst;
660         values[7] = iconNodeCloseLast;
661         values[8] = iconNodeCloseMiddle;
662         values[9] = iconNodeOpen;
663         values[10] = iconNodeOpenFirst;
664         values[11] = iconNodeOpenLast;
665         values[12] = iconNodeOpenMiddle;
666         values[13] = iconNoline;
667         values[14] = var;
668         values[15] = nodeClass;
669         values[16] = selectedNodeClass;
670         values[17] = new Integer(uniqueIdCounter);
671         values[18] = selectedPath;
672         values[19] = iconClass;
673         values[20] = new Integer(internalId);
674         values[21] = expireListeners;
675         values[22] = rowClasses;
676         values[23] = columnClasses;
677         return ((Object) (values));
678     }
679 
680 
681     public void restoreState(FacesContext context, Object state)
682     {
683         Object values[] = (Object[]) state;
684         super.restoreState(context, values[0]);
685         iconChildFirst = (String) values[1];
686         iconChildMiddle = (String) values[2];
687         iconChildLast = (String) values[3];
688         iconLine = (String) values[4];
689         iconNodeClose = (String) values[5];
690         iconNodeCloseFirst = (String) values[6];
691         iconNodeCloseLast = (String) values[7];
692         iconNodeCloseMiddle = (String) values[8];
693         iconNodeOpen = (String) values[9];
694         iconNodeOpenFirst = (String) values[10];
695         iconNodeOpenLast = (String) values[11];
696         iconNodeOpenMiddle = (String) values[12];
697         iconNoline = (String) values[13];
698         var = (String) values[14];
699         nodeClass = (String) values[15];
700         selectedNodeClass = (String) values[16];
701         uniqueIdCounter = ((Integer) values[17]).intValue();
702         selectedPath = (int[]) values[18];
703         iconClass = (String) values[19];
704         internalId = ((Integer) values[20]).intValue();
705         expireListeners = (Long) values[21];
706         rowClasses = (String) values[22];
707         columnClasses = (String) values[23];
708         addToModelListeners();
709     }
710 
711 
712     public void decode(FacesContext context)
713     {
714         super.decode(context);
715 
716         //Save the current view root for later reference...
717         context.getExternalContext().getRequestMap().put(PREVIOUS_VIEW_ROOT, context.getViewRoot());
718         //...and remember that this instance needs NO special treatment on
719         // rendering:
720         itemStatesRestored = true;
721     }
722 
723 
724     public void processDecodes(FacesContext context)
725     {
726         addToModelListeners();
727         super.processDecodes(context);
728     }
729 
730 
731     public void processValidators(FacesContext context)
732     {
733         addToModelListeners();
734         super.processValidators(context);
735     }
736 
737 
738     public void processUpdates(FacesContext context)
739     {
740         addToModelListeners();
741         super.processUpdates(context);
742     }
743 
744 
745     public void encodeBegin(FacesContext context) throws IOException
746     {
747         addToModelListeners();
748         processModelEvents();
749 
750         HtmlTreeNode node = getRootNode();
751 
752         if (node == null)
753         {
754             createRootNode(context);
755         }
756 
757         if (!itemStatesRestored)
758         {
759             UIViewRoot previousRoot = (UIViewRoot) context.getExternalContext().getRequestMap().get(PREVIOUS_VIEW_ROOT);
760             if (previousRoot != null)
761             {
762                 restoreItemStates(context, previousRoot);
763             } else
764             {
765                 //no previous root, means no decode was done
766                 //--> a new request
767             }
768         }
769 
770         super.encodeBegin(context);
771     }
772 
773 
774     public void encodeEnd(FacesContext context) throws IOException
775     {
776         super.encodeEnd(context);
777     }
778 
779 
780     public void restoreItemStates(FacesContext facesContext, UIViewRoot previousRoot)
781     {
782         HtmlTree previousTree = (HtmlTree) previousRoot.findComponent(getClientId(facesContext));
783 
784         if (previousTree != null)
785         {
786             HtmlTreeNode node = previousTree.getRootNode();
787 
788             if (node != null)
789             {
790                 getRootNode().restoreItemState(node);
791             }
792 
793             selectedPath = previousTree.selectedPath;
794         }
795     }
796 
797 
798     public void treeNodesChanged(TreeModelEvent e)
799     {
800         TreePath path = e.getTreePath();
801         FacesContext context = FacesContext.getCurrentInstance();
802         HtmlTreeNode node = findNode(path, context);
803 
804         if (node != null)
805         {
806             node.childrenChanged(e.getChildIndices(), context);
807         }
808     }
809 
810 
811     public void treeNodesInserted(TreeModelEvent e)
812     {
813         TreePath path = e.getTreePath();
814         FacesContext context = FacesContext.getCurrentInstance();
815         HtmlTreeNode node = findNode(path, context);
816 
817         if (node != null)
818         {
819             node.childrenAdded(e.getChildIndices(), context);
820         }
821     }
822 
823 
824     public void treeNodesRemoved(TreeModelEvent e)
825     {
826         TreePath path = e.getTreePath();
827         FacesContext context = FacesContext.getCurrentInstance();
828         HtmlTreeNode node = findNode(path, context);
829 
830         if (node != null)
831         {
832             node.childrenRemoved(e.getChildIndices());
833         }
834     }
835 
836 
837     public void treeStructureChanged(TreeModelEvent e)
838     {
839         TreePath path = e.getTreePath();
840         FacesContext context = FacesContext.getCurrentInstance();
841 
842         if (isExpanded(path, context))
843         {
844             collapsePath(path, context);
845             expandPath(path, context);
846         }
847     }
848 
849 
850     public boolean equals(Object obj)
851     {
852         if (!(obj instanceof HtmlTree))
853         {
854             return false;
855         }
856         HtmlTree other = (HtmlTree) obj;
857 
858         return other.getId().equals(getId());
859     }
860 
861 
862     public int hashCode()
863     {
864         return getClientId(FacesContext.getCurrentInstance()).hashCode();
865     }
866 
867 
868     public void addToModelListeners()
869     {
870         Collection listeners = getModel(FacesContext.getCurrentInstance()).getTreeModelListeners();
871         long currentTime = System.currentTimeMillis();
872         boolean found = false;
873 
874         for (Iterator iterator = listeners.iterator(); iterator.hasNext();)
875         {
876             ModelListener listener = (ModelListener) iterator.next();
877 
878             if (listener.getId() == internalId)
879             {
880                 found = true;
881             } else if (currentTime - listener.getLastAccessTime() > getExpireListeners())
882             {
883                 iterator.remove();
884             }
885         }
886         if (!found)
887         {
888             listeners.add(new ModelListener(internalId));
889         }
890     }
891 
892 
893     private void processModelEvents()
894     {
895         Collection listeners = getModel(FacesContext.getCurrentInstance()).getTreeModelListeners();
896 
897         for (Iterator iterator = listeners.iterator(); iterator.hasNext();)
898         {
899             ModelListener listener = (ModelListener) iterator.next();
900 
901             if (listener.getId() == internalId)
902             {
903                 for (Iterator events = listener.getEvents().iterator(); events.hasNext();)
904                 {
905                     Event event = (Event) events.next();
906                     event.process(this);
907                     events.remove();
908                 }
909                 break;
910             }
911         }
912     }
913 
914 
915     public void collapseAll()
916     {
917         HtmlTreeNode root = getRootNode();
918         FacesContext context = FacesContext.getCurrentInstance();
919         collapsePath(root.getPath(), context);
920         for (int i = 0; i < root.getChildren().size(); i++)
921         {
922             HtmlTreeNode child = (HtmlTreeNode) (root.getChildren().get(i));
923             collapsePath(child.getPath(), context);
924             if (!child.isLeaf(context))
925             {
926                 collapseChildren(context, child);
927             }
928         }
929     }
930 
931 
932     private void collapseChildren(FacesContext context, HtmlTreeNode parent)
933     {
934         for (int i = 0; i < parent.getChildren().size(); i++)
935         {
936             HtmlTreeNode child = (HtmlTreeNode) (parent.getChildren().get(i));
937             collapsePath(child.getPath(), context);
938             if (!child.isLeaf(context))
939             {
940                 collapseChildren(context, child);
941             }
942         }
943 
944     }
945 
946 
947     public void expandAll()
948     {
949         HtmlTreeNode root = getRootNode();
950         FacesContext context = FacesContext.getCurrentInstance();
951         expandPath(root.getPath(), context);
952         for (int i = 0; i < root.getChildren().size(); i++)
953         {
954             HtmlTreeNode child = (HtmlTreeNode) (root.getChildren().get(i));
955             expandPath(child.getPath(), context);
956             if (!child.isLeaf(context))
957             {
958                 expandChildren(context, child);
959             }
960         }
961     }
962 
963 
964     private void expandChildren(FacesContext context, HtmlTreeNode parent)
965     {
966         for (int i = 0; i < parent.getChildren().size(); i++)
967         {
968             HtmlTreeNode child = (HtmlTreeNode) (parent.getChildren().get(i));
969             expandPath(child.getPath(), context);
970             if (!child.isLeaf(context))
971             {
972                 expandChildren(context, child);
973             }
974         }
975     }
976 
977     private static class ModelListener implements TreeModelListener, Serializable
978     {
979 
980         private long lastAccessTime = System.currentTimeMillis();
981 
982         private LinkedList events = new LinkedList();
983 
984         int id;
985 
986 
987         public ModelListener(int id)
988         {
989             this.id = id;
990         }
991 
992 
993         public List getEvents()
994         {
995             lastAccessTime = System.currentTimeMillis();
996             return events;
997         }
998 
999 
1000         public int getId()
1001         {
1002             return id;
1003         }
1004 
1005 
1006         public long getLastAccessTime()
1007         {
1008             return lastAccessTime;
1009         }
1010 
1011 
1012         public void treeNodesChanged(TreeModelEvent e)
1013         {
1014             events.addLast(new Event(EVENT_CHANGED, e));
1015         }
1016 
1017 
1018         public void treeNodesInserted(TreeModelEvent e)
1019         {
1020             events.addLast(new Event(EVENT_INSERTED, e));
1021         }
1022 
1023 
1024         public void treeNodesRemoved(TreeModelEvent e)
1025         {
1026             events.addLast(new Event(EVENT_REMOVED, e));
1027         }
1028 
1029 
1030         public void treeStructureChanged(TreeModelEvent e)
1031         {
1032             events.addLast(new Event(EVENT_STRUCTURE_CHANGED, e));
1033         }
1034     }
1035 
1036 
1037     private static class Event
1038     {
1039 
1040         private int kind;
1041 
1042         private TreeModelEvent event;
1043 
1044 
1045         public Event(int kind, TreeModelEvent event)
1046         {
1047             this.kind = kind;
1048             this.event = event;
1049         }
1050 
1051 
1052         public void process(HtmlTree tree)
1053         {
1054             switch (kind)
1055             {
1056                 case EVENT_CHANGED:
1057                     tree.treeNodesChanged(event);
1058                     break;
1059                 case EVENT_INSERTED:
1060                     tree.treeNodesInserted(event);
1061                     break;
1062                 case EVENT_REMOVED:
1063                     tree.treeNodesRemoved(event);
1064                     break;
1065                 case EVENT_STRUCTURE_CHANGED:
1066                     tree.treeStructureChanged(event);
1067                     break;
1068             }
1069         }
1070     }
1071 }