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