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.*;
24  import javax.el.ValueExpression;
25  import javax.faces.FacesException;
26  import javax.faces.application.FacesMessage;
27  import javax.faces.context.FacesContext;
28  import javax.faces.event.AbortProcessingException;
29  import javax.faces.event.FacesEvent;
30  import javax.faces.event.FacesListener;
31  import javax.faces.event.PhaseId;
32  import javax.faces.model.*;
33  import javax.servlet.jsp.jstl.sql.Result;
34  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
35  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFacet;
36  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
37  
38  /**
39   * Represents an abstraction of a component which has multiple "rows" of data.
40   * <p>
41   * The children of this component are expected to be UIColumn components.
42   * <p>
43   * Note that the same set of child components are reused to implement each row of the table in turn during
44   * such phases as apply-request-values and render-response. Altering any of the members of these components
45   * therefore affects the attribute for every row, except for the following members:
46   * <ul>
47   * <li>submittedValue
48   * <li>value (where no EL binding is used)
49   * <li>valid
50   * </ul>
51   * <p>
52   * This reuse of the child components also means that it is not possible to save a reference to a component
53   * during table processing, then access it later and expect it to still represent the same row of the table.
54   * <h1>
55   * Implementation Notes
56   * </h1>
57   * <p>
58   * Each of the UIColumn children of this component has a few component children of its own to render the contents
59   * of the table cell. However there can be a very large number of rows in a table, so it isn't efficient for the
60   * UIColumn and all its child objects to be duplicated for each row in the table. Instead the "flyweight" pattern
61   * is used where a serialized state is held for each row. When setRowIndex is invoked, the UIColumn objects and
62   * their children serialize their current state then reinitialise themselves from the appropriate saved state.
63   * This allows a single set of real objects to represent multiple objects which have the same types but potentially
64   * different internal state. When a row is selected for the first time, its state is set to a clean "initial" state.
65   * Transient components (including any read-only component) do not save their state; they are just reinitialised as
66   * required. The state saved/restored when changing rows is not the complete component state, just the fields that
67   * are expected to vary between rows: "submittedValue", "value", "isValid".
68   * </p>
69   * <p>
70   * Note that a table is a "naming container", so that components within the table have their ids prefixed with the
71   * id of the table. Actually, when setRowIndex has been called on a table with id of "zzz" the table pretends to
72   * its children that its ID is "zzz_n" where n is the row index. This means that renderers for child components which
73   * call component.getClientId automatically get ids of form "zzz_n:childId" thus ensuring that components in
74   * different rows of the table get different ids.
75   * </p>
76   * <p>
77   * When decoding a submitted page, this class iterates over all its possible rowIndex values, restoring the
78   * appropriate serialized row state then calling processDecodes on the child components. Because the child
79   * components (or their renderers) use getClientId to get the request key to look for parameter data, and because
80   * this object pretends to have a different id per row ("zzz_n") a single child component can decode data from each
81   * table row in turn without being aware that it is within a table. The table's data model is updated before each
82   * call to child.processDecodes, so the child decode method can assume that the data model's rowData points to the
83   * model object associated with the row currently being decoded. Exactly the same process applies for the later
84   * validation and updateModel phases.
85   * </p>
86   * <p>
87   * When the data model for the table is bound to a backing bean property, and no validation errors have occured
88   * during processing of a postback, the data model is refetched at the start of the rendering phase (ie after the
89   * update model phase) so that the contents of the data model can be changed as a result of the latest form
90   * submission. Because the saved row state must correspond to the elements within the data model, the row state
91   * must be discarded whenever a new data model is fetched; not doing this would cause all sorts of inconsistency
92   * issues. This does imply that changing the state of any of the members "submittedValue", "value" or "valid" of
93   * a component within the table during the invokeApplication phase has no effect on the rendering of the table.
94   * When a validation error has occurred, a new DataModel is <i>not</i> fetched, and the saved state of the child
95   * components is <i>not</i> discarded.
96   * </p>
97   * see Javadoc of the <a href="http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
98   * for more information.
99   *
100  * @author Manfred Geiler (latest modification by $Author: lu4242 $)
101  * @version $Revision: 1203044 $ $Date: 2011-11-16 23:22:07 -0500 (Wed, 16 Nov 2011) $
102  */
103 @JSFComponent(defaultRendererType = "javax.faces.Table")
104 public class UIData extends UIComponentBase
105                     implements NamingContainer
106 {
107     public static final String COMPONENT_FAMILY = "javax.faces.Data";
108     public static final String COMPONENT_TYPE = "javax.faces.Data"; // for unit tests
109 
110     private static final String FOOTER_FACET_NAME = "footer";
111     private static final String HEADER_FACET_NAME = "header";
112     private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
113     private static final int PROCESS_DECODES = 1;
114     private static final int PROCESS_VALIDATORS = 2;
115     private static final int PROCESS_UPDATES = 3;
116 
117     private static final Object[] LEAF_NO_STATE = new Object[]{null,null};
118 
119     private int _rowIndex = -1;
120     private String _var;
121 
122     // Holds for each row the states of the child components of this UIData.
123     // Note that only "partial" component state is saved: the component fields
124     // that are expected to vary between rows.
125     private Map _rowStates = new HashMap();
126 
127     /**
128      * Handle case where this table is nested inside another table.
129      * See method getDataModel for more details.
130      * <p>
131      * Key: parentClientId (aka rowId when nested within a parent table)
132      * Value: DataModel
133      */
134     private Map _dataModelMap = new HashMap();
135 
136     // will be set to false if the data should not be refreshed at the beginning of the encode phase
137     private boolean _isValidChilds = true;
138 
139     private Object _initialDescendantComponentState = null;
140 
141     private int _first;
142     private boolean _firstSet;
143     private int _rows;
144     private boolean _rowsSet;
145     private Object _value;
146 
147     private static class FacesEventWrapper extends FacesEvent
148     {
149         private static final long serialVersionUID = 6648047974065628773L;
150         private FacesEvent _wrappedFacesEvent;
151         private int _rowIndex;
152 
153         public FacesEventWrapper(FacesEvent facesEvent, int rowIndex,
154                 UIData redirectComponent)
155         {
156             super(redirectComponent);
157             _wrappedFacesEvent = facesEvent;
158             _rowIndex = rowIndex;
159         }
160 
161         @Override
162         public PhaseId getPhaseId()
163         {
164             return _wrappedFacesEvent.getPhaseId();
165         }
166 
167         @Override
168         public void setPhaseId(PhaseId phaseId)
169         {
170             _wrappedFacesEvent.setPhaseId(phaseId);
171         }
172 
173         @Override
174         public void queue()
175         {
176             _wrappedFacesEvent.queue();
177         }
178 
179         @Override
180         public String toString()
181         {
182             return _wrappedFacesEvent.toString();
183         }
184 
185         @Override
186         public boolean isAppropriateListener(FacesListener faceslistener)
187         {
188             return _wrappedFacesEvent.isAppropriateListener(faceslistener);
189         }
190 
191         @Override
192         public void processListener(FacesListener faceslistener)
193         {
194             _wrappedFacesEvent.processListener(faceslistener);
195         }
196 
197         public FacesEvent getWrappedFacesEvent()
198         {
199             return _wrappedFacesEvent;
200         }
201 
202         public int getRowIndex()
203         {
204             return _rowIndex;
205         }
206     }
207 
208 
209     private static final DataModel EMPTY_DATA_MODEL = new DataModel()
210     {
211         @Override
212         public boolean isRowAvailable()
213         {
214             return false;
215         }
216 
217         @Override
218         public int getRowCount()
219         {
220             return 0;
221         }
222 
223         @Override
224         public Object getRowData()
225         {
226             throw new IllegalArgumentException();
227         }
228 
229         @Override
230         public int getRowIndex()
231         {
232             return -1;
233         }
234 
235         @Override
236         public void setRowIndex(int i)
237         {
238             if (i < -1)
239                 throw new IllegalArgumentException();
240         }
241 
242         @Override
243         public Object getWrappedData()
244         {
245             return null;
246         }
247 
248         @Override
249         public void setWrappedData(Object obj)
250         {
251             if (obj == null)
252             {
253                 return; //Clearing is allowed
254             }
255             throw new UnsupportedOperationException(this.getClass().getName()
256                     + " UnsupportedOperationException");
257         }
258     };
259 
260     private class EditableValueHolderState
261     {
262         private final Object _value;
263         private final boolean _localValueSet;
264         private final boolean _valid;
265         private final Object _submittedValue;
266 
267         public EditableValueHolderState(EditableValueHolder evh)
268         {
269             _value = evh.getLocalValue();
270             _localValueSet = evh.isLocalValueSet();
271             _valid = evh.isValid();
272             _submittedValue = evh.getSubmittedValue();
273         }
274 
275         public void restoreState(EditableValueHolder evh)
276         {
277             evh.setValue(_value);
278             evh.setLocalValueSet(_localValueSet);
279             evh.setValid(_valid);
280             evh.setSubmittedValue(_submittedValue);
281         }
282     }
283 
284     /**
285      * Construct an instance of the UIData.
286      */
287     public UIData()
288     {
289         setRendererType("javax.faces.Table");
290     }
291 
292     @Override
293     public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback) throws FacesException {
294         if (context == null || clientId == null || callback == null)
295         {
296             throw new NullPointerException();
297         }
298 
299         //searching for this component?
300         boolean returnValue = this.getClientId(context).equals(clientId);
301 
302         boolean isCachedFacesContext = isCachedFacesContext();
303         if (!isCachedFacesContext)
304         {
305             setCachedFacesContext(context);
306         }
307         
308         try
309         {
310             if (returnValue)
311             {
312                 try
313                 {
314                     callback.invokeContextCallback(context, this);
315                 }
316                 catch (Exception e)
317                 {
318                     throw new FacesException(e);
319                 }
320                 return returnValue;
321             }
322     
323             //Now Look throught facets on this UIComponent
324             for (Iterator<UIComponent> it = this.getFacets().values().iterator(); !returnValue && it.hasNext();)
325             {
326                 returnValue = it.next().invokeOnComponent(context, clientId, callback);
327             }
328     
329             if (returnValue == true)
330             {
331                 return returnValue;
332             }
333             
334             //Now we have to check if it is searching an inner component
335             String baseClientId = super.getClientId(context);
336             
337             // is the component an inner component?
338             if (clientId.startsWith(baseClientId))
339             {
340                 // Check if the clientId for the component, which we 
341                 // are looking for, has a rowIndex attached
342                 String subId = clientId.substring(baseClientId.length() + 1);
343                 //If the char next to baseClientId is the separator one and
344                 //the subId matches the regular expression
345                 if (clientId.charAt(baseClientId.length()) == NamingContainer.SEPARATOR_CHAR && 
346                         subId.matches("[0-9]+"+NamingContainer.SEPARATOR_CHAR+".*"))
347                 {
348                     String clientRow = subId.substring(0, subId.indexOf(NamingContainer.SEPARATOR_CHAR));
349         
350                     //Now we save the current position
351                     int oldRow = this.getRowIndex();
352                     
353                     // try-finally --> make sure, that the old row index is restored
354                     try
355                     {
356                         //The conversion is safe, because its already checked on the
357                         //regular expresion
358                         this.setRowIndex(Integer.parseInt(clientRow));
359                         
360                         // check, if the row is available
361                         if (!isRowAvailable())
362                         {
363                             return false;
364                         }
365             
366                         for (Iterator<UIComponent> it1 = getChildren().iterator(); 
367                                 !returnValue && it1.hasNext();)
368                         {
369                             //recursive call to find the component
370                             returnValue = it1.next().invokeOnComponent(context, clientId, callback);
371                         }
372                     }
373                     finally
374                     {
375                         //Restore the old position. Doing this prevent
376                         //side effects.
377                         this.setRowIndex(oldRow);
378                     }
379                 }
380                 else
381                 {
382                     // MYFACES-2370: search the component in the childrens' facets too.
383                     // We have to check the childrens' facets here, because in MyFaces
384                     // the rowIndex is not attached to the clientId for the children of
385                     // facets of the UIColumns. However, in RI the rowIndex is 
386                     // attached to the clientId of UIColumns' Facets' children.
387                     for (Iterator<UIComponent> itChildren = this.getChildren().iterator();
388                             !returnValue && itChildren.hasNext();)
389                     {
390                         UIComponent child = itChildren.next();
391                         if (child instanceof UIColumn && clientId.equals(child.getClientId(context)))
392                         {
393                             try {
394                                 callback.invokeContextCallback(context, child);
395                             } catch (Exception e) {
396                                 throw new FacesException(e);
397                             }
398                             returnValue = true;
399                         }
400                         // process the child's facets
401                         for (Iterator<UIComponent> itChildFacets = child.getFacets().values().iterator(); 
402                                 !returnValue && itChildFacets.hasNext();)
403                         {
404                             //recursive call to find the component
405                             returnValue = itChildFacets.next().invokeOnComponent(context, clientId, callback);
406                         }
407                     }
408                 }
409             }
410         }
411         finally
412         {
413             if (!isCachedFacesContext)
414             {
415                 setCachedFacesContext(null);
416             }
417         }
418 
419         return returnValue;
420     }
421 
422     public void setFooter(UIComponent footer)
423     {
424         getFacets().put(FOOTER_FACET_NAME, footer);
425     }
426 
427     @JSFFacet
428     public UIComponent getFooter()
429     {
430         return (UIComponent) getFacets().get(FOOTER_FACET_NAME);
431     }
432 
433     public void setHeader(UIComponent header)
434     {
435         getFacets().put(HEADER_FACET_NAME, header);
436     }
437 
438     @JSFFacet
439     public UIComponent getHeader()
440     {
441         return (UIComponent) getFacets().get(HEADER_FACET_NAME);
442     }
443 
444     public boolean isRowAvailable()
445     {
446         return getDataModel().isRowAvailable();
447     }
448 
449     public int getRowCount()
450     {
451         return getDataModel().getRowCount();
452     }
453 
454     public Object getRowData()
455     {
456         return getDataModel().getRowData();
457     }
458 
459     public int getRowIndex()
460     {
461         return _rowIndex;
462     }
463 
464     /**
465      * Set the current row index that methods like getRowData use.
466      * <p>
467      * Param rowIndex can be -1, meaning "no row".
468      * <p>
469      * 
470      * @param rowIndex
471      */
472     public void setRowIndex(int rowIndex)
473     {
474         if (rowIndex < -1)
475         {
476             throw new IllegalArgumentException("rowIndex is less than -1");
477         }
478 
479         if (_rowIndex == rowIndex)
480         {
481             return;
482         }
483 
484         FacesContext facesContext = getFacesContext();
485 
486         if (_rowIndex == -1)
487         {
488             if (_initialDescendantComponentState == null)
489             {
490                 // Create a template that can be used to initialise any row
491                 // that we haven't visited before, ie a "saved state" that can
492                 // be pushed to the "restoreState" method of all the child
493                 // components to set them up to represent a clean row.
494                 _initialDescendantComponentState = saveDescendantComponentStates(this, false, false);
495             }
496         }
497         else
498         {
499             // If no initial component state, there are no EditableValueHolder instances,
500             // and that means there is no state to be saved for the current row, so we can
501             // skip row state saving code safely.
502             if (_initialDescendantComponentState != null)
503             {
504                 // We are currently positioned on some row, and are about to
505                 // move off it, so save the (partial) state of the components
506                 // representing the current row. Later if this row is revisited
507                 // then we can restore this state.
508                 Collection<Object[]> savedRowState = saveDescendantComponentStates(this, false, false);
509                 if (savedRowState != null)
510                 {
511                     _rowStates.put(getClientId(facesContext), savedRowState);
512                 }
513             }
514         }
515 
516         _rowIndex = rowIndex;
517 
518         DataModel dataModel = getDataModel();
519         dataModel.setRowIndex(rowIndex);
520 
521         String var = _var;
522         if (rowIndex == -1)
523         {
524             if (var != null)
525             {
526                 facesContext.getExternalContext().getRequestMap().remove(var);
527             }
528         }
529         else
530         {
531             if (var != null)
532             {
533                 if (isRowAvailable())
534                 {
535                     Object rowData = dataModel.getRowData();
536                     facesContext.getExternalContext().getRequestMap().put(var,
537                             rowData);
538                 }
539                 else
540                 {
541                     facesContext.getExternalContext().getRequestMap().remove(
542                             var);
543                 }
544             }
545         }
546 
547         if (_rowIndex == -1)
548         {
549             // reset components to initial state
550             // If no initial state, skip row restore state code
551             if (_initialDescendantComponentState != null)
552             {
553                 restoreDescendantComponentStates(this, false, _initialDescendantComponentState, false);
554             }
555             else
556             {
557                 restoreDescendantComponentWithoutRestoreState(this, false, false);
558             }
559         }
560         else
561         {
562             Object rowState = _rowStates.get(getClientId(facesContext));
563             if (rowState == null)
564             {
565                 // We haven't been positioned on this row before, so just
566                 // configure the child components of this component with
567                 // the standard "initial" state
568                 // If no initial state, skip row restore state code
569                 if (_initialDescendantComponentState != null)
570                 {
571                     restoreDescendantComponentStates(this, false, _initialDescendantComponentState, false);
572                 }
573                 else
574                 {
575                     restoreDescendantComponentWithoutRestoreState(this, false, false);
576                 }
577             }
578             else
579             {
580                 // We have been positioned on this row before, so configure
581                 // the child components of this component with the (partial)
582                 // state that was previously saved. Fields not in the
583                 // partial saved state are left with their original values.
584                 restoreDescendantComponentStates(this, false, rowState, false);
585             }
586         }
587     }
588 
589     /**
590      * Overwrite the state of the child components of this component with data previously saved by method
591      * saveDescendantComponentStates.
592      * <p>
593      * The saved state info only covers those fields that are expected to vary between rows of a table. Other fields are
594      * not modified.
595      */
596     @SuppressWarnings("unchecked")
597     private void restoreDescendantComponentStates(UIComponent parent, boolean iterateFacets, Object state,
598                                                   boolean restoreChildFacets)
599     {
600         int descendantStateIndex = -1;
601         List<? extends Object[]> stateCollection = null;
602         
603         if (iterateFacets && parent.getFacetCount() > 0)
604         {
605             Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
606             
607             while (childIterator.hasNext())
608             {
609                 UIComponent component = childIterator.next();
610 
611                 // reset the client id (see spec 3.1.6)
612                 component.setId(component.getId());
613                 if (!component.isTransient())
614                 {
615                     if (descendantStateIndex == -1)
616                     {
617                         stateCollection = ((List<? extends Object[]>) state);
618                         descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
619                     }
620                     
621                     if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
622                     {
623                         Object[] object = stateCollection.get(descendantStateIndex);
624                         if (object[0] != null && component instanceof EditableValueHolder)
625                         {
626                             ((EditableValueHolderState) object[0]).restoreState((EditableValueHolder) component);
627                         }
628                         // If there is descendant state to restore, call it recursively, otherwise
629                         // it is safe to skip iteration.
630                         if (object[1] != null)
631                         {
632                             restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
633                         }
634                         else
635                         {
636                             restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
637                         }
638                     }
639                     else
640                     {
641                         restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
642                     }
643                     descendantStateIndex++;
644                 }
645             }
646         }
647         
648         if (parent.getChildCount() > 0)
649         {
650             for (int i = 0; i < parent.getChildCount(); i++)
651             {
652                 UIComponent component = parent.getChildren().get(i);
653 
654                 // reset the client id (see spec 3.1.6)
655                 component.setId(component.getId());
656                 if (!component.isTransient())
657                 {
658                     if (descendantStateIndex == -1)
659                     {
660                         stateCollection = ((List<? extends Object[]>) state);
661                         descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
662                     }
663                     
664                     if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
665                     {
666                         Object[] object = stateCollection.get(descendantStateIndex);
667                         if (object[0] != null && component instanceof EditableValueHolder)
668                         {
669                             ((EditableValueHolderState) object[0]).restoreState((EditableValueHolder) component);
670                         }
671                         // If there is descendant state to restore, call it recursively, otherwise
672                         // it is safe to skip iteration.
673                         if (object[1] != null)
674                         {
675                             restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
676                         }
677                         else
678                         {
679                             restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
680                         }
681                     }
682                     else
683                     {
684                         restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
685                     }
686                     descendantStateIndex++;
687                 }
688             }
689         }
690     }
691 
692     /**
693      * Just call component.setId(component.getId()) to reset all client ids and 
694      * ensure they will be calculated for the current row, but do not waste time
695      * dealing with row state code.
696      * 
697      * @param parent
698      * @param iterateFacets
699      * @param restoreChildFacets 
700      */
701     private void restoreDescendantComponentWithoutRestoreState(UIComponent parent, boolean iterateFacets, boolean restoreChildFacets)
702     {
703         if (iterateFacets && parent.getFacetCount() > 0)
704         {
705             Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
706             
707             while (childIterator.hasNext())
708             {
709                 UIComponent component = childIterator.next();
710 
711                 // reset the client id (see spec 3.1.6)
712                 component.setId(component.getId());
713                 if (!component.isTransient())
714                 {
715                     restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
716                 }
717             }
718         }
719         
720         if (parent.getChildCount() > 0)
721         {
722             for (int i = 0; i < parent.getChildCount(); i++)
723             {
724                 UIComponent component = parent.getChildren().get(i);
725 
726                 // reset the client id (see spec 3.1.6)
727                 component.setId(component.getId());
728                 if (!component.isTransient())
729                 {
730                     restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
731                 }
732             }
733         }
734     }
735 
736     /**
737      * Walk the tree of child components of this UIData, saving the parts of their state that can vary between rows.
738      * <p>
739      * This is very similar to the process that occurs for normal components when the view is serialized. Transient
740      * components are skipped (no state is saved for them).
741      * <p>
742      * If there are no children then null is returned. If there are one or more children, and all children are transient
743      * then an empty collection is returned; this will happen whenever a table contains only read-only components.
744      * <p>
745      * Otherwise a collection is returned which contains an object for every non-transient child component; that object
746      * may itself contain a collection of the state of that child's child components.
747      */
748     private Collection<Object[]> saveDescendantComponentStates(UIComponent parent, boolean iterateFacets,
749                                                                boolean saveChildFacets)
750     {
751         Collection<Object[]> childStates = null;
752         // Index to indicate how many components has been passed without state to save.
753         int childEmptyIndex = 0;
754         int totalChildCount = 0;
755                 
756         if (iterateFacets && parent.getFacetCount() > 0)
757         {
758             Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
759 
760             while (childIterator.hasNext())
761             {
762                 UIComponent child = childIterator.next();
763                 if (!child.isTransient())
764                 {
765                     // Add an entry to the collection, being an array of two
766                     // elements. The first element is the state of the children
767                     // of this component; the second is the state of the current
768                     // child itself.
769 
770                     if (child instanceof EditableValueHolder)
771                     {
772                         if (childStates == null)
773                         {
774                             childStates = new ArrayList<Object[]>(
775                                     parent.getFacetCount()
776                                     + parent.getChildCount()
777                                     - totalChildCount
778                                     + childEmptyIndex);
779                             for (int ci = 0; ci < childEmptyIndex; ci++)
780                             {
781                                 childStates.add(LEAF_NO_STATE);
782                             }
783                         }
784                     
785                         childStates.add(child.getChildCount() > 0 ? 
786                                 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
787                                     saveDescendantComponentStates(child, saveChildFacets, true)} :
788                                 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
789                                     null});
790                     }
791                     else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
792                     {
793                         Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
794                         
795                         if (descendantSavedState == null)
796                         {
797                             if (childStates == null)
798                             {
799                                 childEmptyIndex++;
800                             }
801                             else
802                             {
803                                 childStates.add(LEAF_NO_STATE);
804                             }
805                         }
806                         else
807                         {
808                             if (childStates == null)
809                             {
810                                 childStates = new ArrayList<Object[]>(
811                                         parent.getFacetCount()
812                                         + parent.getChildCount()
813                                         - totalChildCount
814                                         + childEmptyIndex);
815                                 for (int ci = 0; ci < childEmptyIndex; ci++)
816                                 {
817                                     childStates.add(LEAF_NO_STATE);
818                                 }
819                             }
820                             childStates.add(new Object[]{null, descendantSavedState});
821                         }
822                     }
823                     else
824                     {
825                         if (childStates == null)
826                         {
827                             childEmptyIndex++;
828                         }
829                         else
830                         {
831                             childStates.add(LEAF_NO_STATE);
832                         }
833                     }
834                 }
835                 totalChildCount++;
836             }
837         }
838         
839         if (parent.getChildCount() > 0)
840         {
841             for (int i = 0; i < parent.getChildCount(); i++)
842             {
843                 UIComponent child = parent.getChildren().get(i);
844                 if (!child.isTransient())
845                 {
846                     // Add an entry to the collection, being an array of two
847                     // elements. The first element is the state of the children
848                     // of this component; the second is the state of the current
849                     // child itself.
850 
851                     if (child instanceof EditableValueHolder)
852                     {
853                         if (childStates == null)
854                         {
855                             childStates = new ArrayList<Object[]>(
856                                     parent.getFacetCount()
857                                     + parent.getChildCount()
858                                     - totalChildCount
859                                     + childEmptyIndex);
860                             for (int ci = 0; ci < childEmptyIndex; ci++)
861                             {
862                                 childStates.add(LEAF_NO_STATE);
863                             }
864                         }
865                     
866                         childStates.add(child.getChildCount() > 0 ? 
867                                 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
868                                     saveDescendantComponentStates(child, saveChildFacets, true)} :
869                                 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
870                                     null});
871                     }
872                     else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
873                     {
874                         Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
875                         
876                         if (descendantSavedState == null)
877                         {
878                             if (childStates == null)
879                             {
880                                 childEmptyIndex++;
881                             }
882                             else
883                             {
884                                 childStates.add(LEAF_NO_STATE);
885                             }
886                         }
887                         else
888                         {
889                             if (childStates == null)
890                             {
891                                 childStates = new ArrayList<Object[]>(
892                                         parent.getFacetCount()
893                                         + parent.getChildCount()
894                                         - totalChildCount
895                                         + childEmptyIndex);
896                                 for (int ci = 0; ci < childEmptyIndex; ci++)
897                                 {
898                                     childStates.add(LEAF_NO_STATE);
899                                 }
900                             }
901                             childStates.add(new Object[]{null, descendantSavedState});
902                         }
903                     }
904                     else
905                     {
906                         if (childStates == null)
907                         {
908                             childEmptyIndex++;
909                         }
910                         else
911                         {
912                             childStates.add(LEAF_NO_STATE);
913                         }
914                     }
915                 }
916                 totalChildCount++;
917             }
918         }
919         
920         return childStates;
921     }
922     
923     @Override
924     public void setValueExpression(String name, ValueExpression binding) {
925         if (name == null)
926         {
927             throw new NullPointerException("name");
928         }
929         else if (name.equals("value"))
930         {
931             _dataModelMap.clear();
932         }
933         else if (name.equals("rowIndex"))
934         {
935             throw new IllegalArgumentException("name " + name);
936         }
937         super.setValueExpression(name, binding);
938     }
939 
940     @Override
941     public String getClientId(FacesContext context)
942     {
943         String clientId = super.getClientId(context);
944         int rowIndex = getRowIndex();
945         if (rowIndex == -1)
946         {
947             return clientId;
948         }
949 
950         StringBuilder bld = __getSharedStringBuilder();
951         return bld.append(clientId).append(NamingContainer.SEPARATOR_CHAR).append(rowIndex).toString();
952     }
953 
954     /**
955      * Modify events queued for any child components so that the
956      * UIData state will be correctly configured before the event's
957      * listeners are executed.
958      * <p>
959      * Child components or their renderers may register events against
960      * those child components. When the listener for that event is
961      * eventually invoked, it may expect the uidata's rowData and
962      * rowIndex to be referring to the same object that caused the
963      * event to fire.
964      * <p>
965      * The original queueEvent call against the child component has been
966      * forwarded up the chain of ancestors in the standard way, making
967      * it possible here to wrap the event in a new event whose source
968      * is <i>this</i> component, not the original one. When the event
969      * finally is executed, this component's broadcast method is invoked,
970      * which ensures that the UIData is set to be at the correct row
971      * before executing the original event.
972      */
973     @Override
974     public void queueEvent(FacesEvent event)
975     {
976         super.queueEvent(new FacesEventWrapper(event, getRowIndex(), this));
977     }
978 
979     /**
980      * Ensure that before the event's listeners are invoked this UIData
981      * component's "current row" is set to the row associated with the event.
982      * <p>
983      * See queueEvent for more details.
984      */
985     @Override
986     public void broadcast(FacesEvent event) throws AbortProcessingException
987     {
988         if (event instanceof FacesEventWrapper)
989         {
990             FacesEvent originalEvent = ((FacesEventWrapper) event)
991                     .getWrappedFacesEvent();
992             int eventRowIndex = ((FacesEventWrapper) event).getRowIndex();
993             int currentRowIndex = getRowIndex();
994             setRowIndex(eventRowIndex);
995             try
996             {
997               originalEvent.getComponent().broadcast(originalEvent);
998             }
999             finally
1000             {
1001               setRowIndex(currentRowIndex);
1002             }
1003         }
1004         else
1005         {
1006             super.broadcast(event);
1007         }
1008     }
1009 
1010     /**
1011      * Perform necessary actions when rendering of this component starts,
1012      * before delegating to the inherited implementation which calls the
1013      * associated renderer's encodeBegin method.
1014      */
1015     @Override
1016     public void encodeBegin(FacesContext context) throws IOException
1017     {
1018         _initialDescendantComponentState = null;
1019         if (_isValidChilds && !hasErrorMessages(context))
1020         {
1021             // Clear the data model so that when rendering code calls
1022             // getDataModel a fresh model is fetched from the backing
1023             // bean via the value-binding.
1024             _dataModelMap.clear();
1025 
1026             // When the data model is cleared it is also necessary to
1027             // clear the saved row state, as there is an implicit 1:1
1028             // relation between objects in the _rowStates and the
1029             // corresponding DataModel element.
1030             _rowStates.clear();
1031         }
1032         super.encodeBegin(context);
1033     }
1034 
1035     private boolean hasErrorMessages(FacesContext context)
1036     {
1037         for(Iterator iter = context.getMessages(); iter.hasNext();)
1038         {
1039             FacesMessage message = (FacesMessage) iter.next();
1040             if(FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
1041             {
1042                 return true;
1043             }
1044         }
1045         return false;
1046     }
1047 
1048     /**
1049      * @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
1050      */
1051     @Override
1052     public void encodeEnd(FacesContext context) throws IOException
1053     {
1054         try
1055         {
1056             setCachedFacesContext(context);
1057             setRowIndex(-1);
1058         }
1059         finally
1060         {
1061             setCachedFacesContext(null);
1062         }
1063         super.encodeEnd(context);
1064     }
1065 
1066     @Override
1067     public void processDecodes(FacesContext context)
1068     {
1069         if (context == null)
1070         {
1071             throw new NullPointerException("context");
1072         }
1073         try
1074         {
1075             setCachedFacesContext(context);
1076             if (!isRendered())
1077             {
1078                 return;
1079             }
1080             setRowIndex(-1);
1081             processFacets(context, PROCESS_DECODES);
1082             processColumnFacets(context, PROCESS_DECODES);
1083             processColumnChildren(context, PROCESS_DECODES);
1084             setRowIndex(-1);
1085             try
1086             {
1087                 decode(context);
1088             }
1089             catch (RuntimeException e)
1090             {
1091                 context.renderResponse();
1092                 throw e;
1093             }
1094         }
1095         finally
1096         {
1097             setCachedFacesContext(null);
1098         }
1099     }
1100 
1101     @Override
1102     public void processValidators(FacesContext context)
1103     {
1104         if (context == null)
1105         {
1106             throw new NullPointerException("context");
1107         }
1108 
1109         try
1110         {
1111             setCachedFacesContext(context);
1112             if (!isRendered())
1113             {
1114                 return;
1115             }
1116     
1117             setRowIndex(-1);
1118             processFacets(context, PROCESS_VALIDATORS);
1119             processColumnFacets(context, PROCESS_VALIDATORS);
1120             processColumnChildren(context, PROCESS_VALIDATORS);
1121             setRowIndex(-1);
1122     
1123             // check if an validation error forces the render response for our data
1124             if (context.getRenderResponse())
1125             {
1126                 _isValidChilds = false;
1127             }
1128         }
1129         finally
1130         {
1131             setCachedFacesContext(null);
1132         }
1133     }
1134 
1135     @Override
1136     public void processUpdates(FacesContext context)
1137     {
1138         if (context == null)
1139         {
1140             throw new NullPointerException("context");
1141         }
1142         try
1143         {
1144             setCachedFacesContext(context);
1145             if (!isRendered())
1146             {
1147                 return;
1148             }
1149             setRowIndex(-1);
1150             processFacets(context, PROCESS_UPDATES);
1151             processColumnFacets(context, PROCESS_UPDATES);
1152             processColumnChildren(context, PROCESS_UPDATES);
1153             setRowIndex(-1);
1154     
1155             if (context.getRenderResponse())
1156             {
1157                 _isValidChilds = false;
1158             }
1159         }
1160         finally
1161         {
1162             setCachedFacesContext(null);
1163         }
1164     }
1165 
1166     private void processFacets(FacesContext context, int processAction)
1167     {
1168         for (Iterator it = getFacets().values().iterator(); it.hasNext();)
1169         {
1170             UIComponent facet = (UIComponent) it.next();
1171             process(context, facet, processAction);
1172         }
1173     }
1174 
1175     /**
1176      * Invoke the specified phase on all facets of all UIColumn children
1177      * of this component. Note that no methods are called on the UIColumn
1178      * child objects themselves.
1179      *
1180      * @param context is the current faces context.
1181      * @param processAction specifies a JSF phase: decode, validate or update.
1182      */
1183     private void processColumnFacets(FacesContext context, int processAction)
1184     {
1185         for (Iterator childIter = getChildren().iterator(); childIter.hasNext();)
1186         {
1187             UIComponent child = (UIComponent) childIter.next();
1188             if (child instanceof UIColumn)
1189             {
1190                 if (!child.isRendered())
1191                 {
1192                     //Column is not visible
1193                     continue;
1194                 }
1195                 for (Iterator facetsIter = child.getFacets().values()
1196                         .iterator(); facetsIter.hasNext();)
1197                 {
1198                     UIComponent facet = (UIComponent) facetsIter.next();
1199                     process(context, facet, processAction);
1200                 }
1201             }
1202         }
1203     }
1204 
1205     /**
1206      * Invoke the specified phase on all non-facet children of all UIColumn
1207      * children of this component. Note that no methods are called on the
1208      * UIColumn child objects themselves.
1209      *
1210      * @param context is the current faces context.
1211      * @param processAction specifies a JSF phase: decode, validate or update.
1212      */
1213     private void processColumnChildren(FacesContext context, int processAction)
1214     {
1215         int first = getFirst();
1216         int rows = getRows();
1217         int last;
1218         if (rows == 0)
1219         {
1220             last = getRowCount();
1221         }
1222         else
1223         {
1224             last = first + rows;
1225         }
1226         for (int rowIndex = first; last==-1 || rowIndex < last; rowIndex++)
1227         {
1228             setRowIndex(rowIndex);
1229 
1230             //scrolled past the last row
1231             if (!isRowAvailable())
1232             {
1233                 break;
1234             }
1235 
1236             for (Iterator it = getChildren().iterator(); it.hasNext();)
1237             {
1238                 UIComponent child = (UIComponent) it.next();
1239                 if (child instanceof UIColumn)
1240                 {
1241                     if (!child.isRendered())
1242                     {
1243                         //Column is not visible
1244                         continue;
1245                     }
1246                     for (Iterator columnChildIter = child.getChildren()
1247                             .iterator(); columnChildIter.hasNext();)
1248                     {
1249                         UIComponent columnChild = (UIComponent) columnChildIter
1250                                 .next();
1251                         process(context, columnChild, processAction);
1252                     }
1253                 }
1254             }
1255         }
1256     }
1257 
1258     private void process(FacesContext context, UIComponent component,
1259             int processAction)
1260     {
1261         switch (processAction)
1262         {
1263         case PROCESS_DECODES:
1264             component.processDecodes(context);
1265             break;
1266         case PROCESS_VALIDATORS:
1267             component.processValidators(context);
1268             break;
1269         case PROCESS_UPDATES:
1270             component.processUpdates(context);
1271             break;
1272         }
1273     }
1274 
1275     /**
1276      * Return the datamodel for this table, potentially fetching the data from
1277      * a backing bean via a value-binding if this is the first time this method
1278      * has been called.
1279      * <p>
1280      * This is complicated by the fact that this table may be nested within
1281      * another table. In this case a different datamodel should be fetched
1282      * for each row. When nested within a parent table, the parent reference
1283      * won't change but parent.getClientId() will, as the suffix changes
1284      * depending upon the current row index. A map object on this component
1285      * is therefore used to cache the datamodel for each row of the table.
1286      * In the normal case where this table is not nested inside a component
1287      * that changes its id (like a table does) then this map only ever has
1288      * one entry.
1289      */
1290     protected DataModel getDataModel()
1291     {
1292         DataModel dataModel;
1293         String clientID = "";
1294 
1295         UIComponent parent = getParent();
1296         if (parent != null)
1297         {
1298             clientID = parent.getClientId(getFacesContext());
1299         }
1300         dataModel = (DataModel) _dataModelMap.get(clientID);
1301         if (dataModel == null)
1302         {
1303             dataModel = createDataModel();
1304             _dataModelMap.put(clientID, dataModel);
1305         }
1306         return dataModel;
1307     }
1308 
1309     protected void setDataModel(DataModel dataModel)
1310     {
1311         throw new UnsupportedOperationException(
1312                 "this method is here only to maintain binary compatibility w/ the RI");
1313     }
1314 
1315     /**
1316      * Evaluate this object's value property and convert the result into a
1317      * DataModel. Normally this object's value property will be a value-binding
1318      * which will cause the value to be fetched from some backing bean.
1319      * <p>
1320      * The result of fetching the value may be a DataModel object, in which
1321      * case that object is returned directly. If the value is of type
1322      * List, Array, ResultSet, Result, other object or null then an appropriate
1323      * wrapper is created and returned.
1324      * <p>
1325      * Null is never returned by this method.
1326      */
1327     private DataModel createDataModel()
1328     {
1329         Object value = getValue();
1330 
1331         if (value == null)
1332         {
1333             return EMPTY_DATA_MODEL;
1334         }
1335         else if (value instanceof DataModel)
1336         {
1337             return (DataModel) value;
1338         }
1339         else if (value instanceof List)
1340         {
1341             return new ListDataModel((List) value);
1342         }
1343         else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass()))
1344         {
1345             return new ArrayDataModel((Object[]) value);
1346         }
1347         else if (value instanceof ResultSet)
1348         {
1349             return new ResultSetDataModel((ResultSet) value);
1350         }
1351         else if (value instanceof Result)
1352         {
1353             return new ResultDataModel((Result) value);
1354         }
1355         else
1356         {
1357             return new ScalarDataModel(value);
1358         }
1359     }
1360 
1361     /**
1362      * An EL expression that specifies the data model that backs this table.
1363      * <p>
1364      * The value referenced by the EL expression can be of any type.
1365      * </p>
1366      * <ul>
1367      * <li>A value of type DataModel is used directly.</li>
1368      * <li>Array-like parameters of type array-of-Object, java.util.List, java.sql.ResultSet
1369      *  or javax.servlet.jsp.jstl.sql.Result are wrapped in a corresponding DataModel that
1370      *  knows how to iterate over the elements.</li>
1371      * <li>Other values are wrapped in a DataModel as a single row.</li>
1372      * </ul>
1373      * <p>
1374      * Note in particular that unordered collections, eg Set are not supported. Therefore if the
1375      * value expression references such an object then the table will be considered to contain just
1376      * one element - the collection itself.
1377      * </p>
1378      */
1379     @JSFProperty
1380     public Object getValue()
1381     {
1382       if (_value != null)
1383       {
1384         return _value;
1385       }
1386       ValueExpression expression = getValueExpression("value");
1387       if (expression != null)
1388       {
1389         return expression.getValue(getFacesContext().getELContext());
1390       }
1391       return null;
1392     }
1393 
1394     public void setValue(Object value)
1395     {
1396         _value = value;
1397         _dataModelMap.clear();
1398         _rowStates.clear();
1399         _isValidChilds = true;
1400     }
1401 
1402     /**
1403      * Defines the index of the first row to be displayed, starting from 0.
1404      */
1405     @JSFProperty
1406     public int getFirst()
1407     {
1408       if (_firstSet)
1409       {
1410         return _first;
1411       }
1412       ValueExpression expression = getValueExpression("first");
1413       if (expression != null)
1414       {
1415         return (Integer)expression.getValue(getFacesContext().getELContext());
1416       }
1417       return 0;
1418     }
1419 
1420     public void setFirst(int first)
1421     {
1422         if (first < 0)
1423         {
1424             throw new IllegalArgumentException("Illegal value for first row: " + first);
1425         }
1426         _first = first;
1427         _firstSet=true;
1428     }
1429 
1430     /**
1431      * Defines the maximum number of rows of data to be displayed.
1432      * <p>
1433      * Specify zero to display all rows from the "first" row to the end
1434      * of available data.
1435      * </p>
1436      */
1437     @JSFProperty
1438     public int getRows()
1439     {
1440       if (_rowsSet)
1441       {
1442         return _rows;
1443       }
1444       ValueExpression expression = getValueExpression("rows");
1445       if (expression != null)
1446       {
1447         return (Integer)expression.getValue(getFacesContext().getELContext());
1448       }
1449       return 0;
1450     }
1451 
1452     /**
1453      * Set the maximum number of rows displayed in the table.
1454      */
1455     public void setRows(int rows)
1456     {
1457         if (rows < 0)
1458         {
1459             throw new IllegalArgumentException("rows: " + rows);
1460         }
1461         _rows = rows;
1462         _rowsSet = true;
1463     }
1464 
1465   /**
1466    * Defines the name of the request-scope variable that will hold the current row during iteration.
1467    * <p>
1468    * During rendering of child components of this UIData, the variable with this name can be read to
1469    * learn what the "rowData" object for the row currently being rendered is.
1470    * </p>
1471    * <p>
1472    * This value must be a static value, ie an EL expression is not permitted.
1473    * </p>
1474    */
1475   @JSFProperty(literalOnly = true)
1476   public String getVar()
1477   {
1478     return _var;
1479   }
1480 
1481   public void setVar(String var)
1482   {
1483     this._var = var;
1484   }
1485 
1486 
1487   @Override
1488   public Object saveState(FacesContext facesContext)
1489   {
1490     Object[] values = new Object[7];
1491     values[0] = super.saveState(facesContext);
1492     values[1] = _value;
1493     values[2] = _var;
1494     values[3] = _rows;
1495     values[4] = _rowsSet;
1496     values[5] = _first;
1497     values[6] = _firstSet;
1498 
1499     return values;
1500   }
1501 
1502   @Override
1503   public void restoreState(FacesContext facesContext, Object state)
1504   {
1505     Object[] values = (Object[])state;
1506     super.restoreState(facesContext,values[0]);
1507     _value = values[1];
1508     _var = (String)values[2];
1509     _rows = (Integer)values[3];
1510     _rowsSet = (Boolean)values[4];
1511     _first = (Integer)values[5];
1512     _firstSet = (Boolean)values[6];
1513   }
1514 
1515   @Override
1516   public String getFamily()
1517   {
1518     return COMPONENT_FAMILY;
1519   }
1520 }