Coverage Report - org.equanda.tapestry5.components.JSPagedLoop
 
Classes in this File Line Coverage Branch Coverage Complexity
JSPagedLoop
0%
0/75
0%
0/26
0
JSPagedLoop$FormSubmitAction
0%
0/3
N/A
0
JSPagedLoop$SetupAction
0%
0/6
N/A
0
 
 1  
 /**
 2  
  * This file is part of the equanda project.
 3  
  *
 4  
  * The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
 7  
  *
 8  
  * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
 9  
  * ANY KIND, either express or implied. See the License for the specific language governing rights and
 10  
  * limitations under the License.
 11  
  *
 12  
  * Alternatively, the contents of this file may be used under the terms of
 13  
  * either the GNU General Public License Version 2 or later (the "GPL"), or
 14  
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 15  
  * in which case the provisions of the GPL or the LGPL are applicable instead
 16  
  * of those above. If you wish to allow use of your version of this file only
 17  
  * under the terms of either the GPL or the LGPL, and not to allow others to
 18  
  * use your version of this file under the terms of the MPL, indicate your
 19  
  * decision by deleting the provisions above and replace them with the notice
 20  
  * and other provisions required by the GPL or the LGPL. If you do not delete
 21  
  * the provisions above, a recipient may use your version of this file under
 22  
  * the terms of any one of the MPL, the GPL or the LGPL.
 23  
  */
 24  
 
 25  
 package org.equanda.tapestry5.components;
 26  
 
 27  
 import org.apache.tapestry.commons.components.Hidden;
 28  
 import org.apache.tapestry5.*;
 29  
 import org.apache.tapestry5.annotations.*;
 30  
 import org.apache.tapestry5.corelib.components.Delegate;
 31  
 import org.apache.tapestry5.corelib.components.Loop;
 32  
 import org.apache.tapestry5.ioc.annotations.Inject;
 33  
 import org.apache.tapestry5.services.ComponentDefaultProvider;
 34  
 import org.apache.tapestry5.services.Environment;
 35  
 import org.apache.tapestry5.services.FormSupport;
 36  
 import org.apache.tapestry5.services.Request;
 37  
 import org.equanda.tapestry5.base.ObjectCreatorDelegate;
 38  
 import org.slf4j.Logger;
 39  
 
 40  
 import java.util.ArrayList;
 41  
 import java.util.Collection;
 42  
 import java.util.List;
 43  
 
 44  
 /**
 45  
  * Paged Loop component with Java Script Pager
 46  
  *
 47  
  * @author <a href="mailto:vladimir.tkachenko@gmail.com">Vladimir Tkachenko</a>
 48  
  */
 49  0
 public class JSPagedLoop
 50  
     implements ClientElement
 51  
 {
 52  
     @Environmental
 53  
     private RenderSupport renderSupport;
 54  
 
 55  
     @Inject
 56  
     private Environment environment;
 57  
 
 58  
     @Inject
 59  
     private Logger logger;
 60  
 
 61  
     @Inject
 62  
     private ComponentResources resources;
 63  
 
 64  
     @Environmental
 65  
     private FormSupport formSupport;
 66  
 
 67  
     @Inject
 68  
     private Request request;
 69  
 
 70  
     @Inject
 71  
     private ComponentDefaultProvider componentDefaultProvider;
 72  
 
 73  
     @Parameter( value = "prop:componentResources.id", defaultPrefix = BindingConstants.LITERAL )
 74  
     private String clientId;
 75  
 
 76  
     /**
 77  
      * The element to render. If not null, then the loop will render the indicated element around its body (on each pass
 78  
      * through the loop). The default is derived from the component template.
 79  
      */
 80  
     @Parameter( value = "prop:componentResources.elementName", defaultPrefix = BindingConstants.LITERAL )
 81  
     private String elementName;
 82  
 
 83  
     /**
 84  
      * Defines the collection of values for the loop to iterate over.
 85  
      */
 86  
     @Parameter( required = true, principal = true )
 87  
     private Collection<Object> source;
 88  
 
 89  
     @Persist
 90  
     private List<Object> addedRowsList;
 91  
 
 92  
     @Persist
 93  
     private List<Object> pagedSource;
 94  
 
 95  
     /**
 96  
      * The number of rows of data displayed on each page. If there are more rows than will fit, the Grid will divide up
 97  
      * the rows into "pages" and (normally) provide a pager to allow the user to navigate within the overall result set.
 98  
      */
 99  
     @Parameter( "25" )
 100  
     private int rowsPerPage;
 101  
 
 102  
     @Persist
 103  0
     private int currentPage = 1;
 104  
 
 105  
     // @Persist
 106  
     private int addedRowCount;
 107  
 
 108  
     /**
 109  
      * The current value, set before the component renders its body.
 110  
      */
 111  
     @SuppressWarnings( "unused" )
 112  
     @Parameter
 113  
     private Object value;
 114  
 
 115  
     /**
 116  
      * If true and the Loop is enclosed by a Form, then the normal state saving logic is turned off. Defaults to false,
 117  
      * enabling state saving logic within Forms.
 118  
      */
 119  
     @SuppressWarnings( "unused" )
 120  
     @Parameter
 121  
     private boolean _volatile;
 122  
 
 123  
     @Parameter( defaultPrefix = BindingConstants.PROP )
 124  
     private ObjectCreatorDelegate objectCreatorDelegate;
 125  
 
 126  
     /**
 127  
      * The index into the source items.
 128  
      */
 129  
     @Parameter
 130  
     private int index;
 131  
 
 132  
     @Parameter
 133  
     private int additionalRowCount;
 134  
 
 135  
     /**
 136  
      * Optional primary key converter; if provided and inside a form and not volatile, then each iterated value is
 137  
      * converted and stored into the form.
 138  
      */
 139  
     @SuppressWarnings( "unused" )
 140  
     @Parameter
 141  
     private PrimaryKeyEncoder<?, ?> encoder;
 142  
 
 143  
     @SuppressWarnings( "unused" )
 144  
     @Component( parameters = { "source=pagedSource", "elementName=prop:elementName", "value=inherit:value",
 145  
             "volatile=inherit:volatile", "encoder=inherit:encoder", "index=index",
 146  
             "additionalRowCount=inherit:additionalRowCount" } )
 147  
     private Loop loop;
 148  
 
 149  
     @Component( parameters = { "source=pagedSource", "rowsPerPage=rowsPerPage", "currentPage=currentPage" } )
 150  
     private JSPager internalPager;
 151  
 
 152  
     @Component( id = "CurrentPageHidden", parameters = { "value=currentPage", "translate=translate:integer" } )
 153  
     private Hidden currentPageHidden;
 154  
 
 155  
     @Component( id = "AddedRowsHidden", parameters = { "value=addedRowCount", "translate=translate:integer" } )
 156  
     private Hidden addedRowHidden;
 157  
 
 158  
     @SuppressWarnings( "unused" )
 159  
     @Component( parameters = "to=pagerBottom" )
 160  
     private Delegate pagerBottom;
 161  
 
 162  
     /**
 163  
      * A Block to render instead of the table (and pager, etc.) when the source is empty. The default is simply the text
 164  
      * "There is no data to display". This parameter is used to customize that message, possibly including components to
 165  
      * allow the user to create new objects.
 166  
      */
 167  
     @Parameter( value = "block:empty" )
 168  
     private Block empty;
 169  
 
 170  
     @Parameter( value = "block:bottomBlock" )
 171  
     private Block bottomBlock;
 172  
 
 173  
     @Parameter( value = "message:add.title.label", defaultPrefix = BindingConstants.LITERAL )
 174  
     private String addRowLinkTitle;
 175  
 
 176  
     private String assignedClientId;
 177  
 
 178  
     private List<String> rowIds;
 179  
 
 180  
     private String addedRowCountHiddenName;
 181  
 
 182  0
     static class FormSubmitAction
 183  
         implements ComponentAction<JSPagedLoop>
 184  
     {
 185  
 
 186  
         private static final long serialVersionUID = 456056528334001727L;
 187  
 
 188  
         public void execute( JSPagedLoop component )
 189  
         {
 190  0
             component.processFormAction();
 191  0
         }
 192  
     }
 193  
 
 194  0
     static class SetupAction
 195  
         implements ComponentAction<JSPagedLoop>
 196  
     {
 197  
 
 198  
         private static final long serialVersionUID = 6727709013577574475L;
 199  
         private final String _addedRowCountHiddenName;
 200  
 
 201  
         public SetupAction ( String controlName )
 202  0
         {
 203  0
             _addedRowCountHiddenName = controlName;
 204  0
         }
 205  
 
 206  
         public void execute( JSPagedLoop component )
 207  
         {
 208  0
             component.setupAddedRowCountHiddenName( _addedRowCountHiddenName );
 209  0
         }
 210  
     }
 211  
 
 212  0
     private static final FormSubmitAction FORM_SUBMIT_ACTION = new FormSubmitAction();
 213  
 
 214  
     private void setupAddedRowCountHiddenName( String name )
 215  
     {
 216  0
         addedRowCountHiddenName = name;
 217  0
     }
 218  
 
 219  
     Binding defaultSource()
 220  
     {
 221  0
         return componentDefaultProvider.defaultBinding( "source", resources );
 222  
     }
 223  
 
 224  
     void setSource( Collection<Object> source )
 225  
     {
 226  0
         this.source = source;
 227  0
     }
 228  
 
 229  
     public String getAddRowLinkTitle()
 230  
     {
 231  0
         return addRowLinkTitle;
 232  
     }
 233  
 
 234  
     public int getIndex()
 235  
     {
 236  0
         return index;
 237  
     }
 238  
 
 239  
     public void setIndex( int index )
 240  
     {
 241  0
         this.index = index;
 242  0
     }
 243  
 
 244  
     public String getElementName()
 245  
     {
 246  0
         return elementName;
 247  
     }
 248  
 
 249  
     public Block getBottomBlock()
 250  
     {
 251  0
         return bottomBlock;
 252  
     }
 253  
 
 254  
     public Object getPagerBottom()
 255  
     {
 256  0
         return internalPager;
 257  
     }
 258  
 
 259  
     public List<Object> getPagedSource()
 260  
     {
 261  0
         return pagedSource;
 262  
     }
 263  
 
 264  
     public int getRowsPerPage()
 265  
     {
 266  0
         return rowsPerPage;
 267  
     }
 268  
 
 269  
     public void setRowsPerPage( int rowsPerPage )
 270  
     {
 271  0
         this.rowsPerPage = rowsPerPage;
 272  0
     }
 273  
 
 274  
     public int getCurrentPage()
 275  
     {
 276  0
         return currentPage;
 277  
     }
 278  
 
 279  
     public void setCurrentPage( int currentPage )
 280  
     {
 281  0
         this.currentPage = currentPage;
 282  0
     }
 283  
 
 284  
     public int getAddedRowCount()
 285  
     {
 286  0
         return addedRowCount;
 287  
     }
 288  
 
 289  
     public void setAddedRowCount( int addedRowCount )
 290  
     {
 291  0
         this.addedRowCount = addedRowCount;
 292  0
     }
 293  
 
 294  
     public int getAdditionalRowCount()
 295  
     {
 296  0
         return additionalRowCount;
 297  
     }
 298  
 
 299  
     public void setAdditionalRowCount( int additionalRowCount )
 300  
     {
 301  0
         this.additionalRowCount = additionalRowCount;
 302  0
     }
 303  
 
 304  
     public String getCurrentRowId()
 305  
     {
 306  0
         String newId = renderSupport.allocateClientId( assignedClientId );
 307  0
         String rowId = String.format( "%s_row_%s", assignedClientId, newId.toLowerCase() );
 308  0
         rowIds.add( rowId );
 309  0
         return rowId;
 310  
     }
 311  
 
 312  
     public String getCurrentPageFieldName()
 313  
     {
 314  0
         return currentPageHidden.getClientId();
 315  
     }
 316  
 
 317  
     public String getAddedRowFieldName()
 318  
     {
 319  0
         return addedRowHidden.getClientId();
 320  
     }
 321  
 
 322  
     @SetupRender
 323  
     Object setupRender()
 324  
     {
 325  0
         rowIds = new ArrayList<String>();
 326  0
         assignedClientId = renderSupport.allocateClientId( clientId );
 327  
 
 328  0
         addedRowsList = new ArrayList<Object>();
 329  0
         pagedSource = new ArrayList<Object>();
 330  
 
 331  0
         if ( source != null )
 332  
         {
 333  0
             for ( Object item : source )
 334  
             {
 335  0
                 pagedSource.add( item );
 336  
             }
 337  
         }
 338  
 
 339  0
         if ( objectCreatorDelegate != null )
 340  
         {
 341  0
             for ( int i = 0; i < additionalRowCount; i++ )
 342  
             {
 343  0
                 Object object = objectCreatorDelegate.createObject();
 344  0
                 pagedSource.add( object );
 345  0
                 addedRowsList.add( object );
 346  
             }
 347  
         }
 348  
 
 349  
         // If there's no rows, display the empty block placeholder.
 350  0
         if ( pagedSource.size() == 0 )
 351  
         {
 352  0
             return empty;
 353  
         }
 354  
 
 355  0
         return null;
 356  
     }
 357  
 
 358  
     @BeginRender
 359  
     Object begin( MarkupWriter writer )
 360  
     {
 361  0
         environment.push( JSPagedLoop.class, this );
 362  
 
 363  
         // Skip rendering of component (template, body, etc.) when there's nothing to display.
 364  
         // The empty placeholder will already have rendered.
 365  0
         return pagedSource.size() != 0;
 366  
 
 367  
     }
 368  
 
 369  
     @AfterRender
 370  
     void afterRender()
 371  
     {
 372  0
         formSupport.storeAndExecute( this, new SetupAction( addedRowHidden.getControlName() ) );
 373  0
         formSupport.store( this, FORM_SUBMIT_ACTION );
 374  0
     }
 375  
 
 376  
     @CleanupRender
 377  
     void cleanupRender()
 378  
     {
 379  0
         environment.pop( JSPagedLoop.class );
 380  0
     }
 381  
 
 382  
     void onActionFromAdd()
 383  0
     {/*nothing*/}
 384  
 
 385  
     private void processFormAction()
 386  
     {
 387  0
         int count = 0;
 388  0
         String sCount = null;
 389  
         try
 390  
         {
 391  0
             if ( addedRowCountHiddenName != null )
 392  
             {
 393  0
                 sCount = request.getParameter( addedRowCountHiddenName );
 394  0
                 if ( sCount != null && sCount.trim().length() > 0 )
 395  
                 {
 396  0
                     count = Integer.parseInt( sCount.trim() );
 397  
                 }
 398  
             }
 399  
         }
 400  0
         catch ( Exception e )
 401  
         {
 402  0
             logger.error( "Unable to parse AddedRowCount parameter: " + sCount );
 403  0
         }
 404  
 
 405  0
         if ( objectCreatorDelegate != null )
 406  
         {
 407  0
             for ( int i = 0; i < count && i < addedRowsList.size(); i++ )
 408  
             {
 409  0
                 objectCreatorDelegate.addNewObject( addedRowsList.get( i ) );
 410  
             }
 411  
         }
 412  0
         addedRowsList.clear();
 413  0
         addedRowCount = count;
 414  0
     }
 415  
 
 416  
     /**
 417  
      * Returns a unique id for the element. This value will be unique for any given rendering of a page. This value is
 418  
      * intended for use as the id attribute of the client-side element, and will be used with any DHTML/Ajax related
 419  
      * JavaScript.
 420  
      */
 421  
     public String getClientId()
 422  
     {
 423  0
         return assignedClientId;
 424  
     }
 425  
 
 426  
     public String getFixedClientId()
 427  
     {
 428  0
         return assignedClientId != null ? assignedClientId.toLowerCase() : "";
 429  
     }
 430  
 
 431  
 }