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 javax.faces.component;
20
21 import java.io.IOException;
22 import java.sql.ResultSet;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29
30 import javax.el.ValueExpression;
31 import javax.faces.FacesException;
32 import javax.faces.application.FacesMessage;
33 import javax.faces.application.StateManager;
34 import javax.faces.component.visit.VisitCallback;
35 import javax.faces.component.visit.VisitContext;
36 import javax.faces.component.visit.VisitHint;
37 import javax.faces.component.visit.VisitResult;
38 import javax.faces.context.FacesContext;
39 import javax.faces.event.AbortProcessingException;
40 import javax.faces.event.FacesEvent;
41 import javax.faces.event.FacesListener;
42 import javax.faces.event.PhaseId;
43 import javax.faces.event.PostValidateEvent;
44 import javax.faces.event.PreValidateEvent;
45 import javax.faces.model.ArrayDataModel;
46 import javax.faces.model.DataModel;
47 import javax.faces.model.ListDataModel;
48 import javax.faces.model.ResultDataModel;
49 import javax.faces.model.ResultSetDataModel;
50 import javax.faces.model.ScalarDataModel;
51 import javax.servlet.jsp.jstl.sql.Result;
52
53 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
54 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFacet;
55 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
56
57 /**
58 * Represents an abstraction of a component which has multiple "rows" of data.
59 * <p>
60 * The children of this component are expected to be UIColumn components.
61 * <p>
62 * Note that the same set of child components are reused to implement each row of the table in turn during such phases
63 * as apply-request-values and render-response. Altering any of the members of these components therefore affects the
64 * attribute for every row, except for the following members:
65 * <ul>
66 * <li>submittedValue
67 * <li>value (where no EL binding is used)
68 * <li>valid
69 * </ul>
70 * <p>
71 * This reuse of the child components also means that it is not possible to save a reference to a component during table
72 * processing, then access it later and expect it to still represent the same row of the table.
73 * <h1>
74 * Implementation Notes</h1>
75 * <p>
76 * Each of the UIColumn children of this component has a few component children of its own to render the contents of the
77 * table cell. However there can be a very large number of rows in a table, so it isn't efficient for the UIColumn and
78 * all its child objects to be duplicated for each row in the table. Instead the "flyweight" pattern is used where a
79 * serialized state is held for each row. When setRowIndex is invoked, the UIColumn objects and their children serialize
80 * their current state then reinitialise themselves from the appropriate saved state. This allows a single set of real
81 * objects to represent multiple objects which have the same types but potentially different internal state. When a row
82 * is selected for the first time, its state is set to a clean "initial" state. Transient components (including any
83 * read-only component) do not save their state; they are just reinitialised as required. The state saved/restored when
84 * changing rows is not the complete component state, just the fields that are expected to vary between rows:
85 * "submittedValue", "value", "isValid".
86 * </p>
87 * <p>
88 * Note that a table is a "naming container", so that components within the table have their ids prefixed with the id of
89 * the table. Actually, when setRowIndex has been called on a table with id of "zzz" the table pretends to its children
90 * that its ID is "zzz_n" where n is the row index. This means that renderers for child components which call
91 * component.getClientId automatically get ids of form "zzz_n:childId" thus ensuring that components in different rows
92 * of the table get different ids.
93 * </p>
94 * <p>
95 * When decoding a submitted page, this class iterates over all its possible rowIndex values, restoring the appropriate
96 * serialized row state then calling processDecodes on the child components. Because the child components (or their
97 * renderers) use getClientId to get the request key to look for parameter data, and because this object pretends to
98 * have a different id per row ("zzz_n") a single child component can decode data from each table row in turn without
99 * being aware that it is within a table. The table's data model is updated before each call to child.processDecodes, so
100 * the child decode method can assume that the data model's rowData points to the model object associated with the row
101 * currently being decoded. Exactly the same process applies for the later validation and updateModel phases.
102 * </p>
103 * <p>
104 * When the data model for the table is bound to a backing bean property, and no validation errors have occured during
105 * processing of a postback, the data model is refetched at the start of the rendering phase (ie after the update model
106 * phase) so that the contents of the data model can be changed as a result of the latest form submission. Because the
107 * saved row state must correspond to the elements within the data model, the row state must be discarded whenever a new
108 * data model is fetched; not doing this would cause all sorts of inconsistency issues. This does imply that changing
109 * the state of any of the members "submittedValue", "value" or "valid" of a component within the table during the
110 * invokeApplication phase has no effect on the rendering of the table. When a validation error has occurred, a new
111 * DataModel is <i>not</i> fetched, and the saved state of the child components is <i>not</i> discarded.
112 * </p>
113 * see Javadoc of the <a href="http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
114 * for more information.
115 *
116 * @author Manfred Geiler (latest modification by $Author: lu4242 $)
117 * @version $Revision: 1306709 $ $Date: 2012-03-28 23:26:51 -0500 (Wed, 28 Mar 2012) $
118 */
119 @JSFComponent(defaultRendererType = "javax.faces.Table")
120 public class UIData extends UIComponentBase implements NamingContainer, UniqueIdVendor
121 {
122 public static final String COMPONENT_FAMILY = "javax.faces.Data";
123 public static final String COMPONENT_TYPE = "javax.faces.Data"; // for unit tests
124
125 private static final String FOOTER_FACET_NAME = "footer";
126 private static final String HEADER_FACET_NAME = "header";
127 private static final Class<Object[]> OBJECT_ARRAY_CLASS = Object[].class;
128 private static final int PROCESS_DECODES = 1;
129 private static final int PROCESS_VALIDATORS = 2;
130 private static final int PROCESS_UPDATES = 3;
131 //private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
132
133 private static final Object[] LEAF_NO_STATE = new Object[]{null,null};
134
135 private int _rowIndex = -1;
136
137 // Holds for each row the states of the child components of this UIData.
138 // Note that only "partial" component state is saved: the component fields
139 // that are expected to vary between rows.
140 private Map<String, Object> _rowStates = new HashMap<String, Object>();
141 private Map<String, Map<String, Object> > _rowDeltaStates = new HashMap<String, Map<String, Object> >();
142 private Map<String, Map<String, Object> > _rowTransientStates = new HashMap<String, Map<String, Object> >();
143
144 /**
145 * Handle case where this table is nested inside another table. See method getDataModel for more details.
146 * <p>
147 * Key: parentClientId (aka rowId when nested within a parent table) Value: DataModel
148 */
149 private Map<String, DataModel> _dataModelMap = new HashMap<String, DataModel>();
150
151 // will be set to false if the data should not be refreshed at the beginning of the encode phase
152 private boolean _isValidChilds = true;
153
154 private Object _initialDescendantComponentState = null;
155
156 private Object _initialDescendantFullComponentState = null;
157
158 //private int _first;
159 //private boolean _firstSet;
160 //private int _rows;
161 //private boolean _rowsSet;
162 //private Object _value;
163
164 private static class FacesEventWrapper extends FacesEvent
165 {
166 private static final long serialVersionUID = 6648047974065628773L;
167 private FacesEvent _wrappedFacesEvent;
168 private int _rowIndex;
169
170 public FacesEventWrapper(FacesEvent facesEvent, int rowIndex, UIData redirectComponent)
171 {
172 super(redirectComponent);
173 _wrappedFacesEvent = facesEvent;
174 _rowIndex = rowIndex;
175 }
176
177 @Override
178 public PhaseId getPhaseId()
179 {
180 return _wrappedFacesEvent.getPhaseId();
181 }
182
183 @Override
184 public void setPhaseId(PhaseId phaseId)
185 {
186 _wrappedFacesEvent.setPhaseId(phaseId);
187 }
188
189 @Override
190 public void queue()
191 {
192 _wrappedFacesEvent.queue();
193 }
194
195 @Override
196 public String toString()
197 {
198 return _wrappedFacesEvent.toString();
199 }
200
201 @Override
202 public boolean isAppropriateListener(FacesListener faceslistener)
203 {
204 return _wrappedFacesEvent.isAppropriateListener(faceslistener);
205 }
206
207 @Override
208 public void processListener(FacesListener faceslistener)
209 {
210 _wrappedFacesEvent.processListener(faceslistener);
211 }
212
213 public FacesEvent getWrappedFacesEvent()
214 {
215 return _wrappedFacesEvent;
216 }
217
218 public int getRowIndex()
219 {
220 return _rowIndex;
221 }
222 }
223
224 private static final DataModel EMPTY_DATA_MODEL = new DataModel()
225 {
226 @Override
227 public boolean isRowAvailable()
228 {
229 return false;
230 }
231
232 @Override
233 public int getRowCount()
234 {
235 return 0;
236 }
237
238 @Override
239 public Object getRowData()
240 {
241 throw new IllegalArgumentException();
242 }
243
244 @Override
245 public int getRowIndex()
246 {
247 return -1;
248 }
249
250 @Override
251 public void setRowIndex(int i)
252 {
253 if (i < -1)
254 {
255 throw new IllegalArgumentException();
256 }
257 }
258
259 @Override
260 public Object getWrappedData()
261 {
262 return null;
263 }
264
265 @Override
266 public void setWrappedData(Object obj)
267 {
268 if (obj == null)
269 {
270 return; // Clearing is allowed
271 }
272 throw new UnsupportedOperationException(this.getClass().getName() + " UnsupportedOperationException");
273 }
274 };
275
276 private class EditableValueHolderState
277 {
278 private final Object _value;
279 private final boolean _localValueSet;
280 private final boolean _valid;
281 private final Object _submittedValue;
282
283 public EditableValueHolderState(EditableValueHolder evh)
284 {
285 _value = evh.getLocalValue();
286 _localValueSet = evh.isLocalValueSet();
287 _valid = evh.isValid();
288 _submittedValue = evh.getSubmittedValue();
289 }
290
291 public void restoreState(EditableValueHolder evh)
292 {
293 evh.setValue(_value);
294 evh.setLocalValueSet(_localValueSet);
295 evh.setValid(_valid);
296 evh.setSubmittedValue(_submittedValue);
297 }
298 }
299
300 /**
301 * Construct an instance of the UIData.
302 */
303 public UIData()
304 {
305 setRendererType("javax.faces.Table");
306 }
307
308 @Override
309 public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
310 throws FacesException
311 {
312 if (context == null || clientId == null || callback == null)
313 {
314 throw new NullPointerException();
315 }
316
317 final String baseClientId = getClientId(context);
318
319 // searching for this component?
320 boolean returnValue = baseClientId.equals(clientId);
321
322 boolean isCachedFacesContext = isCachedFacesContext();
323 if (!isCachedFacesContext)
324 {
325 setCachedFacesContext(context);
326 }
327
328 pushComponentToEL(context, this);
329 try
330 {
331 if (returnValue)
332 {
333 try
334 {
335 callback.invokeContextCallback(context, this);
336 return true;
337 }
338 catch (Exception e)
339 {
340 throw new FacesException(e);
341 }
342 }
343
344 // Now Look throught facets on this UIComponent
345 if (this.getFacetCount() > 0)
346 {
347 for (Iterator<UIComponent> it = this.getFacets().values().iterator(); !returnValue && it.hasNext();)
348 {
349 returnValue = it.next().invokeOnComponent(context, clientId, callback);
350 }
351 }
352
353 if (returnValue)
354 {
355 return returnValue;
356 }
357
358 // is the component an inner component?
359 if (clientId.startsWith(baseClientId))
360 {
361 // Check if the clientId for the component, which we
362 // are looking for, has a rowIndex attached
363 char separator = UINamingContainer.getSeparatorChar(context);
364 String subId = clientId.substring(baseClientId.length() + 1);
365 //If the char next to baseClientId is the separator one and
366 //the subId matches the regular expression
367 if (clientId.charAt(baseClientId.length()) == separator &&
368 subId.matches("[0-9]+"+separator+".*"))
369 {
370 String clientRow = subId.substring(0, subId.indexOf(separator));
371
372 //Now we save the current position
373 int oldRow = this.getRowIndex();
374
375 // try-finally --> make sure, that the old row index is restored
376 try
377 {
378 //The conversion is safe, because its already checked on the
379 //regular expresion
380 this.setRowIndex(Integer.parseInt(clientRow));
381
382 // check, if the row is available
383 if (!isRowAvailable())
384 {
385 return false;
386 }
387
388 for (Iterator<UIComponent> it1 = getChildren().iterator();
389 !returnValue && it1.hasNext();)
390 {
391 //recursive call to find the component
392 returnValue = it1.next().invokeOnComponent(context, clientId, callback);
393 }
394 }
395 finally
396 {
397 //Restore the old position. Doing this prevent
398 //side effects.
399 this.setRowIndex(oldRow);
400 }
401 }
402 else
403 {
404 // MYFACES-2370: search the component in the childrens' facets too.
405 // We have to check the childrens' facets here, because in MyFaces
406 // the rowIndex is not attached to the clientId for the children of
407 // facets of the UIColumns. However, in RI the rowIndex is
408 // attached to the clientId of UIColumns' Facets' children.
409 for (Iterator<UIComponent> itChildren = this.getChildren().iterator();
410 !returnValue && itChildren.hasNext();)
411 {
412 UIComponent child = itChildren.next();
413 if (child instanceof UIColumn && clientId.equals(child.getClientId(context)))
414 {
415 try
416 {
417 callback.invokeContextCallback(context, child);
418 }
419 catch (Exception e)
420 {
421 throw new FacesException(e);
422 }
423 returnValue = true;
424 }
425 // process the child's facets
426 for (Iterator<UIComponent> itChildFacets = child.getFacets().values().iterator();
427 !returnValue && itChildFacets.hasNext();)
428 {
429 //recursive call to find the component
430 returnValue = itChildFacets.next().invokeOnComponent(context, clientId, callback);
431 }
432 }
433 }
434 }
435 }
436 finally
437 {
438 //all components must call popComponentFromEl after visiting is finished
439 popComponentFromEL(context);
440 if (!isCachedFacesContext)
441 {
442 setCachedFacesContext(null);
443 }
444 }
445
446 return returnValue;
447 }
448
449 public void setFooter(UIComponent footer)
450 {
451 getFacets().put(FOOTER_FACET_NAME, footer);
452 }
453
454 @JSFFacet
455 public UIComponent getFooter()
456 {
457 return getFacets().get(FOOTER_FACET_NAME);
458 }
459
460 public void setHeader(UIComponent header)
461 {
462 getFacets().put(HEADER_FACET_NAME, header);
463 }
464
465 @JSFFacet
466 public UIComponent getHeader()
467 {
468 return getFacets().get(HEADER_FACET_NAME);
469 }
470
471 public boolean isRowAvailable()
472 {
473 return getDataModel().isRowAvailable();
474 }
475
476 public int getRowCount()
477 {
478 return getDataModel().getRowCount();
479 }
480
481 public Object getRowData()
482 {
483 return getDataModel().getRowData();
484 }
485
486 public int getRowIndex()
487 {
488 return _rowIndex;
489 }
490
491 /**
492 * Set the current row index that methods like getRowData use.
493 * <p>
494 * Param rowIndex can be -1, meaning "no row".
495 * <p>
496 *
497 * @param rowIndex
498 */
499 public void setRowIndex(int rowIndex)
500 {
501 if (isRowStatePreserved())
502 {
503 setRowIndexPreserveComponentState(rowIndex);
504 }
505 else
506 {
507 setRowIndexWithoutPreserveComponentState(rowIndex);
508 }
509 }
510
511 private void setRowIndexWithoutPreserveComponentState(int rowIndex)
512 {
513 if (rowIndex < -1)
514 {
515 throw new IllegalArgumentException("rowIndex is less than -1");
516 }
517
518 if (_rowIndex == rowIndex)
519 {
520 return;
521 }
522
523 FacesContext facesContext = getFacesContext();
524
525 if (_rowIndex == -1)
526 {
527 if (_initialDescendantComponentState == null)
528 {
529 // Create a template that can be used to initialise any row
530 // that we haven't visited before, ie a "saved state" that can
531 // be pushed to the "restoreState" method of all the child
532 // components to set them up to represent a clean row.
533 _initialDescendantComponentState = saveDescendantComponentStates(this, false, false);
534 }
535 }
536 else
537 {
538 // If no initial component state, there are no EditableValueHolder instances,
539 // and that means there is no state to be saved for the current row, so we can
540 // skip row state saving code safely.
541 if (_initialDescendantComponentState != null)
542 {
543 // We are currently positioned on some row, and are about to
544 // move off it, so save the (partial) state of the components
545 // representing the current row. Later if this row is revisited
546 // then we can restore this state.
547 Collection<Object[]> savedRowState = saveDescendantComponentStates(this, false, false);
548 if (savedRowState != null)
549 {
550 _rowStates.put(getContainerClientId(facesContext), savedRowState);
551 }
552 }
553 }
554
555 _rowIndex = rowIndex;
556
557 DataModel dataModel = getDataModel();
558 dataModel.setRowIndex(rowIndex);
559
560 String var = (String) getStateHelper().get(PropertyKeys.var);
561 if (rowIndex == -1)
562 {
563 if (var != null)
564 {
565 facesContext.getExternalContext().getRequestMap().remove(var);
566 }
567 }
568 else
569 {
570 if (var != null)
571 {
572 if (isRowAvailable())
573 {
574 Object rowData = dataModel.getRowData();
575 facesContext.getExternalContext().getRequestMap().put(var, rowData);
576 }
577 else
578 {
579 facesContext.getExternalContext().getRequestMap().remove(var);
580 }
581 }
582 }
583
584 if (_rowIndex == -1)
585 {
586 // reset components to initial state
587 // If no initial state, skip row restore state code
588 if (_initialDescendantComponentState != null)
589 {
590 restoreDescendantComponentStates(this, false, _initialDescendantComponentState, false);
591 }
592 else
593 {
594 restoreDescendantComponentWithoutRestoreState(this, false, false);
595 }
596 }
597 else
598 {
599 Object rowState = _rowStates.get(getContainerClientId(facesContext));
600 if (rowState == null)
601 {
602 // We haven't been positioned on this row before, so just
603 // configure the child components of this component with
604 // the standard "initial" state
605 // If no initial state, skip row restore state code
606 if (_initialDescendantComponentState != null)
607 {
608 restoreDescendantComponentStates(this, false, _initialDescendantComponentState, false);
609 }
610 else
611 {
612 restoreDescendantComponentWithoutRestoreState(this, false, false);
613 }
614 }
615 else
616 {
617 // We have been positioned on this row before, so configure
618 // the child components of this component with the (partial)
619 // state that was previously saved. Fields not in the
620 // partial saved state are left with their original values.
621 restoreDescendantComponentStates(this, false, rowState, false);
622 }
623 }
624 }
625
626 private void setRowIndexPreserveComponentState(int rowIndex)
627 {
628 if (rowIndex < -1)
629 {
630 throw new IllegalArgumentException("rowIndex is less than -1");
631 }
632
633 if (_rowIndex == rowIndex)
634 {
635 return;
636 }
637
638 FacesContext facesContext = getFacesContext();
639
640 if (_initialDescendantFullComponentState != null)
641 {
642 //Just save the row
643 Map<String, Object> sm = saveFullDescendantComponentStates(facesContext, null,
644 getChildren().iterator(), false);
645 if (sm != null && !sm.isEmpty())
646 {
647 _rowDeltaStates.put(getContainerClientId(facesContext), sm);
648 }
649 if (_rowIndex != -1)
650 {
651 _rowTransientStates.put(getContainerClientId(facesContext),
652 saveTransientDescendantComponentStates(facesContext, null, getChildren().iterator(), false));
653 }
654 }
655
656 _rowIndex = rowIndex;
657
658 DataModel dataModel = getDataModel();
659 dataModel.setRowIndex(rowIndex);
660
661 String var = (String) getStateHelper().get(PropertyKeys.var);
662 if (rowIndex == -1)
663 {
664 if (var != null)
665 {
666 facesContext.getExternalContext().getRequestMap().remove(var);
667 }
668 }
669 else
670 {
671 if (var != null)
672 {
673 if (isRowAvailable())
674 {
675 Object rowData = dataModel.getRowData();
676 facesContext.getExternalContext().getRequestMap().put(var, rowData);
677 }
678 else
679 {
680 facesContext.getExternalContext().getRequestMap().remove(var);
681 }
682 }
683 }
684
685 if (_initialDescendantFullComponentState != null)
686 {
687 Map<String, Object> rowState = _rowDeltaStates.get(getContainerClientId(facesContext));
688 if (rowState == null)
689 {
690 //Restore as original
691 restoreFullDescendantComponentStates(facesContext, getChildren().iterator(),
692 _initialDescendantFullComponentState, false);
693 }
694 else
695 {
696 //Restore first original and then delta
697 restoreFullDescendantComponentDeltaStates(facesContext, getChildren().iterator(),
698 rowState, _initialDescendantFullComponentState, false);
699 }
700 if (_rowIndex == -1)
701 {
702 restoreTransientDescendantComponentStates(facesContext, getChildren().iterator(), null, false);
703 }
704 else
705 {
706 rowState = _rowTransientStates.get(getContainerClientId(facesContext));
707 if (rowState == null)
708 {
709 restoreTransientDescendantComponentStates(facesContext, getChildren().iterator(), null, false);
710 }
711 else
712 {
713 restoreTransientDescendantComponentStates(facesContext, getChildren().iterator(), rowState, false);
714 }
715 }
716 }
717
718 }
719
720
721 /**
722 * Overwrite the state of the child components of this component with data previously saved by method
723 * saveDescendantComponentStates.
724 * <p>
725 * The saved state info only covers those fields that are expected to vary between rows of a table. Other fields are
726 * not modified.
727 */
728 @SuppressWarnings("unchecked")
729 private void restoreDescendantComponentStates(UIComponent parent, boolean iterateFacets, Object state,
730 boolean restoreChildFacets)
731 {
732 int descendantStateIndex = -1;
733 List<? extends Object[]> stateCollection = null;
734
735 if (iterateFacets && parent.getFacetCount() > 0)
736 {
737 Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
738
739 while (childIterator.hasNext())
740 {
741 UIComponent component = childIterator.next();
742
743 // reset the client id (see spec 3.1.6)
744 component.setId(component.getId());
745 if (!component.isTransient())
746 {
747 if (descendantStateIndex == -1)
748 {
749 stateCollection = ((List<? extends Object[]>) state);
750 descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
751 }
752
753 if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
754 {
755 Object[] object = stateCollection.get(descendantStateIndex);
756 if (object[0] != null && component instanceof EditableValueHolder)
757 {
758 ((EditableValueHolderState) object[0]).restoreState((EditableValueHolder) component);
759 }
760 // If there is descendant state to restore, call it recursively, otherwise
761 // it is safe to skip iteration.
762 if (object[1] != null)
763 {
764 restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
765 }
766 else
767 {
768 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
769 }
770 }
771 else
772 {
773 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
774 }
775 descendantStateIndex++;
776 }
777 }
778 }
779
780 if (parent.getChildCount() > 0)
781 {
782 for (int i = 0; i < parent.getChildCount(); i++)
783 {
784 UIComponent component = parent.getChildren().get(i);
785
786 // reset the client id (see spec 3.1.6)
787 component.setId(component.getId());
788 if (!component.isTransient())
789 {
790 if (descendantStateIndex == -1)
791 {
792 stateCollection = ((List<? extends Object[]>) state);
793 descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
794 }
795
796 if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
797 {
798 Object[] object = stateCollection.get(descendantStateIndex);
799 if (object[0] != null && component instanceof EditableValueHolder)
800 {
801 ((EditableValueHolderState) object[0]).restoreState((EditableValueHolder) component);
802 }
803 // If there is descendant state to restore, call it recursively, otherwise
804 // it is safe to skip iteration.
805 if (object[1] != null)
806 {
807 restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
808 }
809 else
810 {
811 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
812 }
813 }
814 else
815 {
816 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
817 }
818 descendantStateIndex++;
819 }
820 }
821 }
822 }
823
824 /**
825 * Just call component.setId(component.getId()) to reset all client ids and
826 * ensure they will be calculated for the current row, but do not waste time
827 * dealing with row state code.
828 *
829 * @param parent
830 * @param iterateFacets
831 * @param restoreChildFacets
832 */
833 private void restoreDescendantComponentWithoutRestoreState(UIComponent parent, boolean iterateFacets,
834 boolean restoreChildFacets)
835 {
836 if (iterateFacets && parent.getFacetCount() > 0)
837 {
838 Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
839
840 while (childIterator.hasNext())
841 {
842 UIComponent component = childIterator.next();
843
844 // reset the client id (see spec 3.1.6)
845 component.setId(component.getId());
846 if (!component.isTransient())
847 {
848 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
849 }
850 }
851 }
852
853 if (parent.getChildCount() > 0)
854 {
855 for (int i = 0; i < parent.getChildCount(); i++)
856 {
857 UIComponent component = parent.getChildren().get(i);
858
859 // reset the client id (see spec 3.1.6)
860 component.setId(component.getId());
861 if (!component.isTransient())
862 {
863 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
864 }
865 }
866 }
867 }
868
869 /**
870 * Walk the tree of child components of this UIData, saving the parts of their state that can vary between rows.
871 * <p>
872 * This is very similar to the process that occurs for normal components when the view is serialized. Transient
873 * components are skipped (no state is saved for them).
874 * <p>
875 * If there are no children then null is returned. If there are one or more children, and all children are transient
876 * then an empty collection is returned; this will happen whenever a table contains only read-only components.
877 * <p>
878 * Otherwise a collection is returned which contains an object for every non-transient child component; that object
879 * may itself contain a collection of the state of that child's child components.
880 */
881 private Collection<Object[]> saveDescendantComponentStates(UIComponent parent, boolean iterateFacets,
882 boolean saveChildFacets)
883 {
884 Collection<Object[]> childStates = null;
885 // Index to indicate how many components has been passed without state to save.
886 int childEmptyIndex = 0;
887 int totalChildCount = 0;
888
889 if (iterateFacets && parent.getFacetCount() > 0)
890 {
891 Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
892
893 while (childIterator.hasNext())
894 {
895 UIComponent child = childIterator.next();
896 if (!child.isTransient())
897 {
898 // Add an entry to the collection, being an array of two
899 // elements. The first element is the state of the children
900 // of this component; the second is the state of the current
901 // child itself.
902
903 if (child instanceof EditableValueHolder)
904 {
905 if (childStates == null)
906 {
907 childStates = new ArrayList<Object[]>(
908 parent.getFacetCount()
909 + parent.getChildCount()
910 - totalChildCount
911 + childEmptyIndex);
912 for (int ci = 0; ci < childEmptyIndex; ci++)
913 {
914 childStates.add(LEAF_NO_STATE);
915 }
916 }
917
918 childStates.add(child.getChildCount() > 0 ?
919 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
920 saveDescendantComponentStates(child, saveChildFacets, true)} :
921 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
922 null});
923 }
924 else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
925 {
926 Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
927
928 if (descendantSavedState == null)
929 {
930 if (childStates == null)
931 {
932 childEmptyIndex++;
933 }
934 else
935 {
936 childStates.add(LEAF_NO_STATE);
937 }
938 }
939 else
940 {
941 if (childStates == null)
942 {
943 childStates = new ArrayList<Object[]>(
944 parent.getFacetCount()
945 + parent.getChildCount()
946 - totalChildCount
947 + childEmptyIndex);
948 for (int ci = 0; ci < childEmptyIndex; ci++)
949 {
950 childStates.add(LEAF_NO_STATE);
951 }
952 }
953 childStates.add(new Object[]{null, descendantSavedState});
954 }
955 }
956 else
957 {
958 if (childStates == null)
959 {
960 childEmptyIndex++;
961 }
962 else
963 {
964 childStates.add(LEAF_NO_STATE);
965 }
966 }
967 }
968 totalChildCount++;
969 }
970 }
971
972 if (parent.getChildCount() > 0)
973 {
974 for (int i = 0; i < parent.getChildCount(); i++)
975 {
976 UIComponent child = parent.getChildren().get(i);
977 if (!child.isTransient())
978 {
979 // Add an entry to the collection, being an array of two
980 // elements. The first element is the state of the children
981 // of this component; the second is the state of the current
982 // child itself.
983
984 if (child instanceof EditableValueHolder)
985 {
986 if (childStates == null)
987 {
988 childStates = new ArrayList<Object[]>(
989 parent.getFacetCount()
990 + parent.getChildCount()
991 - totalChildCount
992 + childEmptyIndex);
993 for (int ci = 0; ci < childEmptyIndex; ci++)
994 {
995 childStates.add(LEAF_NO_STATE);
996 }
997 }
998
999 childStates.add(child.getChildCount() > 0 ?
1000 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
1001 saveDescendantComponentStates(child, saveChildFacets, true)} :
1002 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
1003 null});
1004 }
1005 else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
1006 {
1007 Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
1008
1009 if (descendantSavedState == null)
1010 {
1011 if (childStates == null)
1012 {
1013 childEmptyIndex++;
1014 }
1015 else
1016 {
1017 childStates.add(LEAF_NO_STATE);
1018 }
1019 }
1020 else
1021 {
1022 if (childStates == null)
1023 {
1024 childStates = new ArrayList<Object[]>(
1025 parent.getFacetCount()
1026 + parent.getChildCount()
1027 - totalChildCount
1028 + childEmptyIndex);
1029 for (int ci = 0; ci < childEmptyIndex; ci++)
1030 {
1031 childStates.add(LEAF_NO_STATE);
1032 }
1033 }
1034 childStates.add(new Object[]{null, descendantSavedState});
1035 }
1036 }
1037 else
1038 {
1039 if (childStates == null)
1040 {
1041 childEmptyIndex++;
1042 }
1043 else
1044 {
1045 childStates.add(LEAF_NO_STATE);
1046 }
1047 }
1048 }
1049 totalChildCount++;
1050 }
1051 }
1052
1053 return childStates;
1054 }
1055
1056
1057
1058 @Override
1059 public void markInitialState()
1060 {
1061 if (isRowStatePreserved())
1062 {
1063 if (getFacesContext().getAttributes().containsKey(StateManager.IS_BUILDING_INITIAL_STATE))
1064 {
1065 _initialDescendantFullComponentState
1066 = saveDescendantInitialComponentStates(getFacesContext(), getChildren().iterator(), false);
1067 }
1068 }
1069 super.markInitialState();
1070 }
1071
1072 private void restoreFullDescendantComponentStates(FacesContext facesContext,
1073 Iterator<UIComponent> childIterator, Object initialState,
1074 boolean restoreChildFacets)
1075 {
1076 Iterator<? extends Object[]> descendantStateIterator = null;
1077 while (childIterator.hasNext())
1078 {
1079 if (descendantStateIterator == null && initialState != null)
1080 {
1081 descendantStateIterator = ((Collection<? extends Object[]>) initialState)
1082 .iterator();
1083 }
1084 UIComponent component = childIterator.next();
1085
1086 // reset the client id (see spec 3.1.6)
1087 component.setId(component.getId());
1088 if (!component.isTransient())
1089 {
1090 Object childState = null;
1091 Object descendantState = null;
1092 String childId = null;
1093 if (descendantStateIterator != null
1094 && descendantStateIterator.hasNext())
1095 {
1096 do
1097 {
1098 Object[] object = descendantStateIterator.next();
1099 childState = object[0];
1100 descendantState = object[1];
1101 childId = (String) object[2];
1102 }
1103 while(descendantStateIterator.hasNext() && !component.getId().equals(childId));
1104
1105 if (!component.getId().equals(childId))
1106 {
1107 // cannot apply initial state to components correctly.
1108 throw new IllegalStateException("Cannot restore row correctly.");
1109 }
1110 }
1111
1112 component.clearInitialState();
1113 component.restoreState(facesContext, childState);
1114 component.markInitialState();
1115
1116 Iterator<UIComponent> childsIterator;
1117 if (restoreChildFacets)
1118 {
1119 childsIterator = component.getFacetsAndChildren();
1120 }
1121 else
1122 {
1123 childsIterator = component.getChildren().iterator();
1124 }
1125 restoreFullDescendantComponentStates(facesContext, childsIterator,
1126 descendantState, true);
1127 }
1128 }
1129 }
1130
1131 private Collection<Object[]> saveDescendantInitialComponentStates(FacesContext facesContext,
1132 Iterator<UIComponent> childIterator, boolean saveChildFacets)
1133 {
1134 Collection<Object[]> childStates = null;
1135 while (childIterator.hasNext())
1136 {
1137 if (childStates == null)
1138 {
1139 childStates = new ArrayList<Object[]>();
1140 }
1141
1142 UIComponent child = childIterator.next();
1143 if (!child.isTransient())
1144 {
1145 // Add an entry to the collection, being an array of two
1146 // elements. The first element is the state of the children
1147 // of this component; the second is the state of the current
1148 // child itself.
1149
1150 Iterator<UIComponent> childsIterator;
1151 if (saveChildFacets)
1152 {
1153 childsIterator = child.getFacetsAndChildren();
1154 }
1155 else
1156 {
1157 childsIterator = child.getChildren().iterator();
1158 }
1159 Object descendantState = saveDescendantInitialComponentStates(
1160 facesContext, childsIterator, true);
1161 Object state = null;
1162 if (child.initialStateMarked())
1163 {
1164 child.clearInitialState();
1165 state = child.saveState(facesContext);
1166 child.markInitialState();
1167 }
1168 else
1169 {
1170 state = child.saveState(facesContext);
1171 }
1172
1173 childStates.add(new Object[] { state, descendantState, child.getId()});
1174 }
1175 }
1176 return childStates;
1177 }
1178
1179 private Map<String,Object> saveFullDescendantComponentStates(FacesContext facesContext, Map<String,Object> stateMap,
1180 Iterator<UIComponent> childIterator, boolean saveChildFacets)
1181 {
1182 while (childIterator.hasNext())
1183 {
1184 UIComponent child = childIterator.next();
1185 if (!child.isTransient())
1186 {
1187 // Add an entry to the collection, being an array of two
1188 // elements. The first element is the state of the children
1189 // of this component; the second is the state of the current
1190 // child itself.
1191
1192 Iterator<UIComponent> childsIterator;
1193 if (saveChildFacets)
1194 {
1195 childsIterator = child.getFacetsAndChildren();
1196 }
1197 else
1198 {
1199 childsIterator = child.getChildren().iterator();
1200 }
1201 stateMap = saveFullDescendantComponentStates(facesContext, stateMap,
1202 childsIterator, true);
1203 Object state = child.saveState(facesContext);
1204 if (state != null)
1205 {
1206 if (stateMap == null)
1207 {
1208 stateMap = new HashMap<String,Object>();
1209 }
1210 stateMap.put(child.getClientId(facesContext), state);
1211 }
1212 }
1213 }
1214 return stateMap;
1215 }
1216
1217 private void restoreFullDescendantComponentDeltaStates(FacesContext facesContext,
1218 Iterator<UIComponent> childIterator, Map<String, Object> state, Object initialState,
1219 boolean restoreChildFacets)
1220 {
1221 Iterator<? extends Object[]> descendantFullStateIterator = null;
1222 while (childIterator.hasNext())
1223 {
1224 if (descendantFullStateIterator == null && initialState != null)
1225 {
1226 descendantFullStateIterator = ((Collection<? extends Object[]>) initialState).iterator();
1227 }
1228 UIComponent component = childIterator.next();
1229
1230 // reset the client id (see spec 3.1.6)
1231 component.setId(component.getId());
1232 if (!component.isTransient())
1233 {
1234 Object childInitialState = null;
1235 Object descendantInitialState = null;
1236 Object childState = null;
1237 String childId = null;
1238 childState = (state == null) ? null : state.get(component.getClientId(facesContext));
1239 if (descendantFullStateIterator != null
1240 && descendantFullStateIterator.hasNext())
1241 {
1242 do
1243 {
1244 Object[] object = descendantFullStateIterator.next();
1245 childInitialState = object[0];
1246 descendantInitialState = object[1];
1247 childId = (String) object[2];
1248 }while(descendantFullStateIterator.hasNext() && !component.getId().equals(childId));
1249
1250 if (!component.getId().equals(childId))
1251 {
1252 // cannot apply initial state to components correctly. State is corrupt
1253 throw new IllegalStateException("Cannot restore row correctly.");
1254 }
1255 }
1256
1257 component.clearInitialState();
1258 if (childInitialState != null)
1259 {
1260 component.restoreState(facesContext, childInitialState);
1261 component.markInitialState();
1262 component.restoreState(facesContext, childState);
1263 }
1264 else
1265 {
1266 component.restoreState(facesContext, childState);
1267 component.markInitialState();
1268 }
1269
1270 Iterator<UIComponent> childsIterator;
1271 if (restoreChildFacets)
1272 {
1273 childsIterator = component.getFacetsAndChildren();
1274 }
1275 else
1276 {
1277 childsIterator = component.getChildren().iterator();
1278 }
1279 restoreFullDescendantComponentDeltaStates(facesContext, childsIterator,
1280 state, descendantInitialState , true);
1281 }
1282 }
1283 }
1284
1285 /**
1286 * Overwrite the state of the child components of this component with data previously saved by method
1287 * saveDescendantComponentStates.
1288 * <p>
1289 * The saved state info only covers those fields that are expected to vary between rows of a table. Other fields are
1290 * not modified.
1291 */
1292 @SuppressWarnings("unchecked")
1293 /*
1294 private void restoreTransientDescendantComponentStates(FacesContext facesContext,
1295 Iterator<UIComponent> childIterator, Object state,
1296 boolean restoreChildFacets)
1297 {
1298 Iterator<? extends Object[]> descendantStateIterator = null;
1299 while (childIterator.hasNext())
1300 {
1301 if (descendantStateIterator == null && state != null)
1302 {
1303 descendantStateIterator = ((Collection<? extends Object[]>) state).iterator();
1304 }
1305 UIComponent component = childIterator.next();
1306
1307 // reset the client id (see spec 3.1.6)
1308 component.setId(component.getId());
1309 if (!component.isTransient())
1310 {
1311 Object childState = null;
1312 Object descendantState = null;
1313 if (descendantStateIterator != null && descendantStateIterator.hasNext())
1314 {
1315 Object[] object = descendantStateIterator.next();
1316 childState = object[0];
1317 descendantState = object[1];
1318 }
1319 component.restoreTransientState(facesContext, childState);
1320 Iterator<UIComponent> childsIterator;
1321 if (restoreChildFacets)
1322 {
1323 childsIterator = component.getFacetsAndChildren();
1324 }
1325 else
1326 {
1327 childsIterator = component.getChildren().iterator();
1328 }
1329 restoreTransientDescendantComponentStates(facesContext, childsIterator, descendantState, true);
1330 }
1331 }
1332 }*/
1333
1334 private void restoreTransientDescendantComponentStates(FacesContext facesContext,
1335 Iterator<UIComponent> childIterator,
1336 Map<String, Object> state,
1337 boolean restoreChildFacets)
1338 {
1339 while (childIterator.hasNext())
1340 {
1341 UIComponent component = childIterator.next();
1342
1343 // reset the client id (see spec 3.1.6)
1344 component.setId(component.getId());
1345 if (!component.isTransient())
1346 {
1347 component.restoreTransientState(facesContext,
1348 (state == null)
1349 ? null
1350 : state.get(component.getClientId(facesContext)));
1351
1352 Iterator<UIComponent> childsIterator;
1353 if (restoreChildFacets)
1354 {
1355 childsIterator = component.getFacetsAndChildren();
1356 }
1357 else
1358 {
1359 childsIterator = component.getChildren().iterator();
1360 }
1361 restoreTransientDescendantComponentStates(facesContext, childsIterator, state, true);
1362 }
1363 }
1364
1365 }
1366
1367 /**
1368 * Walk the tree of child components of this UIData, saving the parts of their state that can vary between rows.
1369 * <p>
1370 * This is very similar to the process that occurs for normal components when the view is serialized. Transient
1371 * components are skipped (no state is saved for them).
1372 * <p>
1373 * If there are no children then null is returned. If there are one or more children, and all children are transient
1374 * then an empty collection is returned; this will happen whenever a table contains only read-only components.
1375 * <p>
1376 * Otherwise a collection is returned which contains an object for every non-transient child component; that object
1377 * may itself contain a collection of the state of that child's child components.
1378 */
1379 /*
1380 private Collection<Object[]> saveTransientDescendantComponentStates(FacesContext facesContext,
1381 Iterator<UIComponent> childIterator,
1382 boolean saveChildFacets)
1383 {
1384 Collection<Object[]> childStates = null;
1385 while (childIterator.hasNext())
1386 {
1387 if (childStates == null)
1388 {
1389 childStates = new ArrayList<Object[]>();
1390 }
1391
1392 UIComponent child = childIterator.next();
1393 if (!child.isTransient())
1394 {
1395 // Add an entry to the collection, being an array of two
1396 // elements. The first element is the state of the children
1397 // of this component; the second is the state of the current
1398 // child itself.
1399
1400 Iterator<UIComponent> childsIterator;
1401 if (saveChildFacets)
1402 {
1403 childsIterator = child.getFacetsAndChildren();
1404 }
1405 else
1406 {
1407 childsIterator = child.getChildren().iterator();
1408 }
1409 Object descendantState = saveTransientDescendantComponentStates(facesContext, childsIterator, true);
1410 Object state = null;
1411 state = child.saveTransientState(facesContext);
1412 childStates.add(new Object[] { state, descendantState });
1413 }
1414 }
1415 return childStates;
1416 }*/
1417
1418 private Map<String, Object> saveTransientDescendantComponentStates(FacesContext facesContext,
1419 Map<String, Object> childStates,
1420 Iterator<UIComponent> childIterator,
1421 boolean saveChildFacets)
1422 {
1423 while (childIterator.hasNext())
1424 {
1425 UIComponent child = childIterator.next();
1426 if (!child.isTransient())
1427 {
1428 Iterator<UIComponent> childsIterator;
1429 if (saveChildFacets)
1430 {
1431 childsIterator = child.getFacetsAndChildren();
1432 }
1433 else
1434 {
1435 childsIterator = child.getChildren().iterator();
1436 }
1437 childStates = saveTransientDescendantComponentStates(facesContext, childStates, childsIterator, true);
1438 Object state = child.saveTransientState(facesContext);
1439 if (state != null)
1440 {
1441 if (childStates == null)
1442 {
1443 childStates = new HashMap<String, Object>();
1444 }
1445 childStates.put(child.getClientId(facesContext), state);
1446 }
1447 }
1448 }
1449 return childStates;
1450 }
1451
1452 @Override
1453 public void restoreState(FacesContext context, Object state)
1454 {
1455 if (state == null)
1456 {
1457 return;
1458 }
1459
1460 Object values[] = (Object[]) state;
1461 super.restoreState(context, values[0]);
1462 Object restoredRowStates = UIComponentBase.restoreAttachedState(context, values[1]);
1463 if (restoredRowStates == null)
1464 {
1465 if (!_rowDeltaStates.isEmpty())
1466 {
1467 _rowDeltaStates.clear();
1468 }
1469 }
1470 else
1471 {
1472 _rowDeltaStates = (Map<String, Map<String, Object> >) restoredRowStates;
1473 }
1474 }
1475
1476 @Override
1477 public Object saveState(FacesContext context)
1478 {
1479 if (initialStateMarked())
1480 {
1481 Object parentSaved = super.saveState(context);
1482 if (parentSaved == null &&_rowDeltaStates.isEmpty())
1483 {
1484 return null;
1485 }
1486 else
1487 {
1488 Object values[] = new Object[2];
1489 values[0] = super.saveState(context);
1490 values[1] = UIComponentBase.saveAttachedState(context, _rowDeltaStates);
1491 return values;
1492 }
1493 }
1494 else
1495 {
1496 Object values[] = new Object[2];
1497 values[0] = super.saveState(context);
1498 values[1] = UIComponentBase.saveAttachedState(context, _rowDeltaStates);
1499 return values;
1500 }
1501 }
1502
1503 @Override
1504 public void setValueExpression(String name, ValueExpression binding)
1505 {
1506 if (name == null)
1507 {
1508 throw new NullPointerException("name");
1509 }
1510 else if (name.equals("value"))
1511 {
1512 _dataModelMap.clear();
1513 }
1514 else if (name.equals("rowIndex"))
1515 {
1516 throw new IllegalArgumentException("name " + name);
1517 }
1518 super.setValueExpression(name, binding);
1519 }
1520
1521 /*
1522 @Override
1523 public String getClientId(FacesContext context)
1524 {
1525 String clientId = super.getClientId(context);
1526 int rowIndex = getRowIndex();
1527 if (rowIndex == -1)
1528 {
1529 return clientId;
1530 }
1531
1532 StringBuilder bld = _getSharedStringBuilder();
1533 return bld.append(clientId).append(UINamingContainer.getSeparatorChar(context)).append(rowIndex).toString();
1534 }*/
1535
1536 @Override
1537 public String getContainerClientId(FacesContext context)
1538 {
1539 //MYFACES-2744 UIData.getClientId() should not append rowIndex, instead use UIData.getContainerClientId()
1540 String clientId = super.getContainerClientId(context);
1541
1542 int rowIndex = getRowIndex();
1543 if (rowIndex == -1)
1544 {
1545 return clientId;
1546 }
1547
1548 StringBuilder bld = _getSharedStringBuilder(context);
1549 return bld.append(clientId).append(UINamingContainer.getSeparatorChar(context)).append(rowIndex).toString();
1550 }
1551
1552 /**
1553 * Modify events queued for any child components so that the UIData state will be correctly configured before the
1554 * event's listeners are executed.
1555 * <p>
1556 * Child components or their renderers may register events against those child components. When the listener for
1557 * that event is eventually invoked, it may expect the uidata's rowData and rowIndex to be referring to the same
1558 * object that caused the event to fire.
1559 * <p>
1560 * The original queueEvent call against the child component has been forwarded up the chain of ancestors in the
1561 * standard way, making it possible here to wrap the event in a new event whose source is <i>this</i> component, not
1562 * the original one. When the event finally is executed, this component's broadcast method is invoked, which ensures
1563 * that the UIData is set to be at the correct row before executing the original event.
1564 */
1565 @Override
1566 public void queueEvent(FacesEvent event)
1567 {
1568 super.queueEvent(new FacesEventWrapper(event, getRowIndex(), this));
1569 }
1570
1571 /**
1572 * Ensure that before the event's listeners are invoked this UIData component's "current row" is set to the row
1573 * associated with the event.
1574 * <p>
1575 * See queueEvent for more details.
1576 */
1577 @Override
1578 public void broadcast(FacesEvent event) throws AbortProcessingException
1579 {
1580 if (event instanceof FacesEventWrapper)
1581 {
1582 FacesEvent originalEvent = ((FacesEventWrapper) event).getWrappedFacesEvent();
1583 int eventRowIndex = ((FacesEventWrapper) event).getRowIndex();
1584 final int currentRowIndex = getRowIndex();
1585 UIComponent source = originalEvent.getComponent();
1586 UIComponent compositeParent = UIComponent.getCompositeComponentParent(source);
1587
1588 setRowIndex(eventRowIndex);
1589 if (compositeParent != null)
1590 {
1591 pushComponentToEL(getFacesContext(), compositeParent);
1592 }
1593 pushComponentToEL(getFacesContext(), source);
1594 try
1595 {
1596 source.broadcast(originalEvent);
1597 }
1598 finally
1599 {
1600 source.popComponentFromEL(getFacesContext());
1601 if (compositeParent != null)
1602 {
1603 compositeParent.popComponentFromEL(getFacesContext());
1604 }
1605 setRowIndex(currentRowIndex);
1606 }
1607 }
1608 else
1609 {
1610 super.broadcast(event);
1611 }
1612 }
1613
1614 /**
1615 *
1616 * {@inheritDoc}
1617 *
1618 * @since 2.0
1619 */
1620 public String createUniqueId(FacesContext context, String seed)
1621 {
1622 StringBuilder bld = _getSharedStringBuilder(context);
1623
1624 // Generate an identifier for a component. The identifier will be prefixed with UNIQUE_ID_PREFIX,
1625 // and will be unique within this UIViewRoot.
1626 if(seed==null)
1627 {
1628 Long uniqueIdCounter = (Long) getStateHelper().get(PropertyKeys.uniqueIdCounter);
1629 uniqueIdCounter = (uniqueIdCounter == null) ? 0 : uniqueIdCounter;
1630 getStateHelper().put(PropertyKeys.uniqueIdCounter, (uniqueIdCounter+1L));
1631 return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(uniqueIdCounter).toString();
1632 }
1633 // Optionally, a unique seed value can be supplied by component creators
1634 // which should be included in the generated unique id.
1635 else
1636 {
1637 return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(seed).toString();
1638 }
1639 }
1640
1641 /**
1642 * Perform necessary actions when rendering of this component starts, before delegating to the inherited
1643 * implementation which calls the associated renderer's encodeBegin method.
1644 */
1645 @Override
1646 public void encodeBegin(FacesContext context) throws IOException
1647 {
1648 _initialDescendantComponentState = null;
1649 if (_isValidChilds && !hasErrorMessages(context))
1650 {
1651 // Clear the data model so that when rendering code calls
1652 // getDataModel a fresh model is fetched from the backing
1653 // bean via the value-binding.
1654 _dataModelMap.clear();
1655
1656 // When the data model is cleared it is also necessary to
1657 // clear the saved row state, as there is an implicit 1:1
1658 // relation between objects in the _rowStates and the
1659 // corresponding DataModel element.
1660 if (!isRowStatePreserved())
1661 {
1662 _rowStates.clear();
1663 }
1664 }
1665 super.encodeBegin(context);
1666 }
1667
1668 private boolean hasErrorMessages(FacesContext context)
1669 {
1670 // perf: getMessageList() return a RandomAccess instance.
1671 // See org.apache.myfaces.context.servlet.FacesContextImpl.addMessage
1672 List<FacesMessage> messageList = context.getMessageList();
1673 for (int i = 0, size = messageList.size(); i < size; i++)
1674 {
1675 FacesMessage message = messageList.get(i);
1676 if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
1677 {
1678 return true;
1679 }
1680 }
1681 return false;
1682 }
1683
1684 /**
1685 * @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
1686 */
1687 @Override
1688 public void encodeEnd(FacesContext context) throws IOException
1689 {
1690 try
1691 {
1692 setCachedFacesContext(context);
1693 setRowIndex(-1);
1694 }
1695 finally
1696 {
1697 setCachedFacesContext(null);
1698 }
1699 super.encodeEnd(context);
1700 }
1701
1702 @Override
1703 public void processDecodes(FacesContext context)
1704 {
1705 if (context == null)
1706 {
1707 throw new NullPointerException("context");
1708 }
1709 try
1710 {
1711 setCachedFacesContext(context);
1712 pushComponentToEL(context, this);
1713 if (!isRendered())
1714 {
1715 return;
1716 }
1717 setRowIndex(-1);
1718 processFacets(context, PROCESS_DECODES);
1719 processColumnFacets(context, PROCESS_DECODES);
1720 processColumnChildren(context, PROCESS_DECODES);
1721 setRowIndex(-1);
1722 try
1723 {
1724 decode(context);
1725 }
1726 catch (RuntimeException e)
1727 {
1728 context.renderResponse();
1729 throw e;
1730 }
1731 }
1732 finally
1733 {
1734 popComponentFromEL(context);
1735 setCachedFacesContext(null);
1736 }
1737 }
1738
1739 @Override
1740 public void processValidators(FacesContext context)
1741 {
1742 if (context == null)
1743 {
1744 throw new NullPointerException("context");
1745 }
1746
1747 try
1748 {
1749 setCachedFacesContext(context);
1750 pushComponentToEL(context, this);
1751 if (!isRendered())
1752 {
1753 return;
1754 }
1755
1756 //Pre validation event dispatch for component
1757 context.getApplication().publishEvent(context, PreValidateEvent.class, getClass(), this);
1758
1759 try
1760 {
1761 setRowIndex(-1);
1762 processFacets(context, PROCESS_VALIDATORS);
1763 processColumnFacets(context, PROCESS_VALIDATORS);
1764 processColumnChildren(context, PROCESS_VALIDATORS);
1765 setRowIndex(-1);
1766 }
1767 finally
1768 {
1769 context.getApplication().publishEvent(context, PostValidateEvent.class, getClass(), this);
1770 }
1771
1772 // check if an validation error forces the render response for our data
1773 if (context.getRenderResponse())
1774 {
1775 _isValidChilds = false;
1776 }
1777 }
1778 finally
1779 {
1780 popComponentFromEL(context);
1781 setCachedFacesContext(null);
1782 }
1783 }
1784
1785 @Override
1786 public void processUpdates(FacesContext context)
1787 {
1788 if (context == null)
1789 {
1790 throw new NullPointerException("context");
1791 }
1792 try
1793 {
1794 setCachedFacesContext(context);
1795 pushComponentToEL(context, this);
1796 if (!isRendered())
1797 {
1798 return;
1799 }
1800 setRowIndex(-1);
1801 processFacets(context, PROCESS_UPDATES);
1802 processColumnFacets(context, PROCESS_UPDATES);
1803 processColumnChildren(context, PROCESS_UPDATES);
1804 setRowIndex(-1);
1805
1806 if (context.getRenderResponse())
1807 {
1808 _isValidChilds = false;
1809 }
1810 }
1811 finally
1812 {
1813 popComponentFromEL(context);
1814 setCachedFacesContext(null);
1815 }
1816 }
1817
1818 private void processFacets(FacesContext context, int processAction)
1819 {
1820 if (this.getFacetCount() > 0)
1821 {
1822 for (UIComponent facet : getFacets().values())
1823 {
1824 process(context, facet, processAction);
1825 }
1826 }
1827 }
1828
1829 /**
1830 * Invoke the specified phase on all facets of all UIColumn children of this component. Note that no methods are
1831 * called on the UIColumn child objects themselves.
1832 *
1833 * @param context
1834 * is the current faces context.
1835 * @param processAction
1836 * specifies a JSF phase: decode, validate or update.
1837 */
1838 private void processColumnFacets(FacesContext context, int processAction)
1839 {
1840 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1841 {
1842 UIComponent child = getChildren().get(i);
1843 if (child instanceof UIColumn)
1844 {
1845 if (! _ComponentUtils.isRendered(context, child))
1846 {
1847 // Column is not visible
1848 continue;
1849 }
1850
1851 if (child.getFacetCount() > 0)
1852 {
1853 for (UIComponent facet : child.getFacets().values())
1854 {
1855 process(context, facet, processAction);
1856 }
1857 }
1858 }
1859 }
1860 }
1861
1862 /**
1863 * Invoke the specified phase on all non-facet children of all UIColumn children of this component. Note that no
1864 * methods are called on the UIColumn child objects themselves.
1865 *
1866 * @param context
1867 * is the current faces context.
1868 * @param processAction
1869 * specifies a JSF phase: decode, validate or update.
1870 */
1871 private void processColumnChildren(FacesContext context, int processAction)
1872 {
1873 int first = getFirst();
1874 int rows = getRows();
1875 int last;
1876 if (rows == 0)
1877 {
1878 last = getRowCount();
1879 }
1880 else
1881 {
1882 last = first + rows;
1883 }
1884 for (int rowIndex = first; last == -1 || rowIndex < last; rowIndex++)
1885 {
1886 setRowIndex(rowIndex);
1887
1888 // scrolled past the last row
1889 if (!isRowAvailable())
1890 {
1891 break;
1892 }
1893
1894 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1895 {
1896 UIComponent child = getChildren().get(i);
1897 if (child instanceof UIColumn)
1898 {
1899 if (! _ComponentUtils.isRendered(context, child))
1900 {
1901 // Column is not visible
1902 continue;
1903 }
1904 for (int j = 0, columnChildCount = child.getChildCount(); j < columnChildCount; j++)
1905 {
1906 UIComponent columnChild = child.getChildren().get(j);
1907 process(context, columnChild, processAction);
1908 }
1909 }
1910 }
1911 }
1912 }
1913
1914 private void process(FacesContext context, UIComponent component, int processAction)
1915 {
1916 switch (processAction)
1917 {
1918 case PROCESS_DECODES:
1919 component.processDecodes(context);
1920 break;
1921 case PROCESS_VALIDATORS:
1922 component.processValidators(context);
1923 break;
1924 case PROCESS_UPDATES:
1925 component.processUpdates(context);
1926 break;
1927 default:
1928 // do nothing
1929 }
1930 }
1931
1932 /**
1933 * Return the datamodel for this table, potentially fetching the data from a backing bean via a value-binding if
1934 * this is the first time this method has been called.
1935 * <p>
1936 * This is complicated by the fact that this table may be nested within another table. In this case a different
1937 * datamodel should be fetched for each row. When nested within a parent table, the parent reference won't change
1938 * but parent.getContainerClientId() will, as the suffix changes
1939 * depending upon the current row index. A map object on this
1940 * component is therefore used to cache the datamodel for each row of the table. In the normal case where this table
1941 * is not nested inside a component that changes its id (like a table does) then this map only ever has one entry.
1942 */
1943 protected DataModel getDataModel()
1944 {
1945 DataModel dataModel;
1946 String clientID = "";
1947
1948 UIComponent parent = getParent();
1949 if (parent != null)
1950 {
1951 clientID = parent.getContainerClientId(getFacesContext());
1952 }
1953 dataModel = _dataModelMap.get(clientID);
1954 if (dataModel == null)
1955 {
1956 dataModel = createDataModel();
1957 _dataModelMap.put(clientID, dataModel);
1958 }
1959 return dataModel;
1960 }
1961
1962 protected void setDataModel(DataModel dataModel)
1963 {
1964 throw new UnsupportedOperationException("this method is here only to maintain binary compatibility w/ the RI");
1965 }
1966
1967 /**
1968 * Evaluate this object's value property and convert the result into a DataModel. Normally this object's value
1969 * property will be a value-binding which will cause the value to be fetched from some backing bean.
1970 * <p>
1971 * The result of fetching the value may be a DataModel object, in which case that object is returned directly. If
1972 * the value is of type List, Array, ResultSet, Result, other object or null then an appropriate wrapper is created
1973 * and returned.
1974 * <p>
1975 * Null is never returned by this method.
1976 */
1977 private DataModel createDataModel()
1978 {
1979 Object value = getValue();
1980
1981 if (value == null)
1982 {
1983 return EMPTY_DATA_MODEL;
1984 }
1985 else if (value instanceof DataModel)
1986 {
1987 return (DataModel) value;
1988 }
1989 else if (value instanceof List)
1990 {
1991 return new ListDataModel((List<?>) value);
1992 }
1993 else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass()))
1994 {
1995 return new ArrayDataModel((Object[]) value);
1996 }
1997 else if (value instanceof ResultSet)
1998 {
1999 return new ResultSetDataModel((ResultSet) value);
2000 }
2001 else if (value instanceof Result)
2002 {
2003 return new ResultDataModel((Result) value);
2004 }
2005 else
2006 {
2007 return new ScalarDataModel(value);
2008 }
2009 }
2010
2011 /**
2012 * An EL expression that specifies the data model that backs this table.
2013 * <p>
2014 * The value referenced by the EL expression can be of any type.
2015 * </p>
2016 * <ul>
2017 * <li>A value of type DataModel is used directly.</li>
2018 * <li>Array-like parameters of type array-of-Object, java.util.List, java.sql.ResultSet or
2019 * javax.servlet.jsp.jstl.sql.Result are wrapped in a corresponding DataModel that knows how to iterate over the
2020 * elements.</li>
2021 * <li>Other values are wrapped in a DataModel as a single row.</li>
2022 * </ul>
2023 * <p>
2024 * Note in particular that unordered collections, eg Set are not supported. Therefore if the value expression
2025 * references such an object then the table will be considered to contain just one element - the collection itself.
2026 * </p>
2027 */
2028 @JSFProperty
2029 public Object getValue()
2030 {
2031 return getStateHelper().eval(PropertyKeys.value);
2032 }
2033
2034 public void setValue(Object value)
2035 {
2036 getStateHelper().put(PropertyKeys.value, value );
2037 _dataModelMap.clear();
2038 _rowStates.clear();
2039 _isValidChilds = true;
2040 }
2041
2042 /**
2043 * Defines the index of the first row to be displayed, starting from 0.
2044 */
2045 @JSFProperty
2046 public int getFirst()
2047 {
2048 return (Integer) getStateHelper().eval(PropertyKeys.first,0);
2049 }
2050
2051 public void setFirst(int first)
2052 {
2053 if (first < 0)
2054 {
2055 throw new IllegalArgumentException("Illegal value for first row: " + first);
2056 }
2057 getStateHelper().put(PropertyKeys.first, first );
2058 }
2059
2060 /**
2061 * Defines the maximum number of rows of data to be displayed.
2062 * <p>
2063 * Specify zero to display all rows from the "first" row to the end of available data.
2064 * </p>
2065 */
2066 @JSFProperty
2067 public int getRows()
2068 {
2069 return (Integer) getStateHelper().eval(PropertyKeys.rows,0);
2070 }
2071
2072 /**
2073 * Set the maximum number of rows displayed in the table.
2074 */
2075 public void setRows(int rows)
2076 {
2077 if (rows < 0)
2078 {
2079 throw new IllegalArgumentException("rows: " + rows);
2080 }
2081 getStateHelper().put(PropertyKeys.rows, rows );
2082 }
2083
2084 /**
2085 * Defines the name of the request-scope variable that will hold the current row during iteration.
2086 * <p>
2087 * During rendering of child components of this UIData, the variable with this name can be read to learn what the
2088 * "rowData" object for the row currently being rendered is.
2089 * </p>
2090 * <p>
2091 * This value must be a static value, ie an EL expression is not permitted.
2092 * </p>
2093 */
2094 @JSFProperty(literalOnly = true)
2095 public String getVar()
2096 {
2097 return (String) getStateHelper().get(PropertyKeys.var);
2098 }
2099
2100 /**
2101 * Overrides the behavior in
2102 * UIComponent.visitTree(javax.faces.component.visit.VisitContext, javax.faces.component.visit.VisitCallback)
2103 * to handle iteration correctly.
2104 *
2105 * @param context the visit context which handles the processing details
2106 * @param callback the callback to be performed
2107 * @return false if the processing is not done true if we can shortcut
2108 * the visiting because we are done with everything
2109 *
2110 * @since 2.0
2111 */
2112 @Override
2113 public boolean visitTree(VisitContext context, VisitCallback callback)
2114 {
2115 if (!isVisitable(context))
2116 {
2117 return false;
2118 }
2119
2120 boolean isCachedFacesContext = isCachedFacesContext();
2121 if (!isCachedFacesContext)
2122 {
2123 setCachedFacesContext(context.getFacesContext());
2124 }
2125 // save the current row index
2126 int oldRowIndex = getRowIndex();
2127 // set row index to -1 to process the facets and to get the rowless clientId
2128 setRowIndex(-1);
2129 // push the Component to EL
2130 pushComponentToEL(context.getFacesContext(), this);
2131 try
2132 {
2133 VisitResult visitResult = context.invokeVisitCallback(this,
2134 callback);
2135 switch (visitResult)
2136 {
2137 //we are done nothing has to be processed anymore
2138 case COMPLETE:
2139 return true;
2140
2141 case REJECT:
2142 return false;
2143
2144 //accept
2145 default:
2146 // determine if we need to visit our children
2147 Collection<String> subtreeIdsToVisit = context
2148 .getSubtreeIdsToVisit(this);
2149 boolean doVisitChildren = subtreeIdsToVisit != null
2150 && !subtreeIdsToVisit.isEmpty();
2151 if (doVisitChildren)
2152 {
2153 // visit the facets of the component
2154 if (getFacetCount() > 0)
2155 {
2156 for (UIComponent facet : getFacets().values())
2157 {
2158 if (facet.visitTree(context, callback))
2159 {
2160 return true;
2161 }
2162 }
2163 }
2164 //(Boolean) context.getFacesContext().getAttributes().get(SKIP_ITERATION_HINT);
2165 Boolean skipIterationHint = context.getHints().contains(VisitHint.SKIP_ITERATION);
2166 if (skipIterationHint != null && skipIterationHint.booleanValue())
2167 {
2168 // If SKIP_ITERATION is enabled, do not take into account rows.
2169 for (int i = 0, childCount = getChildCount(); i < childCount; i++ )
2170 {
2171 UIComponent child = getChildren().get(i);
2172 if (child.visitTree(context, callback))
2173 {
2174 return true;
2175 }
2176 }
2177 }
2178 else
2179 {
2180 // visit every column directly without visiting its children
2181 // (the children of every UIColumn will be visited later for
2182 // every row) and also visit the column's facets
2183 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
2184 {
2185 UIComponent child = getChildren().get(i);
2186 if (child instanceof UIColumn)
2187 {
2188 VisitResult columnResult = context.invokeVisitCallback(child, callback);
2189 if (columnResult == VisitResult.COMPLETE)
2190 {
2191 return true;
2192 }
2193 for (UIComponent facet : child.getFacets().values())
2194 {
2195 if (facet.visitTree(context, callback))
2196 {
2197 return true;
2198 }
2199 }
2200 }
2201 }
2202 // iterate over the rows
2203 int rowsToProcess = getRows();
2204 // if getRows() returns 0, all rows have to be processed
2205 if (rowsToProcess == 0)
2206 {
2207 rowsToProcess = getRowCount();
2208 }
2209 int rowIndex = getFirst();
2210 for (int rowsProcessed = 0; rowsProcessed < rowsToProcess; rowsProcessed++, rowIndex++)
2211 {
2212 setRowIndex(rowIndex);
2213 if (!isRowAvailable())
2214 {
2215 return false;
2216 }
2217 // visit the children of every child of the UIData that is an instance of UIColumn
2218 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
2219 {
2220 UIComponent child = getChildren().get(i);
2221 if (child instanceof UIColumn)
2222 {
2223 for (int j = 0, grandChildCount = child.getChildCount(); j < grandChildCount; j++)
2224 {
2225 UIComponent grandchild = child.getChildren().get(j);
2226 if (grandchild.visitTree(context, callback))
2227 {
2228 return true;
2229 }
2230 }
2231 }
2232 }
2233 }
2234 }
2235 }
2236 }
2237 }
2238 finally
2239 {
2240 // pop the component from EL and restore the old row index
2241 popComponentFromEL(context.getFacesContext());
2242 setRowIndex(oldRowIndex);
2243 if (!isCachedFacesContext)
2244 {
2245 setCachedFacesContext(null);
2246 }
2247 }
2248
2249 // Return false to allow the visiting to continue
2250 return false;
2251 }
2252
2253 public void setVar(String var)
2254 {
2255 getStateHelper().put(PropertyKeys.var, var );
2256 }
2257
2258 /**
2259 * Indicates whether the state for a component in each row should not be
2260 * discarded before the datatable is rendered again.
2261 *
2262 * This property is similar to tomahawk t:dataTable preserveRowStates
2263 *
2264 * This will only work reliable if the datamodel of the
2265 * datatable did not change either by sorting, removing or
2266 * adding rows. Default: false
2267 *
2268 * @return
2269 */
2270 @JSFProperty(literalOnly=true, faceletsOnly=true)
2271 public boolean isRowStatePreserved()
2272 {
2273 Boolean b = (Boolean) getStateHelper().get(PropertyKeys.rowStatePreserved);
2274 return b == null ? false : b.booleanValue();
2275 }
2276
2277 public void setRowStatePreserved(boolean preserveComponentState)
2278 {
2279 getStateHelper().put(PropertyKeys.rowStatePreserved, preserveComponentState);
2280 }
2281
2282 enum PropertyKeys
2283 {
2284 value
2285 , first
2286 , rows
2287 , var
2288 , uniqueIdCounter
2289 , rowStatePreserved
2290 }
2291
2292 @Override
2293 public String getFamily()
2294 {
2295 return COMPONENT_FAMILY;
2296 }
2297 }