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