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 org.apache.myfaces.component.html.ext;
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.application.FacesMessage;
32  import javax.faces.component.EditableValueHolder;
33  import javax.faces.component.UIComponent;
34  import javax.faces.component.UIComponentBase;
35  import javax.faces.component.UINamingContainer;
36  import javax.faces.context.FacesContext;
37  import javax.faces.el.ValueBinding;
38  import javax.faces.model.ArrayDataModel;
39  import javax.faces.model.DataModel;
40  import javax.faces.model.ListDataModel;
41  import javax.faces.model.ResultDataModel;
42  import javax.faces.model.ResultSetDataModel;
43  import javax.faces.model.ScalarDataModel;
44  import javax.servlet.jsp.jstl.sql.Result;
45  
46  import org.apache.commons.lang.StringUtils;
47  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
48  import org.apache.myfaces.component.html.util.HtmlComponentUtils;
49  import org.apache.myfaces.custom.ExtendedComponentBase;
50  
51  /**
52   * Reimplement all UIData functionality to be able to have (protected) access
53   * the internal DataModel.
54   *
55   * @JSFComponent
56   *  configExcluded = "true"
57   *
58   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
59   * @version $Revision: 691856 $ $Date: 2008-09-03 21:40:30 -0500 (miƩ, 03 sep 2008) $
60   */
61  public abstract class HtmlDataTableHack extends
62                  javax.faces.component.html.HtmlDataTable implements
63                  ExtendedComponentBase
64                  
65  {
66      @SuppressWarnings("unchecked")
67      private Map<String, DataModel> _dataModelMap = new HashMap<String, DataModel>();
68  
69      // will be set to false if the data should not be refreshed at the beginning of the encode phase
70      private boolean _isValidChilds = true;
71  
72      // holds for each row the states of the child components of this UIData
73      private Map<String, Object> _rowStates = new HashMap<String, Object>();
74      
75      // holds delta state information
76      private Map<String, Map<String, Object> > _rowDeltaStates = new HashMap<String, Map<String, Object> >();
77  
78      // contains the initial row state which is used to initialize each row
79      private Object _initialDescendantComponentState = null;
80  
81      private Object _initialDescendantFullComponentState = null;
82      
83      // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
84      // Every field and method from here is identical to UIData !!!!!!!!!
85      // EXCEPTION: we can create a DataModel from a Collection
86  
87      @SuppressWarnings("unchecked")
88      private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
89  
90      private static final boolean DEFAULT_PRESERVEROWSTATES = false;
91      
92      private static final String UNIQUE_ROW_ID_PREFIX = "r_id_";
93  
94      private int _rowIndex = -1;
95  
96      public boolean isRowAvailable()
97      {
98          return getDataModel().isRowAvailable();
99      }
100 
101     public int getRowCount()
102     {
103         return getDataModel().getRowCount();
104     }
105 
106     public Object getRowData()
107     {
108         return getDataModel().getRowData();
109     }
110 
111     public int getRowIndex()
112     {
113         return _rowIndex;
114     }
115     
116     /**
117      * Hack since RI does not call getRowIndex()
118      */
119     public String getClientId(FacesContext context)
120     {
121         // check for forceId
122         String clientId = HtmlComponentUtils.getClientId(this, getRenderer(context), context);
123         if (clientId == null)
124         {
125             clientId = super.getClientId(context);
126             // remove index if necesssary 
127             int rowIndex = getRowIndex();
128             if (rowIndex != -1)
129             {
130                 char separator = UINamingContainer.getSeparatorChar(context);
131                 int index = clientId.lastIndexOf(separator);
132                 if(index != -1)
133                 {
134                     String rowIndexString = clientId.substring(index + 1);
135                     if (rowIndexString.length() > 0 && 
136                         StringUtils.isNumeric(rowIndexString) &&
137                         rowIndex == Integer.valueOf(rowIndexString).intValue())
138                     {
139                         clientId = clientId.substring(0,index - 1);
140                     }
141                 }
142             }
143         }
144         return clientId;
145     }
146     
147     @Override
148     public String getContainerClientId(FacesContext context)
149     {
150         // check for forceId
151         String clientId = HtmlComponentUtils.getClientId(this, getRenderer(context), context);
152         if (clientId == null)
153         {
154             clientId = super.getClientId(context);
155         }
156         int rowIndex = getRowIndex();
157         if (rowIndex == -1)
158         {
159             return clientId;
160         }
161         // the following code tries to avoid rowindex to be twice in the client id
162         char separator = UINamingContainer.getSeparatorChar(context);
163         int index = clientId.lastIndexOf(separator);
164         if(index != -1)
165         {
166             String rowIndexString = clientId.substring(index + 1);
167             
168             if(rowIndexString.length() > 0 && 
169                StringUtils.isNumeric(rowIndexString) &&
170                Integer.valueOf(rowIndexString) == rowIndex)
171             {
172                 //return clientId;
173                 return clientId.substring(0,index+1)+getDerivedSubClientId();
174             }
175         }
176         //return clientId + separator + rowIndex;
177         return clientId + separator + getDerivedSubClientId();
178     }
179 
180     /**
181      * @see javax.faces.component.UIData#processUpdates(javax.faces.context.FacesContext)
182      */
183     public void processUpdates(FacesContext context)
184     {
185         super.processUpdates(context);
186         // check if a update model error forces the render response for our data
187         if (context.getRenderResponse())
188         {
189             _isValidChilds = false;
190         }
191     }
192     
193     /**
194      * This method is used when a custom processUpdates and processValidators
195      * is defined, to check if a update model error forces the render 
196      * response for our data, because _isValidChilds is a private field
197      * and is not available on child components that inherits this 
198      * component class like t:dataList. (TOMAHAWK-1225)
199      */
200     protected void checkUpdateModelError(FacesContext context)
201     {
202         if (context.getRenderResponse())
203         {
204             _isValidChilds = false;
205         }        
206     }
207 
208     /**
209      * @see javax.faces.component.UIData#processValidators(javax.faces.context.FacesContext)
210      */
211     public void processValidators(FacesContext context)
212     {
213         super.processValidators(context);
214         // check if a validation error forces the render response for our data
215         if (context.getRenderResponse())
216         {
217             _isValidChilds = false;
218         }
219     }
220 
221     /**
222      * @see javax.faces.component.UIData#encodeBegin(javax.faces.context.FacesContext)
223      */
224     public void encodeBegin(FacesContext context) throws IOException
225     {
226         _initialDescendantComponentState = null;
227         if (_isValidChilds && !hasErrorMessages(context))
228         {
229             //Refresh DataModel for rendering:
230             _dataModelMap.clear();
231             if (!isPreserveRowStates())
232             {
233                 _rowStates.clear();
234             }
235         }
236         super.encodeBegin(context);
237     }
238 
239     public void setPreserveRowStates(boolean preserveRowStates)
240     {
241         getStateHelper().put(PropertyKeys.preserveRowStates, preserveRowStates);
242     }
243 
244     /**
245      * Indicates whether the state for each row should not be 
246      * discarded before the datatable is rendered again. 
247      * 
248      * Setting this to true might be hepful if an input 
249      * component inside the datatable has no valuebinding and 
250      * the value entered in there should be displayed again.
251      *  
252      * This will only work reliable if the datamodel of the 
253      * datatable did not change either by sorting, removing or 
254      * adding rows. Default: false
255      * 
256      * @JSFProperty
257      *   defaultValue="false"
258      */
259     public boolean isPreserveRowStates()
260     {
261         Object value = getStateHelper().eval(PropertyKeys.preserveRowStates,DEFAULT_PRESERVEROWSTATES);
262         if (value != null)
263         {
264             return (Boolean) value;
265         }
266         return DEFAULT_PRESERVEROWSTATES;
267     }
268     
269     /**
270      * Indicates whether the state for a component in each row should not be 
271      * discarded before the datatable is rendered again.
272      * 
273      * In tomahawk, this property is the same as t:dataTable preserveRowComponentState
274      * 
275      * This will only work reliable if the datamodel of the 
276      * datatable did not change either by sorting, removing or 
277      * adding rows. Default: false
278      * 
279      * @return
280      */
281     @JSFProperty(literalOnly=true, faceletsOnly=true, defaultValue="false")
282     public boolean isRowStatePreserved()
283     {
284         Boolean b = (Boolean) getStateHelper().get(PropertyKeys.rowStatePreserved);
285         return b == null ? false : b.booleanValue(); 
286     }
287     
288     public void setRowStatePreserved(boolean preserveComponentState)
289     {
290         getStateHelper().put(PropertyKeys.rowStatePreserved, preserveComponentState);
291     }
292 
293     protected boolean hasErrorMessages(FacesContext context)
294     {
295         for(Iterator<FacesMessage> iter = context.getMessages(); iter.hasNext();)
296         {
297             FacesMessage message = (FacesMessage) iter.next();
298             if(FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
299             {
300                 return true;
301             }
302         }
303         return false;
304     }
305 
306     /**
307      * @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
308      */
309     public void encodeEnd(FacesContext context) throws IOException
310     {
311         setRowIndex(-1);
312         super.encodeEnd(context);
313     }
314 
315     @SuppressWarnings("unchecked")
316     public void setRowIndex(int rowIndex)
317     {
318         if ( (isPreserveRowComponentState() || isRowStatePreserved()) && _initialDescendantFullComponentState != null)
319         {
320             setRowIndexPreserveComponentState(rowIndex);
321         }
322         else
323         {
324             setRowIndexWithoutPreserveComponentState(rowIndex);
325         }
326     }
327 
328     private void setRowIndexWithoutPreserveComponentState(int rowIndex)
329     {
330         if (rowIndex < -1)
331         {
332             throw new IllegalArgumentException("rowIndex is less than -1");
333         }
334 
335         if (_rowIndex == rowIndex)
336         {
337             return;
338         }
339 
340         FacesContext facesContext = getFacesContext();
341 
342         if (_rowIndex == -1)
343         {
344             if (_initialDescendantComponentState == null)
345             {
346                 _initialDescendantComponentState = saveDescendantComponentStates();
347             }
348         }
349         else
350         {
351             _rowStates.put(getContainerClientId(facesContext),
352                             saveDescendantComponentStates());
353         }
354 
355         _rowIndex = rowIndex;
356 
357         DataModel dataModel = getDataModel();
358         dataModel.setRowIndex(rowIndex);
359 
360         String var = getVar();
361         if (rowIndex == -1)
362         {
363             if (var != null)
364             {
365                 facesContext.getExternalContext().getRequestMap().remove(var);
366             }
367         }
368         else
369         {
370             if (var != null)
371             {
372                 if (isRowAvailable())
373                 {
374                     Object rowData = dataModel.getRowData();
375                     facesContext.getExternalContext().getRequestMap().put(var,
376                                     rowData);
377                 }
378                 else
379                 {
380                     facesContext.getExternalContext().getRequestMap().remove(
381                                     var);
382                 }
383             }
384         }
385 
386         if (_rowIndex == -1)
387         {
388             restoreDescendantComponentStates(_initialDescendantComponentState);
389         }
390         else
391         {
392             Object rowState = _rowStates.get(getContainerClientId(facesContext));
393             if (rowState == null)
394             {
395                 restoreDescendantComponentStates(_initialDescendantComponentState);
396             }
397             else
398             {
399                 restoreDescendantComponentStates(rowState);
400             }
401         }
402     }
403     
404     private void setRowIndexPreserveComponentState(int rowIndex)
405     {
406         if (rowIndex < -1)
407         {
408             throw new IllegalArgumentException("rowIndex is less than -1");
409         }
410 
411         if (_rowIndex == rowIndex)
412         {
413             return;
414         }
415 
416         FacesContext facesContext = getFacesContext();
417 
418         if (_initialDescendantFullComponentState != null)
419         {
420             //Just save the row
421             Map<String, Object> sm = saveFullDescendantComponentStates(facesContext);
422             if (sm != null && !sm.isEmpty())
423             {
424                 _rowDeltaStates.put(getContainerClientId(facesContext), sm);
425             }
426         }
427 
428         _rowIndex = rowIndex;
429 
430         DataModel dataModel = getDataModel();
431         dataModel.setRowIndex(rowIndex);
432 
433         String var = getVar();
434         if (rowIndex == -1)
435         {
436             if (var != null)
437             {
438                 facesContext.getExternalContext().getRequestMap().remove(var);
439             }
440         }
441         else
442         {
443             if (var != null)
444             {
445                 if (isRowAvailable())
446                 {
447                     Object rowData = dataModel.getRowData();
448                     facesContext.getExternalContext().getRequestMap().put(var,
449                                     rowData);
450                 }
451                 else
452                 {
453                     facesContext.getExternalContext().getRequestMap().remove(
454                                     var);
455                 }
456             }
457         }
458 
459         if (_initialDescendantFullComponentState != null)
460         {
461             Map<String, Object> rowState = _rowDeltaStates.get(getContainerClientId(facesContext));
462             if (rowState == null)
463             {
464                 //Restore as original
465                 restoreFullDescendantComponentStates(facesContext, _initialDescendantFullComponentState);
466             }
467             else
468             {
469                 //Restore first original and then delta
470                 restoreFullDescendantComponentDeltaStates(facesContext, rowState, _initialDescendantFullComponentState);
471             }
472         }
473     }
474 
475     @SuppressWarnings("unchecked")
476     protected void restoreDescendantComponentStates(Object state)
477     {
478         restoreDescendantComponentStates(getChildren().iterator(), state, false);
479     }
480     
481     @SuppressWarnings("unchecked")
482     protected void restoreDescendantComponentStates(Iterator<UIComponent> childIterator,
483             Object state, boolean restoreChildFacets)
484     {
485         Iterator descendantStateIterator = null;
486         while (childIterator.hasNext())
487         {
488             if (descendantStateIterator == null && state != null)
489             {
490                 descendantStateIterator = ((Collection) state).iterator();
491             }
492             UIComponent component = (UIComponent) childIterator.next();
493             // reset the client id (see spec 3.1.6)
494             component.setId(component.getId());
495             if(!component.isTransient())
496             {
497                 Object childState = null;
498                 Object descendantState = null;
499                 if (descendantStateIterator != null
500                         && descendantStateIterator.hasNext())
501                 {
502                     Object[] object = (Object[]) descendantStateIterator.next();
503                     childState = object[0];
504                     descendantState = object[1];
505                 }
506                 if (childState != null && component instanceof EditableValueHolder)
507                 {
508                     ((EditableValueHolderState) childState)
509                             .restoreState((EditableValueHolder) component);
510                 }
511                 Iterator<UIComponent> childsIterator;
512                 if (restoreChildFacets)
513                 {
514                     childsIterator = component.getFacetsAndChildren();
515                 }
516                 else
517                 {
518                     childsIterator = component.getChildren().iterator();
519                 }
520                 restoreDescendantComponentStates(childsIterator, descendantState,
521                         true);
522             }
523         }
524     }
525 
526     @SuppressWarnings("unchecked")
527     protected Object saveDescendantComponentStates()
528     {
529         return saveDescendantComponentStates(getChildren().iterator(), false);
530     }
531     
532     @SuppressWarnings("unchecked")
533     protected Object saveDescendantComponentStates(Iterator<UIComponent> childIterator,
534             boolean saveChildFacets)
535     {
536         Collection childStates = null;
537         while (childIterator.hasNext())
538         {
539             if (childStates == null)
540             {
541                 childStates = new ArrayList();
542             }
543             UIComponent child = (UIComponent) childIterator.next();
544             if(!child.isTransient())
545             {
546                 Iterator<UIComponent> childsIterator;
547                 if (saveChildFacets)
548                 {
549                     childsIterator = child.getFacetsAndChildren();
550                 }
551                 else
552                 {
553                     childsIterator = child.getChildren().iterator();
554                 }
555                 Object descendantState = saveDescendantComponentStates(
556                         childsIterator, true);
557                 Object state = null;
558                 if (child instanceof EditableValueHolder)
559                 {
560                     state = new EditableValueHolderState(
561                             (EditableValueHolder) child);
562                 }
563                 childStates.add(new Object[] { state, descendantState });
564             }
565         }
566         return childStates;
567     }
568     
569     
570     
571     
572     @Override
573     public void markInitialState()
574     {
575         if (isPreserveRowComponentState() || isRowStatePreserved())
576         {
577             if (getFacesContext().getAttributes().containsKey("javax.faces.view.ViewDeclarationLanguage.IS_BUILDING_INITIAL_STATE"))
578             {
579                 _initialDescendantFullComponentState = saveDescendantInitialComponentStates(getFacesContext());
580             }
581         }
582         super.markInitialState();
583     }
584 
585     protected void restoreFullDescendantComponentStates(FacesContext facesContext, Object initialState)
586     {
587         restoreFullDescendantComponentStates(facesContext, getChildren().iterator(), initialState, false);
588     }
589 
590     protected void restoreFullDescendantComponentStates(FacesContext facesContext,
591             Iterator<UIComponent> childIterator, Object initialState,
592             boolean restoreChildFacets)
593     {
594         Iterator<? extends Object[]> descendantStateIterator = null;
595         while (childIterator.hasNext())
596         {
597             if (descendantStateIterator == null && initialState != null)
598             {
599                 descendantStateIterator = ((Collection<? extends Object[]>) initialState)
600                         .iterator();
601             }
602             UIComponent component = childIterator.next();
603 
604             // reset the client id (see spec 3.1.6)
605             component.setId(component.getId());
606             if (!component.isTransient())
607             {
608                 Object childState = null;
609                 Object descendantState = null;
610                 String childId = null;
611                 if (descendantStateIterator != null
612                         && descendantStateIterator.hasNext())
613                 {
614                     do
615                     {
616                         Object[] object = descendantStateIterator.next();
617                         childState = object[0];
618                         descendantState = object[1];
619                         childId = (String) object[2];
620                     }
621                     while(descendantStateIterator.hasNext() && !component.getId().equals(childId));
622                     
623                     if (!component.getId().equals(childId))
624                     {
625                         // cannot apply initial state to components correctly.
626                         throw new IllegalStateException("Cannot restore row correctly.");
627                     }
628                 }
629                 
630                 component.clearInitialState();
631                 component.restoreState(facesContext, childState);
632                 component.markInitialState();
633                 
634                 Iterator<UIComponent> childsIterator;
635                 if (restoreChildFacets)
636                 {
637                     childsIterator = component.getFacetsAndChildren();
638                 }
639                 else
640                 {
641                     childsIterator = component.getChildren().iterator();
642                 }
643                 restoreFullDescendantComponentStates(facesContext, childsIterator,
644                         descendantState, true);
645             }
646         }
647     }
648 
649     protected Collection<Object[]> saveDescendantInitialComponentStates(FacesContext facesContext)
650     {
651         return saveDescendantInitialComponentStates(facesContext, getChildren().iterator(), false);
652     }
653     
654     protected Collection<Object[]> saveDescendantInitialComponentStates(FacesContext facesContext,
655             Iterator<UIComponent> childIterator, boolean saveChildFacets)
656     {
657         Collection<Object[]> childStates = null;
658         while (childIterator.hasNext())
659         {
660             if (childStates == null)
661             {
662                 childStates = new ArrayList<Object[]>();
663             }
664 
665             UIComponent child = childIterator.next();
666             if (!child.isTransient())
667             {
668                 // Add an entry to the collection, being an array of two
669                 // elements. The first element is the state of the children
670                 // of this component; the second is the state of the current
671                 // child itself.
672 
673                 Iterator<UIComponent> childsIterator;
674                 if (saveChildFacets)
675                 {
676                     childsIterator = child.getFacetsAndChildren();
677                 }
678                 else
679                 {
680                     childsIterator = child.getChildren().iterator();
681                 }
682                 Object descendantState = saveDescendantInitialComponentStates(
683                         facesContext, childsIterator, true);
684                 Object state = null;
685                 if (child.initialStateMarked())
686                 {
687                     child.clearInitialState();
688                     state = child.saveState(facesContext); 
689                     child.markInitialState();
690                 }
691                 else
692                 {
693                     state = child.saveState(facesContext);
694                 }
695                 
696                 childStates.add(new Object[] { state, descendantState, child.getId()});
697             }
698         }
699         return childStates;
700     }
701 
702     protected Map<String,Object> saveFullDescendantComponentStates(FacesContext facesContext)
703     {
704         return saveFullDescendantComponentStates(facesContext, null, getChildren().iterator(), false, getContainerClientId(facesContext));
705     }
706 
707     protected Map<String,Object> saveFullDescendantComponentStates(FacesContext facesContext, Map<String,Object> stateMap,
708             Iterator<UIComponent> childIterator, boolean saveChildFacets, String containerClientId)
709     {
710         while (childIterator.hasNext())
711         {
712             UIComponent child = childIterator.next();
713             if (!child.isTransient())
714             {
715                 // Add an entry to the collection, being an array of two
716                 // elements. The first element is the state of the children
717                 // of this component; the second is the state of the current
718                 // child itself.
719 
720                 Iterator<UIComponent> childsIterator;
721                 if (saveChildFacets)
722                 {
723                     childsIterator = child.getFacetsAndChildren();
724                 }
725                 else
726                 {
727                     childsIterator = child.getChildren().iterator();
728                 }
729                 stateMap = saveFullDescendantComponentStates(facesContext, stateMap,
730                         childsIterator, true, containerClientId);
731                 Object state = child.saveState(facesContext);
732                 if (state != null)
733                 {
734                     if (stateMap == null)
735                     {
736                         stateMap = new HashMap<String,Object>();
737                     }
738                     stateMap.put(child.getClientId(facesContext).substring(containerClientId.length()+1), state);
739                 }
740             }
741         }
742         return stateMap;
743     }
744     
745     protected void restoreFullDescendantComponentDeltaStates(FacesContext facesContext,
746             Map<String, Object> rowState, Object initialState)
747     {
748         restoreFullDescendantComponentDeltaStates(facesContext, getChildren().iterator(), rowState, initialState, false, getContainerClientId(facesContext));
749     }
750     
751     protected void restoreFullDescendantComponentDeltaStates(FacesContext facesContext,
752             Iterator<UIComponent> childIterator, Map<String, Object> state, Object initialState,
753             boolean restoreChildFacets, String containerClientId)
754     {
755         Iterator<? extends Object[]> descendantFullStateIterator = null;
756         while (childIterator.hasNext())
757         {
758             if (descendantFullStateIterator == null && initialState != null)
759             {
760                 descendantFullStateIterator = ((Collection<? extends Object[]>) initialState).iterator();
761             }
762             UIComponent component = childIterator.next();
763 
764             // reset the client id (see spec 3.1.6)
765             component.setId(component.getId());
766             if (!component.isTransient())
767             {
768                 Object childInitialState = null;
769                 Object descendantInitialState = null;
770                 Object childState = null;
771                 String childId = null;
772                 childState = (state == null) ? null : state.get(component.getClientId(facesContext).substring(containerClientId.length()+1));
773                 if (descendantFullStateIterator != null
774                         && descendantFullStateIterator.hasNext())
775                 {
776                     do
777                     {
778                         Object[] object = descendantFullStateIterator.next();
779                         childInitialState = object[0];
780                         descendantInitialState = object[1];
781                         childId = (String) object[2];
782                     }while(descendantFullStateIterator.hasNext() && !component.getId().equals(childId));
783                     
784                     if (!component.getId().equals(childId))
785                     {
786                         // cannot apply initial state to components correctly. State is corrupt
787                         throw new IllegalStateException("Cannot restore row correctly.");
788                     }
789                 }
790                 
791                 component.clearInitialState();
792                 if (childInitialState != null)
793                 {
794                     component.restoreState(facesContext, childInitialState);
795                     component.markInitialState();
796                     component.restoreState(facesContext, childState);
797                 }
798                 else
799                 {
800                     component.restoreState(facesContext, childState);
801                     component.markInitialState();
802                 }
803                 
804                 Iterator<UIComponent> childsIterator;
805                 if (restoreChildFacets)
806                 {
807                     childsIterator = component.getFacetsAndChildren();
808                 }
809                 else
810                 {
811                     childsIterator = component.getChildren().iterator();
812                 }
813                 restoreFullDescendantComponentDeltaStates(facesContext, childsIterator,
814                         state, descendantInitialState , true, containerClientId);
815             }
816         }
817     }
818     
819     @Override
820     public void restoreState(FacesContext context, Object state)
821     {
822         if (state == null)
823         {
824             return;
825         }
826         
827         Object values[] = (Object[]) state;
828         super.restoreState(context, values[0]);
829         Object restoredRowStates = UIComponentBase.restoreAttachedState(context, values[1]);
830         if (restoredRowStates == null)
831         {
832             if (!_rowDeltaStates.isEmpty())
833             {
834                 _rowDeltaStates.clear();
835             }
836         }
837         else
838         {
839             _rowDeltaStates = (Map<String, Map<String, Object> >) restoredRowStates;
840         } 
841     }
842 
843     @Override
844     public Object saveState(FacesContext context)
845     {
846         if (initialStateMarked())
847         {
848             Object parentSaved = super.saveState(context);
849             if (parentSaved == null &&_rowDeltaStates.isEmpty())
850             {
851                 return null;
852             }
853             else
854             {
855                 Object values[] = new Object[2];
856                 values[0] = parentSaved;
857                 values[1] = UIComponentBase.saveAttachedState(context, _rowDeltaStates);
858                 return values; 
859             }
860         }
861         else
862         {
863             Object values[] = new Object[2];
864             values[0] = super.saveState(context);
865             values[1] = UIComponentBase.saveAttachedState(context, _rowDeltaStates);
866             return values;
867         }
868     }
869 
870     public void setValueBinding(String name, ValueBinding binding)
871     {
872         if (name == null)
873         {
874             throw new NullPointerException("name");
875         }
876         else if (name.equals("value"))
877         {
878             _dataModelMap.clear();
879         }
880         else if (name.equals("var") || name.equals("rowIndex"))
881         {
882             throw new IllegalArgumentException(
883                     "You can never set the 'rowIndex' or the 'var' attribute as a value-binding. Set the property directly instead. Name " + name);
884         }
885         super.setValueBinding(name, binding);
886     }
887     
888     public void setValueExpression(String name, ValueExpression binding)
889     {
890         if (name == null)
891         {
892             throw new NullPointerException("name");
893         }
894         else if (name.equals("value"))
895         {
896             _dataModelMap.clear();
897         }
898         else if (name.equals("var") || name.equals("rowIndex"))
899         {
900             throw new IllegalArgumentException(
901                     "You can never set the 'rowIndex' or the 'var' attribute as a value-binding. Set the property directly instead. Name " + name);
902         }
903         super.setValueExpression(name, binding);
904     }
905 
906     /**
907      * @see javax.faces.component.UIData#setValue(java.lang.Object)
908      */
909     public void setValue(Object value)
910     {
911         super.setValue(value);
912         _dataModelMap.clear();
913         _rowStates.clear();
914         _rowDeltaStates.clear();
915         _isValidChilds = true;
916     }
917 
918     @SuppressWarnings("unchecked")
919     protected DataModel getDataModel()
920     {
921         DataModel dataModel = null;
922         String clientID = "";
923         
924         UIComponent parent = getParent();
925         if (parent != null) 
926         {
927             clientID = parent.getContainerClientId(getFacesContext());
928         }
929         dataModel = (DataModel) _dataModelMap.get(clientID);
930         if (dataModel == null)
931         {
932             dataModel = createDataModel();
933             _dataModelMap.put(clientID, dataModel);
934         }               
935         
936         return dataModel;
937     }
938 
939     @SuppressWarnings("unchecked")
940     protected void setDataModel(DataModel datamodel)
941     {
942         UIComponent parent = getParent();
943         String clientID = "";
944         if(parent != null)
945         {
946             clientID = parent.getContainerClientId(getFacesContext());
947         }
948         _dataModelMap.put(clientID, datamodel);
949     }
950 
951     /**
952      * Creates a new DataModel around the current value.
953      */
954     @SuppressWarnings("unchecked")
955     protected DataModel createDataModel()
956     {
957         Object value = getValue();
958         if (value == null)
959         {
960             return EMPTY_DATA_MODEL;
961         }
962         else if (value instanceof DataModel)
963         {
964             return (DataModel) value;
965         }
966         else if (value instanceof List)
967         {
968             return new ListDataModel((List) value);
969         }
970         // accept a Collection is not supported in the Spec
971         else if (value instanceof Collection)
972         {
973             return new ListDataModel(new ArrayList((Collection) value));
974         }
975         else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass()))
976         {
977             return new ArrayDataModel((Object[]) value);
978         }
979         else if (value instanceof ResultSet)
980         {
981             return new ResultSetDataModel((ResultSet) value);
982         }
983         else if (value instanceof Result)
984         {
985             return new ResultDataModel((Result) value);
986         }
987         else
988         {
989             return new ScalarDataModel(value);
990         }
991     }
992     
993     @SuppressWarnings("unchecked")
994     private static final DataModel EMPTY_DATA_MODEL = new _SerializableDataModel()
995     {
996         public boolean isRowAvailable()
997         {
998             return false;
999         }
1000 
1001         public int getRowCount()
1002         {
1003             return 0;
1004         }
1005 
1006         public Object getRowData()
1007         {
1008             throw new IllegalArgumentException();
1009         }
1010 
1011         public int getRowIndex()
1012         {
1013             return -1;
1014         }
1015 
1016         public void setRowIndex(int i)
1017         {
1018             if (i < -1)
1019                 throw new IndexOutOfBoundsException("Index < 0 : " + i);
1020         }
1021 
1022         public Object getWrappedData()
1023         {
1024             return null;
1025         }
1026 
1027         public void setWrappedData(Object obj)
1028         {
1029             if (obj == null)
1030                 return; //Clearing is allowed
1031             throw new UnsupportedOperationException(this.getClass().getName()
1032                             + " UnsupportedOperationException");
1033         }
1034     };
1035 
1036     private class EditableValueHolderState
1037     {
1038         private final Object _value;
1039         private final boolean _localValueSet;
1040         private final boolean _valid;
1041         private final Object _submittedValue;
1042 
1043         public EditableValueHolderState(EditableValueHolder evh)
1044         {
1045             _value = evh.getLocalValue();
1046             _localValueSet = evh.isLocalValueSet();
1047             _valid = evh.isValid();
1048             _submittedValue = evh.getSubmittedValue();
1049         }
1050 
1051         public void restoreState(EditableValueHolder evh)
1052         {
1053             evh.setValue(_value);
1054             evh.setLocalValueSet(_localValueSet);
1055             evh.setValid(_valid);
1056             evh.setSubmittedValue(_submittedValue);
1057         }
1058     }
1059     
1060     // Property: forceId
1061     
1062     /**
1063      * If true, this component will force the use of the specified id when rendering.
1064      * 
1065      * @JSFProperty
1066      *   literalOnly = "true"
1067      *   defaultValue = "false"
1068      *   
1069      * @return
1070      */
1071     public boolean isForceId()
1072     {
1073         Object value = getStateHelper().get(PropertyKeys.forceId);
1074         if (value != null)
1075         {
1076             return (Boolean) value;        
1077         }
1078         return false;  
1079     }
1080 
1081     public void setForceId(boolean forceId)
1082     {
1083         getStateHelper().put(PropertyKeys.forceId, forceId );
1084     }
1085     // Property: forceIdIndex
1086     
1087     /**
1088      * If false, this component will not append a '[n]' suffix 
1089      * (where 'n' is the row index) to components that are 
1090      * contained within a "list." This value will be true by 
1091      * default and the value will be ignored if the value of 
1092      * forceId is false (or not specified.)
1093      * 
1094      * @JSFProperty
1095      *   literalOnly = "true"
1096      *   defaultValue = "true"
1097      *   
1098      * @return
1099      */
1100     public boolean isForceIdIndex()
1101     {
1102         Object value = getStateHelper().get(PropertyKeys.forceIdIndex);
1103         if (value != null)
1104         {
1105             return (Boolean) value;        
1106         }
1107         return true;
1108     }
1109 
1110     public void setForceIdIndex(boolean forceIdIndex)
1111     {
1112         getStateHelper().put(PropertyKeys.forceIdIndex, forceIdIndex );
1113     }
1114     
1115     /**
1116      * Remove all preserved row state for the dataTable
1117      */
1118     public void clearRowStates()
1119     {
1120         if (isPreserveRowComponentState() || isRowStatePreserved())
1121         {
1122             _rowDeltaStates.clear();
1123         }
1124         else
1125         {
1126             _rowStates.clear();
1127         }
1128     }
1129     
1130     /**
1131      * Remove preserved row state for deleted row and adjust row state to reflect deleted row.
1132      * @param deletedIndex index of row to delete
1133      */
1134     public void deleteRowStateForRow(int deletedIndex)
1135     {
1136         // save row index
1137         int savedRowIndex = getRowIndex();
1138         
1139         FacesContext facesContext = getFacesContext();
1140          setRowIndex(deletedIndex);
1141         String currentRowStateKey = getContainerClientId(facesContext);
1142 
1143         Object rowKey = getRowKey(); 
1144         if (rowKey != null)
1145         {
1146             setRowIndex(deletedIndex);
1147             if (isPreserveRowComponentState() || isRowStatePreserved())
1148             {
1149                 _rowDeltaStates.remove(currentRowStateKey);
1150             }
1151             else
1152             {
1153                 _rowStates.remove(currentRowStateKey);
1154             }
1155             setRowIndex(savedRowIndex);
1156         }
1157         else
1158         {
1159             // copy next rowstate to current row for each row from deleted row onward.
1160             int rowCount = getRowCount();
1161             if (isPreserveRowComponentState() || isRowStatePreserved())
1162             {
1163                 for (int index = deletedIndex + 1; index < rowCount; ++index)
1164                 {
1165                     setRowIndex(index);
1166                     String nextRowStateKey = getContainerClientId(facesContext);
1167         
1168                     Map<String, Object> nextRowState = _rowDeltaStates.get(nextRowStateKey);
1169                     if (nextRowState == null)
1170                     {
1171                         _rowDeltaStates.remove(currentRowStateKey);
1172                     }
1173                     else
1174                     {
1175                         _rowDeltaStates.put(currentRowStateKey, nextRowState);
1176                     }
1177                     currentRowStateKey = nextRowStateKey;
1178                 }
1179     
1180                 // restore saved row index
1181                 setRowIndex(savedRowIndex);
1182         
1183                 // Remove last row
1184                 _rowDeltaStates.remove(currentRowStateKey);
1185             }
1186             else
1187             {
1188                 for (int index = deletedIndex + 1; index < rowCount; ++index)
1189                 {
1190                     setRowIndex(index);
1191                     String nextRowStateKey = getContainerClientId(facesContext);
1192         
1193                     Object nextRowState = _rowStates.get(nextRowStateKey);
1194                     if (nextRowState == null)
1195                     {
1196                         _rowStates.remove(currentRowStateKey);
1197                     }
1198                     else
1199                     {
1200                         _rowStates.put(currentRowStateKey, nextRowState);
1201                     }
1202                     currentRowStateKey = nextRowStateKey;
1203                 }
1204     
1205                 // restore saved row index
1206                 setRowIndex(savedRowIndex);
1207         
1208                 // Remove last row
1209                 _rowStates.remove(currentRowStateKey);
1210             }
1211         }
1212     }
1213     
1214     
1215     /**
1216      * Indicates whether the state for a component in each row should not be 
1217      * discarded before the datatable is rendered again.
1218      * 
1219      * This property is similar to tomahawk t:dataTable preserveRowStates
1220      * 
1221      * This will only work reliable if the datamodel of the 
1222      * datatable did not change either by sorting, removing or 
1223      * adding rows. Default: false
1224      * 
1225      * @return
1226      */
1227     @JSFProperty(faceletsOnly=true, literalOnly=true)
1228     public boolean isPreserveRowComponentState()
1229     {
1230         Boolean b = (Boolean) getStateHelper().get(PropertyKeys.preserveRowComponentState);
1231         return b == null ? false : b.booleanValue(); 
1232     }
1233     
1234     public void setPreserveRowComponentState(boolean preserveComponentState)
1235     {
1236         getStateHelper().put(PropertyKeys.preserveRowComponentState, preserveComponentState);
1237     }
1238     
1239     /**
1240      * Used to assign a value expression that identify in a unique way a row. This value
1241      * will be used later instead of rowIndex as a key to be appended to the container 
1242      * client id using getDerivedSubClientId() method.  
1243      * 
1244      * @return
1245      */
1246     @JSFProperty
1247     public Object getRowKey()
1248     {
1249         return getStateHelper().eval(PropertyKeys.rowKey);
1250     }
1251     
1252     public void setRowKey(Object rowKey)
1253     {
1254         getStateHelper().put(PropertyKeys.rowKey, rowKey);
1255     }
1256     
1257     /**
1258      * This attribute is used to append an unique prefix when rowKey is not used, to prevent
1259      * a key match a existing component id (note two different components can't have the
1260      * same unique id).
1261      * 
1262      * @return
1263      */
1264     @JSFProperty(defaultValue="r_id_")
1265     public String getDerivedRowKeyPrefix()
1266     {
1267         return (String) getStateHelper().eval(PropertyKeys.derivedRowKeyPrefix, UNIQUE_ROW_ID_PREFIX);
1268     }
1269     
1270     public void setDerivedRowKeyPrefix(String derivedRowKeyPrefix)
1271     {
1272         getStateHelper().put(PropertyKeys.derivedRowKeyPrefix, derivedRowKeyPrefix);
1273     }
1274 
1275     /**
1276      * Return the fragment to be used on the container client id to
1277      * identify a row. As a side effect, it will be used to indicate 
1278      * a row component state and a datamodel in nested datatable case.
1279      * 
1280      * <p>
1281      * The returned value must comply with the following rules:
1282      * </p>
1283      * <ul>
1284      * <li> Can be followed by: letters (A-Za-z), digits (0-9), hyphens ("-"), 
1285      *   underscores ("_"), colons (":"), and periods (".") </li>
1286      * <li> Values are case-sensitive </li>
1287      * </ul>
1288      * 
1289      * @return
1290      */
1291     protected String getDerivedSubClientId()
1292     {
1293         Object key = getRowKey();
1294         if (key == null)
1295         {
1296             return _SubIdConverter.encode(Integer.toString(getRowIndex()));
1297         }
1298         else
1299         {
1300             return getDerivedRowKeyPrefix() + _SubIdConverter.encode(key.toString());
1301         }
1302     }
1303 
1304     protected enum PropertyKeys
1305     {
1306         preserveRowStates
1307         , forceId
1308         , forceIdIndex
1309         , preserveRowComponentState
1310         , rowKey
1311         , derivedRowKeyPrefix
1312         , rowStatePreserved
1313     }
1314 }