TAP5-2589: improve data source handling if the pager is not shown
authorJochen Kemnade <jochen.kemnade@eddyson.de>
Fri, 13 Oct 2017 09:27:43 +0000 (11:27 +0200)
committerJochen Kemnade <jochen.kemnade@eddyson.de>
Fri, 13 Oct 2017 09:27:43 +0000 (11:27 +0200)
Counting the number of results in an expensive operation for some data sources (especially databases), so we count
only as far as we need to.

tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java
tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/GridRows.java
tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Grid.tml

index 814d5e1..a884e52 100644 (file)
@@ -285,7 +285,7 @@ public class Grid implements GridModel, ClientElement
     }
 
     /**
-     * A version of GridDataSource that caches the availableRows property. This addresses TAPESTRY-2245.
+     * A version of GridDataSource that caches the availableRows and empty properties. This addresses TAPESTRY-2245.
      */
     static class CachingDataSource implements GridDataSource
     {
@@ -295,17 +295,70 @@ public class Grid implements GridModel, ClientElement
 
         private int availableRows;
 
+        private boolean emptyCached;
+
+        private boolean empty;
+
         CachingDataSource(GridDataSource delegate)
         {
             this.delegate = delegate;
         }
 
+        @Override
+        public boolean isEmpty()
+        {
+            if (!emptyCached)
+            {
+                empty = delegate.isEmpty();
+                emptyCached = true;
+                if (empty)
+                {
+                    availableRows = 0;
+                    availableRowsCached = true;
+                }
+            }
+
+            return empty;
+        }
+
+        @Override
+        public int getAvailableRows(int limit)
+        {
+            if (!availableRowsCached)
+            {
+                int result = delegate.getAvailableRows(limit);
+                if (result == 0)
+                {
+                    empty = true;
+                    emptyCached = true;
+                } else {
+                    empty = false;
+                    emptyCached = true;
+                }
+                if (result < limit) {
+                    availableRows = result;
+                    availableRowsCached = true;
+                }
+                return result;
+            } else {
+              return Math.min(availableRows, limit);
+            }
+        }
+
         public int getAvailableRows()
         {
             if (!availableRowsCached)
             {
                 availableRows = delegate.getAvailableRows();
                 availableRowsCached = true;
+                if (availableRows == 0)
+                {
+                    empty = true;
+                    emptyCached = true;
+                } else {
+                  empty = false;
+                  emptyCached = true;
+              }
             }
 
             return availableRows;
@@ -461,7 +514,7 @@ public class Grid implements GridModel, ClientElement
 
         // If there's no rows, display the empty block placeholder.
 
-        return !renderTableIfEmpty && cachingSource.getAvailableRows() == 0 ? empty : null;
+        return !renderTableIfEmpty && cachingSource.isEmpty() ? empty : null;
     }
 
     void cleanupRender()
@@ -492,25 +545,35 @@ public class Grid implements GridModel, ClientElement
         // cached, and therefore access was very inefficient, and sorting was
         // very inconsistent during the processing of the form submission.
 
-        cachingSource = new CachingDataSource(source);
+        int effectiveCurrentPage = getCurrentPage();
 
-        int availableRows = cachingSource.getAvailableRows();
+        int numberOfRowsRequiredToShowCurrentPage = 1 + (effectiveCurrentPage - 1) * rowsPerPage;
+        int numberOfRowsRequiredToFillCurrentPage = effectiveCurrentPage * rowsPerPage;
 
-        if (availableRows == 0)
-            return;
+        cachingSource = new CachingDataSource(source);
+        if (pagerPosition != GridPagerPosition.NONE)
+        {
+            // We're going to render the pager, so we need to determine the total number of rows anyway.
+            // We do that eagerly here so we don't have to perform two count operations; the subsequent
+            // ones will return a cached result
+            cachingSource.getAvailableRows();
+        }
+        int availableRowsWithLimit = cachingSource.getAvailableRows(numberOfRowsRequiredToFillCurrentPage);
 
-        int maxPage = ((availableRows - 1) / rowsPerPage) + 1;
+        if (availableRowsWithLimit == 0)
+            return;
 
         // This captures when the number of rows has decreased, typically due to deletions.
 
-        int effectiveCurrentPage = getCurrentPage();
-
-        if (effectiveCurrentPage > maxPage)
+        if (numberOfRowsRequiredToShowCurrentPage > availableRowsWithLimit)
+        {
+            int maxPage = ((availableRowsWithLimit - 1) / rowsPerPage) + 1;
             effectiveCurrentPage = maxPage;
+        }
 
         int startIndex = (effectiveCurrentPage - 1) * rowsPerPage;
 
-        int endIndex = Math.min(startIndex + rowsPerPage - 1, availableRows - 1);
+        int endIndex = Math.min(startIndex + rowsPerPage - 1, availableRowsWithLimit - 1);
 
         cachingSource.prepare(startIndex, endIndex, sortModel.getSortConstraints());
     }
@@ -520,7 +583,7 @@ public class Grid implements GridModel, ClientElement
         // Skip rendering of component (template, body, etc.) when there's nothing to display.
         // The empty placeholder will already have rendered.
 
-        if (cachingSource.getAvailableRows() == 0)
+        if (cachingSource.isEmpty())
             return !renderTableIfEmpty ? false : null;
 
         if (inPlace && zone == null)
index 8a4fc32..356ac32 100644 (file)
@@ -268,16 +268,20 @@ public class GridRows
     {
         GridDataSource dataSource = gridModel.getDataSource();
 
-        int availableRows = dataSource.getAvailableRows();
+        int numberOfRowsRequiredToShowCurrentPage = 1 + (currentPage - 1) * rowsPerPage;
+        int numberOfRowsRequiredToFillCurrentPage = currentPage * rowsPerPage;
 
-        int maxPages = ((availableRows - 1) / rowsPerPage) + 1;
+        int availableRowsWithLimit = dataSource.getAvailableRows(numberOfRowsRequiredToFillCurrentPage);
 
         // This can sometimes happen when the number of items shifts between requests.
 
-        if (currentPage > maxPages) currentPage = maxPages;
-
+        if (numberOfRowsRequiredToShowCurrentPage > availableRowsWithLimit)
+        {
+            int maxPages = ((availableRowsWithLimit - 1) / rowsPerPage) + 1;
+            currentPage = maxPages;
+        }
         startRow = (currentPage - 1) * rowsPerPage;
-        endRow = Math.min(availableRows - 1, startRow + rowsPerPage - 1);
+        endRow = Math.min(availableRowsWithLimit - 1, startRow + rowsPerPage - 1);
 
         dataRowIndex = startRow;
 
index 2ba5ede..0269c68 100644 (file)
@@ -5,10 +5,10 @@
     <table t:id="table">
         <thead t:id="columns"/>
         <tbody>
-            <t:if test="dataSource.availableRows">
+            <t:unless test="dataSource.empty">
                 <tr t:id="rows"/>
-            </t:if>
-            <t:if test="!dataSource.availableRows">
+            </t:unless>
+            <t:if test="dataSource.empty">
                 <tr>
                     <td colspan="${numberOfProperties}"><t:delegate to="prop:empty"/></td>
                 </tr>