1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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;
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
115
116
117
118 public HtmlTree()
119 {
120 internalId = counter++;
121 }
122
123
124
125
126
127
128
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
172
173
174 public String getVar()
175 {
176 return getStringValue(var, "var");
177 }
178
179
180
181
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
415
416
417 public String getRowClasses()
418 {
419 return getStringValue(rowClasses, "rowClasses");
420 }
421
422
423
424
425
426 public void setRowClasses(String rowClasses)
427 {
428 this.rowClasses = rowClasses;
429 }
430
431
432
433
434
435
436 public String getColumnClasses()
437 {
438 return getStringValue(columnClasses, "columnClasses");
439 }
440
441
442
443
444
445 public void setColumnClasses(String columnClasses)
446 {
447 this.columnClasses = columnClasses;
448 }
449
450
451
452
453
454
455 public String getSelectedNodeClass()
456 {
457 return getStringValue(selectedNodeClass, "selectedNodeClass");
458 }
459
460
461
462
463
464 public void setSelectedNodeClass(String selectedNodeClass)
465 {
466 this.selectedNodeClass = selectedNodeClass;
467 }
468
469
470
471
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
487
488
489
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
524
525
526
527
528
529 public void expandPath(TreePath path, FacesContext context)
530 {
531
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
555
556
557
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
717 context.getExternalContext().getRequestMap().put(PREVIOUS_VIEW_ROOT, context.getViewRoot());
718
719
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
766
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 }