1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.view.facelets;
20
21 import java.io.Serializable;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.EnumSet;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import javax.el.ValueExpression;
31 import javax.faces.FacesException;
32 import javax.faces.FactoryFinder;
33 import javax.faces.application.ProjectStage;
34 import javax.faces.application.StateManager;
35 import javax.faces.component.ContextCallback;
36 import javax.faces.component.UIComponent;
37 import javax.faces.component.UIComponentBase;
38 import javax.faces.component.UIViewRoot;
39 import javax.faces.component.visit.VisitCallback;
40 import javax.faces.component.visit.VisitContext;
41 import javax.faces.component.visit.VisitContextFactory;
42 import javax.faces.component.visit.VisitHint;
43 import javax.faces.component.visit.VisitResult;
44 import javax.faces.context.FacesContext;
45 import javax.faces.event.PostAddToViewEvent;
46 import javax.faces.event.PreRemoveFromViewEvent;
47 import javax.faces.event.SystemEvent;
48 import javax.faces.event.SystemEventListener;
49 import javax.faces.render.RenderKitFactory;
50 import javax.faces.render.ResponseStateManager;
51 import javax.faces.view.StateManagementStrategy;
52 import javax.faces.view.ViewDeclarationLanguage;
53 import javax.faces.view.ViewDeclarationLanguageFactory;
54 import javax.faces.view.ViewMetadata;
55 import org.apache.myfaces.application.StateManagerImpl;
56 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
57 import org.apache.myfaces.context.RequestViewContext;
58 import org.apache.myfaces.shared.config.MyfacesConfig;
59 import org.apache.myfaces.shared.util.ClassUtils;
60 import org.apache.myfaces.shared.util.HashMapUtils;
61 import org.apache.myfaces.shared.util.WebConfigParamUtils;
62 import org.apache.myfaces.view.facelets.compiler.CheckDuplicateIdFaceletUtils;
63 import org.apache.myfaces.view.facelets.pool.ViewEntry;
64 import org.apache.myfaces.view.facelets.pool.ViewPool;
65 import org.apache.myfaces.view.facelets.pool.ViewStructureMetadata;
66 import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
67 import org.apache.myfaces.view.facelets.tag.jsf.FaceletState;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 public class DefaultFaceletsStateManagementStrategy extends StateManagementStrategy
107 {
108 public static final String CLIENTIDS_ADDED = "oam.CLIENTIDS_ADDED";
109
110 public static final String CLIENTIDS_REMOVED = "oam.CLIENTIDS_REMOVED";
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 public static final String COMPONENT_ADDED_AFTER_BUILD_VIEW = "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW";
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 @JSFWebConfigParam(since="2.0.12, 2.1.6", defaultValue="auto", expectedValues="true, auto, false",
144 group="state", tags="performance")
145 public static final String CHECK_ID_PRODUCTION_MODE
146 = "org.apache.myfaces.CHECK_ID_PRODUCTION_MODE";
147
148 private static final String CHECK_ID_PRODUCTION_MODE_DEFAULT = "auto";
149 private static final String CHECK_ID_PRODUCTION_MODE_TRUE = "true";
150 private static final String CHECK_ID_PRODUCTION_MODE_AUTO = "auto";
151
152 private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
153
154 private static final String SERIALIZED_VIEW_REQUEST_ATTR =
155 StateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
156
157 private static final Object[] EMPTY_STATES = new Object[]{null, null};
158
159 private static final Set<VisitHint> VISIT_HINTS = Collections.unmodifiableSet(
160 EnumSet.of(VisitHint.SKIP_ITERATION));
161
162 private static final String UNIQUE_ID_COUNTER_KEY =
163 "oam.view.uniqueIdCounter";
164
165 private ViewDeclarationLanguageFactory _vdlFactory;
166
167 private RenderKitFactory _renderKitFactory = null;
168
169 private VisitContextFactory _visitContextFactory = null;
170
171 private String _checkIdsProductionMode;
172
173 private MyfacesConfig _config;
174
175 private ViewPoolProcessor _viewPoolProcessor;
176
177 public DefaultFaceletsStateManagementStrategy ()
178 {
179 this(FacesContext.getCurrentInstance());
180 }
181
182 public DefaultFaceletsStateManagementStrategy (FacesContext context)
183 {
184 _vdlFactory = (ViewDeclarationLanguageFactory)
185 FactoryFinder.getFactory(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY);
186 _config = MyfacesConfig.getCurrentInstance(context.getExternalContext());
187 _viewPoolProcessor = ViewPoolProcessor.getInstance(context);
188 }
189
190 @SuppressWarnings("unchecked")
191 @Override
192 public UIViewRoot restoreView (FacesContext context, String viewId, String renderKitId)
193 {
194 ResponseStateManager manager;
195 Object state[];
196 Map<String, Object> states;
197 UIViewRoot view = null;
198
199
200
201 final boolean oldContextEventState = context.isProcessingEvents();
202
203 manager = getRenderKitFactory().getRenderKit(context, renderKitId).getResponseStateManager();
204
205 state = (Object[]) manager.getState(context, viewId);
206
207 if (state == null)
208 {
209
210 return null;
211 }
212
213 if (state[1] instanceof Object[])
214 {
215 Object[] fullState = (Object[]) state[1];
216 view = (UIViewRoot) internalRestoreTreeStructure((TreeStructComponent)fullState[0]);
217
218 if (view != null)
219 {
220 context.setViewRoot (view);
221 view.processRestoreState(context, fullState[1]);
222
223
224
225
226 RequestViewContext.getCurrentInstance(context).
227 refreshRequestViewContext(context, view);
228
229 if (fullState.length == 3 && fullState[2] != null)
230 {
231 context.setResourceLibraryContracts((List) UIComponentBase.
232 restoreAttachedState(context, fullState[2]));
233 }
234 }
235 }
236 else
237 {
238
239 ViewDeclarationLanguage vdl = _vdlFactory.getViewDeclarationLanguage(viewId);
240 Object faceletViewState = null;
241 try
242 {
243 ViewMetadata metadata = vdl.getViewMetadata (context, viewId);
244
245 if (metadata != null)
246 {
247 view = metadata.createMetadataView(context);
248
249
250 if (view == null && context.getResponseComplete())
251 {
252 return null;
253 }
254 }
255 if (view == null)
256 {
257 view = context.getApplication().getViewHandler().createView(context, viewId);
258 }
259
260 context.setViewRoot (view);
261 boolean skipBuildView = false;
262 if (state != null && state[1] != null)
263 {
264
265
266
267
268
269 states = (Map<String, Object>) state[1];
270 faceletViewState = UIComponentBase.restoreAttachedState(
271 context,states.get(ComponentSupport.FACELET_STATE_INSTANCE));
272 if (faceletViewState != null && _viewPoolProcessor != null)
273 {
274 ViewPool viewPool = _viewPoolProcessor.getViewPool(context, view);
275 if (viewPool != null)
276 {
277 ViewStructureMetadata viewMetadata = viewPool.retrieveDynamicViewStructureMetadata(
278 context, view, (FaceletState) faceletViewState);
279 if (viewMetadata != null)
280 {
281 ViewEntry entry = viewPool.popDynamicStructureView(context, view,
282 (FaceletState) faceletViewState);
283 if (entry != null)
284 {
285 skipBuildView = true;
286 _viewPoolProcessor.cloneAndRestoreView(context, view, entry, viewMetadata);
287 }
288 }
289 }
290 }
291 if (view.getId() == null)
292 {
293 view.setId(view.createUniqueId(context, null));
294 }
295 if (faceletViewState != null)
296 {
297
298
299 FaceletState newFaceletState = (FaceletState) view.getAttributes().get(
300 ComponentSupport.FACELET_STATE_INSTANCE);
301 if (newFaceletState != null)
302 {
303 newFaceletState.restoreState(context,
304 ((FaceletState)faceletViewState).saveState(context));
305 faceletViewState = newFaceletState;
306 }
307 else
308 {
309 view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE, faceletViewState);
310 }
311
312
313
314
315
316 }
317 if (state.length == 3)
318 {
319
320 view.getAttributes().put(UNIQUE_ID_COUNTER_KEY, state[2]);
321 }
322 Object viewRootState = states.get(view.getClientId(context));
323 if (viewRootState != null)
324 {
325 try
326 {
327 view.pushComponentToEL(context, view);
328 view.restoreViewScopeState(context, viewRootState);
329 }
330 finally
331 {
332 view.popComponentFromEL(context);
333 }
334 }
335 }
336
337
338
339
340 if (!skipBuildView)
341 {
342 try
343 {
344 context.setProcessingEvents (true);
345 vdl.buildView (context, view);
346
347
348
349 suscribeListeners(view);
350 }
351 finally
352 {
353 context.setProcessingEvents (oldContextEventState);
354 }
355 }
356 }
357 catch (Throwable e)
358 {
359 throw new FacesException ("unable to create view \"" + viewId + "\"", e);
360 }
361
362
363
364 boolean statelessMode = manager.isStateless(context, viewId);
365 if (statelessMode && !view.isTransient())
366 {
367 throw new IllegalStateException("View is not transient");
368 }
369 if (!statelessMode && view.isTransient())
370 {
371 throw new IllegalStateException("Cannot apply state over stateless view");
372 }
373
374 if (state != null && state[1] != null)
375 {
376 states = (Map<String, Object>) state[1];
377
378 Integer lastUniqueIdCounter = (Integer) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
379
380
381 FaceletState oldFaceletState = (FaceletState) view.getAttributes().get(
382 ComponentSupport.FACELET_STATE_INSTANCE);
383
384
385 boolean emptyState = false;
386 boolean containsFaceletState = states.containsKey(
387 ComponentSupport.FACELET_STATE_INSTANCE);
388 if (states.isEmpty())
389 {
390 emptyState = true;
391 }
392 else if (states.size() == 1 &&
393 containsFaceletState)
394 {
395 emptyState = true;
396 }
397
398 if (!emptyState)
399 {
400
401
402
403 if ((states.size() == 1 && !containsFaceletState) ||
404 (states.size() == 2 && containsFaceletState))
405 {
406 Object viewState = states.get(view.getClientId(context));
407 if (viewState != null)
408 {
409 restoreViewRootOnlyFromMap(context,viewState, view);
410 }
411 else
412 {
413
414 restoreStateFromMap(context, states, view);
415 }
416 }
417 else
418 {
419 restoreStateFromMap(context, states, view);
420 }
421 }
422 if (faceletViewState != null)
423 {
424
425 if (oldFaceletState != null && oldFaceletState.getBindings() != null &&
426 !oldFaceletState.getBindings().isEmpty())
427 {
428
429
430
431
432
433
434 FaceletState newFaceletState = (FaceletState) faceletViewState;
435 for (Map.Entry<String, Map<String, ValueExpression> > entry :
436 oldFaceletState.getBindings().entrySet())
437 {
438 for (Map.Entry<String, ValueExpression> entry2 : entry.getValue().entrySet())
439 {
440 ValueExpression expr = newFaceletState.getBinding(entry.getKey(), entry2.getKey());
441 if (expr == null)
442 {
443 newFaceletState.putBinding(entry.getKey(), entry2.getKey(), entry2.getValue());
444 }
445 }
446 }
447 view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE, newFaceletState);
448 }
449 else
450 {
451
452 view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE, faceletViewState);
453 }
454 }
455 if (lastUniqueIdCounter != null)
456 {
457 Integer newUniqueIdCounter = (Integer) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
458 if (newUniqueIdCounter != null &&
459 lastUniqueIdCounter.intValue() > newUniqueIdCounter.intValue())
460 {
461
462
463
464 view.getAttributes().put(UNIQUE_ID_COUNTER_KEY, lastUniqueIdCounter);
465 }
466 }
467 handleDynamicAddedRemovedComponents(context, view, states);
468 }
469 }
470 return view;
471 }
472
473 public void handleDynamicAddedRemovedComponents(FacesContext context, UIViewRoot view, Map<String, Object> states)
474 {
475 List<String> clientIdsRemoved = getClientIdsRemoved(view);
476
477 if (clientIdsRemoved != null)
478 {
479 Set<String> idsRemovedSet = new HashSet<String>(HashMapUtils.calcCapacity(clientIdsRemoved.size()));
480 context.getAttributes().put(FaceletViewDeclarationLanguage.REMOVING_COMPONENTS_BUILD, Boolean.TRUE);
481 try
482 {
483
484 for (int i = 0, size = clientIdsRemoved.size(); i < size; i++)
485 {
486 String clientId = clientIdsRemoved.get(i);
487 if (!idsRemovedSet.contains(clientId))
488 {
489 RemoveComponentCallback callback = new RemoveComponentCallback();
490 view.invokeOnComponent(context, clientId, callback);
491 if (callback.isComponentFound())
492 {
493
494 idsRemovedSet.add(clientId);
495 }
496 }
497 }
498 clientIdsRemoved.clear();
499 clientIdsRemoved.addAll(idsRemovedSet);
500 }
501 finally
502 {
503 context.getAttributes().remove(FaceletViewDeclarationLanguage.REMOVING_COMPONENTS_BUILD);
504 }
505 }
506 List<String> clientIdsAdded = getClientIdsAdded(view);
507 if (clientIdsAdded != null)
508 {
509 Set<String> idsAddedSet = new HashSet<String>(HashMapUtils.calcCapacity(clientIdsAdded.size()));
510
511 for (int i = 0, size = clientIdsAdded.size(); i < size; i++)
512 {
513 String clientId = clientIdsAdded.get(i);
514 if (!idsAddedSet.contains(clientId))
515 {
516 final AttachedFullStateWrapper wrapper = (AttachedFullStateWrapper) states.get(clientId);
517 if (wrapper != null)
518 {
519 final Object[] addedState = (Object[]) wrapper.getWrappedStateObject();
520 if (addedState != null)
521 {
522 if (addedState.length == 2)
523 {
524 view = (UIViewRoot)
525 internalRestoreTreeStructure((TreeStructComponent) addedState[0]);
526 view.processRestoreState(context, addedState[1]);
527 break;
528 }
529 else
530 {
531 final String parentClientId = (String) addedState[0];
532 view.invokeOnComponent(context, parentClientId,
533 new AddComponentCallback(addedState));
534 }
535 }
536 }
537 idsAddedSet.add(clientId);
538 }
539 }
540
541
542 clientIdsAdded.clear();
543
544
545
546
547 RequestViewContext.getCurrentInstance(context).
548 refreshRequestViewContext(context, view);
549 }
550 }
551
552 public static class RemoveComponentCallback implements ContextCallback
553 {
554 private boolean componentFound;
555
556 public RemoveComponentCallback()
557 {
558 this.componentFound = false;
559 }
560
561 public void invokeContextCallback(FacesContext context,
562 UIComponent target)
563 {
564 if (target.getParent() != null &&
565 !target.getParent().getChildren().remove(target))
566 {
567 String key = null;
568 if (target.getParent().getFacetCount() > 0)
569 {
570 for (Map.Entry<String, UIComponent> entry :
571 target.getParent().getFacets().entrySet())
572 {
573 if (entry.getValue()==target)
574 {
575 key = entry.getKey();
576 break;
577 }
578 }
579 }
580 if (key != null)
581 {
582 UIComponent removedTarget = target.getParent().getFacets().remove(key);
583 if (removedTarget != null)
584 {
585 this.componentFound = true;
586 }
587 }
588 }
589 else
590 {
591 this.componentFound = true;
592 }
593 }
594
595 public boolean isComponentFound()
596 {
597 return this.componentFound;
598 }
599 }
600
601 public static class AddComponentCallback implements ContextCallback
602 {
603 private final Object[] addedState;
604
605 public AddComponentCallback(Object[] addedState)
606 {
607 this.addedState = addedState;
608 }
609
610 public void invokeContextCallback(FacesContext context,
611 UIComponent target)
612 {
613 if (addedState[1] != null)
614 {
615 String facetName = (String) addedState[1];
616 UIComponent child
617 = internalRestoreTreeStructure((TreeStructComponent)
618 addedState[3]);
619 child.processRestoreState(context, addedState[4]);
620 target.getFacets().put(facetName,child);
621 }
622 else
623 {
624 Integer childIndex = (Integer) addedState[2];
625 UIComponent child
626 = internalRestoreTreeStructure((TreeStructComponent)
627 addedState[3]);
628 child.processRestoreState(context, addedState[4]);
629
630 boolean done = false;
631
632 if (child.getAttributes().containsKey(ComponentSupport.MARK_CREATED))
633 {
634
635
636
637 UIComponent parent = target;
638 if (parent.getChildCount() > 0)
639 {
640 String tagId = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
641 if (childIndex < parent.getChildCount())
642 {
643
644 UIComponent dup = parent.getChildren().get(childIndex);
645 if (tagId.equals(dup.getAttributes().get(ComponentSupport.MARK_CREATED)))
646 {
647
648 parent.getChildren().remove(childIndex.intValue());
649 parent.getChildren().add(childIndex, child);
650 done = true;
651 }
652 }
653 if (!done)
654 {
655
656 for (int i = 0, childCount = parent.getChildCount(); i < childCount; i ++)
657 {
658 UIComponent dup = parent.getChildren().get(i);
659 if (tagId.equals(dup.getAttributes().get(ComponentSupport.MARK_CREATED)))
660 {
661
662 parent.getChildren().remove(i);
663 parent.getChildren().add(i, child);
664 done = true;
665 break;
666 }
667 }
668 }
669 }
670 }
671 if (!done)
672 {
673 try
674 {
675 target.getChildren().add(childIndex, child);
676 }
677 catch (IndexOutOfBoundsException e)
678 {
679
680
681 target.getChildren().add(child);
682 }
683 }
684 }
685 }
686 }
687
688 @Override
689 public Object saveView (FacesContext context)
690 {
691 UIViewRoot view = context.getViewRoot();
692 Object states;
693
694 if (view == null)
695 {
696
697
698 return null;
699 }
700
701 Object serializedView = context.getAttributes()
702 .get(SERIALIZED_VIEW_REQUEST_ATTR);
703
704
705
706 if (serializedView == null)
707 {
708
709
710
711 if (context.isProjectStage(ProjectStage.Production))
712 {
713 if (CHECK_ID_PRODUCTION_MODE_AUTO.equals(getCheckIdProductionMode(context)))
714 {
715 CheckDuplicateIdFaceletUtils.checkIdsStatefulComponents(context, view);
716 }
717 else if (CHECK_ID_PRODUCTION_MODE_TRUE.equals(getCheckIdProductionMode(context)))
718 {
719 CheckDuplicateIdFaceletUtils.checkIds(context, view);
720 }
721 }
722 else
723 {
724 CheckDuplicateIdFaceletUtils.checkIds(context, view);
725 }
726
727
728
729 boolean viewResetable = false;
730 int count = 0;
731 Object faceletViewState = null;
732 boolean saveViewFully = view.getAttributes().containsKey(COMPONENT_ADDED_AFTER_BUILD_VIEW);
733 if (saveViewFully)
734 {
735 ensureClearInitialState(view);
736 Object rlcStates = !context.getResourceLibraryContracts().isEmpty() ?
737 UIComponentBase.saveAttachedState(context,
738 new ArrayList<String>(context.getResourceLibraryContracts())) : null;
739 states = new Object[]{
740 internalBuildTreeStructureToSave(view),
741 view.processSaveState(context), rlcStates};
742 }
743 else
744 {
745 states = new HashMap<String, Object>();
746
747 faceletViewState = view.getAttributes().get(ComponentSupport.FACELET_STATE_INSTANCE);
748 if (faceletViewState != null)
749 {
750 ((Map<String, Object>)states).put(ComponentSupport.FACELET_STATE_INSTANCE,
751 UIComponentBase.saveAttachedState(context, faceletViewState));
752
753 view.getAttributes().remove(ComponentSupport.FACELET_STATE_INSTANCE);
754 view.getTransientStateHelper().putTransient(
755 ComponentSupport.FACELET_STATE_INSTANCE, faceletViewState);
756 }
757 if (_viewPoolProcessor != null &&
758 _viewPoolProcessor.isViewPoolEnabledForThisView(context, view))
759 {
760 SaveStateAndResetViewCallback cb = saveStateOnMapVisitTreeAndReset(
761 context,(Map<String,Object>) states, view,
762 Boolean.TRUE.equals(
763 context.getAttributes().get(ViewPoolProcessor.FORCE_HARD_RESET)));
764 viewResetable = cb.isViewResetable();
765 count = cb.getCount();
766 }
767 else
768 {
769 saveStateOnMapVisitTree(context,(Map<String,Object>) states, view);
770 }
771
772 if ( ((Map<String,Object>)states).isEmpty())
773 {
774 states = null;
775 }
776 }
777
778 Integer uniqueIdCount = (Integer) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
779 if (uniqueIdCount != null && !uniqueIdCount.equals(1))
780 {
781 serializedView = new Object[] { null, states, uniqueIdCount };
782 }
783 else if (states == null)
784 {
785 serializedView = EMPTY_STATES;
786 }
787 else
788 {
789 serializedView = new Object[] { null, states };
790 }
791
792
793 if (!saveViewFully && _viewPoolProcessor != null)
794 {
795 if (viewResetable)
796 {
797 _viewPoolProcessor.pushResetableView(
798 context, view, (FaceletState) faceletViewState);
799 }
800 else
801 {
802 _viewPoolProcessor.pushPartialView(
803 context, view, (FaceletState) faceletViewState, count);
804 }
805 }
806
807 context.getAttributes().put(SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
808
809 }
810
811 return serializedView;
812 }
813
814 private void restoreViewRootOnlyFromMap(
815 final FacesContext context, final Object viewState,
816 final UIComponent view)
817 {
818
819
820 try
821 {
822
823 view.pushComponentToEL(context, view);
824 if (viewState != null && !(viewState instanceof AttachedFullStateWrapper))
825 {
826 try
827 {
828 view.restoreState(context, viewState);
829 }
830 catch(Exception e)
831 {
832 throw new IllegalStateException(
833 "Error restoring component: "+
834 view.getClientId(context), e);
835 }
836 }
837 }
838 finally
839 {
840 view.popComponentFromEL(context);
841 }
842 }
843
844 private void restoreStateFromMap(final FacesContext context, final Map<String,Object> states,
845 final UIComponent component)
846 {
847 if (states == null)
848 {
849 return;
850 }
851
852 try
853 {
854
855 component.pushComponentToEL(context, component);
856 Object state = states.get(component.getClientId(context));
857 if (state != null)
858 {
859 if (state instanceof AttachedFullStateWrapper)
860 {
861
862 return;
863 }
864 try
865 {
866 component.restoreState(context, state);
867 }
868 catch(Exception e)
869 {
870 throw new IllegalStateException("Error restoring component: "+component.getClientId(context), e);
871 }
872 }
873
874
875 if (component.getChildCount() > 0)
876 {
877
878
879 List<UIComponent> children = component.getChildren();
880 for (int i = 0; i < children.size(); i++)
881 {
882 UIComponent child = children.get(i);
883 if (child != null && !child.isTransient())
884 {
885 restoreStateFromMap( context, states, child);
886 }
887 }
888 }
889
890
891 if (component.getFacetCount() > 0)
892 {
893 Map<String, UIComponent> facetMap = component.getFacets();
894
895 for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
896 {
897 UIComponent child = entry.getValue();
898 if (child != null && !child.isTransient())
899 {
900
901 restoreStateFromMap( context, states, child);
902 }
903 }
904 }
905 }
906 finally
907 {
908 component.popComponentFromEL(context);
909 }
910 }
911
912 static List<String> getClientIdsAdded(UIViewRoot root)
913 {
914 return (List<String>) root.getAttributes().get(CLIENTIDS_ADDED);
915 }
916
917 static void setClientsIdsAdded(UIViewRoot root, List<String> clientIdsList)
918 {
919 root.getAttributes().put(CLIENTIDS_ADDED, clientIdsList);
920 }
921
922 static List<String> getClientIdsRemoved(UIViewRoot root)
923 {
924 return (List<String>) root.getAttributes().get(CLIENTIDS_REMOVED);
925 }
926
927 static void setClientsIdsRemoved(UIViewRoot root, List<String> clientIdsList)
928 {
929 root.getAttributes().put(CLIENTIDS_REMOVED, clientIdsList);
930 }
931
932 @SuppressWarnings("unchecked")
933 private void registerOnAddRemoveList(FacesContext facesContext, String clientId)
934 {
935 UIViewRoot uiViewRoot = facesContext.getViewRoot();
936
937 List<String> clientIdsAdded = (List<String>) getClientIdsAdded(uiViewRoot);
938 if (clientIdsAdded == null)
939 {
940
941 clientIdsAdded = new ArrayList<String>();
942 }
943 clientIdsAdded.add(clientId);
944
945 setClientsIdsAdded(uiViewRoot, clientIdsAdded);
946
947 List<String> clientIdsRemoved = (List<String>) getClientIdsRemoved(uiViewRoot);
948 if (clientIdsRemoved == null)
949 {
950
951 clientIdsRemoved = new ArrayList<String>();
952 }
953
954 clientIdsRemoved.add(clientId);
955
956 setClientsIdsRemoved(uiViewRoot, clientIdsRemoved);
957 }
958
959 @SuppressWarnings("unchecked")
960 private void registerOnAddList(FacesContext facesContext, String clientId)
961 {
962 UIViewRoot uiViewRoot = facesContext.getViewRoot();
963
964 List<String> clientIdsAdded = (List<String>) getClientIdsAdded(uiViewRoot);
965 if (clientIdsAdded == null)
966 {
967
968 clientIdsAdded = new ArrayList<String>();
969 }
970 clientIdsAdded.add(clientId);
971
972 setClientsIdsAdded(uiViewRoot, clientIdsAdded);
973 }
974
975 private void saveStateOnMapVisitTree(final FacesContext facesContext, final Map<String,Object> states,
976 final UIViewRoot uiViewRoot)
977 {
978 facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
979 try
980 {
981 uiViewRoot.visitTree( getVisitContextFactory().getVisitContext(
982 facesContext, null, VISIT_HINTS), new VisitCallback()
983 {
984 public VisitResult visit(VisitContext context, UIComponent target)
985 {
986 FacesContext facesContext = context.getFacesContext();
987 Object state;
988
989 if ((target == null) || target.isTransient())
990 {
991
992
993 return VisitResult.REJECT;
994 }
995
996 ComponentState componentAddedAfterBuildView
997 = (ComponentState) target.getAttributes().get(COMPONENT_ADDED_AFTER_BUILD_VIEW);
998
999
1000 if (componentAddedAfterBuildView != null && (target.getParent() != null))
1001 {
1002 if (ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
1003 {
1004 registerOnAddRemoveList(facesContext, target.getClientId(facesContext));
1005 target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1006 }
1007 else if (ComponentState.ADD.equals(componentAddedAfterBuildView))
1008 {
1009 registerOnAddList(facesContext, target.getClientId(facesContext));
1010 target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1011 }
1012 else if (ComponentState.ADDED.equals(componentAddedAfterBuildView))
1013 {
1014 registerOnAddList(facesContext, target.getClientId(facesContext));
1015 }
1016 ensureClearInitialState(target);
1017
1018
1019
1020 int childIndex = target.getParent().getChildren().indexOf(target);
1021 if (childIndex >= 0)
1022 {
1023 states.put(target.getClientId(facesContext), new AttachedFullStateWrapper(
1024 new Object[]{
1025 target.getParent().getClientId(facesContext),
1026 null,
1027 childIndex,
1028 internalBuildTreeStructureToSave(target),
1029 target.processSaveState(facesContext)}));
1030 }
1031 else
1032 {
1033 String facetName = null;
1034 if (target.getParent().getFacetCount() > 0)
1035 {
1036 for (Map.Entry<String, UIComponent> entry : target.getParent().getFacets().entrySet())
1037 {
1038 if (target.equals(entry.getValue()))
1039 {
1040 facetName = entry.getKey();
1041 break;
1042 }
1043 }
1044 }
1045 states.put(target.getClientId(facesContext),new AttachedFullStateWrapper(new Object[]{
1046 target.getParent().getClientId(facesContext),
1047 facetName,
1048 null,
1049 internalBuildTreeStructureToSave(target),
1050 target.processSaveState(facesContext)}));
1051 }
1052 return VisitResult.REJECT;
1053 }
1054 else if (target.getParent() != null)
1055 {
1056 state = target.saveState (facesContext);
1057
1058 if (state != null)
1059 {
1060
1061
1062 states.put (target.getClientId (facesContext), state);
1063 }
1064
1065 return VisitResult.ACCEPT;
1066 }
1067 else
1068 {
1069
1070 return VisitResult.ACCEPT;
1071 }
1072 }
1073 });
1074 }
1075 finally
1076 {
1077 facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
1078 }
1079 if (!uiViewRoot.isTransient())
1080 {
1081 Object state = uiViewRoot.saveState (facesContext);
1082 if (state != null)
1083 {
1084
1085
1086 states.put (uiViewRoot.getClientId (facesContext), state);
1087 }
1088 }
1089 }
1090
1091
1092 private SaveStateAndResetViewCallback saveStateOnMapVisitTreeAndReset(final FacesContext facesContext,
1093 final Map<String,Object> states, final UIViewRoot uiViewRoot, boolean forceHardReset)
1094 {
1095 facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
1096 SaveStateAndResetViewCallback callback = new SaveStateAndResetViewCallback(
1097 facesContext.getViewRoot(), states, forceHardReset);
1098 if (forceHardReset)
1099 {
1100 uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY,
1101 ViewPoolProcessor.RESET_MODE_HARD);
1102 }
1103 else
1104 {
1105 uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY,
1106 ViewPoolProcessor.RESET_MODE_SOFT);
1107 }
1108 try
1109 {
1110 if (_viewPoolProcessor != null &&
1111 !_viewPoolProcessor.isViewPoolEnabledForThisView(facesContext, uiViewRoot))
1112 {
1113 callback.setViewResetable(false);
1114 }
1115
1116
1117
1118
1119 if (callback.isViewResetable())
1120 {
1121 List<String> removedIds = getClientIdsRemoved(uiViewRoot);
1122 if (removedIds != null && !removedIds.isEmpty())
1123 {
1124 callback.setViewResetable(false);
1125 }
1126 }
1127
1128 try
1129 {
1130 uiViewRoot.visitTree( getVisitContextFactory().getVisitContext(
1131 facesContext, null, VISIT_HINTS), callback);
1132 }
1133 finally
1134 {
1135 facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
1136 }
1137
1138 if (callback.isViewResetable() && callback.isRemoveAddedComponents())
1139 {
1140 List<String> clientIdsToRemove = getClientIdsAdded(uiViewRoot);
1141
1142 if (clientIdsToRemove != null)
1143 {
1144
1145 for (int i = 0, size = clientIdsToRemove.size(); i < size; i++)
1146 {
1147 String clientId = clientIdsToRemove.get(i);
1148 uiViewRoot.invokeOnComponent(facesContext, clientId, new RemoveComponentCallback());
1149 }
1150 }
1151 }
1152
1153 Object state = uiViewRoot.saveState (facesContext);
1154 if (state != null)
1155 {
1156
1157 states.put (uiViewRoot.getClientId (facesContext), state);
1158
1159
1160 Integer oldResetMode = (Integer) uiViewRoot.getAttributes().put(
1161 ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, ViewPoolProcessor.RESET_MODE_HARD);
1162 state = uiViewRoot.saveState (facesContext);
1163 uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, oldResetMode);
1164 if (state != null)
1165 {
1166 callback.setViewResetable(false);
1167 }
1168 }
1169 }
1170 finally
1171 {
1172 uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY,
1173 ViewPoolProcessor.RESET_MODE_OFF);
1174 }
1175 return callback;
1176 }
1177
1178 private class SaveStateAndResetViewCallback implements VisitCallback
1179 {
1180 private final Map<String, Object> states;
1181
1182 private final UIViewRoot view;
1183
1184 private boolean viewResetable;
1185
1186 private boolean skipRoot;
1187
1188 private int count;
1189
1190 private boolean forceHardReset;
1191
1192 private boolean removeAddedComponents;
1193
1194 public SaveStateAndResetViewCallback(UIViewRoot view, Map<String, Object> states,
1195 boolean forceHardReset)
1196 {
1197 this.states = states;
1198 this.view = view;
1199 this.viewResetable = true;
1200 this.skipRoot = true;
1201 this.count = 0;
1202 this.forceHardReset = forceHardReset;
1203 this.removeAddedComponents = false;
1204 }
1205
1206 public VisitResult visit(VisitContext context, UIComponent target)
1207 {
1208 FacesContext facesContext = context.getFacesContext();
1209 Object state;
1210 this.count++;
1211
1212 if ((target == null) || target.isTransient())
1213 {
1214
1215
1216 return VisitResult.REJECT;
1217 }
1218
1219 if (skipRoot && target instanceof UIViewRoot)
1220 {
1221
1222 skipRoot = false;
1223 return VisitResult.ACCEPT;
1224 }
1225
1226 ComponentState componentAddedAfterBuildView
1227 = (ComponentState) target.getAttributes().get(COMPONENT_ADDED_AFTER_BUILD_VIEW);
1228
1229
1230 if (componentAddedAfterBuildView != null && (target.getParent() != null))
1231 {
1232
1233
1234
1235 setRemoveAddedComponents(true);
1236 if (forceHardReset)
1237 {
1238
1239
1240 if (isViewResetable() &&
1241 ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
1242 {
1243 setViewResetable(false);
1244 }
1245
1246 return VisitResult.REJECT;
1247 }
1248 if (ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
1249 {
1250
1251 setViewResetable(false);
1252 registerOnAddRemoveList(facesContext, target.getClientId(facesContext));
1253 target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1254 }
1255 else if (ComponentState.ADD.equals(componentAddedAfterBuildView))
1256 {
1257 registerOnAddList(facesContext, target.getClientId(facesContext));
1258 target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1259 }
1260 else if (ComponentState.ADDED.equals(componentAddedAfterBuildView))
1261 {
1262
1263
1264 registerOnAddList(facesContext, target.getClientId(facesContext));
1265 }
1266 ensureClearInitialState(target);
1267
1268
1269
1270 int childIndex = target.getParent().getChildren().indexOf(target);
1271 if (childIndex >= 0)
1272 {
1273 states.put(target.getClientId(facesContext), new AttachedFullStateWrapper(
1274 new Object[]{
1275 target.getParent().getClientId(facesContext),
1276 null,
1277 childIndex,
1278 internalBuildTreeStructureToSave(target),
1279 target.processSaveState(facesContext)}));
1280 }
1281 else
1282 {
1283 String facetName = null;
1284 if (target.getParent().getFacetCount() > 0)
1285 {
1286 for (Map.Entry<String, UIComponent> entry : target.getParent().getFacets().entrySet())
1287 {
1288 if (target.equals(entry.getValue()))
1289 {
1290 facetName = entry.getKey();
1291 break;
1292 }
1293 }
1294 }
1295 states.put(target.getClientId(facesContext),new AttachedFullStateWrapper(new Object[]{
1296 target.getParent().getClientId(facesContext),
1297 facetName,
1298 null,
1299 internalBuildTreeStructureToSave(target),
1300 target.processSaveState(facesContext)}));
1301 }
1302 return VisitResult.REJECT;
1303 }
1304 else if (target.getParent() != null)
1305 {
1306 if (forceHardReset)
1307 {
1308
1309 state = target.saveState (facesContext);
1310 if (state != null)
1311 {
1312 setViewResetable(false);
1313 return VisitResult.REJECT;
1314 }
1315 }
1316 else
1317 {
1318 state = target.saveState (facesContext);
1319
1320 if (state != null)
1321 {
1322
1323 states.put (target.getClientId (facesContext), state);
1324
1325 if (isViewResetable())
1326 {
1327
1328 Integer oldResetMode = (Integer) view.getAttributes().put(
1329 ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY,
1330 ViewPoolProcessor.RESET_MODE_HARD);
1331 state = target.saveState (facesContext);
1332 view.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY,
1333 oldResetMode);
1334 if (state != null)
1335 {
1336 setViewResetable(false);
1337 }
1338 }
1339 }
1340 }
1341
1342 return VisitResult.ACCEPT;
1343 }
1344 else
1345 {
1346
1347 return VisitResult.ACCEPT;
1348 }
1349 }
1350
1351
1352
1353
1354 public boolean isViewResetable()
1355 {
1356 return viewResetable;
1357 }
1358
1359
1360
1361
1362 public void setViewResetable(boolean viewResetable)
1363 {
1364 this.viewResetable = viewResetable;
1365 }
1366
1367 public int getCount()
1368 {
1369 return count;
1370 }
1371
1372
1373
1374
1375 public boolean isRemoveAddedComponents()
1376 {
1377 return removeAddedComponents;
1378 }
1379
1380
1381
1382
1383 public void setRemoveAddedComponents(boolean removeAddedComponents)
1384 {
1385 this.removeAddedComponents = removeAddedComponents;
1386 }
1387 }
1388
1389 protected void ensureClearInitialState(UIComponent c)
1390 {
1391 c.clearInitialState();
1392 if (c.getChildCount() > 0)
1393 {
1394 for (int i = 0, childCount = c.getChildCount(); i < childCount; i++)
1395 {
1396 UIComponent child = c.getChildren().get(i);
1397 ensureClearInitialState(child);
1398 }
1399 }
1400 if (c.getFacetCount() > 0)
1401 {
1402 for (UIComponent child : c.getFacets().values())
1403 {
1404 ensureClearInitialState(child);
1405 }
1406 }
1407 }
1408
1409 public void suscribeListeners(UIViewRoot uiViewRoot)
1410 {
1411 boolean listenerSubscribed = false;
1412 List<SystemEventListener> pavList = uiViewRoot.getViewListenersForEventClass(PostAddToViewEvent.class);
1413 if (pavList != null)
1414 {
1415 for (SystemEventListener listener : pavList)
1416 {
1417 if (listener instanceof PostAddPreRemoveFromViewListener)
1418 {
1419 listenerSubscribed = true;
1420 break;
1421 }
1422 }
1423 }
1424 if (!listenerSubscribed)
1425 {
1426 PostAddPreRemoveFromViewListener componentListener = new PostAddPreRemoveFromViewListener();
1427 uiViewRoot.subscribeToViewEvent(PostAddToViewEvent.class, componentListener);
1428 uiViewRoot.subscribeToViewEvent(PreRemoveFromViewEvent.class, componentListener);
1429 }
1430 }
1431
1432 protected RenderKitFactory getRenderKitFactory()
1433 {
1434 if (_renderKitFactory == null)
1435 {
1436 _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
1437 }
1438 return _renderKitFactory;
1439 }
1440
1441 protected VisitContextFactory getVisitContextFactory()
1442 {
1443 if (_visitContextFactory == null)
1444 {
1445 _visitContextFactory = (VisitContextFactory)FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY);
1446 }
1447 return _visitContextFactory;
1448 }
1449
1450 protected String getCheckIdProductionMode(FacesContext facesContext)
1451 {
1452 if (_checkIdsProductionMode == null)
1453 {
1454 _checkIdsProductionMode
1455 = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
1456 CHECK_ID_PRODUCTION_MODE, CHECK_ID_PRODUCTION_MODE_DEFAULT);
1457 }
1458 return _checkIdsProductionMode;
1459 }
1460
1461
1462 public static class PostAddPreRemoveFromViewListener implements SystemEventListener
1463 {
1464 private transient FacesContext _facesContext;
1465
1466 private transient Boolean _isRefreshOnTransientBuildPreserveState;
1467
1468 public boolean isListenerForSource(Object source)
1469 {
1470
1471
1472 return (source instanceof UIComponent);
1473 }
1474
1475 private boolean isRefreshOnTransientBuildPreserveState()
1476 {
1477 if (_isRefreshOnTransientBuildPreserveState == null)
1478 {
1479 _isRefreshOnTransientBuildPreserveState = MyfacesConfig.getCurrentInstance(
1480 _facesContext.getExternalContext()).isRefreshTransientBuildOnPSSPreserveState();
1481 }
1482 return _isRefreshOnTransientBuildPreserveState;
1483 }
1484
1485 public void processEvent(SystemEvent event)
1486 {
1487 UIComponent component = (UIComponent) event.getSource();
1488
1489 if (component.isTransient())
1490 {
1491 return;
1492 }
1493
1494
1495
1496
1497 if (_facesContext == null)
1498 {
1499 _facesContext = FacesContext.getCurrentInstance();
1500 }
1501
1502 if (event instanceof PostAddToViewEvent)
1503 {
1504 if (!isRefreshOnTransientBuildPreserveState() &&
1505 Boolean.TRUE.equals(_facesContext.getAttributes().get(StateManager.IS_BUILDING_INITIAL_STATE)))
1506 {
1507 return;
1508 }
1509
1510
1511 component.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADD);
1512 }
1513 else
1514 {
1515
1516
1517
1518
1519
1520 if (FaceletViewDeclarationLanguage.isRemovingComponentBuild(_facesContext))
1521 {
1522 return;
1523 }
1524
1525 if (!isRefreshOnTransientBuildPreserveState() &&
1526 FaceletCompositionContext.getCurrentInstance(_facesContext) != null &&
1527 (component.getAttributes().containsKey(ComponentSupport.MARK_CREATED) ||
1528 component.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
1529 )
1530 {
1531
1532
1533
1534
1535
1536
1537
1538 return;
1539 }
1540
1541
1542 UIViewRoot uiViewRoot = _facesContext.getViewRoot();
1543
1544 List<String> clientIdsRemoved = getClientIdsRemoved(uiViewRoot);
1545 if (clientIdsRemoved == null)
1546 {
1547
1548 clientIdsRemoved = new ArrayList<String>();
1549 }
1550 clientIdsRemoved.add(component.getClientId(_facesContext));
1551 setClientsIdsRemoved(uiViewRoot, clientIdsRemoved);
1552 }
1553 }
1554 }
1555
1556 private static TreeStructComponent internalBuildTreeStructureToSave(UIComponent component)
1557 {
1558 TreeStructComponent structComp = new TreeStructComponent(component.getClass().getName(),
1559 component.getId());
1560
1561
1562 if (component.getChildCount() > 0)
1563 {
1564 List<TreeStructComponent> structChildList = new ArrayList<TreeStructComponent>();
1565 for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
1566 {
1567 UIComponent child = component.getChildren().get(i);
1568 if (!child.isTransient())
1569 {
1570 TreeStructComponent structChild = internalBuildTreeStructureToSave(child);
1571 structChildList.add(structChild);
1572 }
1573 }
1574
1575 TreeStructComponent[] childArray = structChildList.toArray(new TreeStructComponent[structChildList.size()]);
1576 structComp.setChildren(childArray);
1577 }
1578
1579
1580
1581 if (component.getFacetCount() > 0)
1582 {
1583 Map<String, UIComponent> facetMap = component.getFacets();
1584 List<Object[]> structFacetList = new ArrayList<Object[]>();
1585 for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
1586 {
1587 UIComponent child = entry.getValue();
1588 if (!child.isTransient())
1589 {
1590 String facetName = entry.getKey();
1591 TreeStructComponent structChild = internalBuildTreeStructureToSave(child);
1592 structFacetList.add(new Object[] {facetName, structChild});
1593 }
1594 }
1595
1596 Object[] facetArray = structFacetList.toArray(new Object[structFacetList.size()]);
1597 structComp.setFacets(facetArray);
1598 }
1599
1600 return structComp;
1601 }
1602
1603 private static UIComponent internalRestoreTreeStructure(TreeStructComponent treeStructComp)
1604 {
1605 String compClass = treeStructComp.getComponentClass();
1606 String compId = treeStructComp.getComponentId();
1607 UIComponent component = (UIComponent)ClassUtils.newInstance(compClass);
1608 component.setId(compId);
1609
1610
1611 TreeStructComponent[] childArray = treeStructComp.getChildren();
1612 if (childArray != null)
1613 {
1614 List<UIComponent> childList = component.getChildren();
1615 for (int i = 0, len = childArray.length; i < len; i++)
1616 {
1617 UIComponent child = internalRestoreTreeStructure(childArray[i]);
1618 childList.add(child);
1619 }
1620 }
1621
1622
1623 Object[] facetArray = treeStructComp.getFacets();
1624 if (facetArray != null)
1625 {
1626 Map<String, UIComponent> facetMap = component.getFacets();
1627 for (int i = 0, len = facetArray.length; i < len; i++)
1628 {
1629 Object[] tuple = (Object[])facetArray[i];
1630 String facetName = (String)tuple[0];
1631 TreeStructComponent structChild = (TreeStructComponent)tuple[1];
1632 UIComponent child = internalRestoreTreeStructure(structChild);
1633 facetMap.put(facetName, child);
1634 }
1635 }
1636
1637 return component;
1638 }
1639
1640 public static class TreeStructComponent implements Serializable
1641 {
1642 private static final long serialVersionUID = 5069109074684737231L;
1643 private String _componentClass;
1644 private String _componentId;
1645 private TreeStructComponent[] _children = null;
1646 private Object[] _facets = null;
1647
1648 TreeStructComponent(String componentClass, String componentId)
1649 {
1650 _componentClass = componentClass;
1651 _componentId = componentId;
1652 }
1653
1654 public String getComponentClass()
1655 {
1656 return _componentClass;
1657 }
1658
1659 public String getComponentId()
1660 {
1661 return _componentId;
1662 }
1663
1664 void setChildren(TreeStructComponent[] children)
1665 {
1666 _children = children;
1667 }
1668
1669 TreeStructComponent[] getChildren()
1670 {
1671 return _children;
1672 }
1673
1674 Object[] getFacets()
1675 {
1676 return _facets;
1677 }
1678
1679 void setFacets(Object[] facets)
1680 {
1681 _facets = facets;
1682 }
1683 }
1684
1685 }