View Javadoc

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