Coverage Report - org.apache.tapestry.contrib.table.components.TableView
 
Classes in this File Line Coverage Branch Coverage Complexity
TableView
0%
0/148
0%
0/84
2.439
 
 1  
 // Copyright 2004, 2005 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 7  
 //     http://www.apache.org/licenses/LICENSE-2.0
 8  
 //
 9  
 // Unless required by applicable law or agreed to in writing, software
 10  
 // distributed under the License is distributed on an "AS IS" BASIS,
 11  
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 // See the License for the specific language governing permissions and
 13  
 // limitations under the License.
 14  
 
 15  
 package org.apache.tapestry.contrib.table.components;
 16  
 
 17  
 import org.apache.hivemind.ApplicationRuntimeException;
 18  
 import org.apache.tapestry.BaseComponent;
 19  
 import org.apache.tapestry.IComponent;
 20  
 import org.apache.tapestry.IMarkupWriter;
 21  
 import org.apache.tapestry.IRequestCycle;
 22  
 import org.apache.tapestry.contrib.table.model.*;
 23  
 import org.apache.tapestry.contrib.table.model.common.BasicTableModelWrap;
 24  
 import org.apache.tapestry.contrib.table.model.simple.SimpleListTableDataModel;
 25  
 import org.apache.tapestry.contrib.table.model.simple.SimpleTableColumnModel;
 26  
 import org.apache.tapestry.contrib.table.model.simple.SimpleTableModel;
 27  
 import org.apache.tapestry.contrib.table.model.simple.SimpleTableState;
 28  
 import org.apache.tapestry.event.PageBeginRenderListener;
 29  
 import org.apache.tapestry.event.PageDetachListener;
 30  
 import org.apache.tapestry.event.PageEvent;
 31  
 
 32  
 import java.io.Serializable;
 33  
 import java.util.ArrayList;
 34  
 import java.util.Collection;
 35  
 import java.util.Iterator;
 36  
 import java.util.List;
 37  
 
 38  
 /**
 39  
  * A low level Table component that wraps all other low level Table components.
 40  
  * This component carries the
 41  
  * {@link org.apache.tapestry.contrib.table.model.ITableModel}that is used by
 42  
  * the other Table components. Please see the documentation of
 43  
  * {@link org.apache.tapestry.contrib.table.model.ITableModel}if you need to
 44  
  * know more about how a table is represented.
 45  
  * <p>
 46  
  * This component also handles the saving of the state of the model using an
 47  
  * {@link org.apache.tapestry.contrib.table.model.ITableSessionStateManager}to
 48  
  * determine what part of the model is to be saved and an
 49  
  * {@link  org.apache.tapestry.contrib.table.model.ITableSessionStoreManager}to
 50  
  * determine how to save it.
 51  
  * <p>
 52  
  * Upon the beginning of a new request cycle when the table model is first
 53  
  * needed, the model is obtained using the following process:
 54  
  * <ul>
 55  
  * <li>The persistent state of the table is loaded. If the
 56  
  * tableSessionStoreManager binding has not been bound, the state is loaded from
 57  
  * a persistent property within the component (it is null at the beginning).
 58  
  * Otherwise the supplied
 59  
  * {@link  org.apache.tapestry.contrib.table.model.ITableSessionStoreManager}is
 60  
  * used to load the persistent state.
 61  
  * <li>The table model is recreated using the
 62  
  * {@link org.apache.tapestry.contrib.table.model.ITableSessionStateManager}that
 63  
  * could be supplied using the tableSessionStateManager binding (but has a
 64  
  * default value and is therefore not required).
 65  
  * <li>If the
 66  
  * {@link org.apache.tapestry.contrib.table.model.ITableSessionStateManager}returns
 67  
  * null, then a table model is taken from the tableModel binding. Thus, if the
 68  
  * {@link org.apache.tapestry.contrib.table.model.common.NullTableSessionStateManager}is
 69  
  * used, the table model would be taken from the tableModel binding every time.
 70  
  * </ul>
 71  
  * Just before the rendering phase the persistent state of the model is saved in
 72  
  * the session. This process occurs in reverse:
 73  
  * <ul>
 74  
  * <li>The persistent state of the model is taken via the
 75  
  * {@link org.apache.tapestry.contrib.table.model.ITableSessionStateManager}.
 76  
  * <li>If the tableSessionStoreManager binding has not been bound, the
 77  
  * persistent state is saved as a persistent page property. Otherwise the
 78  
  * supplied
 79  
  * {@link  org.apache.tapestry.contrib.table.model.ITableSessionStoreManager}is
 80  
  * used to save the persistent state. Use of the
 81  
  * {@link  org.apache.tapestry.contrib.table.model.ITableSessionStoreManager}is
 82  
  * usually necessary when tables with the same model have to be used across
 83  
  * multiple pages, and hence the state has to be saved in the Visit, rather than
 84  
  * in a persistent component property.
 85  
  * </ul>
 86  
  * <p>
 87  
  * <p>
 88  
  * Please see the Component Reference for details on how to use this component. [
 89  
  * <a
 90  
  * href="../../../../../../../ComponentReference/contrib.TableView.html">Component
 91  
  * Reference </a>]
 92  
  * 
 93  
  * @author mindbridge
 94  
  */
 95  
 public abstract class TableView extends BaseComponent implements
 96  
         PageDetachListener, PageBeginRenderListener, ITableModelSource
 97  
 {
 98  
 
 99  
     // Component properties
 100  0
     private ITableSessionStateManager m_objDefaultSessionStateManager = null;
 101  
 
 102  0
     private ITableColumnModel m_objColumnModel = null;
 103  
 
 104  
     // Transient objects
 105  
     private ITableModel m_objTableModel;
 106  
 
 107  
     private ITableModel m_objCachedTableModelValue;
 108  
 
 109  
     /**
 110  
      * The component constructor. Invokes the component member initializations.
 111  
      */
 112  
     public TableView()
 113  0
     {
 114  0
         initialize();
 115  0
     }
 116  
 
 117  
     /** @since 4.0 */
 118  
     public abstract TableColumnModelSource getModelSource();
 119  
 
 120  
     /** @since 4.0 */
 121  
     public abstract IAdvancedTableColumnSource getColumnSource();
 122  
 
 123  
     // enhanced parameter methods
 124  
     public abstract ITableModel getTableModelValue();
 125  
 
 126  
     public abstract Object getSource();
 127  
 
 128  
     public abstract Object getColumns();
 129  
 
 130  
     public abstract int getInitialPage();
 131  
 
 132  
     public abstract String getInitialSortColumn();
 133  
 
 134  
     public abstract boolean getInitialSortOrder();
 135  
 
 136  
     public abstract ITableSessionStateManager getTableSessionStateManager();
 137  
 
 138  
     public abstract ITableSessionStoreManager getTableSessionStoreManager();
 139  
 
 140  
     public abstract IComponent getColumnSettingsContainer();
 141  
 
 142  
     public abstract int getPageSize();
 143  
 
 144  
     public abstract String getPersist();
 145  
 
 146  
     // enhanced property methods
 147  
     public abstract Serializable getSessionState();
 148  
 
 149  
     public abstract void setSessionState(Serializable sessionState);
 150  
 
 151  
     public abstract Serializable getClientState();
 152  
 
 153  
     public abstract void setClientState(Serializable sessionState);
 154  
 
 155  
     public abstract Serializable getClientAppState();
 156  
 
 157  
     public abstract void setClientAppState(Serializable sessionState);
 158  
 
 159  
     public abstract List getTableActions();
 160  
 
 161  
     public abstract void setTableActions(List actions);
 162  
 
 163  
     /**
 164  
      * Invokes the component member initializations.
 165  
      * 
 166  
      * @see org.apache.tapestry.event.PageDetachListener#pageDetached(PageEvent)
 167  
      */
 168  
     public void pageDetached(PageEvent objEvent)
 169  
     {
 170  0
         initialize();
 171  0
     }
 172  
 
 173  
     /**
 174  
      * Initialize the component member variables.
 175  
      */
 176  
     private void initialize()
 177  
     {
 178  0
         m_objTableModel = null;
 179  0
         m_objCachedTableModelValue = null;
 180  0
     }
 181  
 
 182  
     /**
 183  
      * Resets the table by removing any stored table state. This means that the
 184  
      * current column to sort on and the current page will be forgotten and all
 185  
      * data will be reloaded.
 186  
      */
 187  
     public void reset()
 188  
     {
 189  0
         initialize();
 190  0
         storeSessionState(null);
 191  0
     }
 192  
 
 193  
     public ITableModel getCachedTableModelValue()
 194  
     {
 195  0
         if (m_objCachedTableModelValue == null)
 196  0
             m_objCachedTableModelValue = getTableModelValue();
 197  0
         return m_objCachedTableModelValue;
 198  
     }
 199  
 
 200  
     /**
 201  
      * Returns the tableModel.
 202  
      * 
 203  
      * @return ITableModel the table model used by the table components
 204  
      */
 205  
     public ITableModel getTableModel()
 206  
     {
 207  
         // if null, first try to recreate the model from the session state
 208  0
         if (m_objTableModel == null)
 209  
         {
 210  0
             Serializable objState = loadSessionState();
 211  0
             ITableSessionStateManager objStateManager = getTableSessionStateManager();
 212  0
             m_objTableModel = objStateManager.recreateTableModel(objState);
 213  
         }
 214  
 
 215  
         // if the session state does not help, get the model from the binding
 216  0
         if (m_objTableModel == null)
 217  0
             m_objTableModel = getCachedTableModelValue();
 218  
 
 219  
         // if the model from the binding is null, build a model from source and
 220  
         // columns
 221  0
         if (m_objTableModel == null)
 222  0
             m_objTableModel = generateTableModel(null);
 223  
 
 224  0
         if (m_objTableModel == null)
 225  0
             throw new ApplicationRuntimeException(TableMessages.missingTableModel(this));
 226  
 
 227  0
         return m_objTableModel;
 228  
     }
 229  
 
 230  
     /**
 231  
      * Generate a table model using the 'source' and 'columns' parameters.
 232  
      * 
 233  
      * @return the newly generated table model
 234  
      */
 235  
     protected ITableModel generateTableModel(SimpleTableState objState)
 236  
     {
 237  0
         SimpleTableState usableObjState = objState;
 238  
         // create a new table state if none is passed
 239  0
         if (usableObjState == null)
 240  
         {
 241  0
             usableObjState = new SimpleTableState();
 242  0
             usableObjState.getSortingState().setSortColumn(getInitialSortColumn(),
 243  
                     getInitialSortOrder());
 244  0
             usableObjState.getPagingState().setCurrentPage(getInitialPage());
 245  
         }
 246  
 
 247  
         // update the page size if set in the parameter
 248  0
         if (isParameterBound("pageSize"))
 249  0
             usableObjState.getPagingState().setPageSize(getPageSize());
 250  
 
 251  
         // get the column model. if not possible, return null.
 252  0
         ITableColumnModel objColumnModel = getTableColumnModel();
 253  0
         if (objColumnModel == null) return null;
 254  
 
 255  0
         Object objSourceValue = getSource();
 256  0
         if (objSourceValue == null) return null;
 257  
 
 258  
         // if the source parameter is of type {@link IBasicTableModel},
 259  
         // create and return an appropriate wrapper
 260  0
         if (objSourceValue instanceof IBasicTableModel)
 261  0
             return new BasicTableModelWrap((IBasicTableModel) objSourceValue,
 262  
                     objColumnModel, usableObjState);
 263  
 
 264  
         // otherwise, the source parameter must contain the data to be displayed
 265  0
         ITableDataModel objDataModel = null;
 266  0
         if (objSourceValue instanceof Object[])
 267  0
             objDataModel = new SimpleListTableDataModel(
 268  
                     (Object[]) objSourceValue);
 269  0
         else if (objSourceValue instanceof List)
 270  0
             objDataModel = new SimpleListTableDataModel((List) objSourceValue);
 271  0
         else if (objSourceValue instanceof Collection)
 272  0
             objDataModel = new SimpleListTableDataModel(
 273  
                     (Collection) objSourceValue);
 274  0
         else if (objSourceValue instanceof Iterator)
 275  0
             objDataModel = new SimpleListTableDataModel(
 276  
                     (Iterator) objSourceValue);
 277  
 
 278  0
         if (objDataModel == null)
 279  0
             throw new ApplicationRuntimeException(TableMessages
 280  
                     .invalidTableSource(this, objSourceValue));
 281  
 
 282  0
         return new SimpleTableModel(objDataModel, objColumnModel, usableObjState);
 283  
     }
 284  
 
 285  
     /**
 286  
      * Returns the table column model as specified by the 'columns' binding. If
 287  
      * the value of the 'columns' binding is of a type different than
 288  
      * ITableColumnModel, this method makes the appropriate conversion.
 289  
      * 
 290  
      * @return The table column model as specified by the 'columns' binding
 291  
      */
 292  
     protected ITableColumnModel getTableColumnModel()
 293  
     {
 294  0
         Object objColumns = getColumns();
 295  
 
 296  0
         if (objColumns == null) return null;
 297  
 
 298  0
         if (objColumns instanceof ITableColumnModel) { return (ITableColumnModel) objColumns; }
 299  
 
 300  0
         if (objColumns instanceof Iterator)
 301  
         {
 302  
             // convert to List
 303  0
             Iterator objColumnsIterator = (Iterator) objColumns;
 304  0
             List arrColumnsList = new ArrayList();
 305  0
             addAll(arrColumnsList, objColumnsIterator);
 306  0
             objColumns = arrColumnsList;
 307  
         }
 308  
 
 309  0
         if (objColumns instanceof List)
 310  
         {
 311  
             // validate that the list contains only ITableColumn instances
 312  0
             List arrColumnsList = (List) objColumns;
 313  0
             int nColumnsNumber = arrColumnsList.size();
 314  0
             for(int i = 0; i < nColumnsNumber; i++)
 315  
             {
 316  0
                 if (!(arrColumnsList.get(i) instanceof ITableColumn))
 317  0
                     throw new ApplicationRuntimeException(TableMessages
 318  
                             .columnsOnlyPlease(this));
 319  
             }
 320  
             // objColumns = arrColumnsList.toArray(new
 321  
             // ITableColumn[nColumnsNumber]);
 322  0
             return new SimpleTableColumnModel(arrColumnsList);
 323  
         }
 324  
 
 325  0
         if (objColumns instanceof ITableColumn[]) { return new SimpleTableColumnModel(
 326  
                 (ITableColumn[]) objColumns); }
 327  
 
 328  0
         if (objColumns instanceof String)
 329  
         {
 330  0
             String strColumns = (String) objColumns;
 331  0
             if (getBinding("columns").isInvariant())
 332  
             {
 333  
                 // if the binding is invariant, create the columns only once
 334  0
                 if (m_objColumnModel == null)
 335  0
                     m_objColumnModel = generateTableColumnModel(strColumns);
 336  0
                 return m_objColumnModel;
 337  
             }
 338  
 
 339  
             // if the binding is not invariant, create them every time
 340  0
             return generateTableColumnModel(strColumns);
 341  
         }
 342  
 
 343  0
         throw new ApplicationRuntimeException(TableMessages
 344  
                 .invalidTableColumns(this, objColumns));
 345  
     }
 346  
 
 347  
     private void addAll(List arrColumnsList, Iterator objColumnsIterator)
 348  
     {
 349  0
         while(objColumnsIterator.hasNext())
 350  0
             arrColumnsList.add(objColumnsIterator.next());
 351  0
     }
 352  
 
 353  
     /**
 354  
      * Generate a table column model out of the description string provided.
 355  
      * Entries in the description string are separated by commas. Each column
 356  
      * entry is of the format name, name:expression, or
 357  
      * name:displayName:expression. An entry prefixed with ! represents a
 358  
      * non-sortable column. If the whole description string is prefixed with *,
 359  
      * it represents columns to be included in a Form.
 360  
      * 
 361  
      * @param strDesc
 362  
      *            the description of the column model to be generated
 363  
      * @return a table column model based on the provided description
 364  
      */
 365  
     protected ITableColumnModel generateTableColumnModel(String strDesc)
 366  
     {
 367  0
         IComponent objColumnSettingsContainer = getColumnSettingsContainer();
 368  0
         IAdvancedTableColumnSource objColumnSource = getColumnSource();
 369  
 
 370  0
         return getModelSource().generateTableColumnModel(objColumnSource,
 371  
                 strDesc, this, objColumnSettingsContainer);
 372  
     }
 373  
 
 374  
     /**
 375  
      * The default session state manager to be used in case no such manager is
 376  
      * provided by the corresponding parameter.
 377  
      * 
 378  
      * @return the default session state manager
 379  
      */
 380  
     public ITableSessionStateManager getDefaultTableSessionStateManager()
 381  
     {
 382  0
         if (m_objDefaultSessionStateManager == null)
 383  0
             m_objDefaultSessionStateManager = new TableViewSessionStateManager(
 384  
                     this);
 385  0
         return m_objDefaultSessionStateManager;
 386  
     }
 387  
 
 388  
     /**
 389  
      * Invoked when there is a modification of the table state and it needs to
 390  
      * be saved.
 391  
      * 
 392  
      * @see org.apache.tapestry.contrib.table.model.ITableModelSource#fireObservedStateChange()
 393  
      */
 394  
     public void fireObservedStateChange()
 395  
     {
 396  0
         saveSessionState();
 397  0
     }
 398  
 
 399  
     /**
 400  
      * Ensures that the table state is saved before the render phase begins in
 401  
      * case there are modifications for which {@link #fireObservedStateChange()}has
 402  
      * not been invoked.
 403  
      * 
 404  
      * @see org.apache.tapestry.event.PageBeginRenderListener#pageBeginRender(org.apache.tapestry.event.PageEvent)
 405  
      */
 406  
     public void pageBeginRender(PageEvent event)
 407  
     {
 408  0
         executeTableActions();
 409  
 
 410  
         // 'suspenders': save the table model if it has been already loaded.
 411  
         // this means that if a change has been made explicitly in a listener,
 412  
         // it will be saved. this is the last place before committing the
 413  
         // changes
 414  
         // where a save can occur
 415  0
         if (m_objTableModel != null) saveSessionState();
 416  0
     }
 417  
 
 418  
     /**
 419  
      * Saves the table state using the SessionStateManager to determine what to
 420  
      * save and the SessionStoreManager to determine where to save it.
 421  
      */
 422  
     protected void saveSessionState()
 423  
     {
 424  0
         ITableModel objModel = getTableModel();
 425  0
         Serializable objState = getTableSessionStateManager().getSessionState(
 426  
                 objModel);
 427  0
         storeSessionState(objState);
 428  0
     }
 429  
 
 430  
     /**
 431  
      * Loads the table state using the SessionStoreManager.
 432  
      * 
 433  
      * @return the stored table state
 434  
      */
 435  
     protected Serializable loadSessionState()
 436  
     {
 437  0
         ITableSessionStoreManager objManager = getTableSessionStoreManager();
 438  0
         if (objManager != null)
 439  0
             return objManager.loadState(getPage().getRequestCycle());
 440  0
         String strPersist = getPersist();
 441  0
         if (strPersist.equals("client") || strPersist.equals("client:page"))
 442  0
             return getClientState();
 443  0
         else if (strPersist.equals("client:app"))
 444  0
             return getClientAppState();
 445  0
         else return getSessionState();
 446  
     }
 447  
 
 448  
     /**
 449  
      * Stores the table state using the SessionStoreManager.
 450  
      * 
 451  
      * @param objState
 452  
      *            the table state to store
 453  
      */
 454  
     protected void storeSessionState(Serializable objState)
 455  
     {
 456  0
         ITableSessionStoreManager objManager = getTableSessionStoreManager();
 457  0
         if (objManager != null)
 458  0
             objManager.saveState(getPage().getRequestCycle(), objState);
 459  
         else
 460  
         {
 461  0
             String strPersist = getPersist();
 462  0
             if (strPersist.equals("client") || strPersist.equals("client:page"))
 463  0
                 setClientState(objState);
 464  0
             else if (strPersist.equals("client:app"))
 465  0
                 setClientAppState(objState);
 466  0
             else setSessionState(objState);
 467  
         }
 468  0
     }
 469  
 
 470  
     /**
 471  
      * Make sure that the values stored in the model are useable and correct.
 472  
      * The changes made here are not saved.
 473  
      */
 474  
     protected void validateValues()
 475  
     {
 476  0
         ITableModel objModel = getTableModel();
 477  
 
 478  
         // make sure current page is within the allowed range
 479  0
         ITablePagingState objPagingState = objModel.getPagingState();
 480  0
         int nCurrentPage = objPagingState.getCurrentPage();
 481  0
         int nPageCount = objModel.getPageCount();
 482  0
         if (nCurrentPage >= nPageCount)
 483  
         {
 484  
             // the current page is greater than the page count. adjust.
 485  0
             nCurrentPage = nPageCount - 1;
 486  0
             objPagingState.setCurrentPage(nCurrentPage);
 487  
         }
 488  0
         if (nCurrentPage < 0)
 489  
         {
 490  
             // the current page is before the first page. adjust.
 491  0
             nCurrentPage = 0;
 492  0
             objPagingState.setCurrentPage(nCurrentPage);
 493  
         }
 494  0
     }
 495  
 
 496  
     /**
 497  
      * Stores a pointer to this component in the Request Cycle while rendering
 498  
      * so that wrapped components have access to it.
 499  
      * 
 500  
      * @see org.apache.tapestry.BaseComponent#renderComponent(IMarkupWriter,
 501  
      *      IRequestCycle)
 502  
      */
 503  
     protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
 504  
     {
 505  0
         Object objOldValue = cycle.getAttribute(ITableModelSource.TABLE_MODEL_SOURCE_ATTRIBUTE);
 506  0
         cycle.setAttribute(ITableModelSource.TABLE_MODEL_SOURCE_ATTRIBUTE, this);
 507  
         
 508  0
         initialize();
 509  0
         validateValues();
 510  
         
 511  0
         super.renderComponent(writer, cycle);
 512  
         
 513  0
         cycle.setAttribute(ITableModelSource.TABLE_MODEL_SOURCE_ATTRIBUTE,
 514  
                 objOldValue);
 515  0
     }
 516  
 
 517  
     /**
 518  
      * Stores the provided table action.
 519  
      */
 520  
     public void storeTableAction(ITableAction action)
 521  
     {
 522  0
         List actions = getTableActions();
 523  0
         if (actions == null)
 524  
         {
 525  0
             actions = new ArrayList(5);
 526  0
             setTableActions(actions);
 527  
         }
 528  
 
 529  0
         actions.add(action);
 530  0
     }
 531  
 
 532  
     /**
 533  
      * Executes the stored table actions.
 534  
      */
 535  
     public void executeTableActions()
 536  
     {
 537  0
         List actions = getTableActions();
 538  0
         if (actions == null || actions.isEmpty()) return;
 539  
 
 540  
         // save the actions and clear the list
 541  0
         List savedActions = new ArrayList(actions);
 542  0
         actions.clear();
 543  
 
 544  0
         ITableModel objTableModel = getTableModel();
 545  0
         for(Iterator it = savedActions.iterator(); it.hasNext();)
 546  
         {
 547  0
             ITableAction action = (ITableAction) it.next();
 548  0
             action.executeTableAction(objTableModel);
 549  0
         }
 550  
 
 551  
         // ensure that the changes are saved
 552  0
         fireObservedStateChange();
 553  0
     }
 554  
 
 555  
 }