View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package javax.faces.component;
20  
21  import java.io.IOException;
22  import java.sql.ResultSet;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  
30  import javax.faces.application.FacesMessage;
31  import javax.faces.context.FacesContext;
32  import javax.faces.el.ValueBinding;
33  import javax.faces.event.AbortProcessingException;
34  import javax.faces.event.FacesEvent;
35  import javax.faces.event.FacesListener;
36  import javax.faces.event.PhaseId;
37  import javax.faces.model.ArrayDataModel;
38  import javax.faces.model.DataModel;
39  import javax.faces.model.ListDataModel;
40  import javax.faces.model.ResultDataModel;
41  import javax.faces.model.ResultSetDataModel;
42  import javax.faces.model.ScalarDataModel;
43  import javax.servlet.jsp.jstl.sql.Result;
44  
45  /**
46   * Represents a component which has multiple "rows" of data.
47   * <p>
48   * The children of this component are expected to be UIColumn components.
49   * <p>
50   * Note that the same set of child components are reused to implement each
51   * row of the table in turn during such phases as apply-request-values and
52   * render-response. Altering any of the members of these components therefore
53   * affects the attribute for every row, except for the following members:
54   * <ul>
55   * <li>submittedValue
56   * <li>value (where no EL binding is used)
57   * <li>valid
58   * </ul>
59   * <p>
60   * This reuse of the child components also means that it is not possible
61   * to save a reference to a component during table processing, then access
62   * it later and expect it to still represent the same row of the table.
63   * <h1>
64   * Implementation Notes
65   * </h1>
66   * <p>
67   * Each of the UIColumn children of this component has a few component
68   * children of its own to render the contents of the table cell. However
69   * there can be a very large number of rows in a table, so it isn't
70   * efficient for the UIColumn and all its child objects to be duplicated
71   * for each row in the table. Instead the "flyweight" pattern is used
72   * where a serialized state is held for each row. When setRowIndex is
73   * invoked, the UIColumn objects and their children serialize their
74   * current state then reinitialise themselves from the appropriate saved
75   * state. This allows a single set of real objects to represent multiple
76   * objects which have the same types but potentially different internal
77   * state. When a row is selected for the first time, its state is set to
78   * a clean "initial" state. Transient components (including any read-only
79   * component) do not save their state; they are just reinitialised as required.
80   * The state saved/restored when changing rows is not the complete
81   * component state, just the fields that are expected to vary between
82   * rows: "submittedValue", "value", "isValid".
83   * </p>
84   * <p>
85   * Note that a table is a "naming container", so that components
86   * within the table have their ids prefixed with the id of the
87   * table. Actually, when setRowIndex has been called on a table with
88   * id of "zzz" the table pretends to its children that its ID is
89   * "zzz_n" where n is the row index. This means that renderers for
90   * child components which call component.getClientId automatically
91   * get ids of form "zzz_n:childId" thus ensuring that components in
92   * different rows of the table get different ids.
93   * </p>
94   * <p>
95   * When decoding a submitted page, this class iterates over all
96   * its possible rowIndex values, restoring the appropriate serialized
97   * row state then calling processDecodes on the child components. Because
98   * the child components (or their renderers) use getClientId to get the
99   * request key to look for parameter data, and because this object pretends
100  * to have a different id per row ("zzz_n") a single child component can
101  * decode data from each table row in turn without being aware that it is
102  * within a table. The table's data model is updated before each call to
103  * child.processDecodes, so the child decode method can assume that the
104  * data model's rowData points to the model object associated with the
105  * row currently being decoded. Exactly the same process applies for
106  * the later validation and updateModel phases.
107  * </p>
108  * <p>
109  * When the data model for the table is bound to a backing bean property,
110  * and no validation errors have occured during processing of a postback,
111  * the data model is refetched at the start of the rendering phase
112  * (ie after the update model phase) so that the contents of the data model
113  * can be changed as a result of the latest form submission. Because the
114  * saved row state must correspond to the elements within the data model,
115  * the row state must be discarded whenever a new data model is fetched;
116  * not doing this would cause all sorts of inconsistency issues. This does
117  * imply that changing the state of any of the members "submittedValue", 
118  * "value" or "valid" of a component within the table during the
119  * invokeApplication phase has no effect on the rendering of the table.
120  * When a validation error has occurred, a new DataModel is <i>not</i>
121  * fetched, and the saved state of the child components is <i>not</i>
122  * discarded.
123  * </p>
124  * see Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a> for more.
125  * 
126  * @JSFComponent
127  *   type = "javax.faces.Data"
128  *   family = "javax.faces.Data"
129  *   desc = "UIData"
130  * 
131  * @author Manfred Geiler (latest modification by $Author: lu4242 $)
132  * @version $Revision: 949325 $ $Date: 2010-05-28 19:38:36 -0500 (Fri, 28 May 2010) $
133  */
134 public class UIData extends UIComponentBase implements NamingContainer
135 {
136     public static final String COMPONENT_TYPE = "javax.faces.Data";
137     public static final String COMPONENT_FAMILY = "javax.faces.Data";
138     private static final String DEFAULT_RENDERER_TYPE = "javax.faces.Table";
139     private static final int DEFAULT_FIRST = 0;
140     private static final int DEFAULT_ROWS = 0;
141 
142     private static final int STATE_SIZE = 5;
143     private static final int SUPER_STATE_INDEX = 0;
144     private static final int FIRST_STATE_INDEX = 1;
145     private static final int ROWS_STATE_INDEX = 2;
146     private static final int VALUE_STATE_INDEX = 3;
147     private static final int VAR_STATE_INDEX = 4;
148 
149     private static final String FOOTER_FACET_NAME = "footer";
150     private static final String HEADER_FACET_NAME = "header";
151     private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
152     private static final int PROCESS_DECODES = 1;
153     private static final int PROCESS_VALIDATORS = 2;
154     private static final int PROCESS_UPDATES = 3;
155 
156     private Integer _first = null;
157     private Integer _rows = null;
158     private Object _value = null;
159 
160     private int _rowIndex = -1;
161     private String _var = null;
162 
163     // Holds for each row the states of the child components of this UIData.
164     // Note that only "partial" component state is saved: the component fields
165     // that are expected to vary between rows.
166     private Map _rowStates = new HashMap();
167 
168     /**
169      * Handle case where this table is nested inside another table.
170      * See method getDataModel for more details.
171      * <p>
172      * Key: parentClientId (aka rowId when nested within a parent table)
173      * Value: DataModel
174      */
175     private Map _dataModelMap = new HashMap();
176 
177     // will be set to false if the data should not be refreshed at the beginning of the encode phase
178     private boolean _isValidChilds = true;
179 
180     private Object _initialDescendantComponentState = null;
181 
182     public void setFooter(UIComponent footer)
183     {
184         getFacets().put(FOOTER_FACET_NAME, footer);
185     }
186 
187     /**
188      * @JSFFacet
189      */
190     public UIComponent getFooter()
191     {
192         return (UIComponent) getFacets().get(FOOTER_FACET_NAME);
193     }
194 
195     public void setHeader(UIComponent header)
196     {
197         getFacets().put(HEADER_FACET_NAME, header);
198     }
199 
200     /**
201      * @JSFFacet
202      */
203     public UIComponent getHeader()
204     {
205         return (UIComponent) getFacets().get(HEADER_FACET_NAME);
206     }
207 
208     public boolean isRowAvailable()
209     {
210         return getDataModel().isRowAvailable();
211     }
212 
213     public int getRowCount()
214     {
215         return getDataModel().getRowCount();
216     }
217 
218     public Object getRowData()
219     {
220         return getDataModel().getRowData();
221     }
222 
223     public int getRowIndex()
224     {
225         return _rowIndex;
226     }
227 
228     /**
229      * Set the current row index that methods like getRowData use.
230      * <p>
231      * Param rowIndex can be -1, meaning "no row".
232      * <p>
233      * @param rowIndex
234      */
235     public void setRowIndex(int rowIndex)
236     {
237         if (rowIndex < -1)
238         {
239             throw new IllegalArgumentException("rowIndex is less than -1");
240         }
241 
242         if (_rowIndex == rowIndex)
243         {
244             return;
245         }
246 
247         FacesContext facesContext = getFacesContext();
248 
249         if (_rowIndex == -1)
250         {
251             if (_initialDescendantComponentState == null)
252             {
253                 // Create a template that can be used to initialise any row
254                 // that we haven't visited before, ie a "saved state" that can
255                 // be pushed to the "restoreState" method of all the child
256                 // components to set them up to represent a clean row.
257                 _initialDescendantComponentState = saveDescendantComponentStates(
258                         getChildren().iterator(), false);
259             }
260         }
261         else
262         {
263             // We are currently positioned on some row, and are about to
264             // move off it, so save the (partial) state of the components
265             // representing the current row. Later if this row is revisited
266             // then we can restore this state.
267             _rowStates.put(getClientId(facesContext),
268                     saveDescendantComponentStates(getChildren().iterator(),
269                             false));
270         }
271 
272         _rowIndex = rowIndex;
273 
274         DataModel dataModel = getDataModel();
275         dataModel.setRowIndex(rowIndex);
276 
277         String var = getVar();
278         if (rowIndex == -1)
279         {
280             if (var != null)
281             {
282                 facesContext.getExternalContext().getRequestMap().remove(var);
283             }
284         }
285         else
286         {
287             if (var != null)
288             {
289                 if (isRowAvailable())
290                 {
291                     Object rowData = dataModel.getRowData();
292                     facesContext.getExternalContext().getRequestMap().put(var,
293                             rowData);
294                 }
295                 else
296                 {
297                     facesContext.getExternalContext().getRequestMap().remove(
298                             var);
299                 }
300             }
301         }
302 
303         if (_rowIndex == -1)
304         {
305             // reset components to initial state
306             restoreDescendantComponentStates(getChildren().iterator(),
307                     _initialDescendantComponentState, false);
308         }
309         else
310         {
311             Object rowState = _rowStates.get(getClientId(facesContext));
312             if (rowState == null)
313             {
314                 // We haven't been positioned on this row before, so just
315                 // configure the child components of this component with
316                 // the standard "initial" state
317                 restoreDescendantComponentStates(getChildren().iterator(),
318                         _initialDescendantComponentState, false);
319             }
320             else
321             {
322                 // We have been positioned on this row before, so configure
323                 // the child components of this component with the (partial)
324                 // state that was previously saved. Fields not in the
325                 // partial saved state are left with their original values.
326                 restoreDescendantComponentStates(getChildren().iterator(),
327                         rowState, false);
328             }
329         }
330     }
331 
332     /**
333      * Overwrite the state of the child components of this component
334      * with data previously saved by method saveDescendantComponentStates.
335      * <p>
336      * The saved state info only covers those fields that are expected to
337      * vary between rows of a table. Other fields are not modified.
338      */
339     private void restoreDescendantComponentStates(Iterator childIterator,
340             Object state, boolean restoreChildFacets)
341     {
342         Iterator descendantStateIterator = null;
343         while (childIterator.hasNext())
344         {
345             if (descendantStateIterator == null && state != null)
346             {
347                 descendantStateIterator = ((Collection) state).iterator();
348             }
349             UIComponent component = (UIComponent) childIterator.next();
350 
351             // reset the client id (see spec 3.1.6)
352             component.setId(component.getId());
353             if(!component.isTransient())
354             {
355                 Object childState = null;
356                 Object descendantState = null;
357                 if (descendantStateIterator != null
358                         && descendantStateIterator.hasNext())
359                 {
360                     Object[] object = (Object[]) descendantStateIterator.next();
361                     childState = object[0];
362                     descendantState = object[1];
363                 }
364                 if (component instanceof EditableValueHolder)
365                 {
366                     ((EditableValueHolderState) childState)
367                             .restoreState((EditableValueHolder) component);
368                 }
369                 Iterator childsIterator;
370                 if (restoreChildFacets)
371                 {
372                     childsIterator = component.getFacetsAndChildren();
373                 }
374                 else
375                 {
376                     childsIterator = component.getChildren().iterator();
377                 }
378                 restoreDescendantComponentStates(childsIterator, descendantState,
379                         true);
380             }
381         }
382     }
383 
384     /**
385      * Walk the tree of child components of this UIData, saving the parts of
386      * their state that can vary between rows.
387      * <p>
388      * This is very similar to the process that occurs for normal components
389      * when the view is serialized. Transient components are skipped (no
390      * state is saved for them).
391      * <p>
392      * If there are no children then null is returned. If there are one or
393      * more children, and all children are transient then an empty collection
394      * is returned; this will happen whenever a table contains only read-only
395      * components.
396      * <p>
397      * Otherwise a collection is returned which contains an object for every
398      * non-transient child component; that object may itself contain a collection
399      * of the state of that child's child components.
400      */
401     private Object saveDescendantComponentStates(Iterator childIterator,
402             boolean saveChildFacets)
403     {
404         Collection childStates = null;
405         while (childIterator.hasNext())
406         {
407             if (childStates == null)
408             {
409                 childStates = new ArrayList();
410             }
411             UIComponent child = (UIComponent) childIterator.next();
412             if(!child.isTransient())
413             {
414                 // Add an entry to the collection, being an array of two
415                 // elements. The first element is the state of the children
416                 // of this component; the second is the state of the current
417                 // child itself.
418 
419                 Iterator childsIterator;
420                 if (saveChildFacets)
421                 {
422                     childsIterator = child.getFacetsAndChildren();
423                 }
424                 else
425                 {
426                     childsIterator = child.getChildren().iterator();
427                 }
428                 Object descendantState = saveDescendantComponentStates(
429                         childsIterator, true);
430                 Object state = null;
431                 if (child instanceof EditableValueHolder)
432                 {
433                     state = new EditableValueHolderState(
434                             (EditableValueHolder) child);
435                 }
436                 childStates.add(new Object[] { state, descendantState });
437             }
438         }
439         return childStates;
440     }
441 
442     /**
443      * Set the maximum number of rows displayed in the table.
444      */
445     public void setRows(int rows)
446     {
447         _rows = new Integer(rows);
448         if (rows < 0)
449             throw new IllegalArgumentException("rows: " + rows);
450     }
451 
452     /**
453      * Set the name of the temporary variable that will be exposed to
454      * child components of the table to tell them what the "rowData"
455      * object for the current row is. This value must be a literal
456      * string (EL expression not permitted).
457      */
458     public void setVar(String var)
459     {
460         _var = var;
461     }
462 
463     /**
464      * Defines the name of the request-scope variable that will hold the current row during iteration.  This value must be a static value.
465      * 
466      * @JSFProperty
467      *   literalOnly = "true"
468      *   required = "true"
469      */
470     public String getVar()
471     {
472         return _var;
473     }
474 
475     public void setValueBinding(String name, ValueBinding binding)
476     {
477         if (name == null)
478         {
479             throw new NullPointerException("name");
480         }
481         else if (name.equals("value"))
482         {
483             _dataModelMap.clear();
484         }
485         else if (name.equals("var") || name.equals("rowIndex"))
486         {
487             throw new IllegalArgumentException(
488                     "You can never set the 'rowIndex' or the 'var' attribute as a value-binding. Set the property directly instead. Name " + name);
489         }
490         super.setValueBinding(name, binding);
491     }
492 
493     public String getClientId(FacesContext context)
494     {
495         String clientId = super.getClientId(context);
496         int rowIndex = getRowIndex();
497         if (rowIndex == -1)
498         {
499             return clientId;
500         }
501         return clientId + NamingContainer.SEPARATOR_CHAR + rowIndex;
502     }
503 
504     /**
505      * Modify events queued for any child components so that the
506      * UIData state will be correctly configured before the event's
507      * listeners are executed.
508      * <p>
509      * Child components or their renderers may register events against
510      * those child components. When the listener for that event is
511      * eventually invoked, it may expect the uidata's rowData and
512      * rowIndex to be referring to the same object that caused the
513      * event to fire.
514      * <p>
515      * The original queueEvent call against the child component has been
516      * forwarded up the chain of ancestors in the standard way, making
517      * it possible here to wrap the event in a new event whose source
518      * is <i>this</i> component, not the original one. When the event
519      * finally is executed, this component's broadcast method is invoked,
520      * which ensures that the UIData is set to be at the correct row
521      * before executing the original event.
522      */
523     public void queueEvent(FacesEvent event)
524     {
525         super.queueEvent(new FacesEventWrapper(event, getRowIndex(), this));
526     }
527 
528     /**
529      * Ensure that before the event's listeners are invoked this UIData
530      * component's "current row" is set to the row associated with the event.
531      * <p>
532      * See queueEvent for more details. 
533      */
534     public void broadcast(FacesEvent event) throws AbortProcessingException
535     {
536         if (event instanceof FacesEventWrapper)
537         {
538             FacesEvent originalEvent = ((FacesEventWrapper) event)
539                     .getWrappedFacesEvent();
540             int eventRowIndex = ((FacesEventWrapper) event).getRowIndex();
541             int currentRowIndex = getRowIndex();
542             setRowIndex(eventRowIndex);
543             try
544             {
545               originalEvent.getComponent().broadcast(originalEvent);
546             }
547             finally
548             {
549               setRowIndex(currentRowIndex);
550             }
551         }
552         else
553         {
554             super.broadcast(event);
555         }
556     }
557 
558     /**
559      * Perform necessary actions when rendering of this component starts,
560      * before delegating to the inherited implementation which calls the
561      * associated renderer's encodeBegin method.
562      */
563     public void encodeBegin(FacesContext context) throws IOException
564     {
565         _initialDescendantComponentState = null;
566         if (_isValidChilds && !hasErrorMessages(context))
567         {
568             // Clear the data model so that when rendering code calls
569             // getDataModel a fresh model is fetched from the backing
570             // bean via the value-binding.
571             _dataModelMap.clear();
572             
573             // When the data model is cleared it is also necessary to
574             // clear the saved row state, as there is an implicit 1:1
575             // relation between objects in the _rowStates and the
576             // corresponding DataModel element.
577             _rowStates.clear();
578         }
579         super.encodeBegin(context);
580     }
581 
582     private boolean hasErrorMessages(FacesContext context)
583     {
584         for(Iterator iter = context.getMessages(); iter.hasNext();)
585         {
586             FacesMessage message = (FacesMessage) iter.next();
587             if(FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
588             {
589                 return true;
590             }
591         }
592         return false;
593     }
594 
595     /**
596      * @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
597      */
598     public void encodeEnd(FacesContext context) throws IOException
599     {
600         try
601         {
602             setCachedFacesContext(context);
603             setRowIndex(-1);
604         }
605         finally
606         {
607             setCachedFacesContext(null);
608         }
609         super.encodeEnd(context);
610     }
611 
612     public void processDecodes(FacesContext context)
613     {
614         if (context == null)
615             throw new NullPointerException("context");
616         try
617         {
618             setCachedFacesContext(context);
619             if (!isRendered())
620                 return;
621             setRowIndex(-1);
622             processFacets(context, PROCESS_DECODES);
623             processColumnFacets(context, PROCESS_DECODES);
624             processColumnChildren(context, PROCESS_DECODES);
625             setRowIndex(-1);
626             try
627             {
628                 decode(context);
629             }
630             catch (RuntimeException e)
631             {
632                 context.renderResponse();
633                 throw e;
634             }
635         }
636         finally
637         {
638             setCachedFacesContext(null);
639         }
640     }
641 
642     public void processValidators(FacesContext context)
643     {
644         if (context == null)
645             throw new NullPointerException("context");
646         try
647         {
648             setCachedFacesContext(context);
649             if (!isRendered())
650                 return;
651             setRowIndex(-1);
652             processFacets(context, PROCESS_VALIDATORS);
653             processColumnFacets(context, PROCESS_VALIDATORS);
654             processColumnChildren(context, PROCESS_VALIDATORS);
655             setRowIndex(-1);
656     
657             // check if an validation error forces the render response for our data
658             if (context.getRenderResponse())
659             {
660                 _isValidChilds = false;
661             }
662         }
663         finally
664         {
665             setCachedFacesContext(null);
666         }
667     }
668 
669     public void processUpdates(FacesContext context)
670     {
671         if (context == null)
672             throw new NullPointerException("context");
673         try
674         {
675             setCachedFacesContext(context);
676             if (!isRendered())
677                 return;
678             setRowIndex(-1);
679             processFacets(context, PROCESS_UPDATES);
680             processColumnFacets(context, PROCESS_UPDATES);
681             processColumnChildren(context, PROCESS_UPDATES);
682             setRowIndex(-1);
683         
684             if (context.getRenderResponse())
685             {
686                 _isValidChilds = false;
687             }
688         }
689         finally
690         {
691             setCachedFacesContext(null);
692         }
693     }
694 
695     private void processFacets(FacesContext context, int processAction)
696     {
697         for (Iterator it = getFacets().values().iterator(); it.hasNext();)
698         {
699             UIComponent facet = (UIComponent) it.next();
700             process(context, facet, processAction);
701         }
702     }
703 
704     /**
705      * Invoke the specified phase on all facets of all UIColumn children
706      * of this component. Note that no methods are called on the UIColumn
707      * child objects themselves.
708      * 
709      * @param context is the current faces context.
710      * @param processAction specifies a JSF phase: decode, validate or update.
711      */
712     private void processColumnFacets(FacesContext context, int processAction)
713     {
714         for (Iterator childIter = getChildren().iterator(); childIter.hasNext();)
715         {
716             UIComponent child = (UIComponent) childIter.next();
717             if (child instanceof UIColumn)
718             {
719                 if (!child.isRendered())
720                 {
721                     //Column is not visible
722                     continue;
723                 }
724                 for (Iterator facetsIter = child.getFacets().values()
725                         .iterator(); facetsIter.hasNext();)
726                 {
727                     UIComponent facet = (UIComponent) facetsIter.next();
728                     process(context, facet, processAction);
729                 }
730             }
731         }
732     }
733 
734     /**
735      * Invoke the specified phase on all non-facet children of all UIColumn
736      * children of this component. Note that no methods are called on the
737      * UIColumn child objects themselves.
738      * 
739      * @param context is the current faces context.
740      * @param processAction specifies a JSF phase: decode, validate or update.
741      */
742     private void processColumnChildren(FacesContext context, int processAction)
743     {
744         int first = getFirst();
745         int rows = getRows();
746         int last;
747         if (rows == 0)
748         {
749             last = getRowCount();
750         }
751         else
752         {
753             last = first + rows;
754         }
755         for (int rowIndex = first; last==-1 || rowIndex < last; rowIndex++)
756         {
757             setRowIndex(rowIndex);
758 
759             //scrolled past the last row
760             if (!isRowAvailable())
761                 break;
762 
763             for (Iterator it = getChildren().iterator(); it.hasNext();)
764             {
765                 UIComponent child = (UIComponent) it.next();
766                 if (child instanceof UIColumn)
767                 {
768                     if (!child.isRendered())
769                     {
770                         //Column is not visible
771                         continue;
772                     }
773                     for (Iterator columnChildIter = child.getChildren()
774                             .iterator(); columnChildIter.hasNext();)
775                     {
776                         UIComponent columnChild = (UIComponent) columnChildIter
777                                 .next();
778                         process(context, columnChild, processAction);
779                     }
780                 }
781             }
782         }
783     }
784 
785     private void process(FacesContext context, UIComponent component,
786             int processAction)
787     {
788         switch (processAction)
789         {
790         case PROCESS_DECODES:
791             component.processDecodes(context);
792             break;
793         case PROCESS_VALIDATORS:
794             component.processValidators(context);
795             break;
796         case PROCESS_UPDATES:
797             component.processUpdates(context);
798             break;
799         }
800     }
801 
802     /**
803      * Return the datamodel for this table, potentially fetching the data from
804      * a backing bean via a value-binding if this is the first time this method
805      * has been called.
806      * <p>
807      * This is complicated by the fact that this table may be nested within
808      * another table. In this case a different datamodel should be fetched
809      * for each row. When nested within a parent table, the parent reference
810      * won't change but parent.getClientId() will, as the suffix changes
811      * depending upon the current row index. A map object on this component
812      * is therefore used to cache the datamodel for each row of the table.
813      * In the normal case where this table is not nested inside a component
814      * that changes its id (like a table does) then this map only ever has
815      * one entry.
816      */
817     private DataModel getDataModel()
818     {
819         DataModel dataModel = null;
820         String clientID = "";
821         
822         UIComponent parent = getParent();
823         if (parent != null) {
824             clientID = parent.getClientId(getFacesContext());
825         }
826         dataModel = (DataModel) _dataModelMap.get(clientID);
827         if (dataModel == null)
828         {
829             dataModel = createDataModel();
830             _dataModelMap.put(clientID, dataModel);
831         }
832         return dataModel;
833     }
834 
835     /**
836      * Evaluate this object's value property and convert the result into a 
837      * DataModel. Normally this object's value property will be a value-binding
838      * which will cause the value to be fetched from some backing bean.
839      * <p>
840      * The result of fetching the value may be a DataModel object, in which
841      * case that object is returned directly. If the value is of type
842      * List, Array, ResultSet, Result, other object or null then an appropriate
843      * wrapper is created and returned.
844      * <p>
845      * Null is never returned by this method.
846      */
847     private DataModel createDataModel()
848     {
849         Object value = getValue();
850         if (value == null)
851         {
852             return EMPTY_DATA_MODEL;
853         }
854         else if (value instanceof DataModel)
855         {
856             return (DataModel) value;
857         }
858         else if (value instanceof List)
859         {
860             return new ListDataModel((List) value);
861         }
862         else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass()))
863         {
864             return new ArrayDataModel((Object[]) value);
865         }
866         else if (value instanceof ResultSet)
867         {
868             return new ResultSetDataModel((ResultSet) value);
869         }
870         else if (value instanceof Result)
871         {
872             return new ResultDataModel((Result) value);
873         }
874         else
875         {
876             return new ScalarDataModel(value);
877         }
878     }
879 
880     private static class FacesEventWrapper extends FacesEvent
881     {
882         private static final long serialVersionUID = 6648047974065628773L;
883         private FacesEvent _wrappedFacesEvent;
884         private int _rowIndex;
885 
886         public FacesEventWrapper(FacesEvent facesEvent, int rowIndex,
887                 UIData redirectComponent)
888         {
889             super(redirectComponent);
890             _wrappedFacesEvent = facesEvent;
891             _rowIndex = rowIndex;
892         }
893 
894         public PhaseId getPhaseId()
895         {
896             return _wrappedFacesEvent.getPhaseId();
897         }
898 
899         public void setPhaseId(PhaseId phaseId)
900         {
901             _wrappedFacesEvent.setPhaseId(phaseId);
902         }
903 
904         public void queue()
905         {
906             _wrappedFacesEvent.queue();
907         }
908 
909         public String toString()
910         {
911             return _wrappedFacesEvent.toString();
912         }
913 
914         public boolean isAppropriateListener(FacesListener faceslistener)
915         {
916             return _wrappedFacesEvent.isAppropriateListener(faceslistener);
917         }
918 
919         public void processListener(FacesListener faceslistener)
920         {
921             _wrappedFacesEvent.processListener(faceslistener);
922         }
923 
924         public FacesEvent getWrappedFacesEvent()
925         {
926             return _wrappedFacesEvent;
927         }
928 
929         public int getRowIndex()
930         {
931             return _rowIndex;
932         }
933     }
934 
935     private static final DataModel EMPTY_DATA_MODEL = new DataModel()
936     {
937         public boolean isRowAvailable()
938         {
939             return false;
940         }
941 
942         public int getRowCount()
943         {
944             return 0;
945         }
946 
947         public Object getRowData()
948         {
949             throw new IllegalArgumentException();
950         }
951 
952         public int getRowIndex()
953         {
954             return -1;
955         }
956 
957         public void setRowIndex(int i)
958         {
959             if (i < -1)
960                 throw new IllegalArgumentException();
961         }
962 
963         public Object getWrappedData()
964         {
965             return null;
966         }
967 
968         public void setWrappedData(Object obj)
969         {
970             if (obj == null)
971                 return; //Clearing is allowed
972             throw new UnsupportedOperationException(this.getClass().getName()
973                     + " UnsupportedOperationException");
974         }
975     };
976 
977     public void setValue(Object value)
978     {
979         _value = value;
980         _dataModelMap.clear();
981         _rowStates.clear();
982         _isValidChilds = true;
983     }
984 
985     public Object saveState(FacesContext context)
986     {
987         Object[] values = new Object[STATE_SIZE];
988         values[SUPER_STATE_INDEX] = super.saveState(context);
989         values[FIRST_STATE_INDEX] = _first;
990         values[ROWS_STATE_INDEX] = _rows;
991         values[VALUE_STATE_INDEX] = _value;
992         values[VAR_STATE_INDEX] = _var;
993         return values;
994     }
995 
996     public void restoreState(FacesContext context, Object state)
997     {
998         Object[] values = (Object[]) state;
999         super.restoreState(context, values[0]);
1000         _first = (Integer) values[FIRST_STATE_INDEX];
1001         _rows = (Integer) values[ROWS_STATE_INDEX];
1002         _value = values[VALUE_STATE_INDEX];
1003         _var = (String) values[VAR_STATE_INDEX];
1004     }
1005 
1006     private class EditableValueHolderState
1007     {
1008         private final Object _value;
1009         private final boolean _localValueSet;
1010         private final boolean _valid;
1011         private final Object _submittedValue;
1012 
1013         public EditableValueHolderState(EditableValueHolder evh)
1014         {
1015             _value = evh.getLocalValue();
1016             _localValueSet = evh.isLocalValueSet();
1017             _valid = evh.isValid();
1018             _submittedValue = evh.getSubmittedValue();
1019         }
1020 
1021         public void restoreState(EditableValueHolder evh)
1022         {
1023             evh.setValue(_value);
1024             evh.setLocalValueSet(_localValueSet);
1025             evh.setValid(_valid);
1026             evh.setSubmittedValue(_submittedValue);
1027         }
1028     }
1029 
1030     public UIData()
1031     {
1032         setRendererType(DEFAULT_RENDERER_TYPE);
1033     }
1034 
1035     public String getFamily()
1036     {
1037         return COMPONENT_FAMILY;
1038     }
1039 
1040     public void setFirst(int first)
1041     {
1042         if (first < 0)
1043         {
1044             throw new IllegalArgumentException("Illegal value for first row: " + first);
1045         }
1046         _first = new Integer(first);
1047     }
1048 
1049     /**
1050      * The index of the first row to be displayed, where 0 is the first row.
1051      * 
1052      * @JSFProperty
1053      */
1054     public int getFirst()
1055     {
1056         if (_first != null)
1057         {
1058             return _first.intValue();
1059         }
1060         ValueBinding vb = getValueBinding("first");
1061         Number v = vb != null ? (Number) vb.getValue(getFacesContext()) : null;
1062         return v != null ? v.intValue() : DEFAULT_FIRST;
1063     }
1064 
1065     /**
1066      * The number of rows to be displayed.  Specify zero for all remaining rows in the table.
1067      * 
1068      * @JSFProperty
1069      */
1070     public int getRows()
1071     {
1072         if (_rows != null)
1073         {
1074             return _rows.intValue();
1075         }
1076         ValueBinding vb = getValueBinding("rows");
1077         Number v = vb != null ? (Number) vb.getValue(getFacesContext()) : null;
1078         return v != null ? v.intValue() : DEFAULT_ROWS;
1079     }
1080 
1081     /**
1082      * An EL expression that specifies the data model that backs this table.  The value can be of any type.
1083      * 
1084      * A value of type DataModel is used directly.  Array-like parameters of type java.util.List, array of Object, 
1085      * java.sql.ResultSet, or javax.servlet.jsp.jstl.sql.Result are wrapped in a DataModel.
1086      * 
1087      * Other values are wrapped in a DataModel as a single row.
1088      * @JSFProperty
1089      */
1090     public Object getValue()
1091     {
1092         if (_value != null)
1093             return _value;
1094         ValueBinding vb = getValueBinding("value");
1095         return vb != null ? vb.getValue(getFacesContext()) : null;
1096     }
1097 }