Home » Mojarra-2.0.1 » 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.ValueExpression;
   40   import javax.faces.FacesException;
   41   import javax.faces.application.Application;
   42   import javax.faces.application.FacesMessage;
   43   import javax.faces.component.visit.VisitCallback;
   44   import javax.faces.component.visit.VisitContext;
   45   import javax.faces.component.visit.VisitHint;
   46   import javax.faces.component.visit.VisitResult;
   47   import javax.faces.context.FacesContext;
   48   import javax.faces.el.ValueBinding;
   49   import javax.faces.event.AbortProcessingException;
   50   import javax.faces.event.FacesEvent;
   51   import javax.faces.event.FacesListener;
   52   import javax.faces.event.PhaseId;
   53   import javax.faces.event.PostValidateEvent;
   54   import javax.faces.event.PreValidateEvent;
   55   import javax.faces.model.ArrayDataModel;
   56   import javax.faces.model.DataModel;
   57   import javax.faces.model.ListDataModel;
   58   import javax.faces.model.ResultDataModel;
   59   import javax.faces.model.ResultSetDataModel;
   60   import javax.faces.model.ScalarDataModel;
   61   import javax.servlet.jsp.jstl.sql.Result;
   62   
   63   import java.io.IOException;
   64   import java.io.Serializable;
   65   import java.sql.ResultSet;
   66   import java.util.Collection;
   67   import java.util.Collections;
   68   import java.util.List;
   69   import java.util.Map;
   70   import java.util.Iterator;
   71   
   72   
   73   
   74   // ------------------------------------------------------------- Private Classes
   75   // Private class to represent saved state information
   76   /**
   77    * <p><strong>UIData</strong> is a {@link UIComponent} that supports data
   78    * binding to a collection of data objects represented by a {@link DataModel}
   79    * instance, which is the current value of this component itself (typically
   80    * established via a {@link ValueExpression}). During iterative processing over
   81    * the rows of data in the data model, the object for the current row is exposed
   82    * as a request attribute under the key specified by the <code>var</code>
   83    * property.</p>
   84    * <p/>
   85    * <p>Only children of type {@link UIColumn} should be processed by renderers
   86    * associated with this component.</p>
   87    * <p/>
   88    * <p>By default, the <code>rendererType</code> property is set to
   89    * <code>javax.faces.Table</code>.  This value can be changed by calling the
   90    * <code>setRendererType()</code> method.</p>
   91    */
   92   
   93   public class UIData extends UIComponentBase
   94         implements NamingContainer, UniqueIdVendor {
   95   
   96       // ------------------------------------------------------ Manifest Constants
   97   
   98   
   99       /**
  100        * <p>The standard component type for this component.</p>
  101        */
  102       public static final String COMPONENT_TYPE = "javax.faces.Data";
  103   
  104   
  105       /**
  106        * <p>The standard component family for this component.</p>
  107        */
  108       public static final String COMPONENT_FAMILY = "javax.faces.Data";
  109   
  110       // ------------------------------------------------------------ Constructors
  111   
  112   
  113       /**
  114        * <p>Create a new {@link UIData} instance with default property
  115        * values.</p>
  116        */
  117       public UIData() {
  118   
  119           super();
  120           setRendererType("javax.faces.Table");
  121   
  122       }
  123   
  124       // ------------------------------------------------------ Instance Variables
  125   
  126   
  127       /**
  128        * Properties that are tracked by state saving.
  129        */
  130       enum PropertyKeys {
  131           /**
  132            * <p>The first row number (zero-relative) to be displayed.</p>
  133            */
  134           first,
  135   
  136           /**
  137            * <p>The zero-relative index of the current row number, or -1 for no
  138            * current row association.</p>
  139            */
  140           rowIndex,
  141   
  142           /**
  143            * <p>The number of rows to display, or zero for all remaining rows in the
  144            * table.</p>
  145            */
  146           rows,
  147   
  148           /**
  149            * <p>This map contains <code>SavedState</code> instances for each
  150            * descendant component, keyed by the client identifier of the descendant.
  151            * Because descendant client identifiers will contain the
  152            * <code>rowIndex</code> value of the parent, per-row state information is
  153            * actually preserved.</p>
  154            */
  155           saved,
  156   
  157           /**
  158            * <p>The local value of this {@link UIComponent}.</p>
  159            */
  160           value,
  161   
  162           /**
  163            * <p>The request scope attribute under which the data object for the
  164            * current row will be exposed when iterating.</p>
  165            */
  166           var,
  167   
  168           /**
  169            * <p>Last id vended by {@link UIData#createUniqueId(javax.faces.context.FacesContext, String)}.</p>
  170            */
  171           lastId
  172       }
  173   
  174   
  175       /**
  176        * <p>The {@link DataModel} associated with this component, lazily
  177        * instantiated if requested.  This object is not part of the saved and
  178        * restored state of the component.</p>
  179        */
  180       private DataModel model = null;
  181   
  182   
  183       /**
  184        * <p> During iteration through the rows of this table, This ivar is used to
  185        * store the previous "var" value for this instance.  When the row iteration
  186        * is complete, this value is restored to the request map.
  187        */
  188       private Object oldVar;
  189   
  190   
  191       /**
  192        * <p>Holds the base client ID that will be used to generate per-row
  193        * client IDs (this will be null if this UIData is nested within another).</p>
  194        *
  195        * <p>This is not part of the component state.</p>
  196        */
  197       private String baseClientId = null;
  198   
  199   
  200       /**
  201        * <p> Length of the cached <code>baseClientId</code> plus one for
  202        * the {@link UINamingContainer#getSeparatorChar}. </p>
  203        *
  204        * <p>This is not part of the component state.</p>
  205        */
  206       private int baseClientIdLength;
  207   
  208   
  209       /**
  210        * <p>StringBuilder used to build per-row client IDs.</p>
  211        *
  212        * <p>This is not part of the component state.</p>
  213        */
  214       private StringBuilder clientIdBuilder = null;
  215   
  216   
  217       /**
  218        * <p>Flag indicating whether or not this UIData instance is nested
  219        * within another UIData instance</p>
  220        *
  221        * <p>This is not part of the component state.</p>
  222        */
  223       private Boolean isNested = null;
  224   
  225   
  226       // -------------------------------------------------------------- Properties
  227   
  228   
  229       public String getFamily() {
  230   
  231           return (COMPONENT_FAMILY);
  232   
  233       }
  234   
  235   
  236       /**
  237        * <p>Return the zero-relative row number of the first row to be
  238        * displayed.</p>
  239        */
  240       public int getFirst() {
  241   
  242           return (Integer) getStateHelper().eval(PropertyKeys.first, 0);
  243   
  244       }
  245   
  246   
  247       /**
  248        * <p>Set the zero-relative row number of the first row to be
  249        * displayed.</p>
  250        *
  251        * @param first New first row number
  252        *
  253        * @throws IllegalArgumentException if <code>first</code> is negative
  254        */
  255       public void setFirst(int first) {
  256   
  257           if (first < 0) {
  258               throw new IllegalArgumentException(String.valueOf(first));
  259           }
  260           getStateHelper().put(PropertyKeys.first, first);
  261   
  262       }
  263   
  264   
  265       /**
  266        * <p>Return the footer facet of this component (if any).  A convenience
  267        * method for <code>getFacet("footer")</code>.</p>
  268        */
  269       public UIComponent getFooter() {
  270   
  271           return getFacet("footer");
  272   
  273       }
  274   
  275   
  276       /**
  277        * <p>Set the footer facet of this component.  A convenience method for
  278        * <code>getFacets().put("footer", footer)</code>.</p>
  279        *
  280        * @param footer the new footer facet
  281        *
  282        * @throws NullPointerException if <code>footer</code> is <code>null</code>
  283        */
  284       public void setFooter(UIComponent footer) {
  285   
  286           getFacets().put("footer", footer);
  287   
  288       }
  289   
  290   
  291       /**
  292        * <p>Return the header facet of this component (if any).  A convenience
  293        * method for <code>getFacet("header")</code>.</p>
  294        */
  295       public UIComponent getHeader() {
  296   
  297           return getFacet("header");
  298   
  299       }
  300   
  301   
  302       /**
  303        * <p>Set the header facet of this component.  A convenience method for
  304        * <code>getFacets().put("header", header)</code>.</p>
  305        *
  306        * @param header the new header facet
  307        *
  308        * @throws NullPointerException if <code>header</code> is <code>null</code>
  309        */
  310       public void setHeader(UIComponent header) {
  311   
  312           getFacets().put("header", header);
  313   
  314       }
  315   
  316   
  317       /**
  318        * <p>Return a flag indicating whether there is <code>rowData</code>
  319        * available at the current <code>rowIndex</code>.  If no
  320        * <code>wrappedData</code> is available, return <code>false</code>.</p>
  321        *
  322        * @throws FacesException if an error occurs getting the row availability
  323        */
  324       public boolean isRowAvailable() {
  325   
  326           return (getDataModel().isRowAvailable());
  327   
  328       }
  329   
  330   
  331       /**
  332        * <p>Return the number of rows in the underlying data model.  If the number
  333        * of available rows is unknown, return -1.</p>
  334        *
  335        * @throws FacesException if an error occurs getting the row count
  336        */
  337       public int getRowCount() {
  338   
  339           return (getDataModel().getRowCount());
  340   
  341       }
  342   
  343   
  344       /**
  345        * <p>Return the data object representing the data for the currently
  346        * selected row index, if any.</p>
  347        *
  348        * @throws FacesException           if an error occurs getting the row data
  349        * @throws IllegalArgumentException if now row data is available at the
  350        *                                  currently specified row index
  351        */
  352       public Object getRowData() {
  353   
  354           return (getDataModel().getRowData());
  355   
  356       }
  357   
  358   
  359       /**
  360        * <p>Return the zero-relative index of the currently selected row.  If we
  361        * are not currently positioned on a row, return -1.  This property is
  362        * <strong>not</strong> enabled for value binding expressions.</p>
  363        *
  364        * @throws FacesException if an error occurs getting the row index
  365        */
  366       public int getRowIndex() {
  367   
  368           return (Integer) getStateHelper().eval(PropertyKeys.rowIndex, -1);
  369   
  370       }
  371   
  372   
  373       /**
  374        * <p>Set the zero relative index of the current row, or -1 to indicate that
  375        * no row is currently selected, by implementing the following algorithm.
  376        * It is possible to set the row index at a value for which the underlying
  377        * data collection does not contain any row data.  Therefore, callers may
  378        * use the <code>isRowAvailable()</code> method to detect whether row data
  379        * will be available for use by the <code>getRowData()</code> method.</p>
  380        *</p>
  381        * <ul>
  382        * <li>Save current state information for all descendant components (as
  383        *     described below).
  384        * <li>Store the new row index, and pass it on to the {@link DataModel}
  385        *     associated with this {@link UIData} instance.</li>
  386        * <li>If the new <code>rowIndex</code> value is -1:
  387        *     <ul>
  388        *     <li>If the <code>var</code> property is not null,
  389        *         remove the corresponding request scope attribute (if any).</li>
  390        *     <li>Reset the state information for all descendant components
  391        *         (as described below).</li>
  392        *     </ul></li>
  393        * <li>If the new <code>rowIndex</code> value is not -1:
  394        *     <ul>
  395        *     <li>If the <code>var</code> property is not null, call
  396        *         <code>getRowData()</code> and expose the resulting data object
  397        *         as a request scope attribute whose key is the <code>var</code>
  398        *         property value.</li>
  399        *     <li>Reset the state information for all descendant components
  400        *         (as described below).
  401        *     </ul></li>
  402        * </ul>
  403        *
  404        * <p>To save current state information for all descendant components,
  405        * {@link UIData} must maintain per-row information for each descendant
  406        * as follows:<p>
  407        * <ul>
  408        * <li>If the descendant is an instance of <code>EditableValueHolder</code>, save
  409        *     the state of its <code>localValue</code> property.</li>
  410        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  411        *     save the state of the <code>localValueSet</code> property.</li>
  412        * <li>If the descendant is an instance of <code>EditableValueHolder</code>, save
  413        *     the state of the <code>valid</code> property.</li>
  414        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  415        *     save the state of the <code>submittedValue</code> property.</li>
  416        * </ul>
  417        *
  418        * <p>To restore current state information for all descendant components,
  419        * {@link UIData} must reference its previously stored information for the
  420        * current <code>rowIndex</code> and call setters for each descendant
  421        * as follows:</p>
  422        * <ul>
  423        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  424        *     restore the <code>value</code> property.</li>
  425        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  426        *     restore the state of the <code>localValueSet</code> property.</li>
  427        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  428        *     restore the state of the <code>valid</code> property.</li>
  429        * <li>If the descendant is an instance of <code>EditableValueHolder</code>,
  430        *     restore the state of the <code>submittedValue</code> property.</li>
  431        * </ul>
  432        *
  433        * @param rowIndex The new row index value, or -1 for no associated row
  434        *
  435        * @throws FacesException if an error occurs setting the row index
  436        * @throws IllegalArgumentException if <code>rowIndex</code>
  437        *  is less than -1
  438        */
  439       public void setRowIndex(int rowIndex) {
  440   
  441           // Save current state for the previous row index
  442           saveDescendantState();
  443   
  444           // Update to the new row index        
  445           //this.rowIndex = rowIndex;
  446           getStateHelper().put(PropertyKeys.rowIndex, rowIndex);
  447           DataModel localModel = getDataModel();
  448           localModel.setRowIndex(rowIndex);
  449           
  450           // if rowIndex is -1, clear the cache
  451           if (rowIndex == -1) {
  452               setDataModel(null);
  453           }
  454           
  455           // Clear or expose the current row data as a request scope attribute
  456           String var = (String) getStateHelper().get(PropertyKeys.var);
  457           if (var != null) {
  458               Map<String, Object> requestMap =
  459                     getFacesContext().getExternalContext().getRequestMap();
  460               if (rowIndex == -1) {
  461                   oldVar = requestMap.remove(var);
  462               } else if (isRowAvailable()) {
  463                   requestMap.put(var, getRowData());
  464               } else {
  465                   requestMap.remove(var);
  466                   if (null != oldVar) {
  467                       requestMap.put(var, oldVar);
  468                       oldVar = null;
  469                   }
  470               }
  471           }
  472   
  473           // Reset current state information for the new row index
  474           restoreDescendantState();
  475   
  476       }
  477   
  478   
  479       /**
  480        * <p>Return the number of rows to be displayed, or zero for all remaining
  481        * rows in the table.  The default value of this property is zero.</p>
  482        */
  483       public int getRows() {
  484   
  485   
  486           return (Integer) getStateHelper().eval(PropertyKeys.rows, 0);
  487   
  488       }
  489   
  490   
  491       /**
  492        * <p>Set the number of rows to be displayed, or zero for all remaining rows
  493        * in the table.</p>
  494        *
  495        * @param rows New number of rows
  496        *
  497        * @throws IllegalArgumentException if <code>rows</code> is negative
  498        */
  499       public void setRows(int rows) {
  500   
  501           if (rows < 0) {
  502               throw new IllegalArgumentException(String.valueOf(rows));
  503           }
  504           getStateHelper().put(PropertyKeys.rows, rows);
  505   
  506       }
  507   
  508   
  509       /**
  510        * <p>Return the request-scope attribute under which the data object for the
  511        * current row will be exposed when iterating.  This property is
  512        * <strong>not</strong> enabled for value binding expressions.</p>
  513        */
  514       public String getVar() {
  515   
  516           return (String) getStateHelper().get(PropertyKeys.var);
  517   
  518       }
  519   
  520   
  521       /**
  522        * <p>Set the request-scope attribute under which the data object for the
  523        * current row wil be exposed when iterating.</p>
  524        *
  525        * @param var The new request-scope attribute name
  526        */
  527       public void setVar(String var) {
  528   
  529           getStateHelper().put(PropertyKeys.var, var);
  530   
  531       }
  532   
  533       // ----------------------------------------------------- StateHolder Methods
  534   
  535   
  536   
  537   
  538       /**
  539        * <p>Return the value of the UIData.  This value must either be
  540        * be of type {@link DataModel}, or a type that can be adapted
  541        * into a {@link DataModel}.  <code>UIData</code> will automatically
  542        * adapt the following types:</p>
  543        * <ul>
  544        * <li>Arrays</li>
  545        * <li><code>java.util.List</code></li>
  546        * <li><code>java.sql.ResultSet</code></li>
  547        * <li><code>javax.servlet.jsp.jstl.sql.Result</code></li>
  548        * </ul>
  549        * <p>All other types will be adapted using the {@link ScalarDataModel}
  550        * class, which will treat the object as a single row of data.</p>
  551        */
  552       public Object getValue() {
  553   
  554           return getStateHelper().eval(PropertyKeys.value);
  555   
  556       }
  557   
  558   
  559       /**
  560        * <p>Set the value of the <code>UIData</code>.  This value must either be
  561        * be of type {@link DataModel}, or a type that can be adapted into a {@link
  562        * DataModel}.</p>
  563        *
  564        * @param value the new value
  565        */
  566       public void setValue(Object value) {
  567           setDataModel(null);
  568           getStateHelper().put(PropertyKeys.value, value);
  569   
  570       }
  571   
  572       // ----------------------------------------------------- UIComponent Methods
  573   
  574   
  575       /**
  576        * <p>If "name" is something other than "value", "var", or "rowIndex", rely
  577        * on the superclass conversion from <code>ValueBinding</code> to
  578        * <code>ValueExpression</code>.</p>
  579        *
  580        * @param name    Name of the attribute or property for which to set a
  581        *                {@link ValueBinding}
  582        * @param binding The {@link ValueBinding} to set, or <code>null</code> to
  583        *                remove any currently set {@link ValueBinding}
  584        *
  585        * @throws IllegalArgumentException if <code>name</code> is one of
  586        *                                  <code>id</code>, <code>parent</code>,
  587        *                                  <code>var</code>, or <code>rowIndex</code>
  588        * @throws NullPointerException     if <code>name</code> is <code>null</code>
  589        * @deprecated This has been replaced by {@link #setValueExpression(java.lang.String,
  590        *javax.el.ValueExpression)}.
  591        */
  592       public void setValueBinding(String name, ValueBinding binding) {
  593   
  594           if ("value".equals(name)) {
  595               setDataModel(null);
  596           } else if ("var".equals(name) || "rowIndex".equals(name)) {
  597               throw new IllegalArgumentException();
  598           }
  599           super.setValueBinding(name, binding);
  600   
  601       }
  602   
  603       /**
  604        * <p>Set the {@link ValueExpression} used to calculate the value for the
  605        * specified attribute or property name, if any.  In addition, if a {@link
  606        * ValueExpression} is set for the <code>value</code> property, remove any
  607        * synthesized {@link DataModel} for the data previously bound to this
  608        * component.</p>
  609        *
  610        * @param name    Name of the attribute or property for which to set a
  611        *                {@link ValueExpression}
  612        * @param binding The {@link ValueExpression} to set, or <code>null</code>
  613        *                to remove any currently set {@link ValueExpression}
  614        *
  615        * @throws IllegalArgumentException if <code>name</code> is one of
  616        *                                  <code>id</code>, <code>parent</code>,
  617        *                                  <code>var</code>, or <code>rowIndex</code>
  618        * @throws NullPointerException     if <code>name</code> is <code>null</code>
  619        * @since 1.2
  620        */
  621       public void setValueExpression(String name, ValueExpression binding) {
  622   
  623           if ("value".equals(name)) {
  624               this.model = null;
  625           } else if ("var".equals(name) || "rowIndex".equals(name)) {
  626               throw new IllegalArgumentException();
  627           }
  628           super.setValueExpression(name, binding);
  629   
  630       }
  631   
  632       /**
  633        * <p>Return a client identifier for this component that includes the
  634        * current value of the <code>rowIndex</code> property, if it is not set to
  635        * -1.  This implies that multiple calls to <code>getClientId()</code> may
  636        * return different results, but ensures that child components can
  637        * themselves generate row-specific client identifiers (since {@link UIData}
  638        * is a {@link NamingContainer}).</p>
  639        *
  640        * @throws NullPointerException if <code>context</code> is <code>null</code>
  641        */
  642       public String getClientId(FacesContext context) {
  643   
  644           if (context == null) {
  645               throw new NullPointerException();
  646           }
  647   
  648           // If baseClientId and clientIdBuilder are both null, this is the
  649           // first time that getClientId() has been called.
  650           // If we're not nested within another UIData, then:
  651           //   - create a new StringBuilder assigned to clientIdBuilder containing
  652           //   our client ID.
  653           //   - toString() the builder - this result will be our baseClientId
  654           //     for the duration of the component
  655           //   - append UINamingContainer.getSeparatorChar() to the builder
  656           //  If we are nested within another UIData, then:
  657           //   - create an empty StringBuilder that will be used to build
  658           //     this instance's ID
  659           if (baseClientId == null && clientIdBuilder == null) {
  660               if (!isNestedWithinUIData()) {
  661                   clientIdBuilder = new StringBuilder(super.getClientId(context));
  662                   baseClientId = clientIdBuilder.toString();
  663                   baseClientIdLength = (baseClientId.length() + 1);
  664                   clientIdBuilder.append(UINamingContainer.getSeparatorChar(context));
  665                   clientIdBuilder.setLength(baseClientIdLength);
  666               } else {
  667                   clientIdBuilder = new StringBuilder();
  668               }
  669           }
  670           int rowIndex = getRowIndex();
  671           if (rowIndex >= 0) {
  672               String cid;
  673               if (!isNestedWithinUIData()) {
  674                   // we're not nested, so the clientIdBuilder is already
  675                   // primed with clientID +
  676                   // UINamingContainer.getSeparatorChar().  Append the
  677                   // current rowIndex, and toString() the builder.  reset
  678                   // the builder to it's primed state.
  679                   cid = clientIdBuilder.append(rowIndex).toString();
  680                   clientIdBuilder.setLength(baseClientIdLength);
  681               } else {
  682                   // we're nested, so we have to build the ID from scratch
  683                   // each time.  Reuse the same clientIdBuilder instance
  684                   // for each call by resetting the length to 0 after
  685                   // the ID has been computed.
  686                   cid = clientIdBuilder.append(super.getClientId(context))
  687                         .append(UINamingContainer.getSeparatorChar(context)).append(rowIndex)
  688                         .toString();
  689                   clientIdBuilder.setLength(0);
  690               }
  691               return (cid);
  692           } else {
  693               if (!isNestedWithinUIData()) {
  694                   // Not nested and no row available, so just return our baseClientId
  695                   return (baseClientId);
  696               } else {
  697                   // nested and no row available, return the result of getClientId().
  698                   // this is necessary as the client ID will reflect the row that
  699                   // this table represents
  700                   return super.getClientId(context);
  701               }
  702           }
  703   
  704       }
  705   
  706       /**
  707        * <p>Override behavior from {@link
  708        * UIComponentBase#invokeOnComponent} to provide special care for
  709        * positioning the data properly before finding the component and
  710        * invoking the callback on it.  If the argument
  711        * <code>clientId</code> is equal to <code>this.getClientId()</code>
  712        * simply invoke the <code>contextCallback</code>, passing the
  713        * <code>context</code> argument and <b>this</b> as arguments, and
  714        * return <code>true.</code> If the argument <code>clientId</code>
  715        * is not equal to <code>this.getClientId()</code>, inspect each of
  716        * the facet children of this <code>UIData</code> instance and for
  717        * each one, compare its <code>clientId</code> with the argument
  718        * <code>clientId</code>.  If there is a match, invoke the
  719        * <code>contextCallback</code>, passing the <code>context</code>
  720        * argument and <b>this</b> as arguments, and return
  721        * <code>true</code>. Otherwise, attempt to extract a rowIndex from
  722        * the <code>clientId</code>.  For example, if the argument
  723        * <code>clientId</code> was <code>form:data:3:customerHeader</code>
  724        * the rowIndex would be <code>3</code>.  Let this value be called
  725        * <code>newIndex</code>. The current rowIndex of this instance must
  726        * be saved aside and restored before returning in all cases,
  727        * regardless of the outcome of the search or if any exceptions are
  728        * thrown in the process.</p>
  729        *
  730        * <p>The implementation of this method must never return <code>true</code>
  731        * if setting the rowIndex of this instance to be equal to
  732        * <code>newIndex</code> causes this instance to return <code>false</code>
  733        * from {@link #isRowAvailable}.</p>
  734        *
  735        * @throws NullPointerException {@inheritDoc}
  736        * @throws FacesException       {@inheritDoc}  Also throws <code>FacesException</code>
  737        *                              if any exception is thrown when deriving the
  738        *                              rowIndex from the argument <code>clientId</code>.
  739        * @since 1.2
  740        */
  741       public boolean invokeOnComponent(FacesContext context, String clientId,
  742                                        ContextCallback callback)
  743             throws FacesException {
  744           if (null == context || null == clientId || null == callback) {
  745               throw new NullPointerException();
  746           }
  747   
  748           String myId = super.getClientId(context);
  749           boolean found = false;
  750           if (clientId.equals(myId)) {
  751               try {
  752                   callback.invokeContextCallback(context, this);
  753                   return true;
  754               }
  755               catch (Exception e) {
  756                   throw new FacesException(e);
  757               }
  758           }
  759   
  760           // check the facets, if any, of UIData
  761           if (this.getFacetCount() > 0) {
  762               for (UIComponent c : this.getFacets().values()) {
  763                   c.invokeOnComponent(context, clientId, callback);
  764               }
  765           }
  766           
  767           // check column level facets, if any
  768           if (this.getChildCount() > 0) {
  769               for (UIComponent column : this.getChildren()) {
  770                   if (column instanceof UIColumn) {
  771                       if (column.getFacetCount() > 0) {
  772                           for (UIComponent facet : column.getFacets().values()) {
  773                               facet.invokeOnComponent(context, clientId, callback);
  774                           }
  775                       }
  776                   }
  777               }
  778           }
  779   
  780           int lastSep, newRow, savedRowIndex = this.getRowIndex();
  781           try {
  782               char sepChar = UINamingContainer.getSeparatorChar(context);
  783               // If we need to strip out the rowIndex from our id
  784               // PENDING(edburns): is this safe with respect to I18N?
  785               if (myId.endsWith(sepChar + Integer.toString(savedRowIndex, 10))) {
  786                   lastSep = myId.lastIndexOf(sepChar);
  787                   assert (-1 != lastSep);
  788                   myId = myId.substring(0, lastSep);
  789               }
  790   
  791               // myId will be something like form:outerData for a non-nested table,
  792               // and form:outerData:3:data for a nested table.
  793               // clientId will be something like form:outerData:3:outerColumn
  794               // for a non-nested table.  clientId will be something like
  795               // outerData:3:data:3:input for a nested table.
  796               if (clientId.startsWith(myId)) {
  797                   int preRowIndexSep, postRowIndexSep;
  798   
  799                   if (-1 != (preRowIndexSep =
  800                         clientId.indexOf(sepChar,
  801                                          myId.length()))) {
  802                       // Check the length
  803                       if (++preRowIndexSep < clientId.length()) {
  804                           if (-1 != (postRowIndexSep =
  805                                 clientId.indexOf(sepChar,
  806                                                  preRowIndexSep + 1))) {
  807                               try {
  808                                   newRow = Integer
  809                                         .valueOf(clientId.substring(preRowIndexSep,
  810                                                                     postRowIndexSep))
  811                                         .intValue();
  812                               } catch (NumberFormatException ex) {
  813                                   // PENDING(edburns): I18N
  814                                   String message =
  815                                         "Trying to extract rowIndex from clientId \'"
  816                                         +
  817                                         clientId
  818                                         + "\' "
  819                                         + ex.getMessage();
  820                                   throw new NumberFormatException(message);
  821                               }
  822                               this.setRowIndex(newRow);
  823                               if (this.isRowAvailable()) {
  824                                   found = super.invokeOnComponent(context,
  825                                                                   clientId,
  826                                                                   callback);
  827                               }
  828                           }
  829                       }
  830                   }
  831               }
  832           }
  833           catch (FacesException fe) {
  834               throw fe;
  835           }
  836           catch (Exception e) {
  837               throw new FacesException(e);
  838           }
  839           finally {
  840               this.setRowIndex(savedRowIndex);
  841           }
  842           return found;
  843       }
  844   
  845   
  846       /**
  847        * <p>Override the default {@link UIComponentBase#queueEvent} processing to
  848        * wrap any queued events in a wrapper so that we can reset the current row
  849        * index in <code>broadcast()</code>.</p>
  850        *
  851        * @param event {@link FacesEvent} to be queued
  852        *
  853        * @throws IllegalStateException if this component is not a descendant of a
  854        *                               {@link UIViewRoot}
  855        * @throws NullPointerException  if <code>event</code> is <code>null</code>
  856        */
  857       public void queueEvent(FacesEvent event) {
  858   
  859           super.queueEvent(new WrapperEvent(this, event, getRowIndex()));
  860   
  861       }
  862   
  863   
  864       /**
  865        * <p>Override the default {@link UIComponentBase#broadcast} processing to
  866        * unwrap any wrapped {@link FacesEvent} and reset the current row index,
  867        * before the event is actually broadcast.  For events that we did not wrap
  868        * (in <code>queueEvent()</code>), default processing will occur.</p>
  869        *
  870        * @param event The {@link FacesEvent} to be broadcast
  871        *
  872        * @throws AbortProcessingException Signal the JavaServer Faces
  873        *                                  implementation that no further
  874        *                                  processing on the current event should
  875        *                                  be performed
  876        * @throws IllegalArgumentException if the implementation class of this
  877        *                                  {@link FacesEvent} is not supported by
  878        *                                  this component
  879        * @throws NullPointerException     if <code>event</code> is <code>null</code>
  880        */
  881       public void broadcast(FacesEvent event)
  882             throws AbortProcessingException {
  883   
  884           if (!(event instanceof WrapperEvent)) {
  885               super.broadcast(event);
  886               return;
  887           }
  888           FacesContext context = FacesContext.getCurrentInstance();
  889           // Set up the correct context and fire our wrapped event
  890           WrapperEvent revent = (WrapperEvent) event;
  891           if (isNestedWithinUIData()) {
  892               setDataModel(null);
  893           }
  894           int oldRowIndex = getRowIndex();
  895           setRowIndex(revent.getRowIndex());
  896           FacesEvent rowEvent = revent.getFacesEvent();
  897           UIComponent source = rowEvent.getComponent();
  898           UIComponent compositeParent = null;
  899           try {
  900               if (!UIComponent.isCompositeComponent(source)) {
  901                   compositeParent = UIComponent.getCompositeComponentParent(source);
  902               }
  903               if (compositeParent != null) {
  904                   compositeParent.pushComponentToEL(context, null);
  905               }
  906               source.pushComponentToEL(context, null);
  907               source.broadcast(rowEvent);
  908           } finally {
  909               source.popComponentFromEL(context);
  910               if (compositeParent != null) {
  911                   compositeParent.popComponentFromEL(context);
  912               }
  913           }
  914           setRowIndex(oldRowIndex);
  915   
  916       }
  917   
  918       /**
  919        * <p>In addition to the default behavior, ensure that any saved per-row
  920        * state for our child input components is discarded unless it is needed to
  921        * rerender the current page with errors.
  922        *
  923        * @param context FacesContext for the current request
  924        *
  925        * @throws IOException          if an input/output error occurs while
  926        *                              rendering
  927        * @throws NullPointerException if <code>context</code> is <code>null</code>
  928        */
  929       public void encodeBegin(FacesContext context) throws IOException {
  930   
  931           preEncode(context);
  932           super.encodeBegin(context);
  933   
  934       }
  935   
  936   
  937       /**
  938        * <p>Override the default {@link UIComponentBase#processDecodes} processing
  939        * to perform the following steps.</p> <ul> <li>If the <code>rendered</code>
  940        * property of this {@link UIComponent} is <code>false</code>, skip further
  941        * processing.</li> <li>Set the current <code>rowIndex</code> to -1.</li>
  942        * <li>Call the <code>processDecodes()</code> method of all facets of this
  943        * {@link UIData}, in the order determined by a call to
  944        * <code>getFacets().keySet().iterator()</code>.</li> <li>Call the
  945        * <code>processDecodes()</code> method of all facets of the {@link
  946        * UIColumn} children of this {@link UIData}.</li> <li>Iterate over the set
  947        * of rows that were included when this component was rendered (i.e. those
  948        * defined by the <code>first</code> and <code>rows</code> properties),
  949        * performing the following processing for each row: <ul> <li>Set the
  950        * current <code>rowIndex</code> to the appropriate value for this row.</li>
  951        * <li>If <code>isRowAvailable()</code> returns <code>true</code>, iterate
  952        * over the children components of each {@link UIColumn} child of this
  953        * {@link UIData} component, calling the <code>processDecodes()</code>
  954        * method for each such child.</li> </ul></li> <li>Set the current
  955        * <code>rowIndex</code> to -1.</li> <li>Call the <code>decode()</code>
  956        * method of this component.</li> <li>If a <code>RuntimeException</code> is
  957        * thrown during decode processing, call {@link FacesContext#renderResponse}
  958        * and re-throw the exception.</li> </ul>
  959        *
  960        * @param context {@link FacesContext} for the current request
  961        *
  962        * @throws NullPointerException if <code>context</code> is <code>null</code>
  963        */
  964       public void processDecodes(FacesContext context) {
  965   
  966           if (context == null) {
  967               throw new NullPointerException();
  968           }
  969           if (!isRendered()) {
  970               return;
  971           }
  972   
  973           preDecode(context);
  974           iterate(context, PhaseId.APPLY_REQUEST_VALUES);
  975           decode(context);
  976   
  977       }
  978   
  979   
  980       /**
  981        * <p>Override the default {@link UIComponentBase#processValidators}
  982        * processing to perform the following steps.</p> <ul> <li>If the
  983        * <code>rendered</code> property of this {@link UIComponent} is
  984        * <code>false</code>, skip further processing.</li> <li>Set the current
  985        * <code>rowIndex</code> to -1.</li> <li>Call the <code>processValidators()</code>
  986        * method of all facets of this {@link UIData}, in the order determined by a
  987        * call to <code>getFacets().keySet().iterator()</code>.</li> <li>Call the
  988        * <code>processValidators()</code> method of all facets of the {@link
  989        * UIColumn} children of this {@link UIData}.</li> <li>Iterate over the set
  990        * of rows that were included when this component was rendered (i.e. those
  991        * defined by the <code>first</code> and <code>rows</code> properties),
  992        * performing the following processing for each row: <ul> <li>Set the
  993        * current <code>rowIndex</code> to the appropriate value for this row.</li>
  994        * <li>If <code>isRowAvailable()</code> returns <code>true</code>, iterate
  995        * over the children components of each {@link UIColumn} child of this
  996        * {@link UIData} component, calling the <code>processValidators()</code>
  997        * method for each such child.</li> </ul></li> <li>Set the current
  998        * <code>rowIndex</code> to -1.</li> </ul>
  999        *
 1000        * @param context {@link FacesContext} for the current request
 1001        *
 1002        * @throws NullPointerException if <code>context</code> is <code>null</code>
 1003        */
 1004       public void processValidators(FacesContext context) {
 1005   
 1006           if (context == null) {
 1007               throw new NullPointerException();
 1008           }
 1009           if (!isRendered()) {
 1010               return;
 1011           }
 1012           Application app = context.getApplication();
 1013           app.publishEvent(context, PreValidateEvent.class, this);
 1014           preValidate(context);
 1015           iterate(context, PhaseId.PROCESS_VALIDATIONS);
 1016           app.publishEvent(context, PostValidateEvent.class, this);
 1017   
 1018       }
 1019   
 1020   
 1021       /**
 1022        * <p>Override the default {@link UIComponentBase#processUpdates}
 1023        * processing to perform the following steps.</p>
 1024        * <ul>
 1025        * <li>If the <code>rendered</code> property of this {@link UIComponent}
 1026        *     is <code>false</code>, skip further processing.</li>
 1027        * <li>Set the current <code>rowIndex</code> to -1.</li>
 1028        * <li>Call the <code>processUpdates()</code> method of all facets
 1029        *     of this {@link UIData}, in the order determined
 1030        *     by a call to <code>getFacets().keySet().iterator()</code>.</li>
 1031        * <li>Call the <code>processUpdates()</code> method of all facets
 1032        *     of the {@link UIColumn} children of this {@link UIData}.</li>
 1033        * <li>Iterate over the set of rows that were included when this
 1034        *     component was rendered (i.e. those defined by the <code>first</code>
 1035        *     and <code>rows</code> properties), performing the following
 1036        *     processing for each row:
 1037        *     <ul>
 1038        *     <li>Set the current <code>rowIndex</code> to the appropriate
 1039        *         value for this row.</li>
 1040        *     <li>If <code>isRowAvailable()</code> returns <code>true</code>,
 1041        *         iterate over the children components of each {@link UIColumn}
 1042        *         child of this {@link UIData} component, calling the
 1043        *         <code>processUpdates()</code> method for each such child.</li>
 1044        *     </ul></li>
 1045        * <li>Set the current <code>rowIndex</code> to -1.</li>
 1046        * </ul>
 1047        *
 1048        * @param context {@link FacesContext} for the current request
 1049        *
 1050        * @throws NullPointerException if <code>context</code> is <code>null</code>
 1051        */
 1052       public void processUpdates(FacesContext context) {
 1053   
 1054           if (context == null) {
 1055               throw new NullPointerException();
 1056           }
 1057           if (!isRendered()) {
 1058               return;
 1059           }
 1060   
 1061           preUpdate(context);
 1062           iterate(context, PhaseId.UPDATE_MODEL_VALUES);
 1063           // This is not a EditableValueHolder, so no further processing is required
 1064   
 1065       }
 1066   
 1067       public String createUniqueId(FacesContext context, String seed) {
 1068           Integer i = (Integer) getStateHelper().get(PropertyKeys.lastId);
 1069           int lastId = ((i != null) ? i : 0);
 1070           getStateHelper().put(PropertyKeys.lastId,  ++lastId);
 1071           return UIViewRoot.UNIQUE_ID_PREFIX + (seed == null ? lastId : seed);
 1072       }
 1073   
 1074       /**
 1075        * <p class="changed_added_2_0">Override the behavior in {@link
 1076        * UIComponent#visitTree} to handle iteration correctly.</p>
 1077        *
 1078        * <div class="changed_added_2_0">
 1079   
 1080        * <p>If the {@link UIComponent#isVisitable} method of this instance
 1081        * returns <code>false</code>, take no action and return.</p>
 1082   
 1083        * <p>Otherwise, save aside the result of a call to {@link
 1084        * #getRowIndex}.  Call {@link UIComponent#pushComponentToEL} and
 1085        * invoke the visit callback on this <code>UIData</code> instance as
 1086        * described in {@link UIComponent#visitTree}.  Let the result of
 1087        * the invoctaion be <em>visitResult</em>.  If <em>visitResult</em>
 1088        * is {@link VisitResult#COMPLETE}, take no further action and
 1089        * return <code>true</code>.  Otherwise, determine if we need to
 1090        * visit our children.  The default implementation calls {@link
 1091        * VisitContext#getSubtreeIdsToVisit} passing <code>this</code> as
 1092        * the argument.  If the result of that call is non-empty, let
 1093        * <em>doVisitChildren</em> be <code>true</code>.  If
 1094        * <em>doVisitChildren</em> is <code>true</code> and
 1095        * <em>visitResult</em> is {@link VisitResult#ACCEPT}, take the
 1096        * following action.<p>
 1097   
 1098        * <ul>
 1099   
 1100        * 	  <li><p>If this component has facets, call {@link
 1101        * 	  UIComponent#getFacets} on this instance and invoke the
 1102        * 	  <code>values()</code> method.  For each
 1103        * 	  <code>UIComponent</code> in the returned <code>Map</code>,
 1104        * 	  call {@link UIComponent#visitTree}.</p></li>
 1105   
 1106        * 	  <li><p>If this component has children, for each child
 1107        * 	  <code>UIComponent</code> retured from calling {@link
 1108        * 	  #getChildren} on this instance, if the child has facets, call
 1109        * 	  {@link UIComponent#getFacets} on the child instance and invoke
 1110        * 	  the <code>values()</code> method.  For each
 1111        * 	  <code>UIComponent</code> in the returned <code>Map</code>,
 1112        * 	  call {@link UIComponent#visitTree}.</p></li>
 1113   
 1114        * 	  <li><p>Iterate over the columns and rows.</p>
 1115   
 1116        * <ul>
 1117   
 1118        * 	  <li><p>Let <em>rowsToProcess</em> be the return from {@link
 1119        * 	  #getRows}.  </p></li>
 1120   
 1121        * 	  <li><p>Let <em>rowIndex</em> be the return from {@link
 1122        * 	  #getFirst} - 1.</p></li>
 1123   
 1124        * 	  <li><p>While the number of rows processed is less than
 1125        * 	  <em>rowsToProcess</em>, take the following actions.</p>
 1126   
 1127        * <p>Call {@link #setRowIndex}, passing the current row index.</p>
 1128   
 1129        * <p>If {@link #isRowAvailable} returns <code>false</code>, take no
 1130        * further action and return <code>false</code>.</p>
 1131   
 1132        * <p>For each child component of this <code>UIData</code> that is
 1133        * also an instance of {@link UIColumn}, call {@link
 1134        * UIComponent#visitTree} on the child.  If such a call returns
 1135        * <code>true</code>, terminate iteration and return
 1136        * <code>true</code>.  Take no action on non <code>UIColumn</code>
 1137        * children.</p>
 1138   
 1139        *     </li>
 1140   
 1141        * </ul>
 1142   
 1143        *    </li>
 1144   
 1145        * </ul>
 1146   
 1147        * <p>Call {@link #popComponentFromEL} and restore the saved row
 1148        * index with a call to {@link #setRowIndex}.</p>
 1149   
 1150        * <p>Return <code>false</code> to allow the visiting to
 1151        * continue.</p>
 1152   
 1153        * </div>
 1154        *
 1155        * @param context the <code>VisitContext</code> that provides
 1156        * context for performing the visit.
 1157        *
 1158        * @param callback the callback to be invoked for each node
 1159        * encountered in the visit.
 1160   
 1161        * @throws NullPointerException if any of the parameters are
 1162        * <code>null</code>.
 1163   
 1164        * 
 1165        */
 1166       @Override
 1167       public boolean visitTree(VisitContext context, 
 1168                                VisitCallback callback) {
 1169   
 1170           // First check to see whether we are visitable.  If not
 1171           // short-circuit out of this subtree, though allow the
 1172           // visit to proceed through to other subtrees.
 1173           if (!isVisitable(context))
 1174               return false;
 1175   
 1176           // Clear out the row index is one is set so that
 1177           // we start from a clean slate.
 1178           int oldRowIndex = getRowIndex();
 1179           setRowIndex(-1);
 1180   
 1181           // Push ourselves to EL
 1182           FacesContext facesContext = context.getFacesContext();
 1183           pushComponentToEL(facesContext, null);
 1184   
 1185           try {
 1186   
 1187               // Visit ourselves.  Note that we delegate to the 
 1188               // VisitContext to actually perform the visit.
 1189               VisitResult result = context.invokeVisitCallback(this, callback);
 1190   
 1191               // If the visit is complete, short-circuit out and end the visit
 1192               if (result == VisitResult.COMPLETE)
 1193                   return true;
 1194   
 1195               // Visit children, short-circuiting as necessary
 1196               if ((result == VisitResult.ACCEPT) && doVisitChildren(context)) {
 1197   
 1198                   // First visit facets
 1199                   if (visitFacets(context, callback))
 1200                       return true;
 1201   
 1202                   // Next column facets
 1203                   if (visitColumnFacets(context, callback))
 1204                       return true;
 1205   
 1206                   // And finally, visit rows
 1207                   if (visitColumnsAndRows(context, callback))
 1208                       return true;
 1209               }
 1210           }
 1211           finally {
 1212               // Clean up - pop EL and restore old row index
 1213               popComponentFromEL(facesContext);
 1214               setRowIndex(oldRowIndex);
 1215           }
 1216   
 1217           // Return false to allow the visit to continue
 1218           return false;
 1219       }
 1220   
 1221   
 1222       // --------------------------------------------------------- Protected Methods
 1223   
 1224   
 1225       /**
 1226        * <p>Return the internal {@link DataModel} object representing the data
 1227        * objects that we will iterate over in this component's rendering.</p>
 1228        * <p/>
 1229        * <p>If the model has been cached by a previous call to {@link
 1230        * #setDataModel}, return it.  Otherwise call {@link #getValue}.  If the
 1231        * result is null, create an empty {@link ListDataModel} and return it.  If
 1232        * the result is an instance of {@link DataModel}, return it.  Otherwise,
 1233        * adapt the result as described in {@link #getValue} and return it.</p>
 1234        */
 1235       protected DataModel getDataModel() {
 1236   
 1237           // Return any previously cached DataModel instance
 1238           if (this.model != null) {
 1239               return (model);
 1240           }
 1241   
 1242           // Synthesize a DataModel around our current value if possible
 1243           Object current = getValue();
 1244           if (current == null) {
 1245               setDataModel(new ListDataModel(Collections.EMPTY_LIST));
 1246           } else if (current instanceof DataModel) {
 1247               setDataModel((DataModel) current);
 1248           } else if (current instanceof List) {
 1249               setDataModel(new ListDataModel((List) current));
 1250           } else if (Object[].class.isAssignableFrom(current.getClass())) {
 1251               setDataModel(new ArrayDataModel((Object[]) current));
 1252           } else if (current instanceof ResultSet) {
 1253               setDataModel(new ResultSetDataModel((ResultSet) current));
 1254           } else if (current instanceof Result) {
 1255               setDataModel(new ResultDataModel((Result) current));
 1256           } else {
 1257               setDataModel(new ScalarDataModel(current));
 1258           }
 1259           return (model);
 1260   
 1261       }
 1262   
 1263       /**
 1264        * <p>Set the internal DataModel.  This <code>UIData</code> instance must
 1265        * use the given {@link DataModel} as its internal value representation from
 1266        * now until the next call to <code>setDataModel</code>.  If the given
 1267        * <code>DataModel</code> is <code>null</code>, the internal
 1268        * <code>DataModel</code> must be reset in a manner so that the next call to
 1269        * {@link #getDataModel} causes lazy instantion of a newly refreshed
 1270        * <code>DataModel</code>.</p>
 1271        * <p/>
 1272        * <p>Subclasses might call this method if they either want to restore the
 1273        * internal <code>DataModel</code> during the <em>Restore View</em> phase or
 1274        * if they want to explicitly refresh the current <code>DataModel</code> for
 1275        * the <em>Render Response</em> phase.</p>
 1276        *
 1277        * @param dataModel the new <code>DataModel</code> or <code>null</code> to
 1278        *                  cause the model to be refreshed.
 1279        */
 1280   
 1281       protected void setDataModel(DataModel dataModel) {
 1282           this.model = dataModel;
 1283       }
 1284   
 1285       // ---------------------------------------------------- Private Methods
 1286   
 1287   
 1288       // Perform pre-decode initialization work.  Note that this
 1289       // initialization may be performed either during a normal decode
 1290       // (ie. processDecodes()) or during a tree visit (ie. visitTree()).
 1291       private void preDecode(FacesContext context) {
 1292           setDataModel(null); // Re-evaluate even with server-side state saving
 1293           Map<String, SavedState> saved =
 1294                 (Map<String, SavedState>) getStateHelper().get(PropertyKeys.saved);
 1295           if (null == saved || !keepSaved(context)) {
 1296               //noinspection CollectionWithoutInitialCapacity
 1297               getStateHelper().remove(PropertyKeys.saved);
 1298           }
 1299       }
 1300   
 1301       // Perform pre-validation initialization work.  Note that this
 1302       // initialization may be performed either during a normal validation
 1303       // (ie. processValidators()) or during a tree visit (ie. visitTree()).
 1304       private void preValidate(FacesContext context) {
 1305           if (isNestedWithinUIData()) {
 1306               setDataModel(null);
 1307           }
 1308       }
 1309   
 1310       // Perform pre-update initialization work.  Note that this
 1311       // initialization may be performed either during normal update
 1312       // (ie. processUpdates()) or during a tree visit (ie. visitTree()).
 1313       private void preUpdate(FacesContext context) {
 1314           if (isNestedWithinUIData()) {
 1315               setDataModel(null);
 1316           }
 1317       }
 1318   
 1319       // Perform pre-encode initialization work.  Note that this
 1320       // initialization may be performed either during a normal encode
 1321       // (ie. encodeBegin()) or during a tree visit (ie. visitTree()).
 1322       private void preEncode(FacesContext context) {
 1323           setDataModel(null); // re-evaluate even with server-side state saving
 1324           if (!keepSaved(context)) {
 1325               ////noinspection CollectionWithoutInitialCapacity
 1326               //saved = new HashMap<String, SavedState>();
 1327               getStateHelper().remove(PropertyKeys.saved);
 1328           }
 1329       }
 1330   
 1331       /**
 1332        * <p>Perform the appropriate phase-specific processing and per-row
 1333        * iteration for the specified phase, as follows:
 1334        * <ul>
 1335        * <li>Set the <code>rowIndex</code> property to -1, and process the facets
 1336        *     of this {@link UIData} component exactly once.</li>
 1337        * <li>Set the <code>rowIndex</code> property to -1, and process the facets
 1338        *     of the {@link UIColumn} children of this {@link UIData} component
 1339        *     exactly once.</li>
 1340        * <li>Iterate over the relevant rows, based on the <code>first</code>
 1341        *     and <code>row</code> properties, and process the children
 1342        *     of the {@link UIColumn} children of this {@link UIData} component
 1343        *     once per row.</li>
 1344        * </ul>
 1345        *
 1346        * @param context {@link FacesContext} for the current request
 1347        * @param phaseId {@link PhaseId} of the phase we are currently running
 1348        */
 1349       private void iterate(FacesContext context, PhaseId phaseId) {
 1350   
 1351           // Process each facet of this component exactly once
 1352           setRowIndex(-1);
 1353           if (getFacetCount() > 0) {
 1354               for (UIComponent facet : getFacets().values()) {
 1355                   if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
 1356                       facet.processDecodes(context);
 1357                   } else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
 1358                       facet.processValidators(context);
 1359                   } else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
 1360                       facet.processUpdates(context);
 1361                   } else {
 1362                       throw new IllegalArgumentException();
 1363                   }
 1364               }
 1365           }
 1366   
 1367           // Process each facet of our child UIColumn components exactly once
 1368           setRowIndex(-1);
 1369           if (getChildCount() > 0) {
 1370               for (UIComponent column : getChildren()) {
 1371                   if (!(column instanceof UIColumn) || !column.isRendered()) {
 1372                       continue;
 1373                   }
 1374                   if (column.getFacetCount() > 0) {
 1375                       for (UIComponent columnFacet : column.getFacets().values()) {
 1376                           if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
 1377                               columnFacet.processDecodes(context);
 1378                           } else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
 1379                               columnFacet.processValidators(context);
 1380                           } else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
 1381                               columnFacet.processUpdates(context);
 1382                           } else {
 1383                               throw new IllegalArgumentException();
 1384                           }
 1385                       }
 1386                   }
 1387               }
 1388           }
 1389   
 1390           // Iterate over our UIColumn children, once per row
 1391           int processed = 0;
 1392           int rowIndex = getFirst() - 1;
 1393           int rows = getRows();
 1394   
 1395           while (true) {
 1396   
 1397               // Have we processed the requested number of rows?
 1398               if ((rows > 0) && (++processed > rows)) {
 1399                   break;
 1400               }
 1401   
 1402               // Expose the current row in the specified request attribute
 1403               setRowIndex(++rowIndex);
 1404               if (!isRowAvailable()) {
 1405                   break; // Scrolled past the last row
 1406               }
 1407   
 1408               // Perform phase-specific processing as required
 1409               // on the *children* of the UIColumn (facets have
 1410               // been done a single time with rowIndex=-1 already)
 1411               if (getChildCount() > 0) {
 1412                   for (UIComponent kid : getChildren()) {
 1413                       if (!(kid instanceof UIColumn) || !kid.isRendered()) {
 1414                           continue;
 1415                       }
 1416                       if (kid.getChildCount() > 0) {
 1417                           for (UIComponent grandkid : kid.getChildren()) {
 1418                               if (!grandkid.isRendered()) {
 1419                                   continue;
 1420                               }
 1421                               if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
 1422                                   grandkid.processDecodes(context);
 1423                               } else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
 1424                                   grandkid.processValidators(context);
 1425                               } else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
 1426                                   grandkid.processUpdates(context);
 1427                               } else {
 1428                                   throw new IllegalArgumentException();
 1429                               }
 1430                           }
 1431                       }
 1432                   }
 1433               }
 1434   
 1435           }
 1436   
 1437           // Clean up after ourselves
 1438           setRowIndex(-1);
 1439   
 1440       }
 1441   
 1442       // Tests whether we need to visit our children as part of
 1443       // a tree visit
 1444       private boolean doVisitChildren(VisitContext context) {
 1445   
 1446           // Just need to check whether there are any ids under this
 1447           // subtree.  Make sure row index is cleared out since 
 1448           // getSubtreeIdsToVisit() needs our row-less client id.
 1449           setRowIndex(-1);
 1450           Collection<String> idsToVisit = context.getSubtreeIdsToVisit(this);
 1451           assert(idsToVisit != null);
 1452   
 1453           // All ids or non-empty collection means we need to visit our children.
 1454           return (!idsToVisit.isEmpty());
 1455       }
 1456   
 1457       // Performs pre-phase initialization before visiting children
 1458       // (if necessary).
 1459       private void preVisitChildren(VisitContext visitContext) {
 1460   
 1461           // If EXECUTE_LIFECYCLE hint is set, we need to do
 1462           // lifecycle-related initialization before visiting children
 1463           if (visitContext.getHints().contains(VisitHint.EXECUTE_LIFECYCLE)) {
 1464               FacesContext facesContext = visitContext.getFacesContext();
 1465               PhaseId phaseId = facesContext.getCurrentPhaseId();
 1466   
 1467               if (phaseId == PhaseId.APPLY_REQUEST_VALUES)
 1468                   preDecode(facesContext);
 1469               else if (phaseId == PhaseId.PROCESS_VALIDATIONS)
 1470                   preValidate(facesContext);
 1471               else if (phaseId == PhaseId.UPDATE_MODEL_VALUES)
 1472                   preUpdate(facesContext);
 1473               else if (phaseId == PhaseId.RENDER_RESPONSE)
 1474                   preEncode(facesContext);
 1475           }
 1476       }
 1477   
 1478       // Visit each facet of this component exactly once
 1479       private boolean visitFacets(VisitContext context, VisitCallback callback) {
 1480   
 1481           setRowIndex(-1);
 1482           if (getFacetCount() > 0) {
 1483               for (UIComponent facet : getFacets().values()) {
 1484                   if (facet.visitTree(context, callback))
 1485                       return true;
 1486               }
 1487           }
 1488   
 1489           return false;
 1490       }
 1491   
 1492       // Visit each facet of our child UIColumn components exactly once
 1493       private boolean visitColumnFacets(VisitContext context, 
 1494                                         VisitCallback callback) {
 1495           setRowIndex(-1);
 1496           if (getChildCount() > 0) {
 1497               for (UIComponent column : getChildren()) {
 1498                   if (column.getFacetCount() > 0) {
 1499                       for (UIComponent columnFacet : column.getFacets().values()) {
 1500                           if (columnFacet.visitTree(context, callback))
 1501                               return true;
 1502                       }
 1503                   }
 1504               }
 1505           }
 1506   
 1507           return false;
 1508       }
 1509   
 1510       // Visit each column and row
 1511       private boolean visitColumnsAndRows(VisitContext context,  VisitCallback callback) {
 1512   
 1513   
 1514           // first, visit all columns
 1515           if (getChildCount() > 0) {
 1516               for (UIComponent kid : getChildren()) {
 1517                   if (!(kid instanceof UIColumn)) {
 1518                       continue;
 1519                   }
 1520                   if (kid.visitTree(context, callback)) {
 1521                       return true;
 1522                   }
 1523               }
 1524           }
 1525   
 1526           // Iterate over our UIColumn children, once per row
 1527           int processed = 0;
 1528           int rowIndex = getFirst() - 1;
 1529           int rows = getRows();
 1530   
 1531           while (true) {
 1532   
 1533               // Have we processed the requested number of rows?
 1534               if ((rows > 0) && (++processed > rows)) {
 1535                   break;
 1536               }
 1537   
 1538               // Expose the current row in the specified request attribute
 1539               setRowIndex(++rowIndex);
 1540               if (!isRowAvailable()) {
 1541                   break; // Scrolled past the last row
 1542               }
 1543   
 1544               // Visit as required on the *children* of the UIColumn
 1545               // (facets have been done a single time with rowIndex=-1 already)
 1546               if (getChildCount() > 0) {
 1547                   for (UIComponent kid : getChildren()) {
 1548                       if (!(kid instanceof UIColumn)) {
 1549                           continue;
 1550                       }
 1551                       if (kid.getChildCount() > 0) {
 1552                       for (UIComponent grandkid : kid.getChildren()) {
 1553                               if (grandkid.visitTree(context, callback)) {
 1554                                   return true;
 1555                               }
 1556                           }
 1557                       }
 1558                   }
 1559               }
 1560   
 1561           }
 1562   
 1563           return false;
 1564       }
 1565   
 1566   
 1567       /**
 1568        * <p>Return <code>true</code> if we need to keep the saved
 1569        * per-child state information.  This will be the case if any of the
 1570        * following are true:</p>
 1571        *
 1572        * <ul>
 1573        *
 1574        * <li>there are messages queued with severity ERROR or FATAL.</li>
 1575        *
 1576        * <li>this <code>UIData</code> instance is nested inside of another
 1577        * <code>UIData</code> instance</li>
 1578        *
 1579        * </ul>
 1580        *
 1581        * @param context {@link FacesContext} for the current request
 1582        */
 1583       private boolean keepSaved(FacesContext context) {
 1584   
 1585           return (contextHasErrorMessages(context) || isNestedWithinUIData());
 1586   
 1587       }
 1588   
 1589   
 1590       private Boolean isNestedWithinUIData() {
 1591           if (isNested == null) {
 1592               UIComponent parent = this;
 1593               while (null != (parent = parent.getParent())) {
 1594                   if (parent instanceof UIData) {
 1595                       isNested = Boolean.TRUE;
 1596                       break;
 1597                   }
 1598               }
 1599               if (isNested == null) {
 1600                   isNested = Boolean.FALSE;
 1601               }
 1602               return isNested;
 1603           } else {
 1604               return isNested;
 1605           }
 1606       }
 1607   
 1608   
 1609       private boolean contextHasErrorMessages(FacesContext context) {
 1610   
 1611           FacesMessage.Severity sev = context.getMaximumSeverity();
 1612           return (sev != null && (FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0));
 1613   
 1614       }
 1615   
 1616   
 1617       /**
 1618        * <p>Restore state information for all descendant components, as described
 1619        * for <code>setRowIndex()</code>.</p>
 1620        */
 1621       private void restoreDescendantState() {
 1622   
 1623           FacesContext context = getFacesContext();
 1624           if (getChildCount() > 0) {
 1625               for (UIComponent kid : getChildren()) {
 1626                   if (kid instanceof UIColumn) {
 1627                       restoreDescendantState(kid, context);
 1628                   }
 1629               }
 1630           }
 1631   
 1632       }
 1633   
 1634   
 1635       /**
 1636        * <p>Restore state information for the specified component and its
 1637        * descendants.</p>
 1638        *
 1639        * @param component Component for which to restore state information
 1640        * @param context   {@link FacesContext} for the current request
 1641        */
 1642       private void restoreDescendantState(UIComponent component,
 1643                                           FacesContext context) {
 1644   
 1645           // Reset the client identifier for this component
 1646           String id = component.getId();
 1647           component.setId(id); // Forces client id to be reset
 1648           Map<String, SavedState> saved = (Map<String,SavedState>)
 1649               getStateHelper().get(PropertyKeys.saved);
 1650           // Restore state for this component (if it is a EditableValueHolder)
 1651           if (component instanceof EditableValueHolder) {
 1652               EditableValueHolder input = (EditableValueHolder) component;
 1653               String clientId = component.getClientId(context);
 1654   
 1655               SavedState state = saved.get(clientId);
 1656               if (state == null) {
 1657                   state = new SavedState();
 1658               }
 1659               input.setValue(state.getValue());
 1660               input.setValid(state.isValid());
 1661               input.setSubmittedValue(state.getSubmittedValue());
 1662               // This *must* be set after the call to setValue(), since
 1663               // calling setValue() always resets "localValueSet" to true.
 1664               input.setLocalValueSet(state.isLocalValueSet());
 1665           } else if (component instanceof UIForm) {
 1666               UIForm form = (UIForm) component;
 1667               String clientId = component.getClientId(context);
 1668               SavedState state = saved.get(clientId);
 1669               if (state == null) {
 1670                   state = new SavedState();
 1671               }
 1672               form.setSubmitted(state.getSubmitted());
 1673               state.setSubmitted(form.isSubmitted());
 1674           }
 1675   
 1676           // Restore state for children of this component
 1677           if (component.getChildCount() > 0) {
 1678               for (UIComponent kid : component.getChildren()) {
 1679                   restoreDescendantState(kid, context);
 1680               }
 1681           }
 1682   
 1683           // Restore state for facets of this component
 1684           if (component.getFacetCount() > 0) {
 1685               for (UIComponent facet : component.getFacets().values()) {
 1686                   restoreDescendantState(facet, context);
 1687               }
 1688           }
 1689   
 1690       }
 1691   
 1692   
 1693       /**
 1694        * <p>Save state information for all descendant components, as described for
 1695        * <code>setRowIndex()</code>.</p>
 1696        */
 1697       private void saveDescendantState() {
 1698   
 1699           FacesContext context = getFacesContext();
 1700           if (getChildCount() > 0) {
 1701               for (UIComponent kid : getChildren()) {
 1702                   if (kid instanceof UIColumn) {
 1703                       saveDescendantState(kid, context);
 1704                   }
 1705               }
 1706           }
 1707   
 1708       }
 1709   
 1710   
 1711       /**
 1712        * <p>Save state information for the specified component and its
 1713        * descendants.</p>
 1714        *
 1715        * @param component Component for which to save state information
 1716        * @param context   {@link FacesContext} for the current request
 1717        */
 1718       private void saveDescendantState(UIComponent component,
 1719                                        FacesContext context) {
 1720   
 1721           // Save state for this component (if it is a EditableValueHolder)
 1722           Map<String, SavedState> saved = (Map<String, SavedState>)
 1723                 getStateHelper().get(PropertyKeys.saved);
 1724           if (component instanceof EditableValueHolder) {
 1725               EditableValueHolder input = (EditableValueHolder) component;
 1726               SavedState state = null;
 1727               String clientId = component.getClientId(context);
 1728               if (saved == null) {
 1729                   state = new SavedState();
 1730                   getStateHelper().put(PropertyKeys.saved, clientId, state);
 1731               }
 1732               if (state == null) {
 1733                   state = saved.get(clientId);
 1734                   if (state == null) {
 1735                       state = new SavedState();
 1736                       //saved.put(clientId, state);
 1737                       getStateHelper().put(PropertyKeys.saved, clientId, state);
 1738                   }
 1739               }
 1740               state.setValue(input.getLocalValue());
 1741               state.setValid(input.isValid());
 1742               state.setSubmittedValue(input.getSubmittedValue());
 1743               state.setLocalValueSet(input.isLocalValueSet());
 1744           } else if (component instanceof UIForm) {
 1745               UIForm form = (UIForm) component;
 1746               String clientId = component.getClientId(context);
 1747               SavedState state = null;
 1748               if (saved == null) {
 1749                   state = new SavedState();
 1750                   getStateHelper().put(PropertyKeys.saved, clientId, state);
 1751               }
 1752               if (state == null) {
 1753                   state = saved.get(clientId);
 1754                   if (state == null) {
 1755                       state = new SavedState();
 1756                       //saved.put(clientId, state);
 1757                       getStateHelper().put(PropertyKeys.saved, clientId, state);
 1758                   }
 1759               }
 1760               state.setSubmitted(form.isSubmitted());
 1761           }
 1762   
 1763           // Save state for children of this component
 1764           if (component.getChildCount() > 0) {
 1765               for (UIComponent uiComponent : component.getChildren()) {
 1766                   saveDescendantState(uiComponent, context);
 1767               }
 1768           }
 1769   
 1770           // Save state for facets of this component
 1771           if (component.getFacetCount() > 0) {
 1772               for (UIComponent facet : component.getFacets().values()) {
 1773                   saveDescendantState(facet, context);
 1774               }
 1775           }
 1776   
 1777       }
 1778   
 1779   }
 1780   @SuppressWarnings({"SerializableHasSerializationMethods",
 1781         "NonSerializableFieldInSerializableClass"})
 1782   class SavedState implements Serializable {
 1783   
 1784       private static final long serialVersionUID = 2920252657338389849L;
 1785       private Object submittedValue;
 1786       private boolean submitted;
 1787   
 1788       Object getSubmittedValue() {
 1789           return (this.submittedValue);
 1790       }
 1791   
 1792       void setSubmittedValue(Object submittedValue) {
 1793           this.submittedValue = submittedValue;
 1794       }
 1795   
 1796       private boolean valid = true;
 1797   
 1798       boolean isValid() {
 1799           return (this.valid);
 1800       }
 1801   
 1802       void setValid(boolean valid) {
 1803           this.valid = valid;
 1804       }
 1805   
 1806       private Object value;
 1807   
 1808       Object getValue() {
 1809           return (this.value);
 1810       }
 1811   
 1812       public void setValue(Object value) {
 1813           this.value = value;
 1814       }
 1815   
 1816       private boolean localValueSet;
 1817   
 1818       boolean isLocalValueSet() {
 1819           return (this.localValueSet);
 1820       }
 1821   
 1822       public void setLocalValueSet(boolean localValueSet) {
 1823           this.localValueSet = localValueSet;
 1824       }
 1825   
 1826       public boolean getSubmitted() {
 1827           return this.submitted;
 1828       }
 1829   
 1830       public void setSubmitted(boolean submitted) {
 1831           this.submitted = submitted;
 1832       }
 1833   
 1834       public String toString() {
 1835           return ("submittedValue: " + submittedValue +
 1836                   " value: " + value +
 1837                   " localValueSet: " + localValueSet);
 1838       }
 1839   
 1840   }
 1841   
 1842   
 1843   // Private class to wrap an event with a row index
 1844   class WrapperEvent extends FacesEvent {
 1845   
 1846   
 1847       public WrapperEvent(UIComponent component, FacesEvent event, int rowIndex) {
 1848           super(component);
 1849           this.event = event;
 1850           this.rowIndex = rowIndex;
 1851       }
 1852   
 1853       private FacesEvent event = null;
 1854       private int rowIndex = -1;
 1855   
 1856       public FacesEvent getFacesEvent() {
 1857           return (this.event);
 1858       }
 1859   
 1860       public int getRowIndex() {
 1861           return (this.rowIndex);
 1862       }
 1863   
 1864       public PhaseId getPhaseId() {
 1865           return (this.event.getPhaseId());
 1866       }
 1867   
 1868       public void setPhaseId(PhaseId phaseId) {
 1869           this.event.setPhaseId(phaseId);
 1870       }
 1871   
 1872       public boolean isAppropriateListener(FacesListener listener) {
 1873           return (false);
 1874       }
 1875   
 1876       public void processListener(FacesListener listener) {
 1877           throw new IllegalStateException();
 1878       }
 1879   
 1880   
 1881   }

Home » Mojarra-2.0.1 » javax » faces » component » [javadoc | source]