1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.custom.tree2;
20
21 import java.io.IOException;
22 import java.io.Serializable;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28
29 import javax.faces.FacesException;
30 import javax.faces.application.FacesMessage;
31 import javax.faces.component.ContextCallback;
32 import javax.faces.component.EditableValueHolder;
33 import javax.faces.component.NamingContainer;
34 import javax.faces.component.UIComponent;
35 import javax.faces.component.UIComponentBase;
36 import javax.faces.component.UINamingContainer;
37 import javax.faces.component.UIViewRoot;
38 import javax.faces.component.UniqueIdVendor;
39 import javax.faces.component.visit.VisitCallback;
40 import javax.faces.component.visit.VisitContext;
41 import javax.faces.component.visit.VisitResult;
42 import javax.faces.context.ExternalContext;
43 import javax.faces.context.FacesContext;
44 import javax.faces.el.ValueBinding;
45 import javax.faces.event.AbortProcessingException;
46 import javax.faces.event.ActionEvent;
47 import javax.faces.event.FacesEvent;
48 import javax.faces.event.FacesListener;
49 import javax.faces.event.PhaseId;
50
51 import org.apache.commons.logging.Log;
52 import org.apache.commons.logging.LogFactory;
53 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
54 import org.apache.myfaces.shared_tomahawk.util.MessageUtils;
55 import org.apache.myfaces.tomahawk.util.Constants;
56
57
58
59
60
61
62
63
64
65
66
67
68 @JSFComponent
69 public class UITreeData extends UIComponentBase implements NamingContainer, Tree, UniqueIdVendor {
70 private Log log = LogFactory.getLog(UITreeData.class);
71
72 public static final String COMPONENT_TYPE = "org.apache.myfaces.UITree2";
73 public static final String COMPONENT_FAMILY = "org.apache.myfaces.HtmlTree2";
74
75 private static final String MISSING_NODE = "org.apache.myfaces.tree2.MISSING_NODE";
76 private static final int PROCESS_DECODES = 1;
77 private static final int PROCESS_VALIDATORS = 2;
78 private static final int PROCESS_UPDATES = 3;
79
80 private TreeModel _cachedModel;
81 private String _nodeId;
82 private TreeNode _node;
83
84 private Object _value;
85 private String _var;
86 private Map _saved = new HashMap();
87
88 private TreeState _restoredState = null;
89
90 private transient FacesContext _facesContext;
91
92 private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
93
94
95
96
97 public UITreeData()
98 {
99
100 }
101
102
103
104 public String getFamily()
105 {
106 return COMPONENT_FAMILY;
107 }
108
109
110 public Object saveState(FacesContext context)
111 {
112 Object values[] = new Object[3];
113 values[0] = super.saveState(context);
114 values[1] = _var;
115 values[2] = _restoredState;
116 return ((Object) (values));
117 }
118
119
120
121 public void restoreState(FacesContext context, Object state)
122 {
123 Object values[] = (Object[]) state;
124 super.restoreState(context, values[0]);
125
126 _var = (String)values[1];
127 _restoredState = (TreeState) values[2];
128 }
129
130 public void encodeEnd(FacesContext context) throws IOException {
131 super.encodeEnd(context);
132
133
134
135
136 TreeState state = getDataModel().getTreeState();
137 if ( state == null)
138 {
139
140 state = new TreeStateBase();
141 }
142
143 _restoredState = (state.isTransient()) ? null : state;
144
145 }
146
147 public void queueEvent(FacesEvent event)
148 {
149 super.queueEvent(new FacesEventWrapper(event, getNodeId(), this));
150 }
151
152
153 public void broadcast(FacesEvent event) throws AbortProcessingException
154 {
155 if (event instanceof FacesEventWrapper)
156 {
157 FacesEventWrapper childEvent = (FacesEventWrapper) event;
158 String currNodeId = getNodeId();
159 setNodeId(childEvent.getNodeId());
160 FacesEvent nodeEvent = childEvent.getFacesEvent();
161 nodeEvent.getComponent().broadcast(nodeEvent);
162 setNodeId(currNodeId);
163 return;
164 }
165 else if(event instanceof ToggleExpandedEvent)
166 {
167 ToggleExpandedEvent toggleEvent = (ToggleExpandedEvent) event;
168 String currentNodeId = getNodeId();
169 setNodeId(toggleEvent.getNodeId());
170 toggleExpanded();
171 setNodeId(currentNodeId);
172 }
173 else
174 {
175 super.broadcast(event);
176 return;
177 }
178 }
179
180
181
182 public void processDecodes(FacesContext context)
183 {
184 if (context == null) throw new NullPointerException("context");
185 if (!isRendered()) return;
186
187 _cachedModel = null;
188 _saved = new HashMap();
189
190 setNodeId(null);
191 decode(context);
192
193 processNodes(context, PROCESS_DECODES, getDataModel().getTreeWalker());
194
195
196
197 setNodeId(null);
198
199 }
200
201
202 public void processValidators(FacesContext context)
203 {
204 if (context == null) throw new NullPointerException("context");
205 if (!isRendered()) return;
206
207 processNodes(context, PROCESS_VALIDATORS, getDataModel().getTreeWalker());
208
209 setNodeId(null);
210 }
211
212
213
214 public void processUpdates(FacesContext context)
215 {
216 if (context == null) throw new NullPointerException("context");
217 if (!isRendered()) return;
218
219 processNodes(context, PROCESS_UPDATES, getDataModel().getTreeWalker());
220
221 setNodeId(null);
222 }
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238 @Override
239 public String getContainerClientId(FacesContext context)
240 {
241 String ownClientId = super.getContainerClientId(context);
242 if (_nodeId != null)
243 {
244 return ownClientId + UINamingContainer.getSeparatorChar(context) + _nodeId;
245 }
246 else
247 {
248 return ownClientId;
249 }
250 }
251
252
253 public void setValueBinding(String name, ValueBinding binding)
254 {
255 if ("value".equals(name))
256 {
257 _cachedModel = null;
258 } else if ("nodeVar".equals(name) || "nodeId".equals(name) || "treeVar".equals(name))
259 {
260 throw new IllegalArgumentException("name " + name);
261 }
262 super.setValueBinding(name, binding);
263 }
264
265
266 public void encodeBegin(FacesContext context) throws IOException
267 {
268
269
270
271
272
273
274
275 if (!keepSaved(context))
276 {
277 _saved = new HashMap();
278 }
279
280
281
282 _cachedModel = null;
283
284 super.encodeBegin(context);
285 }
286
287
288
289
290
291
292
293
294 public void setValue(Object value)
295 {
296 _cachedModel = null;
297 _value = value;
298 }
299
300
301
302
303
304
305
306
307 public Object getModel()
308 {
309 return getValue();
310 }
311
312
313
314
315
316
317
318 public void setModel(Object model)
319 {
320 setValue(model);
321 }
322
323
324
325
326
327
328
329
330
331
332
333 public Object getValue()
334 {
335 if (_value != null) return _value;
336 ValueBinding vb = getValueBinding("value");
337 return vb != null ? vb.getValue(getFacesContext()) : null;
338 }
339
340
341
342
343
344
345
346 public void setVar(String var)
347 {
348 _var = var;
349 }
350
351
352
353
354
355
356
357
358
359 public String getVar()
360 {
361 return _var;
362 }
363
364
365
366
367
368
369 public TreeNode getNode()
370 {
371 return _node;
372 }
373
374
375 public String getNodeId()
376 {
377 return _nodeId;
378 }
379
380
381 public void setNodeId(String nodeId)
382 {
383 saveDescendantState();
384
385 _nodeId = nodeId;
386
387 TreeModel model = getDataModel();
388 if (model == null)
389 {
390 return;
391 }
392
393 try
394 {
395 _node = model.getNodeById(nodeId);
396 }
397
398 catch (IndexOutOfBoundsException aob)
399 {
400
401
402
403
404
405
406 FacesMessage message = MessageUtils.getMessageFromBundle(Constants.TOMAHAWK_DEFAULT_BUNDLE, MISSING_NODE, new String[] {nodeId});
407 message.setSeverity(FacesMessage.SEVERITY_WARN);
408 FacesContext.getCurrentInstance().addMessage(getId(), message);
409
410
411
412 }
413
414 restoreDescendantState();
415
416 if (_var != null)
417 {
418 Map requestMap = getFacesContext().getExternalContext().getRequestMap();
419
420 if (nodeId == null)
421 {
422 requestMap.remove(_var);
423 } else
424 {
425 requestMap.put(_var, getNode());
426 }
427 }
428 }
429
430 @Override
431 public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
432 throws FacesException
433 {
434 if (context == null || clientId == null || callback == null)
435 {
436 throw new NullPointerException();
437 }
438
439 final String baseClientId = getClientId(context);
440
441
442 boolean returnValue = baseClientId.equals(clientId);
443
444 boolean isTemporalFacesContext = isTemporalFacesContext();
445 if (!isTemporalFacesContext)
446 {
447 setTemporalFacesContext(context);
448 }
449
450 pushComponentToEL(context, this);
451 try
452 {
453 if (returnValue)
454 {
455 try
456 {
457 callback.invokeContextCallback(context, this);
458 return true;
459 }
460 catch (Exception e)
461 {
462 throw new FacesException(e);
463 }
464 }
465
466
467 for (Iterator<UIComponent> it = this.getFacets().values().iterator(); !returnValue && it.hasNext();)
468 {
469 returnValue = it.next().invokeOnComponent(context, clientId, callback);
470 }
471
472 if (returnValue)
473 {
474 return returnValue;
475 }
476
477
478 if (clientId.startsWith(baseClientId))
479 {
480 TreeModel model = getDataModel();
481
482
483 TreeWalker walker = model.getTreeWalker();
484 UIComponent facet = null;
485 walker.reset();
486 walker.setTree(this);
487
488 String oldNodeId = getNodeId();
489
490 try
491 {
492 while(!returnValue && walker.next())
493 {
494 TreeNode node = getNode();
495 facet = getFacet(node.getType());
496
497 if (facet == null)
498 {
499 log.warn("Unable to locate facet with the name: " + node.getType());
500 continue;
501 }
502
503 returnValue = facet.invokeOnComponent(context, baseClientId, callback);
504 }
505 }
506 finally
507 {
508 setNodeId(oldNodeId);
509 }
510 }
511 }
512 finally
513 {
514
515 popComponentFromEL(context);
516 if (!isTemporalFacesContext)
517 {
518 setTemporalFacesContext(null);
519 }
520 }
521
522 return returnValue;
523 }
524
525 @Override
526 public boolean visitTree(VisitContext context, VisitCallback callback)
527 {
528 if (!isVisitable(context))
529 {
530 return false;
531 }
532
533 boolean isTemporalFacesContext = isTemporalFacesContext();
534 if (!isTemporalFacesContext)
535 {
536 setTemporalFacesContext(context.getFacesContext());
537 }
538
539 String oldNodeId = getNodeId();
540
541 setNodeId(null);
542
543 pushComponentToEL(context.getFacesContext(), this);
544 try
545 {
546 VisitResult visitResult = context.invokeVisitCallback(this,
547 callback);
548 switch (visitResult)
549 {
550
551 case COMPLETE:
552 return true;
553
554 case REJECT:
555 return false;
556
557
558 default:
559
560 Collection<String> subtreeIdsToVisit = context
561 .getSubtreeIdsToVisit(this);
562 boolean doVisitChildren = subtreeIdsToVisit != null
563 && !subtreeIdsToVisit.isEmpty();
564 if (doVisitChildren)
565 {
566 Boolean skipIterationHint = (Boolean) context.getFacesContext().getAttributes().get(SKIP_ITERATION_HINT);
567 if (skipIterationHint != null && skipIterationHint.booleanValue())
568 {
569
570 if (getChildCount() > 0) {
571 for (UIComponent child : getChildren()) {
572 if (child.visitTree(context, callback)) {
573 return true;
574 }
575 }
576 }
577 }
578 else
579 {
580 TreeWalker walker = getDataModel().getTreeWalker();
581 UIComponent facet = null;
582 walker.reset();
583 walker.setTree(this);
584
585 while(walker.next())
586 {
587 TreeNode node = getNode();
588 facet = getFacet(node.getType());
589
590 if (facet == null)
591 {
592 log.warn("Unable to locate facet with the name: " + node.getType());
593 continue;
594
595 }
596
597 if (facet.visitTree(context, callback))
598 {
599 return true;
600 }
601 }
602 }
603 }
604 }
605 }
606 finally
607 {
608
609 popComponentFromEL(context.getFacesContext());
610 setNodeId(oldNodeId);
611 if (!isTemporalFacesContext)
612 {
613 setTemporalFacesContext(null);
614 }
615 }
616
617
618 return false;
619 }
620
621 @Override
622 protected FacesContext getFacesContext()
623 {
624 if (_facesContext == null)
625 {
626 return super.getFacesContext();
627 }
628 else
629 {
630 return _facesContext;
631 }
632 }
633
634 private boolean isTemporalFacesContext()
635 {
636 return _facesContext != null;
637 }
638
639 private void setTemporalFacesContext(FacesContext facesContext)
640 {
641 _facesContext = facesContext;
642 }
643
644
645
646
647
648
649
650
651
652
653 public String[] getPathInformation(String nodeId)
654 {
655 return getDataModel().getPathInformation(nodeId);
656 }
657
658
659
660
661
662
663
664
665 public boolean isLastChild(String nodeId)
666 {
667 return getDataModel().isLastChild(nodeId);
668 }
669
670
671
672
673
674
675
676
677 public TreeModel getDataModel()
678 {
679 if (_cachedModel != null)
680 {
681 return _cachedModel;
682 }
683
684 Object value = getValue();
685 if (value != null)
686 {
687 if (value instanceof TreeModel)
688 {
689 _cachedModel = (TreeModel) value;
690 }
691 else if (value instanceof TreeNode)
692 {
693 _cachedModel = new TreeModelBase((TreeNode) value);
694 } else
695 {
696 throw new IllegalArgumentException("Value must be a TreeModel or TreeNode");
697 }
698 }
699
700 if (_restoredState != null)
701 _cachedModel.setTreeState(_restoredState);
702
703 return _cachedModel;
704 }
705
706
707
708
709 public void expandAll()
710 {
711 toggleAll(true);
712 }
713
714
715
716
717 public void collapseAll()
718 {
719 toggleAll(false);
720 }
721
722
723
724
725
726
727
728
729 private void toggleAll(boolean expanded)
730 {
731 TreeWalker walker = getDataModel().getTreeWalker();
732 walker.reset();
733
734 TreeState state = getDataModel().getTreeState();
735 walker.setCheckState(false);
736 walker.setTree(this);
737
738 while(walker.next())
739 {
740 String id = getNodeId();
741 if ((expanded && !state.isNodeExpanded(id)) || (!expanded && state.isNodeExpanded(id)))
742 {
743 state.toggleExpanded(id);
744 }
745 }
746 }
747
748
749
750
751
752 public void expandPath(String[] nodePath)
753 {
754 getDataModel().getTreeState().expandPath(nodePath);
755 }
756
757
758
759
760
761 public void collapsePath(String[] nodePath)
762 {
763 getDataModel().getTreeState().collapsePath(nodePath);
764 }
765
766
767 protected void processNodes(FacesContext context, int processAction, TreeWalker walker)
768 {
769 UIComponent facet = null;
770 walker.reset();
771 walker.setTree(this);
772
773 while(walker.next())
774 {
775 TreeNode node = getNode();
776 facet = getFacet(node.getType());
777
778 if (facet == null)
779 {
780 log.warn("Unable to locate facet with the name: " + node.getType());
781 continue;
782
783 }
784
785 switch (processAction)
786 {
787 case PROCESS_DECODES:
788
789 facet.processDecodes(context);
790 break;
791
792 case PROCESS_VALIDATORS:
793
794 facet.processValidators(context);
795 break;
796
797 case PROCESS_UPDATES:
798
799 facet.processUpdates(context);
800 break;
801 }
802 }
803
804 }
805
806
807
808
809
810
811 private void saveDescendantState()
812 {
813 FacesContext context = getFacesContext();
814 Iterator i = getFacets().values().iterator();
815 while (i.hasNext())
816 {
817 UIComponent facet = (UIComponent) i.next();
818 saveDescendantState(facet, context);
819 }
820 }
821
822
823
824
825
826
827
828 private void saveDescendantState(UIComponent component, FacesContext context)
829 {
830 if (component instanceof EditableValueHolder)
831 {
832 EditableValueHolder input = (EditableValueHolder) component;
833 String clientId = component.getClientId(context);
834 SavedState state = (SavedState) _saved.get(clientId);
835 if (state == null)
836 {
837 state = new SavedState();
838 _saved.put(clientId, state);
839 }
840 state.setValue(input.getLocalValue());
841 state.setValid(input.isValid());
842 state.setSubmittedValue(input.getSubmittedValue());
843 state.setLocalValueSet(input.isLocalValueSet());
844 }
845
846 List kids = component.getChildren();
847 for (int i = 0; i < kids.size(); i++)
848 {
849 saveDescendantState((UIComponent) kids.get(i), context);
850 }
851 }
852
853
854
855
856
857 private void restoreDescendantState()
858 {
859 FacesContext context = getFacesContext();
860 Iterator i = getFacets().values().iterator();
861 while (i.hasNext())
862 {
863 UIComponent facet = (UIComponent) i.next();
864 restoreDescendantState(facet, context);
865 }
866 }
867
868
869
870
871
872
873
874 private void restoreDescendantState(UIComponent component, FacesContext context)
875 {
876 String id = component.getId();
877 component.setId(id);
878
879 if (component instanceof EditableValueHolder)
880 {
881 EditableValueHolder input = (EditableValueHolder) component;
882 String clientId = component.getClientId(context);
883 SavedState state = (SavedState) _saved.get(clientId);
884 if (state == null)
885 {
886 state = new SavedState();
887 }
888 input.setValue(state.getValue());
889 input.setValid(state.isValid());
890 input.setSubmittedValue(state.getSubmittedValue());
891 input.setLocalValueSet(state.isLocalValueSet());
892 }
893
894 List kids = component.getChildren();
895 for (int i = 0; i < kids.size(); i++)
896 {
897 restoreDescendantState((UIComponent)kids.get(i), context);
898 }
899 Map facets = component.getFacets();
900 for(Iterator i = facets.values().iterator(); i.hasNext();)
901 {
902 restoreDescendantState((UIComponent)i.next(), context);
903 }
904 }
905
906
907
908
909
910
911
912
913 private static class SavedState implements Serializable
914 {
915 private static final long serialVersionUID = 273343276957070557L;
916 private Object submittedValue;
917 private boolean valid = true;
918 private Object value;
919 private boolean localValueSet;
920
921 Object getSubmittedValue()
922 {
923 return submittedValue;
924 }
925
926 void setSubmittedValue(Object submittedValue)
927 {
928 this.submittedValue = submittedValue;
929 }
930
931 boolean isValid()
932 {
933 return valid;
934 }
935
936 void setValid(boolean valid)
937 {
938 this.valid = valid;
939 }
940
941 Object getValue()
942 {
943 return value;
944 }
945
946 void setValue(Object value)
947 {
948 this.value = value;
949 }
950
951 boolean isLocalValueSet()
952 {
953 return localValueSet;
954 }
955
956 void setLocalValueSet(boolean localValueSet)
957 {
958 this.localValueSet = localValueSet;
959 }
960 }
961
962
963
964
965
966
967
968 private static class FacesEventWrapper extends FacesEvent
969 {
970 private static final long serialVersionUID = -3056153249469828447L;
971 private FacesEvent _wrappedFacesEvent;
972 private String _nodeId;
973
974
975 public FacesEventWrapper(FacesEvent facesEvent, String nodeId, UIComponent component)
976 {
977 super(component);
978 _wrappedFacesEvent = facesEvent;
979 _nodeId = nodeId;
980 }
981
982
983 public PhaseId getPhaseId()
984 {
985 return _wrappedFacesEvent.getPhaseId();
986 }
987
988
989 public void setPhaseId(PhaseId phaseId)
990 {
991 _wrappedFacesEvent.setPhaseId(phaseId);
992 }
993
994
995 public void queue()
996 {
997 _wrappedFacesEvent.queue();
998 }
999
1000
1001 public String toString()
1002 {
1003 return _wrappedFacesEvent.toString();
1004 }
1005
1006
1007 public boolean isAppropriateListener(FacesListener faceslistener)
1008 {
1009
1010 return false;
1011 }
1012
1013
1014 public void processListener(FacesListener faceslistener)
1015 {
1016 throw new UnsupportedOperationException("This event type is only intended for wrapping a real event");
1017 }
1018
1019
1020 public FacesEvent getFacesEvent()
1021 {
1022 return _wrappedFacesEvent;
1023 }
1024
1025
1026 public String getNodeId()
1027 {
1028 return _nodeId;
1029 }
1030 }
1031
1032
1033
1034
1035
1036
1037
1038 private boolean keepSaved(FacesContext context)
1039 {
1040 Iterator clientIds = _saved.keySet().iterator();
1041 while (clientIds.hasNext())
1042 {
1043 String clientId = (String) clientIds.next();
1044 Iterator messages = context.getMessages(clientId);
1045 while (messages.hasNext())
1046 {
1047 FacesMessage message = (FacesMessage) messages.next();
1048 if (message.getSeverity().compareTo(FacesMessage.SEVERITY_ERROR) >= 0)
1049 {
1050 return true;
1051 }
1052 }
1053 }
1054
1055 return false;
1056 }
1057
1058
1059
1060
1061 public void toggleExpanded()
1062 {
1063 getDataModel().getTreeState().toggleExpanded(getNodeId());
1064 }
1065
1066
1067
1068
1069
1070 public boolean isNodeExpanded()
1071 {
1072 return getDataModel().getTreeState().isNodeExpanded(getNodeId());
1073 }
1074
1075
1076
1077
1078
1079
1080
1081
1082 public void setNodeSelected(ActionEvent event)
1083 {
1084 getDataModel().getTreeState().setSelected(getNodeId());
1085 }
1086
1087
1088
1089
1090
1091 public boolean isNodeSelected()
1092 {
1093 return (getNodeId() != null) ? getDataModel().getTreeState().isSelected(getNodeId()) : false;
1094 }
1095
1096
1097
1098
1099
1100
1101
1102 public String createUniqueId(FacesContext context, String seed)
1103 {
1104 StringBuilder bld = new StringBuilder();
1105
1106
1107 if(seed==null)
1108 {
1109 Long uniqueIdCounter = (Long) getStateHelper().get(PropertyKeys.uniqueIdCounter);
1110 uniqueIdCounter = (uniqueIdCounter == null) ? 0 : uniqueIdCounter;
1111 getStateHelper().put(PropertyKeys.uniqueIdCounter, (uniqueIdCounter+1L));
1112 return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(uniqueIdCounter).toString();
1113 }
1114
1115 else
1116 {
1117 return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(seed).toString();
1118 }
1119 }
1120
1121 enum PropertyKeys
1122 {
1123 uniqueIdCounter
1124 }
1125 }