You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2012/03/20 16:48:10 UTC

svn commit: r1302949 - in /myfaces/tobago/trunk: tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/ tobago-core/src/test/java/org/apache/myfaces/tobago/component/ tobago-example/tobago-example-test/src/main/java/org/apache/myfaces/...

Author: lofwyr
Date: Tue Mar 20 15:48:09 2012
New Revision: 1302949

URL: http://svn.apache.org/viewvc?rev=1302949&view=rev
Log:
TOBAGO-1102: Sheet paging isn't usable, when the data model has no defined size (getRowCount() == -1)
 - renaming some getter methods to a more explicit name
 - throw IllegalArgumentException when values are undefined

Modified:
    myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
    myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheetLayout.java
    myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/component/UISheetUnitTest.java
    myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/java/org/apache/myfaces/tobago/example/test/SheetController.java
    myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/webapp/tc/sheet/sheet-auto-height-rows.xhtml
    myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/SheetRenderer.java
    myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago.properties.xml
    myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_de.properties.xml
    myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_es.properties.xml

Modified: myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java?rev=1302949&r1=1302948&r2=1302949&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java (original)
+++ myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java Tue Mar 20 15:48:09 2012
@@ -99,7 +99,7 @@ public abstract class AbstractUISheet ex
   @Override
   public void encodeBegin(FacesContext facesContext) throws IOException {
     SheetState state = getSheetState(facesContext);
-    if (state.getFirst() > -1 && state.getFirst() < getRowCount()) {
+    if (state.getFirst() > -1 && (!hasRowCount() || state.getFirst() < getRowCount())) {
       if (FacesUtils.hasValueBindingOrValueExpression(this, Attributes.FIRST)) {
         FacesUtils.setValueOfBindingOrExpression(facesContext, state.getFirst(), this, Attributes.FIRST);
       } else {
@@ -160,31 +160,72 @@ public abstract class AbstractUISheet ex
     getAttributes().remove(Attributes.WIDTH_LIST_STRING);
   }
 
+  /**
+   * @deprecated The name of this method is ambiguous.
+   * You may use {@link #getLastRowIndexOfCurrentPage()}. Deprecated since 1.5.5.
+   */
   public int getLast() {
     int last = getFirst() + getRows();
     return last < getRowCount() ? last : getRowCount();
   }
 
-  public int getPage() {
-    int first = getFirst() + 1;
+  /**
+   * The rowIndex of the last row on the current page plus one (because of zero based iterating).
+   * @throws IllegalArgumentException If the number of rows in the model returned
+   * by {@link #getRowCount()} is -1 (undefined).
+   */
+  public int getLastRowIndexOfCurrentPage() {
+    if (!hasRowCount()) {
+      throw new IllegalArgumentException(
+          "Can't determine the last row, because the row count of the model is unknown.");
+    }
+    if (isRowsUnlimited()) {
+      return getRowCount();
+    }
+    int last = getFirst() + getRows();
+    return last < getRowCount() ? last : getRowCount();
+  }
+
+  /**
+   * @return returns the current page (based by 0).
+   */
+  public int getCurrentPage() {
     int rows = getRows();
     if (rows == 0) {
-      // avoid division by zero
+      // if the rows are unlimited, there is only one page
       return 0;
     }
-    if ((first % rows) > 0) {
-      return (first / rows) + 1;
+    int first = getFirst();
+    if (hasRowCount() && first >= getRowCount()) {
+      return getPages() - 1; // last page
     } else {
       return (first / rows);
     }
   }
+  
+  /**
+   * @return returns the current page (based by 1).
+   * @deprecated Please use {@link #getCurrentPage()} which returns the value zero-based. Deprecated since 1.5.5.
+   */
+  @Deprecated
+  public int getPage() {
+    return getCurrentPage() + 1;
+  }
 
+  /**
+   * The number of pages to render.
+   * @throws IllegalArgumentException If the number of rows in the model returned
+   * by {@link #getRowCount()} is -1 (undefined).
+   */
   public int getPages() {
-    int rows = getRows();
-    if (rows == 0) {
-      return 0;
+    if (isRowsUnlimited()) {
+      return 1;
+    }
+    if (!hasRowCount()) {
+      throw new IllegalArgumentException(
+          "Can't determine the number of pages, because the row count of the model is unknown.");
     }
-    return getRowCount() / rows + (getRowCount() % rows == 0 ? 0 : 1);
+    return (getRowCount() - 1) / getRows() + 1;
   }
 
   public List<UIComponent> getRenderedChildrenOf(UIColumn column) {
@@ -198,42 +239,103 @@ public abstract class AbstractUISheet ex
     return children;
   }
 
+  /**
+   * @return Is the interval to display starting with the first row?
+   */
   public boolean isAtBeginning() {
     return getFirst() == 0;
   }
 
+  /**
+   * @return Does the data model knows the number of rows?
+   */
   public boolean hasRowCount() {
     return getRowCount() != -1;
   }
 
+  /**
+   * @deprecated The name of this method is ambiguous.
+   * You may use {@link #isRowsUnlimited()}. Deprecated since 1.5.5.
+   */
+  @Deprecated
   public boolean hasRows() {
     return getRows() != 0;
   }
 
+  /**
+   * @return Is the (maximum) number of rows to display set to zero?
+   */
+  public boolean isRowsUnlimited() {
+    return getRows() == 0;
+  }
+
+  /**
+   * @return Should the paging controls be rendered? Either because of the need of paging or because
+   * the show is enforced by {@link #isShowPagingAlways()}
+   */
   public boolean isPagingVisible() {
-    return isShowPagingAlways() || hasRows() && (!hasRowCount() || getRowCount() > getRows());
+    return isShowPagingAlways() || needMoreThanOnePage();
+  }
+
+  /**
+   * @return Is panging needed to display all rows? If the number of rows is unknown this method returns true.
+   */
+  public boolean needMoreThanOnePage() {
+    if (isRowsUnlimited()) {
+      return false;
+    } else if (!hasRowCount()) {
+      return true;
+    } else {
+      return getRowCount() > getRows();
+    }
   }
 
   public abstract boolean isShowPagingAlways();
 
   public boolean isAtEnd() {
     if (!hasRowCount()) {
+      final int old = getRowIndex();
       setRowIndex(getFirst() + getRows() + 1);
-      return !isRowAvailable();
+      final boolean atEnd = !isRowAvailable();
+      setRowIndex(old);
+      return atEnd;
     } else {
-      return getFirst() >= getLastPageIndex();
+      return getFirst() >= getFirstRowIndexOfLastPage();
     }
   }
 
+  /**
+   * @deprecated The name of this method is ambiguous.
+   * You may use {@link #getFirstRowIndexOfLastPage()}. Deprecated since 1.5.5.
+   */
+  @Deprecated
   public int getLastPageIndex() {
-    int rows = getRows();
-    if (rows == 0) {
-      // avoid division by zero
+    if (hasRowCount()) {
+      return getFirstRowIndexOfLastPage();
+    } else {
+      return 0;
+    }
+  }
+
+  /**
+   * Determines the beginning of the last page in the model.
+   * If the number of rows to display on one page is unlimited, the value is 0 (there is only one page).
+   * @return The index of the first row of the last paging page.
+   * @throws IllegalArgumentException If the number of rows in the model returned
+   * by {@link #getRowCount()} is -1 (undefined).
+   */
+  public int getFirstRowIndexOfLastPage() {
+    if (isRowsUnlimited()) {
       return 0;
+    } else if (!hasRowCount()) {
+      throw new IllegalArgumentException(
+          "Can't determine the last page, because the row count of the model is unknown.");
+    } else {
+      int rows = getRows();
+      int rowCount = getRowCount();
+      int tail = rowCount % rows;
+      return rowCount - (tail != 0 ? tail : rows);
     }
-    int rowCount = getRowCount();
-    int tail = rowCount % rows;
-    return rowCount - (tail != 0 ? tail : rows);
   }
 
   @Override
@@ -450,25 +552,24 @@ public abstract class AbstractUISheet ex
 
   public void performPaging(PageActionEvent pageEvent) {
 
-    int first = -1;
+    int first;
 
     if (LOG.isDebugEnabled()) {
       LOG.debug("action = '" + pageEvent.getAction().name() + "'");
     }
 
-    int start;
     switch (pageEvent.getAction()) {
       case FIRST:
         first = 0;
         break;
       case PREV:
-        start = getFirst() - getRows();
-        first = start < 0 ? 0 : start;
+        first = getFirst() - getRows();
+        first = first < 0 ? 0 : first;
         break;
       case NEXT:
         if (hasRowCount()) {
-          start = getFirst() + getRows();
-          first = start > getRowCount() ? getLastPageIndex() : start;
+          first = getFirst() + getRows();
+          first = first > getRowCount() ? getFirstRowIndexOfLastPage() : first;
         } else {
           if (isAtEnd()) {
             first = getFirst();
@@ -478,33 +579,28 @@ public abstract class AbstractUISheet ex
         }
         break;
       case LAST:
-        first = getLastPageIndex();
+        first = getFirstRowIndexOfLastPage();
         break;
       case TO_ROW:
-        start = pageEvent.getValue() - 1;
-        if (start > getLastPageIndex()) {
-          start = getLastPageIndex();
-        } else if (start < 0) {
-          start = 0;
+        first = pageEvent.getValue() - 1;
+        if (hasRowCount() && first > getFirstRowIndexOfLastPage()) {
+          first = getFirstRowIndexOfLastPage();
+        } else if (first < 0) {
+          first = 0;
         }
-        first = start;
         break;
       case TO_PAGE:
-        start = pageEvent.getValue() - 1;
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("start = " + start + "  sheet.getRows() = "
-              + getRows() + " => start = " + (start * getRows()));
-        }
-        start = start * getRows();
-        if (start > getLastPageIndex()) {
-          start = getLastPageIndex();
-        } else if (start < 0) {
-          start = 0;
+        int pageIndex = pageEvent.getValue() - 1;
+        first = pageIndex * getRows();
+        if (hasRowCount() && first > getFirstRowIndexOfLastPage()) {
+          first = getFirstRowIndexOfLastPage();
+        } else if (first < 0) {
+          first = 0;
         }
-        first = start;
         break;
       default:
-        // can't happen
+        // may not happen
+        first = -1;
     }
 
     if (FacesUtils.hasValueBindingOrValueExpression(this, Attributes.FIRST)) {

Modified: myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheetLayout.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheetLayout.java?rev=1302949&r1=1302948&r2=1302949&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheetLayout.java (original)
+++ myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheetLayout.java Tue Mar 20 15:48:09 2012
@@ -293,9 +293,9 @@ public abstract class AbstractUISheetLay
     if (result == null) {
       if (sheet.getCurrentHeight() != null) {
         int first = sheet.getFirst();
-        int rows = sheet.hasRows()
-            ? Math.min(sheet.getRowCount(), first + sheet.getRows()) - first
-            : sheet.getRowCount();
+        int rows = sheet.isRowsUnlimited()
+            ? sheet.getRowCount()
+            : Math.min(sheet.getRowCount(), first + sheet.getRows()) - first;
         Measure heightNeeded = getRowHeight(facesContext, sheet).multiply(rows);
         if (sheet.isShowHeader()) {
           heightNeeded = heightNeeded.add(getHeaderHeight(facesContext, sheet));

Modified: myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/component/UISheetUnitTest.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/component/UISheetUnitTest.java?rev=1302949&r1=1302948&r2=1302949&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/component/UISheetUnitTest.java (original)
+++ myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/component/UISheetUnitTest.java Tue Mar 20 15:48:09 2012
@@ -17,87 +17,367 @@ package org.apache.myfaces.tobago.compon
  * limitations under the License.
  */
 
+import org.apache.myfaces.tobago.internal.mock.faces.AbstractTobagoTestBase;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 import javax.faces.model.ListDataModel;
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
-public class UISheetUnitTest {
-  private String[] nineRows =
-      {"one", "two", "three", "four", "five",
-          "six", "seven", "eight", "nine"};
+public class UISheetUnitTest extends AbstractTobagoTestBase {
 
+  private static final String[] DATA = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
+  private List<String> nineRows;
+  private UISheet data;
+  private UISheet unknown;
+
+  @Before
+  public void setUp() throws Exception {
+    super.setUp();
+    nineRows = new ArrayList<String>();
+    Collections.addAll(nineRows, DATA);
+
+    data = new UISheet();
+    data.setValue(new ListDataModel(nineRows));
+
+    unknown = new UISheet();
+    unknown.setValue(new DataModelWithUnknownRows(nineRows));
+  }
+  
   @Test
-  public void testPage() {
-    List<String> list = new ArrayList<String>();
-    list.addAll(Arrays.asList(nineRows));
-    UISheet data = new UISheet();
-    data.setValue(new ListDataModel(list));
+  public void test5RowsPerPage() {
     data.setRows(5);
-    Assert.assertEquals(1, data.getPage());
-    Assert.assertEquals(2, data.getPages());
+    // having rowCount 9 and 5 rows to display it looks like this (rotated, X X X X X means one page with 5 rows):
+    // X X X X X
+    // X X X X
+    Assert.assertEquals("number of pages", 2, data.getPages());
+  }
 
+  @Test
+  public void test9RowsPerPage() {
     data.setRows(9);
-    Assert.assertEquals(1, data.getPage());
-    Assert.assertEquals(1, data.getPages());
+    // X X X X X X X X X
+    Assert.assertEquals("number of pages", 1, data.getPages());
+  }
 
+  @Test
+  public void test2RowsPerPage() {
     data.setRows(2);
-    Assert.assertEquals(1, data.getPage());
-    Assert.assertEquals(5, data.getPages());
+    // X X
+    // X X
+    // X X
+    // X X
+    // X
+    Assert.assertEquals("number of pages", 5, data.getPages());
+  }
 
+  @Test
+  public void test3RowsPerPage() {
     data.setRows(3);
-    Assert.assertEquals(1, data.getPage());
-    Assert.assertEquals(3, data.getPages());
-
+    // X X X
+    // X X X
+    // X X X
+    Assert.assertEquals("number of pages", 3, data.getPages());
+  }
 
+  @Test
+  public void test1RowPerPage() {
     data.setRows(1);
-    Assert.assertEquals(1, data.getPage());
-    Assert.assertEquals(9, data.getPages());
+    // X
+    // X
+    // X
+    // X
+    // X
+    // X
+    // X
+    // X
+    // X
+    Assert.assertEquals("number of pages", 9, data.getPages());
+  }
+
+  @Test
+  public void testAllRowsPerPage() {
+    data.setRows(0); // zero means all
+    // X X X X X X X X X
+    Assert.assertEquals("number of pages", 1, data.getPages());
+  }
 
+  @Test
+  public void testCurrentPageRows5() {
     data.setRows(5);
-    data.setFirst(5);
-    Assert.assertEquals(2, data.getPage());
+    // initially first = 0
+    // * X X X X
+    // X X X X
+    Assert.assertEquals("current page", 0, data.getCurrentPage());
+    Assert.assertEquals("is at beginning", true, data.isAtBeginning());
 
-    data.setRows(9);
-    data.setFirst(6);
-    Assert.assertEquals(1, data.getPage());
+    data.setFirst(5);
+    // now we set the first (show as an F)
+    // X X X X X
+    // * X X X
+    Assert.assertEquals("current page", 1, data.getCurrentPage());
+    Assert.assertEquals("is at beginning", false, data.isAtBeginning());
 
-    data.setRows(2);
     data.setFirst(0);
-    Assert.assertEquals(1, data.getPage());
+    // * X X X X
+    // X X X X
+    Assert.assertEquals("current page", 0, data.getCurrentPage());
+    Assert.assertEquals("is at beginning", true, data.isAtBeginning());
+
+    data.setFirst(4);
+    // X X X X *
+    // X X X X
+    Assert.assertEquals("current page", 0, data.getCurrentPage());
+    Assert.assertEquals("is at beginning", false, data.isAtBeginning());
+
+    data.setFirst(100);
+    // out of rage
+    Assert.assertEquals("current page", 1, data.getCurrentPage());
+    Assert.assertEquals("is at beginning", false, data.isAtBeginning());
+  }
 
-    data.setRows(2);
-    data.setFirst(1);
-    Assert.assertEquals(1, data.getPage());
+  @Test
+  public void testCurrentPageRows20() {
+    data.setRows(20); // more rows than data entries
+    // initially first = 0
+    // * X X X X X X X X
+    Assert.assertEquals("current page", 0, data.getCurrentPage());
+    Assert.assertEquals("is at beginning", true, data.isAtBeginning());
 
-    data.setRows(2);
-    data.setFirst(2);
-    Assert.assertEquals(2, data.getPage());
+    data.setFirst(8);
+    // now we set the first (show as an F)
+    // X X X X X X X X *
+    Assert.assertEquals("current page", 0, data.getCurrentPage());
+    Assert.assertEquals("is at beginning", false, data.isAtBeginning());
 
-    data.setRows(2);
-    data.setFirst(3);
-    Assert.assertEquals(2, data.getPage());
+    data.setFirst(0);
+    // * X X X X X X X X
+    Assert.assertEquals("current page", 0, data.getCurrentPage());
+    Assert.assertEquals("is at beginning", true, data.isAtBeginning());
+
+    data.setFirst(100);
+    // out of range
+    Assert.assertEquals("current page", 0, data.getCurrentPage());
+    Assert.assertEquals("is at beginning", false, data.isAtBeginning());
+  }
 
-    data.setRows(2);
-    data.setFirst(6);
-    Assert.assertEquals(4, data.getPage());
-    //TODO enable this
-    /*data.setRows(1);
-    data.setFirst(8);
+  @Test
+  public void testRowData() {
+    data.setRowIndex(0);
+    Assert.assertEquals("one", data.getRowData());
     data.setRowIndex(8);
-    Assert.assertEquals(data.getRowData(), list.get(8));
-    Assert.assertEquals(9, data.getPage());
-    Assert.assertEquals(9, data.getPages());
+    Assert.assertEquals("nine", data.getRowData());
+    data.setRowIndex(9);
+    try {
+      data.getRowData();
+      Assert.fail();
+    } catch (IllegalArgumentException e) {
+      // okay: row is unavailable
+    }
+  }
 
-    list.remove(8);
-    Assert.assertEquals(list.size(), data.getRowCount());
-    data.setFirst(0);
-    Assert.assertEquals(1, data.getPage());
-    Assert.assertEquals(8, data.getPages());
-      */
+  @Test
+  public void testHasRowCount() {
+    Assert.assertEquals("has row count", true, data.hasRowCount());
+    Assert.assertEquals("has row count", false, unknown.hasRowCount());
+  }
+
+  @Test
+  public void testRowsUnlimited() {
+    data.setRows(5);
+    unknown.setRows(5);
+
+    Assert.assertEquals("unlimited rows", false, data.isRowsUnlimited());
+    Assert.assertEquals("unlimited rows", false, unknown.isRowsUnlimited());
+
+    data.setRows(0);
+    unknown.setRows(0);
+
+    Assert.assertEquals("unlimited rows", true, data.isRowsUnlimited());
+    Assert.assertEquals("unlimited rows", true, unknown.isRowsUnlimited());
+  }
+
+  @Test
+  public void testNeedMoreThanOnePage() {
+
+    // known length:
+
+    data.setRows(0);
+    // * X X X X X X X X
+    Assert.assertEquals("need more than one page", false, data.needMoreThanOnePage());
+
+    data.setRows(5);
+    // * X X X X
+    // X X X X
+    Assert.assertEquals("need more than one page", true, data.needMoreThanOnePage());
+
+    data.setRows(20);
+    // * X X X X X X X X
+    Assert.assertEquals("need more than one page", false, data.needMoreThanOnePage());
+
+    // unknown length:
+
+    unknown.setRows(0);
+    // * X X X X X X X X
+    Assert.assertEquals("need more than one page", false, unknown.needMoreThanOnePage());
+
+    unknown.setRows(5);
+    // * X X X X
+    // X X X X
+    Assert.assertEquals("need more than one page", true, unknown.needMoreThanOnePage());
+
+    unknown.setRows(20);
+    // * X X X X X X X X
+    Assert.assertEquals("need more than one page", true, unknown.needMoreThanOnePage());
+  }
+
+  @Test
+  public void testFirstRowIndexOfLastPage() {
+
+    // known length:
+
+    data.setRows(0);
+    // * X X X X X X X X
+    Assert.assertEquals("first row index of last page", 0, data.getFirstRowIndexOfLastPage());
+
+    data.setRows(5);
+    // * X X X X
+    // X X X X
+    Assert.assertEquals("first row index of last page", 5, data.getFirstRowIndexOfLastPage());
+
+    data.setRows(20);
+    // * X X X X X X X X
+    Assert.assertEquals("first row index of last page", 0, data.getFirstRowIndexOfLastPage());
+
+    // unknown length:
+
+    unknown.setRows(0);
+    // * X X X X X X X X
+    Assert.assertEquals("first row index of last page", 0, unknown.getFirstRowIndexOfLastPage());
+
+    unknown.setRows(5);
+    // * X X X X
+    // X X X X
+    try {
+      unknown.getFirstRowIndexOfLastPage();
+      Assert.fail("first row index of last page");
+    } catch (IllegalArgumentException e) {
+      // okay: last page can't determined
+    }
+
+    unknown.setRows(20);
+    // * X X X X X X X X
+    try {
+      unknown.getFirstRowIndexOfLastPage();
+      Assert.fail("first row index of last page");
+    } catch (IllegalArgumentException e) {
+      // okay: last page can't determined
+    }
+  }
+
+  @Test
+  public void testLastRowIndexOfCurrentPage() {
+
+    // known length:
+
+    data.setRows(0);
+    // * X X X X X X X X
+    Assert.assertEquals("last row index of current page", 9, data.getLastRowIndexOfCurrentPage());
+
+    data.setRows(5);
+    // * X X X X
+    // X X X X
+    Assert.assertEquals("last row index of current page", 5, data.getLastRowIndexOfCurrentPage());
+
+    data.setRows(20);
+    // * X X X X X X X X
+    Assert.assertEquals("last row index of current page", 9, data.getLastRowIndexOfCurrentPage());
+
+    // unknown length:
+
+    unknown.setRows(0);
+    // * X X X X X X X X
+    try {
+      unknown.getLastRowIndexOfCurrentPage();
+      Assert.fail("last row index of current page");
+    } catch (IllegalArgumentException e) {
+      // okay: last row index of current page can't determined
+    }
+
+    unknown.setRows(5);
+    // * X X X X
+    // X X X X
+    try {
+      unknown.getLastRowIndexOfCurrentPage();
+      Assert.fail("last row index of current page");
+    } catch (IllegalArgumentException e) {
+      // okay: last row index of current page can't determined
+    }
+
+    unknown.setRows(20);
+    // * X X X X X X X X
+    try {
+      unknown.getLastRowIndexOfCurrentPage();
+      Assert.fail("last row index of current page");
+    } catch (IllegalArgumentException e) {
+      // okay: last row index of current page can't determined
+    }
+  }
+
+  @Test
+  public void testGetCurrentPageOnUnknown() {
+
+    // unknown length:
+
+    unknown.setRows(0);
+    // * X X X X X X X X
+    Assert.assertEquals("current page", 0, unknown.getCurrentPage());
+
+    unknown.setRows(5);
+    // * X X X X
+    // X X X X
+    Assert.assertEquals("current page", 0, unknown.getCurrentPage());
+
+    unknown.setFirst(5);
+    // X X X X X
+    // * X X X
+    Assert.assertEquals("current page", 1, unknown.getCurrentPage());
+  }
+
+  @Test
+  public void testGetPagesOnUnknown() {
+
+    // unknown length:
+
+    unknown.setRows(0);
+    // * X X X X X X X X
+    Assert.assertEquals("pages", 1, unknown.getPages());
+
+    unknown.setRows(5);
+    // * X X X X
+    // X X X X
+    try {
+      unknown.getPages();
+      Assert.fail("pages");
+    } catch (IllegalArgumentException e) {
+      // okay: pages can't determined
+    }
+  }
+
+  @Test
+  public void testDynamicRemoval() {
+    nineRows.remove(0);
+    nineRows.remove(0);
+    nineRows.remove(0);
+    data.setRows(5);
+    Assert.assertEquals(2, data.getPages());
+    nineRows.remove(0);
+    nineRows.remove(0);
+    nineRows.remove(0);
+    Assert.assertEquals(1, data.getPages());
   }
 
   @Test
@@ -106,4 +386,16 @@ public class UISheetUnitTest {
     Assert.assertEquals("comp1:comp2", new UISheet().stripRowIndex("comp1:comp2"));
   }
 
+  private static class DataModelWithUnknownRows extends ListDataModel {
+
+    public DataModelWithUnknownRows(List list) {
+      super(list);
+    }
+
+    @Override
+    public int getRowCount() {
+      return -1;
+    }
+  }
+  
 }

Modified: myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/java/org/apache/myfaces/tobago/example/test/SheetController.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/java/org/apache/myfaces/tobago/example/test/SheetController.java?rev=1302949&r1=1302948&r2=1302949&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/java/org/apache/myfaces/tobago/example/test/SheetController.java (original)
+++ myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/java/org/apache/myfaces/tobago/example/test/SheetController.java Tue Mar 20 15:48:09 2012
@@ -29,7 +29,7 @@ public class SheetController {
 
   private SolarObject[] solarArray = SolarObject.getArray();
   private SolarObject[] solarArray3 = init3();
-  private DataModel undefined = new UndefinedRowCountDataModel(solarArray3);
+  private DataModel undefined = new UndefinedRowCountDataModel(solarArray);
 
   public SheetController() {
     init3();

Modified: myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/webapp/tc/sheet/sheet-auto-height-rows.xhtml
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/webapp/tc/sheet/sheet-auto-height-rows.xhtml?rev=1302949&r1=1302948&r2=1302949&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/webapp/tc/sheet/sheet-auto-height-rows.xhtml (original)
+++ myfaces/tobago/trunk/tobago-example/tobago-example-test/src/main/webapp/tc/sheet/sheet-auto-height-rows.xhtml Tue Mar 20 15:48:09 2012
@@ -28,7 +28,7 @@
     <tc:box>
 
       <f:facet name="layout">
-        <tc:gridLayout rows="auto;auto;auto;auto" rowSpacing="50px" margin="50px"/>
+        <tc:gridLayout rows="auto;auto;auto;100px" rowSpacing="50px" margin="50px"/>
       </f:facet>
 
       <!-- fixed rows = 3 - show exactly 3 rows, but have more -->
@@ -93,7 +93,7 @@
     <!--
      height: sheet1 + 7 * (20 + 1)
     -->
-    <tc:script onload="TobagoAssert.assertLayout('page:sheet4', 56, 526, 487, 247);"/>
+    <tc:script onload="TobagoAssert.assertLayout('page:sheet4', 56, 526, 487, 100);"/>
 
   </tc:page>
 </f:view>

Modified: myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/SheetRenderer.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/SheetRenderer.java?rev=1302949&r1=1302948&r2=1302949&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/SheetRenderer.java (original)
+++ myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/SheetRenderer.java Tue Mar 20 15:48:09 2012
@@ -288,7 +288,7 @@ public class SheetRenderer extends Layou
     boolean odd = false;
     boolean emptySheet = true;
     // rows = 0 means: show all
-    final int last = sheet.hasRows() ? sheet.getFirst() + sheet.getRows() : Integer.MAX_VALUE;
+    final int last = sheet.isRowsUnlimited() ? Integer.MAX_VALUE : sheet.getFirst() + sheet.getRows();
     for (int rowIndex = sheet.getFirst(); rowIndex < last; rowIndex++) {
       sheet.setRowIndex(rowIndex);
       if (!sheet.isRowAvailable()) {
@@ -508,30 +508,24 @@ public class SheetRenderer extends Layou
 
   private String createSheetPagingInfo(UISheet sheet, FacesContext facesContext, String pagerCommandId, boolean row) {
     String sheetPagingInfo;
-    if (sheet.getRowCount() > 0) {
+    if (sheet.getRowCount() != 0) {
       Locale locale = facesContext.getViewRoot().getLocale();
-      int first;
-      int last;
-      if (row) {
-        first = sheet.getFirst() + 1;
-        last = sheet.getLast();
-      } else { // page
-        first = sheet.getPage();
-        last = sheet.getPages();
-      }
-      String key;
-      if (first != last) {
-        key = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago",
-            "sheetPagingInfo" + (row ? "Rows" : "Pages"));
-      } else {
-        key = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago",
-            "sheetPagingInfoSingle" + (row ? "Row" : "Page"));
-      }
-      MessageFormat detail = new MessageFormat(key, locale);
+      int first = row ? sheet.getFirst() + 1 : sheet.getCurrentPage() + 1;
+      int last = sheet.hasRowCount()
+          ? row ? sheet.getLastRowIndexOfCurrentPage() : sheet.getPages()
+          : -1;
+      final boolean unknown = !sheet.hasRowCount();
+      final String key = "sheetPagingInfo"
+          + (unknown ? "Undefined" : "")
+          + (first == last ? "Single" : "") 
+          + (row ? "Row" : "Page")
+          + (first == last ? "" : "s"); // plural
+      final String message = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", key);
+      final MessageFormat detail = new MessageFormat(message, locale);
       Object[] args = {
           first,
-          last,
-          sheet.getRowCount(),
+          last == -1 ? "?" : last,
+          unknown ? "" : sheet.getRowCount(),
           pagerCommandId + ComponentUtils.SUB_SEPARATOR + "text"
       };
       sheetPagingInfo = detail.format(args);
@@ -867,7 +861,7 @@ public class SheetRenderer extends Layou
     int linkCount = ComponentUtils.getIntAttribute(sheet, Attributes.DIRECT_LINK_COUNT);
     linkCount--;  // current page needs no link
     ArrayList<Integer> prevs = new ArrayList<Integer>(linkCount);
-    int page = sheet.getPage();
+    int page = sheet.getCurrentPage() + 1;
     for (int i = 0; i < linkCount && page > 1; i++) {
       page--;
       if (page > 0) {
@@ -876,8 +870,9 @@ public class SheetRenderer extends Layou
     }
 
     ArrayList<Integer> nexts = new ArrayList<Integer>(linkCount);
-    page = sheet.getPage();
-    for (int i = 0; i < linkCount && page < sheet.getPages(); i++) {
+    page = sheet.getCurrentPage() + 1;
+    final int pages = sheet.hasRowCount() || sheet.isRowsUnlimited() ? sheet.getPages() : Integer.MAX_VALUE;
+    for (int i = 0; i < linkCount && page < pages; i++) {
       page++;
       if (page > 1) {
         nexts.add(page);
@@ -920,7 +915,7 @@ public class SheetRenderer extends Layou
       name = prev.toString();
       writeLinkElement(writer, sheet, name, name, pagerCommandId, true);
     }
-    name = Integer.toString(sheet.getPage());
+    name = Integer.toString(sheet.getCurrentPage() + 1);
     writeLinkElement(writer, sheet, name, name, pagerCommandId, false);
 
     for (Integer next : nexts) {
@@ -928,13 +923,13 @@ public class SheetRenderer extends Layou
       writeLinkElement(writer, sheet, name, name, pagerCommandId, true);
     }
 
-    skip = nexts.size() > 0 ? nexts.get(nexts.size() - 1) : sheet.getPages();
-    if (skip < sheet.getPages()) {
+    skip = nexts.size() > 0 ? nexts.get(nexts.size() - 1) : pages;
+    if (skip < pages) {
       skip += linkCount / 2;
       skip++;
       name = "...";
-      if (skip > sheet.getPages()) {
-        skip = sheet.getPages();
+      if (skip > pages) {
+        skip = pages;
         if ((nexts.get(nexts.size() - 1)) == skip - 1) {
           name = Integer.toString(skip);
         }

Modified: myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago.properties.xml
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago.properties.xml?rev=1302949&r1=1302948&r2=1302949&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago.properties.xml (original)
+++ myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago.properties.xml Tue Mar 20 15:48:09 2012
@@ -43,10 +43,14 @@
   <entry key="sheetDescending">Descending</entry>
   <entry key="sheetPagingInfoRows">Rows &lt;span id="{3}"&gt;{0}&lt;/span&gt; to {1} of {2}</entry>
   <entry key="sheetPagingInfoSingleRow">Row &lt;span id="{3}"&gt;{0}&lt;/span&gt; of {2}</entry>
+  <entry key="sheetPagingInfoUndefinedRows">Rows &lt;span id="{3}"&gt;{0}&lt;/span&gt; to {1}</entry>
+  <entry key="sheetPagingInfoUndefinedSingleRow">Row &lt;span id="{3}"&gt;{0}&lt;/span&gt;</entry>
   <entry key="sheetPagingInfoEmptyRow">No rows available</entry>
   <entry key="sheetPagingInfoRowPagingTip">Click here, to change the first displayed row</entry>
   <entry key="sheetPagingInfoPages">Page &lt;span id="{3}"&gt;{0}&lt;/span&gt; of {1}</entry>
   <entry key="sheetPagingInfoSinglePage">Page &lt;span id="{3}"&gt;{0}&lt;/span&gt; of {1}</entry>
+  <entry key="sheetPagingInfoUndefinedPages">Page &lt;span id="{3}"&gt;{0}&lt;/span&gt;</entry>
+  <entry key="sheetPagingInfoUndefinedSinglePage">Page &lt;span id="{3}"&gt;{0}&lt;/span&gt;</entry>
   <entry key="sheetPagingInfoEmptyPage"></entry>
   <entry key="sheetPagingInfoPagePagingTip">Click here, to change the displayed page</entry>
   <entry key="sheetMenuToggleselect">Invert selections</entry>

Modified: myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_de.properties.xml
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_de.properties.xml?rev=1302949&r1=1302948&r2=1302949&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_de.properties.xml (original)
+++ myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_de.properties.xml Tue Mar 20 15:48:09 2012
@@ -44,11 +44,15 @@
   <entry key="sheetAscending">aufsteigend sortiert</entry>
   <entry key="sheetDescending">absteigend sortiert</entry>
   <entry key="sheetPagingInfoRows">Zeilen &lt;span id="{3}"&gt;{0}&lt;/span&gt; bis {1} von {2}</entry>
-  <entry key="sheetPagingInfoSingleRow">Zeilen &lt;span id="{3}">{0}&lt;/span> von {2}</entry>
+  <entry key="sheetPagingInfoSingleRow">Zeile &lt;span id="{3}">{0}&lt;/span> von {2}</entry>
+  <entry key="sheetPagingInfoUndefinedRows">Zeilen &lt;span id="{3}"&gt;{0}&lt;/span&gt; bis {1}</entry>
+  <entry key="sheetPagingInfoUndefinedSingleRow">Zeile &lt;span id="{3}">{0}&lt;/span></entry>
   <entry key="sheetPagingInfoEmptyRow">Keine Zeilen verf&amp;uuml;gbar</entry>
   <entry key="sheetPagingInfoRowPagingTip">Hier klicken, zum Eingeben der ersten anzuzeigenden Zeile.</entry>
   <entry key="sheetPagingInfoPages">Seite &lt;span id="{3}">{0}&lt;/span&gt; von {1}</entry>
   <entry key="sheetPagingInfoSinglePage">Seite &lt;span id="{3}"&gt;{0}&lt;/span&gt; von {1}</entry>
+  <entry key="sheetPagingInfoUndefinedPages">Seite &lt;span id="{3}">{0}&lt;/span&gt;</entry>
+  <entry key="sheetPagingInfoUndefinedSinglePage">Seite &lt;span id="{3}"&gt;{0}&lt;/span&gt;</entry>
   <entry key="sheetPagingInfoEmptyPage"></entry>
   <entry key="sheetPagingInfoPagePagingTip">Hier klicken, zum Eingeben der anzuzeigenden Seite.</entry>
   <entry key="sheetMenuToggleselect">Markierung umkehren</entry>

Modified: myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_es.properties.xml
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_es.properties.xml?rev=1302949&r1=1302948&r2=1302949&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_es.properties.xml (original)
+++ myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/property/tobago_es.properties.xml Tue Mar 20 15:48:09 2012
@@ -43,10 +43,14 @@
   <entry key="sheetDescending">Descendente</entry>
   <entry key="sheetPagingInfoRows">Registros &lt;span id="{3}"&gt;{0}&lt;/span&gt; {1} de {2}</entry>
   <entry key="sheetPagingInfoSingleRow">Registro &lt;span id="{3}"&gt;{0}&lt;/span&gt; de {2}</entry>
+  <entry key="sheetPagingInfoUndefinedRows">Registros &lt;span id="{3}"&gt;{0}&lt;/span&gt; {1}</entry>
+  <entry key="sheetPagingInfoUndefinedSingleRow">Registro &lt;span id="{3}"&gt;{0}&lt;/span&gt;</entry>
   <entry key="sheetPagingInfoEmptyRow">No hay registros disponibles</entry>
   <entry key="sheetPagingInfoRowPagingTip">Click aqui, para cambiar el primer registro desplegado.</entry>
   <entry key="sheetPagingInfoPages">Página &lt;span id="{3}"&gt;{0}&lt;/span&gt; de {1}</entry>
   <entry key="sheetPagingInfoSinglePage">Página &lt;span id="{3}"&gt;{0}&lt;/span&gt; de {1}</entry>
+  <entry key="sheetPagingInfoUndefinedPages">Página &lt;span id="{3}"&gt;{0}&lt;/span&gt;</entry>
+  <entry key="sheetPagingInfoUndefinedSinglePage">Página &lt;span id="{3}"&gt;{0}&lt;/span&gt;</entry>
   <entry key="sheetPagingInfoEmptyPage"></entry>
   <entry key="sheetPagingInfoPagePagingTip">Click aquí, para cambiar el nombre desplegado.</entry>
   <entry key="sheetMenuToggleselect">Invertir seleccion</entry>