Save This Page
Home » mojarra-1.2_09-b02-FCS-source » javax.faces.component » [javadoc | source]
    1   /*
    2    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    3    * 
    4    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
    5    * 
    6    * The contents of this file are subject to the terms of either the GNU
    7    * General Public License Version 2 only ("GPL") or the Common Development
    8    * and Distribution License("CDDL") (collectively, the "License").  You
    9    * may not use this file except in compliance with the License. You can obtain
   10    * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
   11    * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
   12    * language governing permissions and limitations under the License.
   13    * 
   14    * When distributing the software, include this License Header Notice in each
   15    * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
   16    * Sun designates this particular file as subject to the "Classpath" exception
   17    * as provided by Sun in the GPL Version 2 section of the License file that
   18    * accompanied this code.  If applicable, add the following below the License
   19    * Header, with the fields enclosed by brackets [] replaced by your own
   20    * identifying information: "Portions Copyrighted [year]
   21    * [name of copyright owner]"
   22    * 
   23    * Contributor(s):
   24    * 
   25    * If you wish your version of this file to be governed by only the CDDL or
   26    * only the GPL Version 2, indicate your decision by adding "[Contributor]
   27    * elects to include this software in this distribution under the [CDDL or GPL
   28    * Version 2] license."  If you don't indicate a single choice of license, a
   29    * recipient has the option to distribute your version of this file under
   30    * either the CDDL, the GPL Version 2 or to extend the choice of license to
   31    * its licensees as provided above.  However, if you add GPL Version 2 code
   32    * and therefore, elected the GPL Version 2 license, then the option applies
   33    * only if the new code is made subject to such option by the copyright
   34    * holder.
   35    */
   36   
   37   package javax.faces.component;
   38   
   39   import javax.el.ELException;
   40   import javax.el.ValueExpression;
   41   import javax.faces.FacesException;
   42   import javax.faces.application.FacesMessage;
   43   import javax.faces.context.FacesContext;
   44   import javax.faces.el.ValueBinding;
   45   import javax.faces.event.AbortProcessingException;
   46   import javax.faces.event.FacesEvent;
   47   import javax.faces.event.FacesListener;
   48   import javax.faces.event.PhaseId;
   49   import javax.faces.model.ArrayDataModel;
   50   import javax.faces.model.DataModel;
   51   import javax.faces.model.ListDataModel;
   52   import javax.faces.model.ResultDataModel;
   53   import javax.faces.model.ResultSetDataModel;
   54   import javax.faces.model.ScalarDataModel;
   55   import javax.servlet.jsp.jstl.sql.Result;
   56   
   57   import java.io.IOException;
   58   import java.io.Serializable;
   59   import java.sql.ResultSet;
   60   import java.util.Collections;
   61   import java.util.HashMap;
   62   import java.util.List;
   63   import java.util.Map;
   64   import java.util.Iterator;
   65   
   66   
   67   /**
   68    * <p><strong>UIData</strong> is a {@link UIComponent} that supports data
   69    * binding to a collection of data objects represented by a {@link DataModel}
   70    * instance, which is the current value of this component itself (typically
   71    * established via a {@link ValueExpression}). During iterative processing over
   72    * the rows of data in the data model, the object for the current row is exposed
   73    * as a request attribute under the key specified by the <code>var</code>
   74    * property.</p>
   75    * <p/>
   76    * <p>Only children of type {@link UIColumn} should be processed by renderers
   77    * associated with this component.</p>
   78    * <p/>
   79    * <p>By default, the <code>rendererType</code> property is set to
   80    * <code>javax.faces.Table</code>.  This value can be changed by calling the
   81    * <code>setRendererType()</code> method.</p>
   82    */
   83   
   84   public class UIData extends UIComponentBase
   85         implements NamingContainer {
   86   
   87       // ------------------------------------------------------ Manifest Constants
   88   
   89   
   90       /**
   91        * <p>The standard component type for this component.</p>
   92        */
   93       public static final String COMPONENT_TYPE = "javax.faces.Data";
   94   
   95   
   96       /**
   97        * <p>The standard component family for this component.</p>
   98        */
   99       public static final String COMPONENT_FAMILY = "javax.faces.Data";
  100   
  101       // ------------------------------------------------------------ Constructors
  102   
  103   
  104       /**
  105        * <p>Create a new {@link UIData} instance with default property
  106        * values.</p>
  107        */
  108       public UIData() {
  109   
  110           super();
  111           setRendererType("javax.faces.Table");
  112   
  113       }
  114   
  115       // ------------------------------------------------------ Instance Variables
  116   
  117   
  118       /**
  119        * <p>The first row number (zero-relative) to be displayed.</p>
  120        */
  121       private Integer first;
  122   
  123   
  124       /**
  125        * <p>The {@link DataModel} associated with this component, lazily
  126        * instantiated if requested.  This object is not part of the saved and
  127        * restored state of the component.</p>
  128        */
  129       private DataModel model = null;
  130   
  131   
  132       /**
  133        * <p> During iteration through the rows of this table, This ivar is used to
  134        * store the previous "var" value for this instance.  When the row iteration
  135        * is complete, this value is restored to the request map.
  136        */
  137       private Object oldVar;
  138   
  139   
  140       /**
  141        * <p>The zero-relative index of the current row number, or -1 for no
  142        * current row association.</p>
  143        */
  144       private int rowIndex = -1;
  145   
  146   
  147       /**
  148        * <p>The number of rows to display, or zero for all remaining rows in the
  149        * table.</p>
  150        */
  151       private Integer rows;
  152   
  153   
  154       /**
  155        * <p>This map contains <code>SavedState</code> instances for each
  156        * descendant component, keyed by the client identifier of the descendant.
  157        * Because descendant client identifiers will contain the
  158        * <code>rowIndex</code> value of the parent, per-row state information is
  159        * actually preserved.</p>
  160        */
  161       @SuppressWarnings({"CollectionWithoutInitialCapacity"})
  162       private Map<String, SavedState> saved = new HashMap<String, SavedState>();
  163   
  164   
  165       /**
  166        * <p>The local value of this {@link UIComponent}.</p>
  167        */
  168       private Object value = null;
  169   
  170   
  171       /**
  172        * <p>The request scope attribute under which the data object for the
  173        * current row will be exposed when iterating.</p>
  174        */
  175       private String var = null;
  176   
  177   
  178       /**
  179        * <p>Holds the base client ID that will be used to generate per-row
  180        * client IDs (this will be null if this UIData is nested within another).</p>
  181        *
  182        * <p>This is not part of the component state.</p>
  183        */
  184       private String baseClientId = null;
  185   
  186   
  187       /**
  188        * <p> Length of the cached <code>baseClientId</code> plus one for the
  189        * NamingContainer.SEPARATOR_CHAR. </p>
  190        *
  191        * <p>This is not part of the component state.</p>
  192        */
  193       private int baseClientIdLength;
  194   
  195   
  196       /**
  197        * <p>StringBuilder used to build per-row client IDs.</p>
  198        *
  199        * <p>This is not part of the component state.</p>
  200        */
  201       private StringBuilder clientIdBuilder = null;
  202   
  203   
  204       /**
  205        * <p>Flag indicating whether or not this UIData instance is nested
  206        * within another UIData instance</p>
  207        *
  208        * <p>This is not part of the component state.</p>
  209        */
  210       private Boolean isNested = null;
  211       
  212   
  213       // -------------------------------------------------------------- Properties
  214   
  215   
  216       public String getFamily() {
  217   
  218           return (COMPONENT_FAMILY);
  219   
  220       }
  221   
  222   
  223       /**
  224        * <p>Return the zero-relative row number of the first row to be
  225        * displayed.</p>
  226        */
  227       public int getFirst() {
  228   
  229           if (this.first != null) {
  230               return (this.first);
  231           }
  232           ValueExpression ve = getValueExpression("first");
  233           if (ve != null) {
  234               Integer value;
  235               try {
  236                   value = (Integer) ve.getValue(getFacesContext().getELContext());
  237               }
  238               catch (ELException e) {
  239                   throw new FacesException(e);
  240               }
  241               if (null == value) {
  242                   return first;
  243               }
  244               return (value.intValue());
  245           } else {
  246               return (0);
  247           }
  248   
  249       }
  250   
  251   
  252       /**
  253        * <p>Set the zero-relative row number of the first row to be
  254        * displayed.</p>
  255        *
  256        * @param first New first row number
  257        *
  258        * @throws IllegalArgumentException if <code>first</code> is negative
  259        */
  260       public void setFirst(int first) {
  261   
  262           if (first < 0) {
  263               throw new IllegalArgumentException(String.valueOf(first));
  264           }
  265           this.first = first;
  266   
  267       }
  268   
  269   
  270       /**
  271        * <p>Return the footer facet of this component (if any).  A convenience
  272        * method for <code>getFacet("footer")</code>.</p>
  273        */
  274       public UIComponent getFooter() {
  275   
  276           return getFacet("footer");
  277   
  278       }
  279   
  280   
  281       /**
  282        * <p>Set the footer facet of this component.  A convenience method for
  283        * <code>getFacets().put("footer", footer)</code>.</p>
  284        *
  285        * @param footer the new footer facet
  286        *
  287        * @throws NullPointerException if <code>footer</code> is <code>null</code>
  288        */
  289       public void setFooter(UIComponent footer) {
  290   
  291           getFacets().put("footer", footer);
  292   
  293       }
  294   
  295   
  296       /**
  297        * <p>Return the header facet of this component (if any).  A convenience
  298        * method for <code>getFacet("header")</code>.</p>
  299        */
  300       public UIComponent getHeader() {
  301   
  302           return getFacet("header");
  303   
  304       }
  305   
  306   
  307       /**
  308        * <p>Set the header facet of this component.  A convenience method for
  309        * <code>getFacets().put("header", header)</code>.</p>
  310        *
  311        * @param header the new header facet
  312        *
  313        * @throws NullPointerException if <code>header</code> is <code>null</code>
  314        */
  315       public void setHeader(UIComponent header) {
  316   
  317           getFacets().put("header", header);
  318   
  319       }
  320   
  321   
  322       /**
  323        * <p>Return a flag indicating whether there is <code>rowData</code>
  324        * available at the current <code>rowIndex</code>.  If no
  325        * <code>wrappedData</code> is available, return <code>false</code>.</p>
  326        *
  327        * @throws FacesException if an error occurs getting the row availability
  328        */
  329       public boolean isRowAvailable() {
  330   
  331           return (getDataModel().isRowAvailable());
  332   
  333       }
  334   
  335   
  336       /**
  337        * <p>Return the number of rows in the underlying data model.  If the number
  338        * of available rows is unknown, return -1.</p>
  339        *
  340        * @throws FacesException if an error occurs getting the row count
  341        */
  342       public int getRowCount() {
  343   
  344           return (getDataModel().getRowCount());
  345   
  346       }
  347   
  348   
  349       /**
  350        * <p>Return the data object representing the data for the currently
  351        * selected row index, if any.</p>
  352        *
  353        * @throws FacesException           if an error occurs getting the row data
  354        * @throws IllegalArgumentException if now row data is available at the
  355        *                                  currently specified row index
  356        */
  357       public Object getRowData() {
  358   
  359           return (getDataModel().getRowData());
  360   
  361       }
  362   
  363   
  364       /**
  365        * <p>Return the zero-relative index of the currently selected row.  If we
  366        * are not currently positioned on a row, return -1.  This property is
  367        * <strong>not</strong> enabled for value binding expressions.</p>
  368        *
  369        * @throws FacesException if an error occurs getting the row index
  370        */
  371       public int getRowIndex() {
  372   
  373           return (this.rowIndex);
  374   
  375       }
  376   
  377   
  378       /**
  379        * <p>Set the zero relative index of the current row, or -1 to indicate that
  380        * no row is currently selected, by implementing the following algorithm.
  381        * It is possible to set the row index at a value for which the underlying
  382        * data collection does not contain any row data.  Therefore, callers may
  383        * use the <code>isRowAvailable()</code> method to detect whether row data
  384        * will be available for use by the <code>getRowData()</code> method.</p>
  385        *</p>
  386        * <ul>
  387        * <li>Save current state information for all descendant components (as
  388        *     described below).
  389        * <li>Store the new row index, and pass it on to the {@link DataModel}
  390        *     associated with this {@link UIData} instance.</li>
  391        * <li>If the new <code>rowIndex</code> value is -1:
  392        *     <ul>
  393        *     <li>If the <code>var</code> property is not null,
  394        *         remove the corresponding request scope attribute (if any).</li>
  395        *     <li>Reset the state information for all descendant components
  396        *         (as described below).</li>
  397        *     </ul></li>
  398        * <li>If the new <code>rowIndex</code> value is not -1:
  399        *     <ul>
  400        *     <li>If the <code>var</code> property is not null, call
  401        *         <code>getRowData()</code> and expose the resulting data object
  402        *         as a request scope attribute whose key is the <code>var</code>
  403        *         property value.</li>
  404        *     <li>Reset the state information for all descendant components
  405        *         (as described below).
  406        *     </ul></li>
  407        * </ul>
  408        *
  409        * <p>To save current state information for all descendant components,
  410        * {@link UIData} must maintain per-row information for each descendant
  411        * as follows:<p>
  412        * <ul>
  413        * <li>If the descendant is an instance of <code>EditableValueHolder</code>, save
  414        *     the state of its <code>localValue</code> property.</li>
  415        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  416        *     save the state of the <code>localValueSet</code> property.</li>
  417        * <li>If the descendant is an instance of <code>EditableValueHolder</code>, save
  418        *     the state of the <code>valid</code> property.</li>
  419        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  420        *     save the state of the <code>submittedValue</code> property.</li>
  421        * </ul>
  422        *
  423        * <p>To restore current state information for all descendant components,
  424        * {@link UIData} must reference its previously stored information for the
  425        * current <code>rowIndex</code> and call setters for each descendant
  426        * as follows:</p>
  427        * <ul>
  428        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  429        *     restore the <code>value</code> property.</li>
  430        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  431        *     restore the state of the <code>localValueSet</code> property.</li>
  432        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  433        *     restore the state of the <code>valid</code> property.</li>
  434        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  435        *     restore the state of the <code>submittedValue</code> property.</li>
  436        * </ul>
  437        *
  438        * @param rowIndex The new row index value, or -1 for no associated row
  439        *
  440        * @throws FacesException if an error occurs setting the row index
  441        * @throws IllegalArgumentException if <code>rowIndex</code>
  442        *  is less than -1
  443        */
  444       public void setRowIndex(int rowIndex) {
  445   
  446           // Save current state for the previous row index
  447           saveDescendantState();
  448   
  449           // Update to the new row index        
  450           this.rowIndex = rowIndex;
  451           DataModel localModel = getDataModel();
  452           localModel.setRowIndex(rowIndex);
  453   
  454           // Clear or expose the current row data as a request scope attribute
  455           if (var != null) {
  456               Map<String, Object> requestMap =
  457                     getFacesContext().getExternalContext().getRequestMap();
  458               if (rowIndex == -1) {
  459                   oldVar = requestMap.remove(var);
  460               } else if (isRowAvailable()) {
  461                   requestMap.put(var, getRowData());
  462               } else {
  463                   requestMap.remove(var);
  464                   if (null != oldVar) {
  465                       requestMap.put(var, oldVar);
  466                       oldVar = null;
  467                   }
  468               }
  469           }
  470   
  471           // Reset current state information for the new row index
  472           restoreDescendantState();
  473   
  474       }
  475   
  476   
  477       /**
  478        * <p>Return the number of rows to be displayed, or zero for all remaining
  479        * rows in the table.  The default value of this property is zero.</p>
  480        */
  481       public int getRows() {
  482   
  483           if (this.rows != null) {
  484               return (this.rows);
  485           }
  486           ValueExpression ve = getValueExpression("rows");
  487           if (ve != null) {
  488               Integer value;
  489               try {
  490                   value = (Integer) ve.getValue(getFacesContext().getELContext());
  491               }
  492               catch (ELException e) {
  493                   throw new FacesException(e);
  494               }
  495   
  496               if (null == value) {
  497                   return rows;
  498               }
  499               return (value.intValue());
  500           } else {
  501               return (0);
  502           }
  503   
  504       }
  505   
  506   
  507       /**
  508        * <p>Set the number of rows to be displayed, or zero for all remaining rows
  509        * in the table.</p>
  510        *
  511        * @param rows New number of rows
  512        *
  513        * @throws IllegalArgumentException if <code>rows</code> is negative
  514        */
  515       public void setRows(int rows) {
  516   
  517           if (rows < 0) {
  518               throw new IllegalArgumentException(String.valueOf(rows));
  519           }
  520           this.rows = rows;
  521   
  522       }
  523   
  524   
  525       /**
  526        * <p>Return the request-scope attribute under which the data object for the
  527        * current row will be exposed when iterating.  This property is
  528        * <strong>not</strong> enabled for value binding expressions.</p>
  529        */
  530       public String getVar() {
  531   
  532           return (this.var);
  533   
  534       }
  535   
  536   
  537       /**
  538        * <p>Set the request-scope attribute under which the data object for the
  539        * current row wil be exposed when iterating.</p>
  540        *
  541        * @param var The new request-scope attribute name
  542        */
  543       public void setVar(String var) {
  544   
  545           this.var = var;
  546   
  547       }
  548   
  549       // ----------------------------------------------------- StateHolder Methods
  550   
  551   
  552       private Object[] values;
  553   
  554       public Object saveState(FacesContext context) {
  555   
  556           if (values == null) {
  557               values = new Object[7];
  558           }
  559   
  560           values[0] = super.saveState(context);
  561           values[1] = first;
  562           values[2] = rowIndex;
  563           values[3] = rows;
  564           values[4] = saved;
  565           values[5] = value;
  566           values[6] = var;
  567           return (values);
  568   
  569       }
  570   
  571   
  572       public void restoreState(FacesContext context, Object state) {
  573   
  574           values = (Object[]) state;
  575           super.restoreState(context, values[0]);
  576           first = (Integer) values[1];
  577           rowIndex = (Integer) values[2];
  578           rows = (Integer) values[3];
  579           saved = TypedCollections
  580                 .dynamicallyCastMap((Map) values[4], String.class, SavedState.class);
  581           value = values[5];
  582           var = (String) values[6];
  583   
  584       }
  585   
  586   
  587       /**
  588        * <p>Return the value of the UIData.  This value must either be
  589        * be of type {@link DataModel}, or a type that can be adapted
  590        * into a {@link DataModel}.  <code>UIData</code> will automatically
  591        * adapt the following types:</p>
  592        * <ul>
  593        * <li>Arrays</li>
  594        * <li><code>java.util.List</code></li>
  595        * <li><code>java.sql.ResultSet</code></li>
  596        * <li><code>javax.servlet.jsp.jstl.sql.Result</code></li>
  597        * </ul>
  598        * <p>All other types will be adapted using the {@link ScalarDataModel}
  599        * class, which will treat the object as a single row of data.</p>
  600        */
  601       public Object getValue() {
  602   
  603           if (this.value != null) {
  604               return (this.value);
  605           }
  606           ValueExpression ve = getValueExpression("value");
  607           if (ve != null) {
  608               try {
  609                   return (ve.getValue(getFacesContext().getELContext()));
  610               }
  611               catch (ELException e) {
  612                   throw new FacesException(e);
  613               }
  614   
  615           } else {
  616               return (null);
  617           }
  618   
  619       }
  620   
  621   
  622       /**
  623        * <p>Set the value of the <code>UIData</code>.  This value must either be
  624        * be of type {@link DataModel}, or a type that can be adapted into a {@link
  625        * DataModel}.</p>
  626        *
  627        * @param value the new value
  628        */
  629       public void setValue(Object value) {
  630           setDataModel(null);
  631           this.value = value;
  632   
  633       }
  634   
  635       // ----------------------------------------------------- UIComponent Methods
  636   
  637   
  638       /**
  639        * <p>If "name" is something other than "value", "var", or "rowIndex", rely
  640        * on the superclass conversion from <code>ValueBinding</code> to
  641        * <code>ValueExpression</code>.</p>
  642        *
  643        * @param name    Name of the attribute or property for which to set a
  644        *                {@link ValueBinding}
  645        * @param binding The {@link ValueBinding} to set, or <code>null</code> to
  646        *                remove any currently set {@link ValueBinding}
  647        *
  648        * @throws IllegalArgumentException if <code>name</code> is one of
  649        *                                  <code>id</code>, <code>parent</code>,
  650        *                                  <code>var</code>, or <code>rowIndex</code>
  651        * @throws NullPointerException     if <code>name</code> is <code>null</code>
  652        * @deprecated This has been replaced by {@link #setValueExpression(java.lang.String,
  653        *javax.el.ValueExpression)}.
  654        */
  655       public void setValueBinding(String name, ValueBinding binding) {
  656   
  657           if ("value".equals(name)) {
  658               setDataModel(null);
  659           } else if ("var".equals(name) || "rowIndex".equals(name)) {
  660               throw new IllegalArgumentException();
  661           }
  662           super.setValueBinding(name, binding);
  663   
  664       }
  665   
  666       /**
  667        * <p>Set the {@link ValueExpression} used to calculate the value for the
  668        * specified attribute or property name, if any.  In addition, if a {@link
  669        * ValueExpression} is set for the <code>value</code> property, remove any
  670        * synthesized {@link DataModel} for the data previously bound to this
  671        * component.</p>
  672        *
  673        * @param name    Name of the attribute or property for which to set a
  674        *                {@link ValueExpression}
  675        * @param binding The {@link ValueExpression} to set, or <code>null</code>
  676        *                to remove any currently set {@link ValueExpression}
  677        *
  678        * @throws IllegalArgumentException if <code>name</code> is one of
  679        *                                  <code>id</code>, <code>parent</code>,
  680        *                                  <code>var</code>, or <code>rowIndex</code>
  681        * @throws NullPointerException     if <code>name</code> is <code>null</code>
  682        * @since 1.2
  683        */
  684       public void setValueExpression(String name, ValueExpression binding) {
  685   
  686           if ("value".equals(name)) {
  687               this.model = null;
  688           } else if ("var".equals(name) || "rowIndex".equals(name)) {
  689               throw new IllegalArgumentException();
  690           }
  691           super.setValueExpression(name, binding);
  692   
  693       }
  694   
  695       /**
  696        * <p>Return a client identifier for this component that includes the
  697        * current value of the <code>rowIndex</code> property, if it is not set to
  698        * -1.  This implies that multiple calls to <code>getClientId()</code> may
  699        * return different results, but ensures that child components can
  700        * themselves generate row-specific client identifiers (since {@link UIData}
  701        * is a {@link NamingContainer}).</p>
  702        *
  703        * @throws NullPointerException if <code>context</code> is <code>null</code>
  704        */
  705       public String getClientId(FacesContext context) {
  706   
  707           if (context == null) {
  708               throw new NullPointerException();
  709           }
  710   
  711           // If baseClientId and clientIdBuilder are both null, this is the
  712           // first time that getClientId() has been called.
  713           // If we're not nested within another UIData, then:
  714           //   - create a new StringBuilder assigned to clientIdBuilder containing
  715           //   our client ID.
  716           //   - toString() the builder - this result will be our baseClientId
  717           //     for the duration of the component
  718           //   - append SEPARATOR_CHAR to the builder
  719           //  If we are nested within another UIData, then:
  720           //   - create an empty StringBuilder that will be used to build
  721           //     this instance's ID
  722           if (baseClientId == null && clientIdBuilder == null) {
  723               if (!isNestedWithinUIData()) {
  724                   clientIdBuilder = new StringBuilder(super.getClientId(context));
  725                   baseClientId = clientIdBuilder.toString();
  726                   baseClientIdLength = (baseClientId.length() + 1);
  727                   clientIdBuilder.append(NamingContainer.SEPARATOR_CHAR);
  728                   clientIdBuilder.setLength(baseClientIdLength);
  729               } else {
  730                   clientIdBuilder = new StringBuilder();
  731               }
  732           }
  733           if (rowIndex >= 0) {
  734               String cid;
  735               if (!isNestedWithinUIData()) {
  736                   // we're not nested, so the clientIdBuilder is already
  737                   // primed with clientID + SEPARATOR_CHAR.  Append
  738                   // the current rowIndex, and toString() the builder.
  739                   // reset the builder to it's primed state.
  740                   cid = clientIdBuilder.append(rowIndex).toString();
  741                   clientIdBuilder.setLength(baseClientIdLength);
  742               } else {
  743                   // we're nested, so we have to build the ID from scratch
  744                   // each time.  Reuse the same clientIdBuilder instance
  745                   // for each call by resetting the length to 0 after
  746                   // the ID has been computed.
  747                   cid = clientIdBuilder.append(super.getClientId(context))
  748                         .append(NamingContainer.SEPARATOR_CHAR).append(rowIndex)
  749                         .toString();
  750                   clientIdBuilder.setLength(0);
  751               }
  752               return (cid);
  753           } else {
  754               if (!isNestedWithinUIData()) {
  755                   // Not nested and no row available, so just return our baseClientId
  756                   return (baseClientId);
  757               } else {
  758                   // nested and no row available, return the result of getClientId().
  759                   // this is necessary as the client ID will reflect the row that
  760                   // this table represents
  761                   return super.getClientId(context);
  762               }
  763           }
  764   
  765       }
  766   
  767       /**
  768        * <p>Override behavior from {@link UIComponentBase#invokeOnComponent} to
  769        * provide special care for positioning the data properly before finding the
  770        * component and invoking the callback on it.  If the argument
  771        * <code>clientId</code> is equal to <code>this.getClientId()</code> simply
  772        * invoke the <code>contextCallback</code>, passing the <code>context</code>
  773        * argument and <b>this</b> as arguments, and return <code>true.</code>
  774        * Otherwise, attempt to extract a rowIndex from the <code>clientId</code>.
  775        * For example, if the argument <code>clientId</code> was
  776        * <code>form:data:3:customerHeader</code> the rowIndex would be
  777        * <code>3</code>.  Let this value be called <code>newIndex</code>. The
  778        * current rowIndex of this instance must be saved aside and restored before
  779        * returning in all cases, regardless of the outcome of the search or if any
  780        * exceptions are thrown in the process.</p>
  781        * 
  782        * <p>The implementation of this method must never return <code>true</code>
  783        * if setting the rowIndex of this instance to be equal to
  784        * <code>newIndex</code> causes this instance to return <code>false</code>
  785        * from {@link #isRowAvailable}.</p>
  786        *
  787        * @throws NullPointerException {@inheritDoc}
  788        * @throws FacesException       {@inheritDoc}  Also throws <code>FacesException</code>
  789        *                              if any exception is thrown when deriving the
  790        *                              rowIndex from the argument <code>clientId</code>.
  791        * @since 1.2
  792        */
  793       public boolean invokeOnComponent(FacesContext context, String clientId,
  794                                        ContextCallback callback)
  795             throws FacesException {
  796           if (null == context || null == clientId || null == callback) {
  797               throw new NullPointerException();
  798           }
  799           String myId = super.getClientId(context);
  800           boolean found = false;
  801           if (clientId.equals(myId)) {
  802               try {
  803                   callback.invokeContextCallback(context, this);
  804                   return true;
  805               }
  806               catch (Exception e) {
  807                   throw new FacesException(e);
  808               }
  809           }
  810   
  811           // check the facets, if any, of UIData
  812           if (this.getFacetCount() > 0) {
  813               for (Iterator<UIComponent> i = this.getFacets().values().iterator(); i.hasNext(); ) {
  814                   UIComponent c = i.next();
  815                   if (clientId.equals(c.getClientId(context))) {
  816                       callback.invokeContextCallback(context, c);
  817                       return true;
  818                   }
  819               }
  820           }
  821           
  822           int lastSep, newRow, savedRowIndex = this.getRowIndex();
  823           try {
  824               // If we need to strip out the rowIndex from our id
  825               // PENDING(edburns): is this safe with respect to I18N?
  826               if (myId.endsWith(NamingContainer.SEPARATOR_CHAR + Integer.toString(savedRowIndex, 10))) {
  827                   lastSep = myId.lastIndexOf(NamingContainer.SEPARATOR_CHAR);
  828                   assert (-1 != lastSep);
  829                   myId = myId.substring(0, lastSep);
  830               }
  831   
  832               // myId will be something like form:outerData for a non-nested table,
  833               // and form:outerData:3:data for a nested table.
  834               // clientId will be something like form:outerData:3:outerColumn
  835               // for a non-nested table.  clientId will be something like
  836               // outerData:3:data:3:input for a nested table.
  837               if (clientId.startsWith(myId)) {
  838                   int preRowIndexSep, postRowIndexSep;
  839   
  840                   if (-1 != (preRowIndexSep =
  841                         clientId.indexOf(NamingContainer.SEPARATOR_CHAR,
  842                                          myId.length()))) {
  843                       // Check the length
  844                       if (++preRowIndexSep < clientId.length()) {
  845                           if (-1 != (postRowIndexSep =
  846                                 clientId.indexOf(NamingContainer.SEPARATOR_CHAR,
  847                                                  preRowIndexSep + 1))) {
  848                               try {
  849                                   newRow = Integer
  850                                         .valueOf(clientId.substring(preRowIndexSep,
  851                                                                     postRowIndexSep))
  852                                         .intValue();
  853                               } catch (NumberFormatException ex) {
  854                                   // PENDING(edburns): I18N
  855                                   String message =
  856                                         "Trying to extract rowIndex from clientId \'"
  857                                         +
  858                                         clientId
  859                                         + "\' "
  860                                         + ex.getMessage();
  861                                   throw new NumberFormatException(message);
  862                               }
  863                               this.setRowIndex(newRow);
  864                               if (this.isRowAvailable()) {
  865                                   found = super.invokeOnComponent(context,
  866                                                                   clientId,
  867                                                                   callback);
  868                               }
  869                           }
  870                       }
  871                   }
  872               }
  873           }
  874           catch (FacesException fe) {
  875               throw fe;
  876           }
  877           catch (Exception e) {
  878               throw new FacesException(e);
  879           }
  880           finally {
  881               this.setRowIndex(savedRowIndex);
  882           }
  883           return found;
  884       }
  885   
  886   
  887       /**
  888        * <p>Override the default {@link UIComponentBase#queueEvent} processing to
  889        * wrap any queued events in a wrapper so that we can reset the current row
  890        * index in <code>broadcast()</code>.</p>
  891        *
  892        * @param event {@link FacesEvent} to be queued
  893        *
  894        * @throws IllegalStateException if this component is not a descendant of a
  895        *                               {@link UIViewRoot}
  896        * @throws NullPointerException  if <code>event</code> is <code>null</code>
  897        */
  898       public void queueEvent(FacesEvent event) {
  899   
  900           super.queueEvent(new WrapperEvent(this, event, getRowIndex()));
  901   
  902       }
  903   
  904   
  905       /**
  906        * <p>Override the default {@link UIComponentBase#broadcast} processing to
  907        * unwrap any wrapped {@link FacesEvent} and reset the current row index,
  908        * before the event is actually broadcast.  For events that we did not wrap
  909        * (in <code>queueEvent()</code>), default processing will occur.</p>
  910        *
  911        * @param event The {@link FacesEvent} to be broadcast
  912        *
  913        * @throws AbortProcessingException Signal the JavaServer Faces
  914        *                                  implementation that no further
  915        *                                  processing on the current event should
  916        *                                  be performed
  917        * @throws IllegalArgumentException if the implementation class of this
  918        *                                  {@link FacesEvent} is not supported by
  919        *                                  this component
  920        * @throws NullPointerException     if <code>event</code> is <code>null</code>
  921        */
  922       public void broadcast(FacesEvent event)
  923             throws AbortProcessingException {
  924   
  925           if (!(event instanceof WrapperEvent)) {
  926               super.broadcast(event);
  927               return;
  928           }
  929   
  930           // Set up the correct context and fire our wrapped event
  931           WrapperEvent revent = (WrapperEvent) event;
  932           if (isNestedWithinUIData()) {
  933               setDataModel(null);
  934           }
  935           int oldRowIndex = getRowIndex();
  936           setRowIndex(revent.getRowIndex());
  937           FacesEvent rowEvent = revent.getFacesEvent();
  938           rowEvent.getComponent().broadcast(rowEvent);
  939           setRowIndex(oldRowIndex);
  940   
  941       }
  942   
  943   
  944       /**
  945        * <p>In addition to the default behavior, ensure that any saved per-row
  946        * state for our child input components is discarded unless it is needed to
  947        * rerender the current page with errors.
  948        *
  949        * @param context FacesContext for the current request
  950        *
  951        * @throws IOException          if an input/output error occurs while
  952        *                              rendering
  953        * @throws NullPointerException if <code>context</code> is <code>null</code>
  954        */
  955       public void encodeBegin(FacesContext context) throws IOException {
  956   
  957           setDataModel(null); // re-evaluate even with server-side state saving
  958           if (!keepSaved(context)) {
  959               //noinspection CollectionWithoutInitialCapacity
  960               saved = new HashMap<String, SavedState>();
  961           }
  962           super.encodeBegin(context);
  963   
  964       }
  965   
  966   
  967       /**
  968        * <p>Override the default {@link UIComponentBase#processDecodes} processing
  969        * to perform the following steps.</p> <ul> <li>If the <code>rendered</code>
  970        * property of this {@link UIComponent} is <code>false</code>, skip further
  971        * processing.</li> <li>Set the current <code>rowIndex</code> to -1.</li>
  972        * <li>Call the <code>processDecodes()</code> method of all facets of this
  973        * {@link UIData}, in the order determined by a call to
  974        * <code>getFacets().keySet().iterator()</code>.</li> <li>Call the
  975        * <code>processDecodes()</code> method of all facets of the {@link
  976        * UIColumn} children of this {@link UIData}.</li> <li>Iterate over the set
  977        * of rows that were included when this component was rendered (i.e. those
  978        * defined by the <code>first</code> and <code>rows</code> properties),
  979        * performing the following processing for each row: <ul> <li>Set the
  980        * current <code>rowIndex</code> to the appropriate value for this row.</li>
  981        * <li>If <code>isRowAvailable()</code> returns <code>true</code>, iterate
  982        * over the children components of each {@link UIColumn} child of this
  983        * {@link UIData} component, calling the <code>processDecodes()</code>
  984        * method for each such child.</li> </ul></li> <li>Set the current
  985        * <code>rowIndex</code> to -1.</li> <li>Call the <code>decode()</code>
  986        * method of this component.</li> <li>If a <code>RuntimeException</code> is
  987        * thrown during decode processing, call {@link FacesContext#renderResponse}
  988        * and re-throw the exception.</li> </ul>
  989        *
  990        * @param context {@link FacesContext} for the current request
  991        *
  992        * @throws NullPointerException if <code>context</code> is <code>null</code>
  993        */
  994       public void processDecodes(FacesContext context) {
  995   
  996           if (context == null) {
  997               throw new NullPointerException();
  998           }
  999           if (!isRendered()) {
 1000               return;
 1001           }
 1002   
 1003           setDataModel(null); // Re-evaluate even with server-side state saving
 1004           if (null == saved || !keepSaved(context)) {
 1005               //noinspection CollectionWithoutInitialCapacity
 1006               saved = new HashMap<String, SavedState>(); // We don't need saved state here
 1007           }
 1008   
 1009           iterate(context, PhaseId.APPLY_REQUEST_VALUES);
 1010           decode(context);
 1011   
 1012       }
 1013   
 1014   
 1015       /**
 1016        * <p>Override the default {@link UIComponentBase#processValidators}
 1017        * processing to perform the following steps.</p> <ul> <li>If the
 1018        * <code>rendered</code> property of this {@link UIComponent} is
 1019        * <code>false</code>, skip further processing.</li> <li>Set the current
 1020        * <code>rowIndex</code> to -1.</li> <li>Call the <code>processValidators()</code>
 1021        * method of all facets of this {@link UIData}, in the order determined by a
 1022        * call to <code>getFacets().keySet().iterator()</code>.</li> <li>Call the
 1023        * <code>processValidators()</code> method of all facets of the {@link
 1024        * UIColumn} children of this {@link UIData}.</li> <li>Iterate over the set
 1025        * of rows that were included when this component was rendered (i.e. those
 1026        * defined by the <code>first</code> and <code>rows</code> properties),
 1027        * performing the following processing for each row: <ul> <li>Set the
 1028        * current <code>rowIndex</code> to the appropriate value for this row.</li>
 1029        * <li>If <code>isRowAvailable()</code> returns <code>true</code>, iterate
 1030        * over the children components of each {@link UIColumn} child of this
 1031        * {@link UIData} component, calling the <code>processValidators()</code>
 1032        * method for each such child.</li> </ul></li> <li>Set the current
 1033        * <code>rowIndex</code> to -1.</li> </ul>
 1034        *
 1035        * @param context {@link FacesContext} for the current request
 1036        *
 1037        * @throws NullPointerException if <code>context</code> is <code>null</code>
 1038        */
 1039       public void processValidators(FacesContext context) {
 1040   
 1041           if (context == null) {
 1042               throw new NullPointerException();
 1043           }
 1044           if (!isRendered()) {
 1045               return;
 1046           }
 1047           if (isNestedWithinUIData()) {
 1048               setDataModel(null);
 1049           }
 1050           iterate(context, PhaseId.PROCESS_VALIDATIONS);
 1051           // This is not a EditableValueHolder, so no further processing is required
 1052   
 1053       }
 1054   
 1055   
 1056       /**
 1057        * <p>Override the default {@link UIComponentBase#processUpdates}
 1058        * processing to perform the following steps.</p>
 1059        * <ul>
 1060        * <li>If the <code>rendered</code> property of this {@link UIComponent}
 1061        *     is <code>false</code>, skip further processing.</li>
 1062        * <li>Set the current <code>rowIndex</code> to -1.</li>
 1063        * <li>Call the <code>processUpdates()</code> method of all facets
 1064        *     of this {@link UIData}, in the order determined
 1065        *     by a call to <code>getFacets().keySet().iterator()</code>.</li>
 1066        * <li>Call the <code>processUpdates()</code> method of all facets
 1067        *     of the {@link UIColumn} children of this {@link UIData}.</li>
 1068        * <li>Iterate over the set of rows that were included when this
 1069        *     component was rendered (i.e. those defined by the <code>first</code>
 1070        *     and <code>rows</code> properties), performing the following
 1071        *     processing for each row:
 1072        *     <ul>
 1073        *     <li>Set the current <code>rowIndex</code> to the appropriate
 1074        *         value for this row.</li>
 1075        *     <li>If <code>isRowAvailable()</code> returns <code>true</code>,
 1076        *         iterate over the children components of each {@link UIColumn}
 1077        *         child of this {@link UIData} component, calling the
 1078        *         <code>processUpdates()</code> method for each such child.</li>
 1079        *     </ul></li>
 1080        * <li>Set the current <code>rowIndex</code> to -1.</li>
 1081        * </ul>
 1082        *
 1083        * @param context {@link FacesContext} for the current request
 1084        *
 1085        * @throws NullPointerException if <code>context</code> is <code>null</code>
 1086        */
 1087       public void processUpdates(FacesContext context) {
 1088   
 1089           if (context == null) {
 1090               throw new NullPointerException();
 1091           }
 1092           if (!isRendered()) {
 1093               return;
 1094           }
 1095           if (isNestedWithinUIData()) {
 1096               setDataModel(null);
 1097           }
 1098           iterate(context, PhaseId.UPDATE_MODEL_VALUES);
 1099           // This is not a EditableValueHolder, so no further processing is required
 1100   
 1101       }
 1102   
 1103       // --------------------------------------------------------- Protected Methods
 1104   
 1105   
 1106       /**
 1107        * <p>Return the internal {@link DataModel} object representing the data
 1108        * objects that we will iterate over in this component's rendering.</p>
 1109        * <p/>
 1110        * <p>If the model has been cached by a previous call to {@link
 1111        * #setDataModel}, return it.  Otherwise call {@link #getValue}.  If the
 1112        * result is null, create an empty {@link ListDataModel} and return it.  If
 1113        * the result is an instance of {@link DataModel}, return it.  Otherwise,
 1114        * adapt the result as described in {@link #getValue} and return it.</p>
 1115        */
 1116       protected DataModel getDataModel() {
 1117   
 1118           // Return any previously cached DataModel instance
 1119           if (this.model != null) {
 1120               return (model);
 1121           }
 1122   
 1123           // Synthesize a DataModel around our current value if possible
 1124           Object current = getValue();
 1125           if (current == null) {
 1126               setDataModel(new ListDataModel(Collections.EMPTY_LIST));
 1127           } else if (current instanceof DataModel) {
 1128               setDataModel((DataModel) current);
 1129           } else if (current instanceof List) {
 1130               setDataModel(new ListDataModel((List) current));
 1131           } else if (Object[].class.isAssignableFrom(current.getClass())) {
 1132               setDataModel(new ArrayDataModel((Object[]) current));
 1133           } else if (current instanceof ResultSet) {
 1134               setDataModel(new ResultSetDataModel((ResultSet) current));
 1135           } else if (current instanceof Result) {
 1136               setDataModel(new ResultDataModel((Result) current));
 1137           } else {
 1138               setDataModel(new ScalarDataModel(current));
 1139           }
 1140           return (model);
 1141   
 1142       }
 1143   
 1144       /**
 1145        * <p>Set the internal DataModel.  This <code>UIData</code> instance must
 1146        * use the given {@link DataModel} as its internal value representation from
 1147        * now until the next call to <code>setDataModel</code>.  If the given
 1148        * <code>DataModel</code> is <code>null</code>, the internal
 1149        * <code>DataModel</code> must be reset in a manner so that the next call to
 1150        * {@link #getDataModel} causes lazy instantion of a newly refreshed
 1151        * <code>DataModel</code>.</p>
 1152        * <p/>
 1153        * <p>Subclasses might call this method if they either want to restore the
 1154        * internal <code>DataModel</code> during the <em>Restore View</em> phase or
 1155        * if they want to explicitly refresh the current <code>DataModel</code> for
 1156        * the <em>Render Response</em> phase.</p>
 1157        *
 1158        * @param dataModel the new <code>DataModel</code> or <code>null</code> to
 1159        *                  cause the model to be refreshed.
 1160        */
 1161   
 1162       protected void setDataModel(DataModel dataModel) {
 1163           this.model = dataModel;
 1164       }
 1165   
 1166       // ---------------------------------------------------- Private Methods
 1167   
 1168   
 1169       /**
 1170        * <p>Perform the appropriate phase-specific processing and per-row
 1171        * iteration for the specified phase, as follows:
 1172        * <ul>
 1173        * <li>Set the <code>rowIndex</code> property to -1, and process the facets
 1174        *     of this {@link UIData} component exactly once.</li>
 1175        * <li>Set the <code>rowIndex</code> property to -1, and process the facets
 1176        *     of the {@link UIColumn} children of this {@link UIData} component
 1177        *     exactly once.</li>
 1178        * <li>Iterate over the relevant rows, based on the <code>first</code>
 1179        *     and <code>row</code> properties, and process the children
 1180        *     of the {@link UIColumn} children of this {@link UIData} component
 1181        *     once per row.</li>
 1182        * </ul>
 1183        *
 1184        * @param context {@link FacesContext} for the current request
 1185        * @param phaseId {@link PhaseId} of the phase we are currently running
 1186        */
 1187       private void iterate(FacesContext context, PhaseId phaseId) {
 1188   
 1189           // Process each facet of this component exactly once
 1190           setRowIndex(-1);
 1191           if (getFacetCount() > 0) {
 1192               for (UIComponent facet : getFacets().values()) {
 1193                   if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
 1194                       facet.processDecodes(context);
 1195                   } else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
 1196                       facet.processValidators(context);
 1197                   } else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
 1198                       facet.processUpdates(context);
 1199                   } else {
 1200                       throw new IllegalArgumentException();
 1201                   }
 1202               }
 1203           }
 1204   
 1205           // Process each facet of our child UIColumn components exactly once
 1206           setRowIndex(-1);
 1207           if (getChildCount() > 0) {
 1208               for (UIComponent column : getChildren()) {
 1209                   if (!(column instanceof UIColumn) || !column.isRendered()) {
 1210                       continue;
 1211                   }
 1212                   if (column.getFacetCount() > 0) {
 1213                       for (UIComponent columnFacet : column.getFacets().values()) {                      
 1214                           if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
 1215                               columnFacet.processDecodes(context);
 1216                           } else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
 1217                               columnFacet.processValidators(context);
 1218                           } else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
 1219                               columnFacet.processUpdates(context);
 1220                           } else {
 1221                               throw new IllegalArgumentException();
 1222                           }
 1223                       }
 1224                   }
 1225               }
 1226           }
 1227   
 1228           // Iterate over our UIColumn children, once per row
 1229           int processed = 0;
 1230           int rowIndex = getFirst() - 1;
 1231           int rows = getRows();
 1232   
 1233           while (true) {
 1234   
 1235               // Have we processed the requested number of rows?
 1236               if ((rows > 0) && (++processed > rows)) {
 1237                   break;
 1238               }
 1239   
 1240               // Expose the current row in the specified request attribute
 1241               setRowIndex(++rowIndex);
 1242               if (!isRowAvailable()) {
 1243                   break; // Scrolled past the last row
 1244               }
 1245   
 1246               // Perform phase-specific processing as required
 1247               // on the *children* of the UIColumn (facets have
 1248               // been done a single time with rowIndex=-1 already)
 1249               if (getChildCount() > 0) {
 1250                   for (UIComponent kid : getChildren()) {
 1251                       if (!(kid instanceof UIColumn) || !kid.isRendered()) {
 1252                           continue;
 1253                       }
 1254                       if (kid.getChildCount() > 0) {
 1255                           for (UIComponent grandkid : kid.getChildren()) {
 1256                               if (!grandkid.isRendered()) {
 1257                                   continue;
 1258                               }
 1259                               if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
 1260                                   grandkid.processDecodes(context);
 1261                               } else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
 1262                                   grandkid.processValidators(context);
 1263                               } else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
 1264                                   grandkid.processUpdates(context);
 1265                               } else {
 1266                                   throw new IllegalArgumentException();
 1267                               }
 1268                           }
 1269                       }
 1270                   }
 1271               }
 1272   
 1273           }
 1274   
 1275           // Clean up after ourselves
 1276           setRowIndex(-1);
 1277   
 1278       }
 1279   
 1280   
 1281       /**
 1282        * <p>Return <code>true</code> if we need to keep the saved
 1283        * per-child state information.  This will be the case if any of the
 1284        * following are true:</p>
 1285        *
 1286        * <ul>
 1287        *
 1288        * <li>there are messages queued with severity ERROR or FATAL.</li>
 1289        *
 1290        * <li>this <code>UIData</code> instance is nested inside of another
 1291        * <code>UIData</code> instance</li>
 1292        *
 1293        * </ul>
 1294        *
 1295        * @param context {@link FacesContext} for the current request
 1296        */
 1297       private boolean keepSaved(FacesContext context) {
 1298   
 1299           return (contextHasErrorMessages(context) || isNestedWithinUIData());
 1300           
 1301       }
 1302   
 1303   
 1304       private Boolean isNestedWithinUIData() {
 1305           if (isNested == null) {
 1306               UIComponent parent = this;
 1307               while (null != (parent = parent.getParent())) {
 1308                   if (parent instanceof UIData) {
 1309                       isNested = Boolean.TRUE;
 1310                       break;
 1311                   }
 1312               }
 1313               if (isNested == null) {
 1314                   isNested = Boolean.FALSE;
 1315               }
 1316               return isNested;
 1317           } else {
 1318               return isNested;
 1319           }
 1320       }
 1321   
 1322   
 1323       private boolean contextHasErrorMessages(FacesContext context) {
 1324   
 1325           FacesMessage.Severity sev = context.getMaximumSeverity();
 1326           return (sev != null && (FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0));
 1327   
 1328       }
 1329   
 1330   
 1331       /**
 1332        * <p>Restore state information for all descendant components, as described
 1333        * for <code>setRowIndex()</code>.</p>
 1334        */
 1335       private void restoreDescendantState() {
 1336   
 1337           FacesContext context = getFacesContext();
 1338           if (getChildCount() > 0) {
 1339               for (UIComponent kid : getChildren()) {
 1340                   if (kid instanceof UIColumn) {
 1341                       restoreDescendantState(kid, context);
 1342                   }
 1343               }
 1344           }
 1345   
 1346       }
 1347   
 1348   
 1349       /**
 1350        * <p>Restore state information for the specified component and its
 1351        * descendants.</p>
 1352        *
 1353        * @param component Component for which to restore state information
 1354        * @param context   {@link FacesContext} for the current request
 1355        */
 1356       private void restoreDescendantState(UIComponent component,
 1357                                           FacesContext context) {
 1358   
 1359           // Reset the client identifier for this component
 1360           String id = component.getId();
 1361           component.setId(id); // Forces client id to be reset
 1362   
 1363           // Restore state for this component (if it is a EditableValueHolder)
 1364           if (component instanceof EditableValueHolder) {
 1365               EditableValueHolder input = (EditableValueHolder) component;
 1366               String clientId = component.getClientId(context);
 1367               SavedState state = saved.get(clientId);
 1368               if (state == null) {
 1369                   state = new SavedState();
 1370               }
 1371               input.setValue(state.getValue());
 1372               input.setValid(state.isValid());
 1373               input.setSubmittedValue(state.getSubmittedValue());
 1374               // This *must* be set after the call to setValue(), since
 1375               // calling setValue() always resets "localValueSet" to true.
 1376               input.setLocalValueSet(state.isLocalValueSet());
 1377           }
 1378   
 1379           // Restore state for children of this component
 1380           if (component.getChildCount() > 0) {
 1381               for (UIComponent kid : component.getChildren()) {
 1382                   restoreDescendantState(kid, context);
 1383               }
 1384           }
 1385   
 1386           // Restore state for facets of this component
 1387           if (component.getFacetCount() > 0) {
 1388               for (UIComponent facet : component.getFacets().values()) {
 1389                   restoreDescendantState(facet, context);
 1390               }
 1391           }
 1392   
 1393       }
 1394   
 1395   
 1396       /**
 1397        * <p>Save state information for all descendant components, as described for
 1398        * <code>setRowIndex()</code>.</p>
 1399        */
 1400       private void saveDescendantState() {
 1401   
 1402           FacesContext context = getFacesContext();
 1403           if (getChildCount() > 0) {
 1404               for (UIComponent kid : getChildren()) {
 1405                   if (kid instanceof UIColumn) {
 1406                       saveDescendantState(kid, context);
 1407                   }
 1408               }
 1409           }
 1410   
 1411       }
 1412   
 1413   
 1414       /**
 1415        * <p>Save state information for the specified component and its
 1416        * descendants.</p>
 1417        *
 1418        * @param component Component for which to save state information
 1419        * @param context   {@link FacesContext} for the current request
 1420        */
 1421       private void saveDescendantState(UIComponent component,
 1422                                        FacesContext context) {
 1423   
 1424           // Save state for this component (if it is a EditableValueHolder)
 1425           if (component instanceof EditableValueHolder) {
 1426               EditableValueHolder input = (EditableValueHolder) component;
 1427               String clientId = component.getClientId(context);
 1428               SavedState state = saved.get(clientId);
 1429               if (state == null) {
 1430                   state = new SavedState();
 1431                   saved.put(clientId, state);
 1432               }
 1433               state.setValue(input.getLocalValue());
 1434               state.setValid(input.isValid());
 1435               state.setSubmittedValue(input.getSubmittedValue());
 1436               state.setLocalValueSet(input.isLocalValueSet());
 1437           }
 1438   
 1439           // Save state for children of this component
 1440           if (component.getChildCount() > 0) {
 1441               for (UIComponent uiComponent : component.getChildren()) {
 1442                   saveDescendantState(uiComponent, context);
 1443               }
 1444           }
 1445   
 1446           // Save state for facets of this component
 1447           if (component.getFacetCount() > 0) {
 1448               for (UIComponent facet : component.getFacets().values()) {
 1449                   saveDescendantState(facet, context);
 1450               }
 1451           }
 1452   
 1453       }
 1454   
 1455   }
 1456   
 1457   // ------------------------------------------------------------- Private Classes
 1458   
 1459   
 1460   // Private class to represent saved state information
 1461   @SuppressWarnings({"SerializableHasSerializationMethods",
 1462         "NonSerializableFieldInSerializableClass"})
 1463   class SavedState implements Serializable {
 1464   
 1465       private static final long serialVersionUID = 2920252657338389849L;
 1466       private Object submittedValue;
 1467   
 1468       Object getSubmittedValue() {
 1469           return (this.submittedValue);
 1470       }
 1471   
 1472       void setSubmittedValue(Object submittedValue) {
 1473           this.submittedValue = submittedValue;
 1474       }
 1475   
 1476       private boolean valid = true;
 1477   
 1478       boolean isValid() {
 1479           return (this.valid);
 1480       }
 1481   
 1482       void setValid(boolean valid) {
 1483           this.valid = valid;
 1484       }
 1485   
 1486       private Object value;
 1487   
 1488       Object getValue() {
 1489           return (this.value);
 1490       }
 1491   
 1492       public void setValue(Object value) {
 1493           this.value = value;
 1494       }
 1495   
 1496       private boolean localValueSet;
 1497   
 1498       boolean isLocalValueSet() {
 1499           return (this.localValueSet);
 1500       }
 1501   
 1502       public void setLocalValueSet(boolean localValueSet) {
 1503           this.localValueSet = localValueSet;
 1504       }
 1505   
 1506       public String toString() {
 1507           return ("submittedValue: " + submittedValue +
 1508                   " value: " + value +
 1509                   " localValueSet: " + localValueSet);
 1510       }
 1511   
 1512   }
 1513   
 1514   
 1515   // Private class to wrap an event with a row index
 1516   class WrapperEvent extends FacesEvent {
 1517   
 1518   
 1519       public WrapperEvent(UIComponent component, FacesEvent event, int rowIndex) {
 1520           super(component);
 1521           this.event = event;
 1522           this.rowIndex = rowIndex;
 1523       }
 1524   
 1525       private FacesEvent event = null;
 1526       private int rowIndex = -1;
 1527   
 1528       public FacesEvent getFacesEvent() {
 1529           return (this.event);
 1530       }
 1531   
 1532       public int getRowIndex() {
 1533           return (this.rowIndex);
 1534       }
 1535   
 1536       public PhaseId getPhaseId() {
 1537           return (this.event.getPhaseId());
 1538       }
 1539   
 1540       public void setPhaseId(PhaseId phaseId) {
 1541           this.event.setPhaseId(phaseId);
 1542       }
 1543   
 1544       public boolean isAppropriateListener(FacesListener listener) {
 1545           return (false);
 1546       }
 1547   
 1548       public void processListener(FacesListener listener) {
 1549           throw new IllegalStateException();
 1550       }
 1551   
 1552   
 1553   }

Save This Page
Home » mojarra-1.2_09-b02-FCS-source » javax.faces.component » [javadoc | source]