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.Collections;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  
31  import javax.el.ValueExpression;
32  import javax.faces.FacesException;
33  import javax.faces.application.FacesMessage;
34  import javax.faces.component.visit.VisitCallback;
35  import javax.faces.component.visit.VisitContext;
36  import javax.faces.component.visit.VisitResult;
37  import javax.faces.context.ExternalContext;
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: 1470767 $ $Date: 2013-04-23 01:07:29 +0000 (Tue, 23 Apr 2013) $
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, Collection<Object[]>> _rowStates = new HashMap<String, Collection<Object[]>>();
141 
142     /**
143      * Handle case where this table is nested inside another table. See method getDataModel for more details.
144      * <p>
145      * Key: parentClientId (aka rowId when nested within a parent table) Value: DataModel
146      */
147     private Map<String, DataModel> _dataModelMap = new HashMap<String, DataModel>();
148 
149     // will be set to false if the data should not be refreshed at the beginning of the encode phase
150     private boolean _isValidChilds = true;
151 
152     private Object _initialDescendantComponentState = null;
153 
154     //private int _first;
155     //private boolean _firstSet;
156     //private int _rows;
157     //private boolean _rowsSet;
158     //private Object _value;
159 
160     private static class FacesEventWrapper extends FacesEvent
161     {
162         private static final long serialVersionUID = 6648047974065628773L;
163         private FacesEvent _wrappedFacesEvent;
164         private int _rowIndex;
165 
166         public FacesEventWrapper(FacesEvent facesEvent, int rowIndex, UIData redirectComponent)
167         {
168             super(redirectComponent);
169             _wrappedFacesEvent = facesEvent;
170             _rowIndex = rowIndex;
171         }
172 
173         @Override
174         public PhaseId getPhaseId()
175         {
176             return _wrappedFacesEvent.getPhaseId();
177         }
178 
179         @Override
180         public void setPhaseId(PhaseId phaseId)
181         {
182             _wrappedFacesEvent.setPhaseId(phaseId);
183         }
184 
185         @Override
186         public void queue()
187         {
188             _wrappedFacesEvent.queue();
189         }
190 
191         @Override
192         public String toString()
193         {
194             return _wrappedFacesEvent.toString();
195         }
196 
197         @Override
198         public boolean isAppropriateListener(FacesListener faceslistener)
199         {
200             return _wrappedFacesEvent.isAppropriateListener(faceslistener);
201         }
202 
203         @Override
204         public void processListener(FacesListener faceslistener)
205         {
206             _wrappedFacesEvent.processListener(faceslistener);
207         }
208 
209         public FacesEvent getWrappedFacesEvent()
210         {
211             return _wrappedFacesEvent;
212         }
213 
214         public int getRowIndex()
215         {
216             return _rowIndex;
217         }
218     }
219 
220     private static final DataModel EMPTY_DATA_MODEL = new DataModel()
221     {
222         @Override
223         public boolean isRowAvailable()
224         {
225             return false;
226         }
227 
228         @Override
229         public int getRowCount()
230         {
231             return 0;
232         }
233 
234         @Override
235         public Object getRowData()
236         {
237             throw new IllegalArgumentException();
238         }
239 
240         @Override
241         public int getRowIndex()
242         {
243             return -1;
244         }
245 
246         @Override
247         public void setRowIndex(int i)
248         {
249             if (i < -1)
250             {
251                 throw new IllegalArgumentException();
252             }
253         }
254 
255         @Override
256         public Object getWrappedData()
257         {
258             return null;
259         }
260 
261         @Override
262         public void setWrappedData(Object obj)
263         {
264             if (obj == null)
265             {
266                 return; // Clearing is allowed
267             }
268             throw new UnsupportedOperationException(this.getClass().getName() + " UnsupportedOperationException");
269         }
270     };
271 
272     private class EditableValueHolderState
273     {
274         private final Object _value;
275         private final boolean _localValueSet;
276         private final boolean _valid;
277         private final Object _submittedValue;
278 
279         public EditableValueHolderState(EditableValueHolder evh)
280         {
281             _value = evh.getLocalValue();
282             _localValueSet = evh.isLocalValueSet();
283             _valid = evh.isValid();
284             _submittedValue = evh.getSubmittedValue();
285         }
286 
287         public void restoreState(EditableValueHolder evh)
288         {
289             evh.setValue(_value);
290             evh.setLocalValueSet(_localValueSet);
291             evh.setValid(_valid);
292             evh.setSubmittedValue(_submittedValue);
293         }
294     }
295 
296     /**
297      * Construct an instance of the UIData.
298      */
299     public UIData()
300     {
301         setRendererType("javax.faces.Table");
302     }
303 
304     @Override
305     public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
306         throws FacesException
307     {
308         if (context == null || clientId == null || callback == null)
309         {
310             throw new NullPointerException();
311         }
312         
313         final String baseClientId = getClientId(context);
314 
315         // searching for this component?
316         boolean returnValue = baseClientId.equals(clientId);
317 
318         boolean isCachedFacesContext = isCachedFacesContext();
319         if (!isCachedFacesContext)
320         {
321             setCachedFacesContext(context);
322         }
323         
324         pushComponentToEL(context, this);
325         try
326         {
327             if (returnValue)
328             {
329                 try
330                 {
331                     callback.invokeContextCallback(context, this);
332                     return true;
333                 }
334                 catch (Exception e)
335                 {
336                     throw new FacesException(e);
337                 }
338             }
339     
340             // Now Look throught facets on this UIComponent
341             if (this.getFacetCount() > 0)
342             {
343                 for (Iterator<UIComponent> it = this.getFacets().values().iterator(); !returnValue && it.hasNext();)
344                 {
345                     returnValue = it.next().invokeOnComponent(context, clientId, callback);
346                 }
347             }
348     
349             if (returnValue)
350             {
351                 return returnValue;
352             }
353             
354             // is the component an inner component?
355             if (clientId.startsWith(baseClientId))
356             {
357                 // Check if the clientId for the component, which we 
358                 // are looking for, has a rowIndex attached
359                 char separator = UINamingContainer.getSeparatorChar(context);
360                 String subId = clientId.substring(baseClientId.length() + 1);
361                 //If the char next to baseClientId is the separator one and
362                 //the subId matches the regular expression
363                 if (clientId.charAt(baseClientId.length()) == separator && 
364                         subId.matches("[0-9]+"+separator+".*"))
365                 {
366                     String clientRow = subId.substring(0, subId.indexOf(separator));
367         
368                     //Now we save the current position
369                     int oldRow = this.getRowIndex();
370                     
371                     // try-finally --> make sure, that the old row index is restored
372                     try
373                     {
374                         //The conversion is safe, because its already checked on the
375                         //regular expresion
376                         this.setRowIndex(Integer.parseInt(clientRow));
377                         
378                         // check, if the row is available
379                         if (!isRowAvailable())
380                         {
381                             return false;
382                         }
383             
384                         for (Iterator<UIComponent> it1 = getChildren().iterator(); 
385                                 !returnValue && it1.hasNext();)
386                         {
387                             //recursive call to find the component
388                             returnValue = it1.next().invokeOnComponent(context, clientId, callback);
389                         }
390                     }
391                     finally
392                     {
393                         //Restore the old position. Doing this prevent
394                         //side effects.
395                         this.setRowIndex(oldRow);
396                     }
397                 }
398                 else
399                 {
400                     // MYFACES-2370: search the component in the childrens' facets too.
401                     // We have to check the childrens' facets here, because in MyFaces
402                     // the rowIndex is not attached to the clientId for the children of
403                     // facets of the UIColumns. However, in RI the rowIndex is 
404                     // attached to the clientId of UIColumns' Facets' children.
405                     for (Iterator<UIComponent> itChildren = this.getChildren().iterator();
406                             !returnValue && itChildren.hasNext();)
407                     {
408                         UIComponent child = itChildren.next();
409                         if (child instanceof UIColumn && clientId.equals(child.getClientId(context)))
410                         {
411                             try
412                             {
413                                 callback.invokeContextCallback(context, child);
414                             }
415                             catch (Exception e)
416                             {
417                                 throw new FacesException(e);
418                             }
419                             returnValue = true;
420                         }
421                         // process the child's facets
422                         if (child.getFacetCount() > 0)
423                         {
424                             for (Iterator<UIComponent> itChildFacets = 
425                                 child.getFacets().values().iterator(); 
426                                 !returnValue && itChildFacets.hasNext();)
427                             {
428                                 //recursive call to find the component
429                                 returnValue = itChildFacets.next().invokeOnComponent(
430                                     context, clientId, callback);
431                             }
432                         }
433                     }
434                 }
435             }
436         }
437         finally
438         {
439             //all components must call popComponentFromEl after visiting is finished
440             popComponentFromEL(context);
441             if (!isCachedFacesContext)
442             {
443                 setCachedFacesContext(null);
444             }
445         }
446 
447         return returnValue;
448     }
449 
450     public void setFooter(UIComponent footer)
451     {
452         getFacets().put(FOOTER_FACET_NAME, footer);
453     }
454 
455     @JSFFacet
456     public UIComponent getFooter()
457     {
458         return getFacets().get(FOOTER_FACET_NAME);
459     }
460 
461     public void setHeader(UIComponent header)
462     {
463         getFacets().put(HEADER_FACET_NAME, header);
464     }
465 
466     @JSFFacet
467     public UIComponent getHeader()
468     {
469         return getFacets().get(HEADER_FACET_NAME);
470     }
471 
472     public boolean isRowAvailable()
473     {
474         return getDataModel().isRowAvailable();
475     }
476 
477     public int getRowCount()
478     {
479         return getDataModel().getRowCount();
480     }
481 
482     public Object getRowData()
483     {
484         return getDataModel().getRowData();
485     }
486 
487     public int getRowIndex()
488     {
489         return _rowIndex;
490     }
491 
492     /**
493      * Set the current row index that methods like getRowData use.
494      * <p>
495      * Param rowIndex can be -1, meaning "no row".
496      * <p>
497      * 
498      * @param rowIndex
499      */
500     public void setRowIndex(int rowIndex)
501     {
502         if (rowIndex < -1)
503         {
504             throw new IllegalArgumentException("rowIndex is less than -1");
505         }
506 
507         if (_rowIndex == rowIndex)
508         {
509             return;
510         }
511 
512         FacesContext facesContext = getFacesContext();
513 
514         if (_rowIndex == -1)
515         {
516             if (_initialDescendantComponentState == null)
517             {
518                 // Create a template that can be used to initialise any row
519                 // that we haven't visited before, ie a "saved state" that can
520                 // be pushed to the "restoreState" method of all the child
521                 // components to set them up to represent a clean row.
522                 _initialDescendantComponentState = saveDescendantComponentStates(this, false, false);
523             }
524         }
525         else
526         {
527             // If no initial component state, there are no EditableValueHolder instances,
528             // and that means there is no state to be saved for the current row, so we can
529             // skip row state saving code safely.
530             if (_initialDescendantComponentState != null)
531             {
532                 // We are currently positioned on some row, and are about to
533                 // move off it, so save the (partial) state of the components
534                 // representing the current row. Later if this row is revisited
535                 // then we can restore this state.
536                 Collection<Object[]> savedRowState = saveDescendantComponentStates(this, false, false);
537                 if (savedRowState != null)
538                 {
539                     _rowStates.put(getContainerClientId(facesContext), savedRowState);
540                 }
541             }
542         }
543 
544         _rowIndex = rowIndex;
545 
546         DataModel dataModel = getDataModel();
547         dataModel.setRowIndex(rowIndex);
548 
549         String var = (String) getStateHelper().get(PropertyKeys.var);
550         if (rowIndex == -1)
551         {
552             if (var != null)
553             {
554                 facesContext.getExternalContext().getRequestMap().remove(var);
555             }
556         }
557         else
558         {
559             if (var != null)
560             {
561                 if (isRowAvailable())
562                 {
563                     Object rowData = dataModel.getRowData();
564                     facesContext.getExternalContext().getRequestMap().put(var, rowData);
565                 }
566                 else
567                 {
568                     facesContext.getExternalContext().getRequestMap().remove(var);
569                 }
570             }
571         }
572 
573         if (_rowIndex == -1)
574         {
575             // reset components to initial state
576             // If no initial state, skip row restore state code
577             if (_initialDescendantComponentState != null)
578             {
579                 restoreDescendantComponentStates(this, false, _initialDescendantComponentState, false);
580             }
581             else
582             {
583                 restoreDescendantComponentWithoutRestoreState(this, false, false);
584             }
585         }
586         else
587         {
588             Object rowState = _rowStates.get(getContainerClientId(facesContext));
589             if (rowState == null)
590             {
591                 // We haven't been positioned on this row before, so just
592                 // configure the child components of this component with
593                 // the standard "initial" state
594                 // If no initial state, skip row restore state code
595                 if (_initialDescendantComponentState != null)
596                 {
597                     restoreDescendantComponentStates(this, false, _initialDescendantComponentState, false);
598                 }
599                 else
600                 {
601                     restoreDescendantComponentWithoutRestoreState(this, false, false);
602                 }
603             }
604             else
605             {
606                 // We have been positioned on this row before, so configure
607                 // the child components of this component with the (partial)
608                 // state that was previously saved. Fields not in the
609                 // partial saved state are left with their original values.
610                 restoreDescendantComponentStates(this, false, rowState, false);
611             }
612         }
613     }
614 
615     /**
616      * Overwrite the state of the child components of this component with data previously saved by method
617      * saveDescendantComponentStates.
618      * <p>
619      * The saved state info only covers those fields that are expected to vary between rows of a table. Other fields are
620      * not modified.
621      */
622     @SuppressWarnings("unchecked")
623     private void restoreDescendantComponentStates(UIComponent parent, boolean iterateFacets, Object state,
624                                                   boolean restoreChildFacets)
625     {
626         int descendantStateIndex = -1;
627         List<? extends Object[]> stateCollection = null;
628         
629         if (iterateFacets && parent.getFacetCount() > 0)
630         {
631             Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
632             
633             while (childIterator.hasNext())
634             {
635                 UIComponent component = childIterator.next();
636 
637                 // reset the client id (see spec 3.1.6)
638                 component.setId(component.getId());
639                 if (!component.isTransient())
640                 {
641                     if (descendantStateIndex == -1)
642                     {
643                         stateCollection = ((List<? extends Object[]>) state);
644                         descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
645                     }
646                     
647                     if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
648                     {
649                         Object[] object = stateCollection.get(descendantStateIndex);
650                         if (object[0] != null && component instanceof EditableValueHolder)
651                         {
652                             ((EditableValueHolderState) object[0]).restoreState((EditableValueHolder) component);
653                         }
654                         // If there is descendant state to restore, call it recursively, otherwise
655                         // it is safe to skip iteration.
656                         if (object[1] != null)
657                         {
658                             restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
659                         }
660                         else
661                         {
662                             restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
663                         }
664                     }
665                     else
666                     {
667                         restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
668                     }
669                     descendantStateIndex++;
670                 }
671             }
672         }
673         
674         if (parent.getChildCount() > 0)
675         {
676             for (int i = 0; i < parent.getChildCount(); i++)
677             {
678                 UIComponent component = parent.getChildren().get(i);
679 
680                 // reset the client id (see spec 3.1.6)
681                 component.setId(component.getId());
682                 if (!component.isTransient())
683                 {
684                     if (descendantStateIndex == -1)
685                     {
686                         stateCollection = ((List<? extends Object[]>) state);
687                         descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
688                     }
689                     
690                     if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
691                     {
692                         Object[] object = stateCollection.get(descendantStateIndex);
693                         if (object[0] != null && component instanceof EditableValueHolder)
694                         {
695                             ((EditableValueHolderState) object[0]).restoreState((EditableValueHolder) component);
696                         }
697                         // If there is descendant state to restore, call it recursively, otherwise
698                         // it is safe to skip iteration.
699                         if (object[1] != null)
700                         {
701                             restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
702                         }
703                         else
704                         {
705                             restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
706                         }
707                     }
708                     else
709                     {
710                         restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
711                     }
712                     descendantStateIndex++;
713                 }
714             }
715         }
716     }
717 
718     /**
719      * Just call component.setId(component.getId()) to reset all client ids and 
720      * ensure they will be calculated for the current row, but do not waste time
721      * dealing with row state code.
722      * 
723      * @param parent
724      * @param iterateFacets
725      * @param restoreChildFacets 
726      */
727     private void restoreDescendantComponentWithoutRestoreState(UIComponent parent, boolean iterateFacets,
728                                                                boolean restoreChildFacets)
729     {
730         if (iterateFacets && parent.getFacetCount() > 0)
731         {
732             Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
733             
734             while (childIterator.hasNext())
735             {
736                 UIComponent component = childIterator.next();
737 
738                 // reset the client id (see spec 3.1.6)
739                 component.setId(component.getId());
740                 if (!component.isTransient())
741                 {
742                     restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
743                 }
744             }
745         }
746         
747         if (parent.getChildCount() > 0)
748         {
749             for (int i = 0; i < parent.getChildCount(); i++)
750             {
751                 UIComponent component = parent.getChildren().get(i);
752 
753                 // reset the client id (see spec 3.1.6)
754                 component.setId(component.getId());
755                 if (!component.isTransient())
756                 {
757                     restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
758                 }
759             }
760         }
761     }
762 
763     /**
764      * Walk the tree of child components of this UIData, saving the parts of their state that can vary between rows.
765      * <p>
766      * This is very similar to the process that occurs for normal components when the view is serialized. Transient
767      * components are skipped (no state is saved for them).
768      * <p>
769      * If there are no children then null is returned. If there are one or more children, and all children are transient
770      * then an empty collection is returned; this will happen whenever a table contains only read-only components.
771      * <p>
772      * Otherwise a collection is returned which contains an object for every non-transient child component; that object
773      * may itself contain a collection of the state of that child's child components.
774      */
775     private Collection<Object[]> saveDescendantComponentStates(UIComponent parent, boolean iterateFacets,
776                                                                boolean saveChildFacets)
777     {
778         Collection<Object[]> childStates = null;
779         // Index to indicate how many components has been passed without state to save.
780         int childEmptyIndex = 0;
781         int totalChildCount = 0;
782                 
783         if (iterateFacets && parent.getFacetCount() > 0)
784         {
785             Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
786 
787             while (childIterator.hasNext())
788             {
789                 UIComponent child = childIterator.next();
790                 if (!child.isTransient())
791                 {
792                     // Add an entry to the collection, being an array of two
793                     // elements. The first element is the state of the children
794                     // of this component; the second is the state of the current
795                     // child itself.
796 
797                     if (child instanceof EditableValueHolder)
798                     {
799                         if (childStates == null)
800                         {
801                             childStates = new ArrayList<Object[]>(
802                                     parent.getFacetCount()
803                                     + parent.getChildCount()
804                                     - totalChildCount
805                                     + childEmptyIndex);
806                             for (int ci = 0; ci < childEmptyIndex; ci++)
807                             {
808                                 childStates.add(LEAF_NO_STATE);
809                             }
810                         }
811                     
812                         childStates.add(child.getChildCount() > 0 ? 
813                                 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
814                                     saveDescendantComponentStates(child, saveChildFacets, true)} :
815                                 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
816                                     null});
817                     }
818                     else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
819                     {
820                         Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
821                         
822                         if (descendantSavedState == null)
823                         {
824                             if (childStates == null)
825                             {
826                                 childEmptyIndex++;
827                             }
828                             else
829                             {
830                                 childStates.add(LEAF_NO_STATE);
831                             }
832                         }
833                         else
834                         {
835                             if (childStates == null)
836                             {
837                                 childStates = new ArrayList<Object[]>(
838                                         parent.getFacetCount()
839                                         + parent.getChildCount()
840                                         - totalChildCount
841                                         + childEmptyIndex);
842                                 for (int ci = 0; ci < childEmptyIndex; ci++)
843                                 {
844                                     childStates.add(LEAF_NO_STATE);
845                                 }
846                             }
847                             childStates.add(new Object[]{null, descendantSavedState});
848                         }
849                     }
850                     else
851                     {
852                         if (childStates == null)
853                         {
854                             childEmptyIndex++;
855                         }
856                         else
857                         {
858                             childStates.add(LEAF_NO_STATE);
859                         }
860                     }
861                 }
862                 totalChildCount++;
863             }
864         }
865         
866         if (parent.getChildCount() > 0)
867         {
868             for (int i = 0; i < parent.getChildCount(); i++)
869             {
870                 UIComponent child = parent.getChildren().get(i);
871                 if (!child.isTransient())
872                 {
873                     // Add an entry to the collection, being an array of two
874                     // elements. The first element is the state of the children
875                     // of this component; the second is the state of the current
876                     // child itself.
877 
878                     if (child instanceof EditableValueHolder)
879                     {
880                         if (childStates == null)
881                         {
882                             childStates = new ArrayList<Object[]>(
883                                     parent.getFacetCount()
884                                     + parent.getChildCount()
885                                     - totalChildCount
886                                     + childEmptyIndex);
887                             for (int ci = 0; ci < childEmptyIndex; ci++)
888                             {
889                                 childStates.add(LEAF_NO_STATE);
890                             }
891                         }
892                     
893                         childStates.add(child.getChildCount() > 0 ? 
894                                 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
895                                     saveDescendantComponentStates(child, saveChildFacets, true)} :
896                                 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
897                                     null});
898                     }
899                     else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
900                     {
901                         Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
902                         
903                         if (descendantSavedState == null)
904                         {
905                             if (childStates == null)
906                             {
907                                 childEmptyIndex++;
908                             }
909                             else
910                             {
911                                 childStates.add(LEAF_NO_STATE);
912                             }
913                         }
914                         else
915                         {
916                             if (childStates == null)
917                             {
918                                 childStates = new ArrayList<Object[]>(
919                                         parent.getFacetCount()
920                                         + parent.getChildCount()
921                                         - totalChildCount
922                                         + childEmptyIndex);
923                                 for (int ci = 0; ci < childEmptyIndex; ci++)
924                                 {
925                                     childStates.add(LEAF_NO_STATE);
926                                 }
927                             }
928                             childStates.add(new Object[]{null, descendantSavedState});
929                         }
930                     }
931                     else
932                     {
933                         if (childStates == null)
934                         {
935                             childEmptyIndex++;
936                         }
937                         else
938                         {
939                             childStates.add(LEAF_NO_STATE);
940                         }
941                     }
942                 }
943                 totalChildCount++;
944             }
945         }
946         
947         return childStates;
948     }
949     
950     @Override
951     public void setValueExpression(String name, ValueExpression binding)
952     {
953         if (name == null)
954         {
955             throw new NullPointerException("name");
956         }
957         else if (name.equals("value"))
958         {
959             _dataModelMap.clear();
960         }
961         else if (name.equals("rowIndex"))
962         {
963             throw new IllegalArgumentException("name " + name);
964         }
965         super.setValueExpression(name, binding);
966     }
967 
968     /*
969     @Override
970     public String getClientId(FacesContext context)
971     {
972         String clientId = super.getClientId(context);
973         int rowIndex = getRowIndex();
974         if (rowIndex == -1)
975         {
976             return clientId;
977         }
978 
979         StringBuilder bld = __getSharedStringBuilder();
980         return bld.append(clientId).append(UINamingContainer.getSeparatorChar(context)).append(rowIndex).toString();
981     }*/
982 
983     @Override
984     public String getContainerClientId(FacesContext context)
985     {
986         //MYFACES-2744 UIData.getClientId() should not append rowIndex, instead use UIData.getContainerClientId()
987         String clientId = super.getContainerClientId(context);
988         
989         int rowIndex = getRowIndex();
990         if (rowIndex == -1)
991         {
992             return clientId;
993         }
994 
995         StringBuilder bld = __getSharedStringBuilder(context);
996         return bld.append(clientId).append(UINamingContainer.getSeparatorChar(context)).append(rowIndex).toString();        
997     }
998 
999     /**
1000      * Modify events queued for any child components so that the UIData state will be correctly configured before the
1001      * event's listeners are executed.
1002      * <p>
1003      * Child components or their renderers may register events against those child components. When the listener for
1004      * that event is eventually invoked, it may expect the uidata's rowData and rowIndex to be referring to the same
1005      * object that caused the event to fire.
1006      * <p>
1007      * The original queueEvent call against the child component has been forwarded up the chain of ancestors in the
1008      * standard way, making it possible here to wrap the event in a new event whose source is <i>this</i> component, not
1009      * the original one. When the event finally is executed, this component's broadcast method is invoked, which ensures
1010      * that the UIData is set to be at the correct row before executing the original event.
1011      */
1012     @Override
1013     public void queueEvent(FacesEvent event)
1014     {
1015         super.queueEvent(new FacesEventWrapper(event, getRowIndex(), this));
1016     }
1017 
1018     /**
1019      * Ensure that before the event's listeners are invoked this UIData component's "current row" is set to the row
1020      * associated with the event.
1021      * <p>
1022      * See queueEvent for more details.
1023      */
1024     @Override
1025     public void broadcast(FacesEvent event) throws AbortProcessingException
1026     {
1027         if (event instanceof FacesEventWrapper)
1028         {
1029             FacesEvent originalEvent = ((FacesEventWrapper) event).getWrappedFacesEvent();
1030             int eventRowIndex = ((FacesEventWrapper) event).getRowIndex();
1031             final int currentRowIndex = getRowIndex();
1032             UIComponent source = originalEvent.getComponent();
1033             UIComponent compositeParent = UIComponent.getCompositeComponentParent(source);
1034 
1035             setRowIndex(eventRowIndex);
1036             if (compositeParent != null)
1037             {
1038                 pushComponentToEL(getFacesContext(), compositeParent);
1039             }
1040             pushComponentToEL(getFacesContext(), source);
1041             try
1042             {
1043                 source.broadcast(originalEvent);
1044             }
1045             finally
1046             {
1047                 source.popComponentFromEL(getFacesContext());
1048                 if (compositeParent != null)
1049                 {
1050                     compositeParent.popComponentFromEL(getFacesContext());
1051                 }
1052                 setRowIndex(currentRowIndex);
1053             }
1054         }
1055         else
1056         {
1057             super.broadcast(event);
1058         }
1059     }
1060 
1061     /**
1062      * 
1063      * {@inheritDoc}
1064      * 
1065      * @since 2.0
1066      */
1067     public String createUniqueId(FacesContext context, String seed)
1068     {
1069         StringBuilder bld = __getSharedStringBuilder(context);
1070 
1071         // Generate an identifier for a component. The identifier will be prefixed with UNIQUE_ID_PREFIX,
1072         // and will be unique within this UIViewRoot.
1073         if(seed==null)
1074         {
1075             Long uniqueIdCounter = (Long) getStateHelper().get(PropertyKeys.uniqueIdCounter);
1076             uniqueIdCounter = (uniqueIdCounter == null) ? 0 : uniqueIdCounter;
1077             getStateHelper().put(PropertyKeys.uniqueIdCounter, (uniqueIdCounter+1L));
1078             return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(uniqueIdCounter).toString();    
1079         }
1080         // Optionally, a unique seed value can be supplied by component creators
1081         // which should be included in the generated unique id.
1082         else
1083         {
1084             return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(seed).toString();
1085         }
1086     }
1087 
1088     /**
1089      * Perform necessary actions when rendering of this component starts, before delegating to the inherited
1090      * implementation which calls the associated renderer's encodeBegin method.
1091      */
1092     @Override
1093     public void encodeBegin(FacesContext context) throws IOException
1094     {
1095         _initialDescendantComponentState = null;
1096         if (_isValidChilds && !hasErrorMessages(context))
1097         {
1098             // Clear the data model so that when rendering code calls
1099             // getDataModel a fresh model is fetched from the backing
1100             // bean via the value-binding.
1101             _dataModelMap.clear();
1102 
1103             // When the data model is cleared it is also necessary to
1104             // clear the saved row state, as there is an implicit 1:1
1105             // relation between objects in the _rowStates and the
1106             // corresponding DataModel element.
1107             _rowStates.clear();
1108         }
1109         super.encodeBegin(context);
1110     }
1111 
1112     private boolean hasErrorMessages(FacesContext context)
1113     {
1114         // perf: getMessageList() return a RandomAccess instance.
1115         // See org.apache.myfaces.context.servlet.FacesContextImpl.addMessage
1116         List<FacesMessage> messageList = context.getMessageList();
1117         for (int i = 0, size = messageList.size(); i < size;  i++)
1118         {
1119             FacesMessage message = messageList.get(i);
1120             if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
1121             {
1122                 return true;
1123             }
1124         }
1125         return false;
1126     }
1127 
1128     /**
1129      * @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
1130      */
1131     @Override
1132     public void encodeEnd(FacesContext context) throws IOException
1133     {
1134         try
1135         {
1136             setCachedFacesContext(context);
1137             setRowIndex(-1);
1138         }
1139         finally
1140         {
1141             setCachedFacesContext(null);
1142         }
1143         super.encodeEnd(context);
1144     }
1145 
1146     @Override
1147     public void processDecodes(FacesContext context)
1148     {
1149         if (context == null)
1150         {
1151             throw new NullPointerException("context");
1152         }
1153         try
1154         {
1155             setCachedFacesContext(context);
1156             pushComponentToEL(context, this);
1157             if (!isRendered())
1158             {
1159                 return;
1160             }
1161             setRowIndex(-1);
1162             processFacets(context, PROCESS_DECODES);
1163             processColumnFacets(context, PROCESS_DECODES);
1164             processColumnChildren(context, PROCESS_DECODES);
1165             setRowIndex(-1);
1166             try
1167             {
1168                 decode(context);
1169             }
1170             catch (RuntimeException e)
1171             {
1172                 context.renderResponse();
1173                 throw e;
1174             }
1175         }
1176         finally
1177         {
1178             popComponentFromEL(context);
1179             setCachedFacesContext(null);
1180         }
1181     }
1182 
1183     @Override
1184     public void processValidators(FacesContext context)
1185     {
1186         if (context == null)
1187         {
1188             throw new NullPointerException("context");
1189         }
1190 
1191         try
1192         {
1193             setCachedFacesContext(context);
1194             pushComponentToEL(context, this);
1195             if (!isRendered())
1196             {
1197                 return;
1198             }
1199             
1200             //Pre validation event dispatch for component
1201             context.getApplication().publishEvent(context,  PreValidateEvent.class, getClass(), this);
1202             
1203             try
1204             {
1205                 setRowIndex(-1);
1206                 processFacets(context, PROCESS_VALIDATORS);
1207                 processColumnFacets(context, PROCESS_VALIDATORS);
1208                 processColumnChildren(context, PROCESS_VALIDATORS);
1209                 setRowIndex(-1);
1210             }
1211             finally
1212             {
1213                 context.getApplication().publishEvent(context,  PostValidateEvent.class, getClass(), this);
1214             }
1215             
1216             // check if an validation error forces the render response for our data
1217             if (context.getRenderResponse())
1218             {
1219                 _isValidChilds = false;
1220             }
1221         }
1222         finally
1223         {
1224             popComponentFromEL(context);
1225             setCachedFacesContext(null);
1226         }
1227     }
1228 
1229     @Override
1230     public void processUpdates(FacesContext context)
1231     {
1232         if (context == null)
1233         {
1234             throw new NullPointerException("context");
1235         }
1236         try
1237         {
1238             setCachedFacesContext(context);
1239             pushComponentToEL(context, this);
1240             if (!isRendered())
1241             {
1242                 return;
1243             }
1244             setRowIndex(-1);
1245             processFacets(context, PROCESS_UPDATES);
1246             processColumnFacets(context, PROCESS_UPDATES);
1247             processColumnChildren(context, PROCESS_UPDATES);
1248             setRowIndex(-1);
1249     
1250             if (context.getRenderResponse())
1251             {
1252                 _isValidChilds = false;
1253             }
1254         }
1255         finally
1256         {
1257             popComponentFromEL(context);
1258             setCachedFacesContext(null);
1259         }
1260     }
1261 
1262     private void processFacets(FacesContext context, int processAction)
1263     {
1264         if (this.getFacetCount() > 0)
1265         {
1266             for (UIComponent facet : getFacets().values())
1267             {
1268                 process(context, facet, processAction);
1269             }
1270         }
1271     }
1272 
1273     /**
1274      * Invoke the specified phase on all facets of all UIColumn children of this component. Note that no methods are
1275      * called on the UIColumn child objects themselves.
1276      * 
1277      * @param context
1278      *            is the current faces context.
1279      * @param processAction
1280      *            specifies a JSF phase: decode, validate or update.
1281      */
1282     private void processColumnFacets(FacesContext context, int processAction)
1283     {
1284         for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1285         {
1286             UIComponent child = getChildren().get(i);
1287             if (child instanceof UIColumn)
1288             {
1289                 if (! _ComponentUtils.isRendered(context, child))
1290                 {
1291                     // Column is not visible
1292                     continue;
1293                 }
1294                 
1295                 if (child.getFacetCount() > 0)
1296                 {
1297                     for (UIComponent facet : child.getFacets().values())
1298                     {
1299                         process(context, facet, processAction);
1300                     }
1301                 }
1302             }
1303         }
1304     }
1305 
1306     /**
1307      * Invoke the specified phase on all non-facet children of all UIColumn children of this component. Note that no
1308      * methods are called on the UIColumn child objects themselves.
1309      * 
1310      * @param context
1311      *            is the current faces context.
1312      * @param processAction
1313      *            specifies a JSF phase: decode, validate or update.
1314      */
1315     private void processColumnChildren(FacesContext context, int processAction)
1316     {
1317         int first = getFirst();
1318         int rows = getRows();
1319         int last;
1320         if (rows == 0)
1321         {
1322             last = getRowCount();
1323         }
1324         else
1325         {
1326             last = first + rows;
1327         }
1328         for (int rowIndex = first; last == -1 || rowIndex < last; rowIndex++)
1329         {
1330             setRowIndex(rowIndex);
1331 
1332             // scrolled past the last row
1333             if (!isRowAvailable())
1334             {
1335                 break;
1336             }
1337             
1338             for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1339             {
1340                 UIComponent child = getChildren().get(i);
1341                 if (child instanceof UIColumn)
1342                 {
1343                     if (! _ComponentUtils.isRendered(context, child))
1344                     {
1345                         // Column is not visible
1346                         continue;
1347                     }
1348                     for (int j = 0, columnChildCount = child.getChildCount(); j < columnChildCount; j++)
1349                     {
1350                         UIComponent columnChild = child.getChildren().get(j);
1351                         process(context, columnChild, processAction);
1352                     }
1353                 }
1354             }
1355         }
1356     }
1357 
1358     private void process(FacesContext context, UIComponent component, int processAction)
1359     {
1360         switch (processAction)
1361         {
1362         case PROCESS_DECODES:
1363             component.processDecodes(context);
1364             break;
1365         case PROCESS_VALIDATORS:
1366             component.processValidators(context);
1367             break;
1368         case PROCESS_UPDATES:
1369             component.processUpdates(context);
1370             break;
1371         }
1372     }
1373 
1374     /**
1375      * Return the datamodel for this table, potentially fetching the data from a backing bean via a value-binding if
1376      * this is the first time this method has been called.
1377      * <p>
1378      * This is complicated by the fact that this table may be nested within another table. In this case a different
1379      * datamodel should be fetched for each row. When nested within a parent table, the parent reference won't change
1380      * but parent.getContainerClientId() will, as the suffix changes
1381      * depending upon the current row index. A map object on this
1382      * component is therefore used to cache the datamodel for each row of the table. In the normal case where this table
1383      * is not nested inside a component that changes its id (like a table does) then this map only ever has one entry.
1384      */
1385     protected DataModel getDataModel()
1386     {
1387         DataModel dataModel;
1388         String clientID = "";
1389 
1390         UIComponent parent = getParent();
1391         if (parent != null)
1392         {
1393             clientID = parent.getContainerClientId(getFacesContext());
1394         }
1395         dataModel = _dataModelMap.get(clientID);
1396         if (dataModel == null)
1397         {
1398             dataModel = createDataModel();
1399             _dataModelMap.put(clientID, dataModel);
1400         }
1401         return dataModel;
1402     }
1403 
1404     protected void setDataModel(DataModel dataModel)
1405     {
1406         throw new UnsupportedOperationException("this method is here only to maintain binary compatibility w/ the RI");
1407     }
1408 
1409     /**
1410      * Evaluate this object's value property and convert the result into a DataModel. Normally this object's value
1411      * property will be a value-binding which will cause the value to be fetched from some backing bean.
1412      * <p>
1413      * The result of fetching the value may be a DataModel object, in which case that object is returned directly. If
1414      * the value is of type List, Array, ResultSet, Result, other object or null then an appropriate wrapper is created
1415      * and returned.
1416      * <p>
1417      * Null is never returned by this method.
1418      */
1419     private DataModel createDataModel()
1420     {
1421         Object value = getValue();
1422 
1423         if (value == null)
1424         {
1425             return EMPTY_DATA_MODEL;
1426         }
1427         else if (value instanceof DataModel)
1428         {
1429             return (DataModel) value;
1430         }
1431         else if (value instanceof List)
1432         {
1433             return new ListDataModel((List<?>) value);
1434         }
1435         else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass()))
1436         {
1437             return new ArrayDataModel((Object[]) value);
1438         }
1439         else if (value instanceof ResultSet)
1440         {
1441             return new ResultSetDataModel((ResultSet) value);
1442         }
1443         else if (value instanceof Result)
1444         {
1445             return new ResultDataModel((Result) value);
1446         }
1447         else
1448         {
1449             return new ScalarDataModel(value);
1450         }
1451     }
1452 
1453     /**
1454      * An EL expression that specifies the data model that backs this table.
1455      * <p>
1456      * The value referenced by the EL expression can be of any type.
1457      * </p>
1458      * <ul>
1459      * <li>A value of type DataModel is used directly.</li>
1460      * <li>Array-like parameters of type array-of-Object, java.util.List, java.sql.ResultSet or
1461      * javax.servlet.jsp.jstl.sql.Result are wrapped in a corresponding DataModel that knows how to iterate over the
1462      * elements.</li>
1463      * <li>Other values are wrapped in a DataModel as a single row.</li>
1464      * </ul>
1465      * <p>
1466      * Note in particular that unordered collections, eg Set are not supported. Therefore if the value expression
1467      * references such an object then the table will be considered to contain just one element - the collection itself.
1468      * </p>
1469      */
1470     @JSFProperty
1471     public Object getValue()
1472     {
1473         return  getStateHelper().eval(PropertyKeys.value);
1474     }
1475 
1476     public void setValue(Object value)
1477     {
1478         getStateHelper().put(PropertyKeys.value, value );
1479         _dataModelMap.clear();
1480         _rowStates.clear();
1481         _isValidChilds = true;
1482     }
1483 
1484     /**
1485      * Defines the index of the first row to be displayed, starting from 0.
1486      */
1487     @JSFProperty
1488     public int getFirst()
1489     {
1490         return (Integer) getStateHelper().eval(PropertyKeys.first,0);
1491     }
1492 
1493     public void setFirst(int first)
1494     { 
1495         if (first < 0)
1496         {
1497             throw new IllegalArgumentException("Illegal value for first row: " + first);
1498         }
1499         getStateHelper().put(PropertyKeys.first, first );
1500     }
1501 
1502     /**
1503      * Defines the maximum number of rows of data to be displayed.
1504      * <p>
1505      * Specify zero to display all rows from the "first" row to the end of available data.
1506      * </p>
1507      */
1508     @JSFProperty
1509     public int getRows()
1510     {
1511         return (Integer) getStateHelper().eval(PropertyKeys.rows,0);
1512     }
1513 
1514     /**
1515      * Set the maximum number of rows displayed in the table.
1516      */
1517     public void setRows(int rows)
1518     {
1519         if (rows < 0)
1520         {
1521             throw new IllegalArgumentException("rows: " + rows);
1522         }
1523         getStateHelper().put(PropertyKeys.rows, rows ); 
1524     }
1525 
1526     /**
1527      * Defines the name of the request-scope variable that will hold the current row during iteration.
1528      * <p>
1529      * During rendering of child components of this UIData, the variable with this name can be read to learn what the
1530      * "rowData" object for the row currently being rendered is.
1531      * </p>
1532      * <p>
1533      * This value must be a static value, ie an EL expression is not permitted.
1534      * </p>
1535      */
1536     @JSFProperty(literalOnly = true)
1537     public String getVar()
1538     {
1539         return (String) getStateHelper().get(PropertyKeys.var);
1540     }
1541 
1542     /**
1543      * Overrides the behavior in 
1544      * UIComponent.visitTree(javax.faces.component.visit.VisitContext, javax.faces.component.visit.VisitCallback)
1545      * to handle iteration correctly.
1546      * 
1547      * @param context the visit context which handles the processing details
1548      * @param callback the callback to be performed
1549      * @return false if the processing is not done true if we can shortcut
1550      * the visiting because we are done with everything
1551      * 
1552      * @since 2.0
1553      */
1554     @Override
1555     public boolean visitTree(VisitContext context, VisitCallback callback)
1556     {
1557         if (!isVisitable(context))
1558         {
1559             return false;
1560         }
1561 
1562         boolean isCachedFacesContext = isCachedFacesContext();
1563         if (!isCachedFacesContext)
1564         {
1565             setCachedFacesContext(context.getFacesContext());
1566         }
1567         // save the current row index
1568         int oldRowIndex = getRowIndex();
1569         // set row index to -1 to process the facets and to get the rowless clientId
1570         setRowIndex(-1);
1571         // push the Component to EL
1572         pushComponentToEL(context.getFacesContext(), this);
1573         try
1574         {
1575             VisitResult visitResult = context.invokeVisitCallback(this,
1576                     callback);
1577             switch (visitResult)
1578             {
1579             //we are done nothing has to be processed anymore
1580             case COMPLETE:
1581                 return true;
1582 
1583             case REJECT:
1584                 return false;
1585 
1586                 //accept
1587             default:
1588                 // determine if we need to visit our children 
1589                 Collection<String> subtreeIdsToVisit = context
1590                         .getSubtreeIdsToVisit(this);
1591                 boolean doVisitChildren = subtreeIdsToVisit != null
1592                         && !subtreeIdsToVisit.isEmpty();
1593                 if (doVisitChildren)
1594                 {
1595                     // visit the facets of the component
1596                     if (getFacetCount() > 0)
1597                     {
1598                         for (UIComponent facet : getFacets().values())
1599                         {
1600                             if (facet.visitTree(context, callback))
1601                             {
1602                                 return true;
1603                             }
1604                         }
1605                     }
1606                     Boolean skipIterationHint = (Boolean) context.getFacesContext().getAttributes().get(SKIP_ITERATION_HINT);
1607                     if (skipIterationHint != null && skipIterationHint.booleanValue())
1608                     {
1609                         // If SKIP_ITERATION is enabled, do not take into account rows.
1610                         for (int i = 0, childCount = getChildCount(); i < childCount; i++ )
1611                         {
1612                             UIComponent child = getChildren().get(i);
1613                             if (child.visitTree(context, callback))
1614                             {
1615                                 return true;
1616                             }
1617                         }
1618                     }
1619                     else
1620                     {
1621                         // visit every column directly without visiting its children 
1622                         // (the children of every UIColumn will be visited later for 
1623                         // every row) and also visit the column's facets
1624                         for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1625                         {
1626                             UIComponent child = getChildren().get(i);
1627                             if (child instanceof UIColumn)
1628                             {
1629                                 VisitResult columnResult = context.invokeVisitCallback(child, callback);
1630                                 if (columnResult == VisitResult.COMPLETE)
1631                                 {
1632                                     return true;
1633                                 }
1634                                 if (child.getFacetCount() > 0)
1635                                 {
1636                                     for (UIComponent facet : child.getFacets().values())
1637                                     {
1638                                         if (facet.visitTree(context, callback))
1639                                         {
1640                                             return true;
1641                                         }
1642                                     }
1643                                 }
1644                             }
1645                         }
1646                         // iterate over the rows
1647                         int rowsToProcess = getRows();
1648                         // if getRows() returns 0, all rows have to be processed
1649                         if (rowsToProcess == 0)
1650                         {
1651                             rowsToProcess = getRowCount();
1652                         }
1653                         int rowIndex = getFirst();
1654                         for (int rowsProcessed = 0; rowsProcessed < rowsToProcess; rowsProcessed++, rowIndex++)
1655                         {
1656                             setRowIndex(rowIndex);
1657                             if (!isRowAvailable())
1658                             {
1659                                 return false;
1660                             }
1661                             // visit the children of every child of the UIData that is an instance of UIColumn
1662                             for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1663                             {
1664                                 UIComponent child = getChildren().get(i);
1665                                 if (child instanceof UIColumn)
1666                                 {
1667                                     for (int j = 0, grandChildCount = child.getChildCount(); j < grandChildCount; j++)
1668                                     {
1669                                         UIComponent grandchild = child.getChildren().get(j);
1670                                         if (grandchild.visitTree(context, callback))
1671                                         {
1672                                             return true;
1673                                         }
1674                                     }
1675                                 }
1676                             }
1677                         }
1678                     }
1679                 }
1680             }
1681         }
1682         finally
1683         {
1684             // pop the component from EL and restore the old row index
1685             popComponentFromEL(context.getFacesContext());
1686             setRowIndex(oldRowIndex);
1687             if (!isCachedFacesContext)
1688             {
1689                 setCachedFacesContext(null);
1690             }
1691         }
1692 
1693         // Return false to allow the visiting to continue
1694         return false;
1695     }
1696 
1697     public void setVar(String var)
1698     {
1699         getStateHelper().put(PropertyKeys.var, var ); 
1700     }
1701 
1702     enum PropertyKeys
1703     {
1704          value
1705         , first
1706         , rows
1707         , var
1708         , uniqueIdCounter
1709     }
1710 
1711     @Override
1712     public String getFamily()
1713     {
1714         return COMPONENT_FAMILY;
1715     }
1716 }