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