From 7a111fc541dcc507032cdad6799b477df6d4833f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Johannes=20Dahlstr=C3=B6m?= Date: Wed, 23 Jan 2013 13:14:18 +0200 Subject: [PATCH] Merge of (#6160) and (#10470) to Vaadin 7. Cache handling update. Change-Id: I81ba74d457eb484f6f2c350629534ab284ead7b7 --- .../com/vaadin/client/ui/VScrollTable.java | 216 ++++++++++---- .../client/ui/table/TableConnector.java | 7 + server/src/com/vaadin/ui/Table.java | 207 +++++++++---- .../table/EmptyRowsWhenScrolling.java | 275 ++++++++++++++++++ .../treetable/EmptyingTreeTableTest.html | 52 ++++ .../TreeTableCacheOnPartialUpdate.html | 100 +++---- .../TreeTableCacheOnPartialUpdates.java | 46 ++- .../treetable/TreeTableInternalError.html | 61 ++++ .../treetable/TreeTableInternalError.java | 82 ++++++ 9 files changed, 864 insertions(+), 182 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/table/EmptyRowsWhenScrolling.java create mode 100644 uitest/src/com/vaadin/tests/components/treetable/EmptyingTreeTableTest.html create mode 100644 uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.html create mode 100644 uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.java diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index 8553398718..75f5550790 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -539,6 +539,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets, public int serverCacheFirst = -1; public int serverCacheLast = -1; + /** + * In several cases TreeTable depends on the scrollBody.lastRendered being + * 'out of sync' while the update is being done. In those cases the sanity + * check must be performed afterwards. + */ + public boolean postponeSanityCheckForLastRendered; + /** For internal use only. May be removed or replaced in the future. */ public boolean sizeNeedsInit = true; @@ -1375,9 +1382,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets, if (uidl == null || reqRows < 1) { // container is empty, remove possibly existing rows if (firstRow <= 0) { - while (scrollBody.getLastRendered() > scrollBody.firstRendered) { + postponeSanityCheckForLastRendered = true; + while (scrollBody.getLastRendered() > scrollBody + .getFirstRendered()) { scrollBody.unlinkRow(false); } + postponeSanityCheckForLastRendered = false; scrollBody.unlinkRow(false); } return; @@ -1408,6 +1418,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * cache_rate); int lastRowToKeep = (int) (firstRowInViewPort + pageLength + pageLength * cache_rate); + // sanity checks: + if (firstRowToKeep < 0) { + firstRowToKeep = 0; + } + if (lastRowToKeep > totalRows) { + lastRowToKeep = totalRows - 1; + } debug("Client side calculated cache rows to keep: " + firstRowToKeep + "-" + lastRowToKeep); @@ -1999,15 +2016,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets, if (totalRows - 1 > scrollBody.getLastRendered()) { // fetch cache rows int firstInNewSet = scrollBody.getLastRendered() + 1; - rowRequestHandler.setReqFirstRow(firstInNewSet); int lastInNewSet = (int) (firstRowInViewPort + pageLength + cache_rate * pageLength); if (lastInNewSet > totalRows - 1) { lastInNewSet = totalRows - 1; } - rowRequestHandler.setReqRows(lastInNewSet - firstInNewSet - + 1); - rowRequestHandler.deferRowFetch(1); + rowRequestHandler.triggerRowFetch(firstInNewSet, + lastInNewSet - firstInNewSet + 1, 1); } } } @@ -2093,6 +2108,18 @@ public class VScrollTable extends FlowPanel implements HasWidgets, private int reqRows = 0; private boolean isRunning = false; + public void triggerRowFetch(int first, int rows) { + setReqFirstRow(first); + setReqRows(rows); + deferRowFetch(); + } + + public void triggerRowFetch(int first, int rows, int delay) { + setReqFirstRow(first); + setReqRows(rows); + deferRowFetch(delay); + } + public void deferRowFetch() { deferRowFetch(250); } @@ -2119,17 +2146,28 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } } + public int getReqFirstRow() { + return reqFirstRow; + } + public void setReqFirstRow(int reqFirstRow) { if (reqFirstRow < 0) { - reqFirstRow = 0; + this.reqFirstRow = 0; } else if (reqFirstRow >= totalRows) { - reqFirstRow = totalRows - 1; + this.reqFirstRow = totalRows - 1; + } else { + this.reqFirstRow = reqFirstRow; } - this.reqFirstRow = reqFirstRow; } public void setReqRows(int reqRows) { - this.reqRows = reqRows; + if (reqRows < 0) { + this.reqRows = 0; + } else if (reqFirstRow + reqRows > totalRows) { + this.reqRows = totalRows - reqFirstRow; + } else { + this.reqRows = reqRows; + } } @Override @@ -2140,7 +2178,15 @@ public class VScrollTable extends FlowPanel implements HasWidgets, schedule(250); } else { - int firstToBeRendered = scrollBody.firstRendered; + int firstRendered = scrollBody.getFirstRendered(); + int lastRendered = scrollBody.getLastRendered(); + if (lastRendered > totalRows) { + lastRendered = totalRows - 1; + } + boolean rendered = firstRendered >= 0 && lastRendered >= 0; + + int firstToBeRendered = firstRendered; + if (reqFirstRow < firstToBeRendered) { firstToBeRendered = reqFirstRow; } else if (firstRowInViewPort - (int) (cache_rate * pageLength) > firstToBeRendered) { @@ -2149,12 +2195,24 @@ public class VScrollTable extends FlowPanel implements HasWidgets, if (firstToBeRendered < 0) { firstToBeRendered = 0; } + } else if (rendered && firstRendered + 1 < reqFirstRow + && lastRendered + 1 < reqFirstRow) { + // requested rows must fall within the requested rendering + // area + firstToBeRendered = reqFirstRow; + } + if (firstToBeRendered + reqRows < firstRendered) { + // must increase the required row count accordingly, + // otherwise may leave a gap and the rows beyond will get + // removed + setReqRows(firstRendered - firstToBeRendered); } - int lastToBeRendered = scrollBody.lastRendered; + int lastToBeRendered = lastRendered; + int lastReqRow = reqFirstRow + reqRows - 1; - if (reqFirstRow + reqRows - 1 > lastToBeRendered) { - lastToBeRendered = reqFirstRow + reqRows - 1; + if (lastReqRow > lastToBeRendered) { + lastToBeRendered = lastReqRow; } else if (firstRowInViewPort + pageLength + pageLength * cache_rate < lastToBeRendered) { lastToBeRendered = (firstRowInViewPort + pageLength + (int) (pageLength * cache_rate)); @@ -2163,14 +2221,36 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } // due Safari 3.1 bug (see #2607), verify reqrows, original // problem unknown, but this should catch the issue - if (reqFirstRow + reqRows - 1 > lastToBeRendered) { - reqRows = lastToBeRendered - reqFirstRow; + if (lastReqRow > lastToBeRendered) { + setReqRows(lastToBeRendered - reqFirstRow); } + } else if (rendered && lastRendered - 1 > lastReqRow + && firstRendered - 1 > lastReqRow) { + // requested rows must fall within the requested rendering + // area + lastToBeRendered = lastReqRow; + } + + if (lastToBeRendered > totalRows) { + lastToBeRendered = totalRows - 1; + } + if (reqFirstRow < firstToBeRendered + || (reqFirstRow > firstToBeRendered && (reqFirstRow < firstRendered || reqFirstRow > lastRendered + 1))) { + setReqFirstRow(firstToBeRendered); + } + if (lastRendered < lastToBeRendered + && lastRendered + reqRows < lastToBeRendered) { + // must increase the required row count accordingly, + // otherwise may leave a gap and the rows after will get + // removed + setReqRows(lastToBeRendered - lastRendered); + } else if (lastToBeRendered >= firstRendered + && reqFirstRow + reqRows < firstRendered) { + setReqRows(lastToBeRendered - lastRendered); } client.updateVariable(paintableId, "firstToBeRendered", firstToBeRendered, false); - client.updateVariable(paintableId, "lastToBeRendered", lastToBeRendered, false); // remember which firstvisible we requested, in case the server @@ -2191,10 +2271,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } } - public int getReqFirstRow() { - return reqFirstRow; - } - /** * Sends request to refresh content at this position. */ @@ -3991,6 +4067,24 @@ public class VScrollTable extends FlowPanel implements HasWidgets, setElement(container); } + public void setLastRendered(int lastRendered) { + if (totalRows >= 0 && lastRendered > totalRows) { + VConsole.log("setLastRendered: " + this.lastRendered + " -> " + + lastRendered); + this.lastRendered = totalRows - 1; + } else { + this.lastRendered = lastRendered; + } + } + + public int getLastRendered() { + return lastRendered; + } + + public int getFirstRendered() { + return firstRendered; + } + public VScrollTableRow getRowByRowIndex(int indexInTable) { int internalIndex = indexInTable - firstRendered; if (internalIndex >= 0 && internalIndex < renderedRows.size()) { @@ -4045,7 +4139,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, public void renderInitialRows(UIDL rowData, int firstIndex, int rows) { firstRendered = firstIndex; - lastRendered = firstIndex + rows - 1; + setLastRendered(firstIndex + rows - 1); final Iterator it = rowData.getChildIterator(); aligns = tHead.getColumnAlignments(); while (it.hasNext()) { @@ -4065,7 +4159,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, while (it.hasNext()) { final VScrollTableRow row = prepareRow((UIDL) it.next()); addRow(row); - lastRendered++; + setLastRendered(lastRendered + 1); } fixSpacers(); } else if (firstIndex + rows == firstRendered) { @@ -4081,19 +4175,27 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } } else { // completely new set of rows + + // there can't be sanity checks for last rendered within this + // while loop regardless of what has been set previously, so + // change it temporarily to true and then return the original + // value + boolean temp = postponeSanityCheckForLastRendered; + postponeSanityCheckForLastRendered = true; while (lastRendered + 1 > firstRendered) { unlinkRow(false); } - final VScrollTableRow row = prepareRow((UIDL) it.next()); + postponeSanityCheckForLastRendered = temp; + VScrollTableRow row = prepareRow((UIDL) it.next()); firstRendered = firstIndex; - lastRendered = firstIndex - 1; + setLastRendered(firstIndex - 1); addRow(row); - lastRendered++; + setLastRendered(lastRendered + 1); setContainerHeight(); fixSpacers(); while (it.hasNext()) { addRow(prepareRow((UIDL) it.next())); - lastRendered++; + setLastRendered(lastRendered + 1); } fixSpacers(); } @@ -4129,14 +4231,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * not waste time rendering a set of rows that will never be * visible... */ - rowRequestHandler.setReqFirstRow(reactFirstRow); - rowRequestHandler.setReqRows(reactLastRow - reactFirstRow + 1); - rowRequestHandler.deferRowFetch(1); + rowRequestHandler.triggerRowFetch(reactFirstRow, reactLastRow + - reactFirstRow + 1, 1); } else if (lastRendered < reactLastRow) { // get some cache rows below visible area - rowRequestHandler.setReqFirstRow(lastRendered + 1); - rowRequestHandler.setReqRows(reactLastRow - lastRendered); - rowRequestHandler.deferRowFetch(1); + rowRequestHandler.triggerRowFetch(lastRendered + 1, + reactLastRow - lastRendered, 1); } else if (firstRendered > reactFirstRow) { /* * Branch for fetching cache above visible area. @@ -4146,9 +4246,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * some rare situations the table may make two cache visits to * server. */ - rowRequestHandler.setReqFirstRow(reactFirstRow); - rowRequestHandler.setReqRows(firstRendered - reactFirstRow); - rowRequestHandler.deferRowFetch(1); + rowRequestHandler.triggerRowFetch(reactFirstRow, firstRendered + - reactFirstRow, 1); } } @@ -4172,7 +4271,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, final VScrollTableRow row = prepareRow((UIDL) it.next()); addRow(row); insertedRows.add(row); - lastRendered++; + if (postponeSanityCheckForLastRendered) { + lastRendered++; + } else { + setLastRendered(lastRendered + 1); + } } fixSpacers(); } else if (firstIndex + rows == firstRendered) { @@ -4194,7 +4297,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, VScrollTableRow row = prepareRow((UIDL) it.next()); insertRowAt(row, ix); insertedRows.add(row); - lastRendered++; + if (postponeSanityCheckForLastRendered) { + lastRendered++; + } else { + setLastRendered(lastRendered + 1); + } ix++; } fixSpacers(); @@ -4307,7 +4414,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, firstRendered++; } else { actualIx = renderedRows.size() - 1; - lastRendered--; + if (postponeSanityCheckForLastRendered) { + --lastRendered; + } else { + setLastRendered(lastRendered - 1); + } } if (actualIx >= 0) { unlinkRowAtActualIndex(actualIx); @@ -4323,6 +4434,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } if (firstRendered > firstIndex && firstRendered < firstIndex + count) { + count = count - (firstRendered - firstIndex); firstIndex = firstRendered; } int lastIndex = firstIndex + count - 1; @@ -4331,7 +4443,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } for (int ix = lastIndex; ix >= firstIndex; ix--) { unlinkRowAtActualIndex(actualIndex(ix)); - lastRendered--; + if (postponeSanityCheckForLastRendered) { + // partialUpdate handles sanity check later + lastRendered--; + } else { + setLastRendered(lastRendered - 1); + } } fixSpacers(); } @@ -4352,7 +4469,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } for (int ix = renderedRows.size() - 1; ix >= index; ix--) { unlinkRowAtActualIndex(actualIndex(ix)); - lastRendered--; + setLastRendered(lastRendered - 1); } fixSpacers(); } @@ -4538,14 +4655,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } } - public int getLastRendered() { - return lastRendered; - } - - public int getFirstRendered() { - return firstRendered; - } - public void moveCol(int oldIndex, int newIndex) { // loop all rows and move given index to its new place @@ -5911,8 +6020,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets, client.updateVariable(paintableId, "pagelength", pageLength, false); if (!rendering) { - int currentlyVisible = scrollBody.lastRendered - - scrollBody.firstRendered; + int currentlyVisible = scrollBody.getLastRendered() + - scrollBody.getFirstRendered(); if (currentlyVisible < pageLength && currentlyVisible < totalRows) { // shake scrollpanel to fill empty space @@ -6397,10 +6506,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } if (postLimit > lastRendered) { // need some rows to the end of the rendered area - rowRequestHandler.setReqFirstRow(lastRendered + 1); - rowRequestHandler.setReqRows((int) ((firstRowInViewPort - + pageLength + pageLength * cache_rate) - lastRendered)); - rowRequestHandler.deferRowFetch(); + int reqRows = (int) ((firstRowInViewPort + pageLength + pageLength + * cache_rate) - lastRendered); + rowRequestHandler.triggerRowFetch(lastRendered + 1, reqRows); } } diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index dedcab84fa..c967642059 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -165,12 +165,19 @@ public class TableConnector extends AbstractHasComponentsConnector implements UIDL partialRowAdditions = uidl.getChildByTagName("prows"); UIDL partialRowUpdates = uidl.getChildByTagName("urows"); if (partialRowUpdates != null || partialRowAdditions != null) { + getWidget().postponeSanityCheckForLastRendered = true; // we may have pending cache row fetch, cancel it. See #2136 getWidget().rowRequestHandler.cancel(); getWidget().updateRowsInBody(partialRowUpdates); getWidget().addAndRemoveRows(partialRowAdditions); + + // sanity check (in case the value has slipped beyond the total + // amount of rows) + getWidget().scrollBody.setLastRendered(getWidget().scrollBody + .getLastRendered()); } else { + getWidget().postponeSanityCheckForLastRendered = false; UIDL rowData = uidl.getChildByTagName("rows"); if (rowData != null) { // we may have pending cache row fetch, cancel it. See #2136 diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java index a9632da038..641c0ff1f6 100644 --- a/server/src/com/vaadin/ui/Table.java +++ b/server/src/com/vaadin/ui/Table.java @@ -1645,6 +1645,9 @@ public class Table extends AbstractSelect implements Action.Container, if (rows > 0) { pageBufferFirstIndex = firstIndex; } + if (getPageLength() != 0) { + removeUnnecessaryRows(); + } setRowCacheInvalidated(true); markAsDirty(); @@ -1710,6 +1713,47 @@ public class Table extends AbstractSelect implements Action.Container, } + /** + * Removes rows that fall outside the required cache. + */ + private void removeUnnecessaryRows() { + int minPageBufferIndex = getMinPageBufferIndex(); + int maxPageBufferIndex = getMaxPageBufferIndex(); + + int maxBufferSize = maxPageBufferIndex - minPageBufferIndex + 1; + + /* + * Number of rows that were previously cached. This is not necessarily + * the same as pageLength if we do not have enough rows in the + * container. + */ + int currentlyCachedRowCount = pageBuffer[CELL_ITEMID].length; + + if (currentlyCachedRowCount <= maxBufferSize) { + // removal unnecessary + return; + } + + /* Figure out which rows to get rid of. */ + int firstCacheRowToRemoveInPageBuffer = -1; + if (minPageBufferIndex > pageBufferFirstIndex) { + firstCacheRowToRemoveInPageBuffer = pageBufferFirstIndex; + } else if (maxPageBufferIndex < pageBufferFirstIndex + + currentlyCachedRowCount) { + firstCacheRowToRemoveInPageBuffer = maxPageBufferIndex + 1; + } + + if (firstCacheRowToRemoveInPageBuffer - pageBufferFirstIndex < currentlyCachedRowCount) { + /* + * Unregister all components that fall beyond the cache limits after + * inserting the new rows. + */ + unregisterComponentsAndPropertiesInRows( + firstCacheRowToRemoveInPageBuffer, currentlyCachedRowCount + - firstCacheRowToRemoveInPageBuffer); + } + } + /** * Requests that the Table should be repainted as soon as possible. * @@ -1856,41 +1900,24 @@ public class Table extends AbstractSelect implements Action.Container, "Insert {0} rows at index {1} to existing page buffer requested", new Object[] { rows, firstIndex }); - // Page buffer must not become larger than pageLength*cacheRate before - // or after the current page - int minPageBufferIndex = getCurrentPageFirstItemIndex() - - (int) (getPageLength() * getCacheRate()); - if (minPageBufferIndex < 0) { - minPageBufferIndex = 0; - } + int minPageBufferIndex = getMinPageBufferIndex(); + int maxPageBufferIndex = getMaxPageBufferIndex(); - int maxPageBufferIndex = getCurrentPageFirstItemIndex() - + (int) (getPageLength() * (1 + getCacheRate())); - int maxBufferSize = maxPageBufferIndex - minPageBufferIndex; + int maxBufferSize = maxPageBufferIndex - minPageBufferIndex + 1; if (getPageLength() == 0) { // If pageLength == 0 then all rows should be rendered - maxBufferSize = pageBuffer[0].length + rows; + maxBufferSize = pageBuffer[CELL_ITEMID].length + rows; } /* * Number of rows that were previously cached. This is not necessarily - * the same as pageLength if we do not have enough rows in the - * container. + * the same as maxBufferSize. */ int currentlyCachedRowCount = pageBuffer[CELL_ITEMID].length; - /* - * firstIndexInPageBuffer is the offset in pageBuffer where the new rows - * will be inserted (firstIndex is the index in the whole table). - * - * E.g. scrolled down to row 1000: firstIndex==1010, - * pageBufferFirstIndex==1000 -> cacheIx==10 - */ - int firstIndexInPageBuffer = firstIndex - pageBufferFirstIndex; - /* If rows > size available in page buffer */ - if (firstIndexInPageBuffer + rows > maxBufferSize) { - rows = maxBufferSize - firstIndexInPageBuffer; + if (firstIndex + rows - 1 > maxPageBufferIndex) { + rows = maxPageBufferIndex - firstIndex + 1; } /* @@ -1899,38 +1926,59 @@ public class Table extends AbstractSelect implements Action.Container, * the cache. */ - /* All rows until the insertion point remain, always. */ - int firstCacheRowToRemoveInPageBuffer = firstIndexInPageBuffer; + /* + * if there are rows before the new pageBuffer limits they must be + * removed + */ + int lastCacheRowToRemove = minPageBufferIndex - 1; + int rowsFromBeginning = lastCacheRowToRemove - pageBufferFirstIndex + 1; + if (lastCacheRowToRemove >= pageBufferFirstIndex) { + unregisterComponentsAndPropertiesInRows(pageBufferFirstIndex, + rowsFromBeginning); + } else { + rowsFromBeginning = 0; + } + /* + * the rows that fall outside of the new pageBuffer limits after the new + * rows are inserted must also be removed + */ + int firstCacheRowToRemove = firstIndex; /* * IF there is space remaining in the buffer after the rows have been * inserted, we can keep more rows. */ - int numberOfOldRowsAfterInsertedRows = maxBufferSize - - firstIndexInPageBuffer - rows; + int numberOfOldRowsAfterInsertedRows = Math.min(pageBufferFirstIndex + + currentlyCachedRowCount + rows, maxPageBufferIndex + 1) + - (firstIndex + rows - 1); if (numberOfOldRowsAfterInsertedRows > 0) { - firstCacheRowToRemoveInPageBuffer += numberOfOldRowsAfterInsertedRows; + firstCacheRowToRemove += numberOfOldRowsAfterInsertedRows; } + int rowsFromAfter = currentlyCachedRowCount + - (firstCacheRowToRemove - pageBufferFirstIndex); - if (firstCacheRowToRemoveInPageBuffer <= currentlyCachedRowCount) { + if (rowsFromAfter > 0) { /* * Unregister all components that fall beyond the cache limits after * inserting the new rows. */ - unregisterComponentsAndPropertiesInRows( - firstCacheRowToRemoveInPageBuffer + pageBufferFirstIndex, - currentlyCachedRowCount - firstCacheRowToRemoveInPageBuffer - + pageBufferFirstIndex); + unregisterComponentsAndPropertiesInRows(firstCacheRowToRemove, + rowsFromAfter); } // Calculate the new cache size - int newCachedRowCount = currentlyCachedRowCount; - if (maxBufferSize == 0 || currentlyCachedRowCount < maxBufferSize) { - newCachedRowCount = currentlyCachedRowCount + rows; - if (maxBufferSize > 0 && newCachedRowCount > maxBufferSize) { - newCachedRowCount = maxBufferSize; - } - } + int newCachedRowCount = maxBufferSize; + if (pageBufferFirstIndex + currentlyCachedRowCount + rows - 1 < maxPageBufferIndex) { + // there aren't enough rows to fill the whole potential -> use what + // there is + newCachedRowCount -= maxPageBufferIndex + - (pageBufferFirstIndex + currentlyCachedRowCount + rows - 1); + } else if (minPageBufferIndex < pageBufferFirstIndex) { + newCachedRowCount -= pageBufferFirstIndex - minPageBufferIndex; + } + /* calculate the internal location of the new rows within the new cache */ + int firstIndexInNewPageBuffer = firstIndex - pageBufferFirstIndex + - rowsFromBeginning; /* Paint the new rows into a separate buffer */ Object[][] cells = getVisibleCellsNoCache(firstIndex, rows, false); @@ -1942,21 +1990,25 @@ public class Table extends AbstractSelect implements Action.Container, Object[][] newPageBuffer = new Object[pageBuffer.length][newCachedRowCount]; for (int i = 0; i < pageBuffer.length; i++) { - for (int row = 0; row < firstIndexInPageBuffer; row++) { + for (int row = 0; row < firstIndexInNewPageBuffer; row++) { // Copy the first rows - newPageBuffer[i][row] = pageBuffer[i][row]; + newPageBuffer[i][row] = pageBuffer[i][rowsFromBeginning + row]; } - for (int row = firstIndexInPageBuffer; row < firstIndexInPageBuffer + for (int row = firstIndexInNewPageBuffer; row < firstIndexInNewPageBuffer + rows; row++) { // Copy the newly created rows - newPageBuffer[i][row] = cells[i][row - firstIndexInPageBuffer]; + newPageBuffer[i][row] = cells[i][row + - firstIndexInNewPageBuffer]; } - for (int row = firstIndexInPageBuffer + rows; row < newCachedRowCount; row++) { + for (int row = firstIndexInNewPageBuffer + rows; row < newCachedRowCount; row++) { // Move the old rows down below the newly inserted rows - newPageBuffer[i][row] = pageBuffer[i][row - rows]; + newPageBuffer[i][row] = pageBuffer[i][rowsFromBeginning + row + - rows]; } } pageBuffer = newPageBuffer; + pageBufferFirstIndex = Math.max(pageBufferFirstIndex + + rowsFromBeginning, minPageBufferIndex); if (getLogger().isLoggable(Level.FINEST)) { getLogger().log( Level.FINEST, @@ -1970,6 +2022,40 @@ public class Table extends AbstractSelect implements Action.Container, return cells; } + private int getMaxPageBufferIndex() { + int total = size(); + if (getPageLength() == 0) { + // everything is shown at once, no caching + return total - 1; + } + // Page buffer must not become larger than pageLength*cacheRate after + // the current page + int maxPageBufferIndex = getCurrentPageFirstItemIndex() + + (int) (getPageLength() * (1 + getCacheRate())); + if (shouldHideNullSelectionItem()) { + --total; + } + if (maxPageBufferIndex >= total) { + maxPageBufferIndex = total - 1; + } + return maxPageBufferIndex; + } + + private int getMinPageBufferIndex() { + if (getPageLength() == 0) { + // everything is shown at once, no caching + return 0; + } + // Page buffer must not become larger than pageLength*cacheRate before + // the current page + int minPageBufferIndex = getCurrentPageFirstItemIndex() + - (int) (getPageLength() * getCacheRate()); + if (minPageBufferIndex < 0) { + minPageBufferIndex = 0; + } + return minPageBufferIndex; + } + /** * Render rows with index "firstIndex" to "firstIndex+rows-1" to a new * buffer. @@ -3076,26 +3162,23 @@ public class Table extends AbstractSelect implements Action.Container, // Rows if (isPartialRowUpdate() && painted && !target.isFullRepaint()) { paintPartialRowUpdate(target, actionSet); - /* - * Send the page buffer indexes to ensure that the client side stays - * in sync. Otherwise we _might_ have the situation where the client - * side discards too few or too many rows, causing out of sync - * issues. - * - * This could probably be done for full repaints also to simplify - * the client side. - */ - int pageBufferLastIndex = pageBufferFirstIndex - + pageBuffer[CELL_ITEMID].length - 1; - target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_FIRST, - pageBufferFirstIndex); - target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_LAST, - pageBufferLastIndex); } else if (target.isFullRepaint() || isRowCacheInvalidated()) { paintRows(target, cells, actionSet); setRowCacheInvalidated(false); } + /* + * Send the page buffer indexes to ensure that the client side stays in + * sync. Otherwise we _might_ have the situation where the client side + * discards too few or too many rows, causing out of sync issues. + */ + int pageBufferLastIndex = pageBufferFirstIndex + + pageBuffer[CELL_ITEMID].length - 1; + target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_FIRST, + pageBufferFirstIndex); + target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_LAST, + pageBufferLastIndex); + paintSorting(target); resetVariablesAndPageBuffer(target); diff --git a/uitest/src/com/vaadin/tests/components/table/EmptyRowsWhenScrolling.java b/uitest/src/com/vaadin/tests/components/table/EmptyRowsWhenScrolling.java new file mode 100644 index 0000000000..c1ae9b4118 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/EmptyRowsWhenScrolling.java @@ -0,0 +1,275 @@ +package com.vaadin.tests.components.table; + +import java.util.Random; + +import com.vaadin.annotations.AutoGenerated; +import com.vaadin.data.util.BeanContainer; +import com.vaadin.event.ShortcutAction.KeyCode; +import com.vaadin.server.ClassResource; +import com.vaadin.server.Resource; +import com.vaadin.server.VaadinRequest; +import com.vaadin.ui.AbsoluteLayout; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.CustomComponent; +import com.vaadin.ui.Embedded; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Notification; +import com.vaadin.ui.Table; +import com.vaadin.ui.Table.ColumnGenerator; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; + +/** + * This test cannot be automated. http://dev.vaadin.com/ticket/6160, base code + * by user radosdesign. + * + * The test fails if even occasionally empty rows appear in the table. A + * relatively good way to get them (before the fix) is to wait for the page to + * load, move the scrollbar down, press 'R' before the rows have time to be + * rendered, and then move the scrollbar up when no rows have been rendered yet. + * After this, when you scroll down slowly there may be empty rows. This doesn't + * happen always, and you may need to force the threads slower to get it to + * happen at all. On a slow 32-bit Windows 7 with Chrome version 22.0.1229.94 m + * (no GWT dev mode) this test has managed to reproduce the problem maybe nine + * times out of ten. + * + * @author Anna Koskinen / Vaadin Oy + * + */ +public class EmptyRowsWhenScrolling extends UI { + + @Override + public void init(VaadinRequest request) { + getPage().setTitle("Simpletable Application"); + AppView appView = new AppView(this); + setContent(appView); + addAction(new Button.ClickShortcut(appView.getBtnRefreshTable(), + KeyCode.R)); + } + + private class AppView extends CustomComponent { + + @AutoGenerated + private AbsoluteLayout mainLayout; + @AutoGenerated + private VerticalLayout verticalLayout_1; + @AutoGenerated + private Table table; + @AutoGenerated + private HorizontalLayout horizontalLayout_1; + @AutoGenerated + private Button btnRefreshTable; + + /*- VaadinEditorProperties={"grid":"RegularGrid,20","showGrid":true,"snapToGrid":true,"snapToObject":true,"movingGuides":false,"snappingDistance":10} */ + + /*- VaadinEditorProperties={"grid":"RegularGrid,20","showGrid":true,"snapToGrid":true,"snapToObject":true,"movingGuides":false,"snappingDistance":10} */ + + /** + * The constructor should first build the main layout, set the + * composition root and then do any custom initialization. + * + * The constructor will not be automatically regenerated by the visual + * editor. + */ + public AppView(final UI application) { + buildMainLayout(); + setCompositionRoot(mainLayout); + + // Container with sample data + BeanContainer container = new BeanContainer( + SimpleBean.class); + container.setBeanIdProperty("id"); + for (int i = 1; i <= 50; ++i) { + container.addBean(new SimpleBean(i, "image", + "Column1 row " + i, "Column2 row " + i, "Column3 row " + + i, "Column4 row " + i)); + } + table.setContainerDataSource(container); + table.setEditable(true); + table.setColumnReorderingAllowed(true); + table.setVisibleColumns(new String[] { "image", "id", "col1", + "col2", "col3", "col4" }); + table.addGeneratedColumn("image", new ColumnGenerator() { + public Object generateCell(Table source, Object itemId, + Object columnId) { + int imgNum = new Random().nextInt(5) + 1; + try { + // Simulate background work + System.out + .println("Generated column for image /com/example/simpletable/img/px50-" + + imgNum + ".jpg"); + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Resource resource = new ClassResource( + "/com/example/simpletable/img/px50-" + imgNum + + ".jpg"); + Embedded image = new Embedded("", resource); + image.setWidth("50px"); + image.setHeight("50px"); + image.addClickListener(new com.vaadin.event.MouseEvents.ClickListener() { + public void click( + com.vaadin.event.MouseEvents.ClickEvent event) { + Notification.show("Image clicked!"); + } + }); + return image; + } + }); + + // Refresh table button + getBtnRefreshTable().addClickListener(new ClickListener() { + public void buttonClick(ClickEvent event) { + table.refreshRowCache(); + } + }); + } + + @AutoGenerated + private AbsoluteLayout buildMainLayout() { + // common part: create layout + mainLayout = new AbsoluteLayout(); + mainLayout.setImmediate(false); + mainLayout.setWidth("100%"); + mainLayout.setHeight("100%"); + + // top-level component properties + setWidth("100.0%"); + setHeight("100.0%"); + + // verticalLayout_1 + verticalLayout_1 = buildVerticalLayout_1(); + mainLayout.addComponent(verticalLayout_1, "top:0.0px;left:0.0px;"); + + return mainLayout; + } + + @AutoGenerated + private VerticalLayout buildVerticalLayout_1() { + // common part: create layout + verticalLayout_1 = new VerticalLayout(); + verticalLayout_1.setImmediate(false); + verticalLayout_1.setWidth("100.0%"); + verticalLayout_1.setHeight("100.0%"); + verticalLayout_1.setMargin(false); + + // horizontalLayout_1 + horizontalLayout_1 = buildHorizontalLayout_1(); + verticalLayout_1.addComponent(horizontalLayout_1); + + // table_1 + table = new Table(); + table.setImmediate(false); + table.setWidth("100.0%"); + table.setHeight("100.0%"); + verticalLayout_1.addComponent(table); + verticalLayout_1.setExpandRatio(table, 1.0f); + + return verticalLayout_1; + } + + @AutoGenerated + private HorizontalLayout buildHorizontalLayout_1() { + // common part: create layout + horizontalLayout_1 = new HorizontalLayout(); + horizontalLayout_1.setImmediate(false); + horizontalLayout_1.setWidth("100.0%"); + horizontalLayout_1.setHeight("-1px"); + horizontalLayout_1.setMargin(false); + + // btnRefreshTable + setBtnRefreshTable(new Button()); + getBtnRefreshTable().setCaption("Reload table row cache"); + getBtnRefreshTable().setImmediate(false); + getBtnRefreshTable().setWidth("-1px"); + getBtnRefreshTable().setHeight("-1px"); + horizontalLayout_1.addComponent(getBtnRefreshTable()); + horizontalLayout_1.setComponentAlignment(getBtnRefreshTable(), + new Alignment(33)); + + return horizontalLayout_1; + } + + public Button getBtnRefreshTable() { + return btnRefreshTable; + } + + public void setBtnRefreshTable(Button btnRefreshTable) { + this.btnRefreshTable = btnRefreshTable; + } + + } + + protected class SimpleBean { + private int id; + private String image; + private String col1; + private String col2; + private String col3; + private String col4; + + public SimpleBean(int id, String image, String col1, String col2, + String col3, String col4) { + super(); + this.id = id; + this.image = image; + this.col1 = col1; + this.col2 = col2; + this.col3 = col3; + this.col4 = col4; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public String getCol1() { + return col1; + } + + public void setCol1(String col1) { + this.col1 = col1; + } + + public String getCol2() { + return col2; + } + + public void setCol2(String col2) { + this.col2 = col2; + } + + public String getCol3() { + return col3; + } + + public void setCol3(String col3) { + this.col3 = col3; + } + + public String getCol4() { + return col4; + } + + public void setCol4(String col4) { + this.col4 = col4; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + } + +} diff --git a/uitest/src/com/vaadin/tests/components/treetable/EmptyingTreeTableTest.html b/uitest/src/com/vaadin/tests/components/treetable/EmptyingTreeTableTest.html new file mode 100644 index 0000000000..0c43d41aca --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/treetable/EmptyingTreeTableTest.html @@ -0,0 +1,52 @@ + + + + + + +EmptyingTreeTableTest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EmptyingTreeTableTest
open/run/com.vaadin.tests.components.treetable.TreeTableTest?restartApplication
screenCaptureinitial
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_Smenu#item046,6
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[0]/VMenuBar[0]#item512,7
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[1]/VMenuBar[0]#item184,9
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[2]/VMenuBar[0]#item016,10
screenCaptureitemsRemoved
+ + diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdate.html b/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdate.html index d9366385ce..df1656dd23 100644 --- a/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdate.html +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdate.html @@ -4,12 +4,12 @@ -New Test +TreeTableCacheOnPartialUpdates - + @@ -18,17 +18,17 @@ - + - + - + @@ -48,7 +48,7 @@ - + @@ -58,27 +58,27 @@ - + - + - + - + - + @@ -108,7 +108,7 @@ - + @@ -118,22 +118,22 @@ - + - + - + - + @@ -173,22 +173,22 @@ - + - + - + - + @@ -234,7 +234,7 @@ - + @@ -249,7 +249,7 @@ - + @@ -259,7 +259,7 @@ - + @@ -269,7 +269,7 @@ - + @@ -279,7 +279,7 @@ - + @@ -289,7 +289,7 @@ - + @@ -304,7 +304,7 @@ - + @@ -314,7 +314,7 @@ - + @@ -324,12 +324,12 @@ - + - + @@ -339,7 +339,7 @@ - + @@ -359,22 +359,22 @@ - + - + - + - + @@ -399,17 +399,17 @@ - + - + - + @@ -424,7 +424,7 @@ - + @@ -434,7 +434,7 @@ - + @@ -454,22 +454,22 @@ - + - + - + - + @@ -495,7 +495,7 @@ - + @@ -505,12 +505,12 @@ - + - + @@ -520,7 +520,7 @@ - + @@ -530,7 +530,7 @@ - + @@ -540,7 +540,7 @@ - + diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdates.java b/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdates.java index c2bd3da860..f792a32f8f 100644 --- a/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdates.java +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdates.java @@ -72,13 +72,11 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { public Component generateCell(final com.vaadin.ui.Table source, final Object itemId, Object columnId) { TestBean tb = (TestBean) itemId; - // if (!tb.getCol1().contains("children")) { - // return null; - // } String identifier = "Item " + itemId + "/" + columnId; - System.out.println("Generating new Button for " + identifier); Button btnCol3 = new NativeButton(identifier); - btnCol3.addListener(new Button.ClickListener() { + btnCol3.setId("cacheTestButton-" + tb.getCol1() + "-" + + tb.getCol2()); + btnCol3.addClickListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { log.log("Button " + event.getButton().getCaption() @@ -91,6 +89,25 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { } + public class Col4ColumnGenerator implements ColumnGenerator { + public Component generateCell(final com.vaadin.ui.Table source, + final Object itemId, Object columnId) { + TestBean tb = (TestBean) itemId; + String identifier = "Expand/Collapse"; + Button btnCol4 = new NativeButton(identifier); + btnCol4.setId("cacheTestButtonToggle-" + tb.getCol1() + "-" + + tb.getCol2()); + btnCol4.addClickListener(new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + treeTable.setCollapsed(itemId, + !treeTable.isCollapsed(itemId)); + } + }); + return btnCol4; + } + + } + protected int indexOfId(Table source, Object itemId) { Container.Ordered c = (Ordered) source.getContainerDataSource(); if (c instanceof Container.Indexed) { @@ -105,19 +122,17 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { private TreeTable treeTable; private BeanItemContainer testBeanContainer; private static String[] columnHeaders = new String[] { "Col1", "Col2", - "Col3" }; + "Col3", "Col4" }; private static Object[] visibleColumns = new Object[] { "col1", "col2", - "col3" }; + "col3", "col4" }; @Override public void setup() { - setTheme("reindeer-tests"); - // Force row height to be the same in all browsers so scrolling based on // pixels works as expected Button b = new Button("Show first"); addComponent(b); - b.addListener(new ClickListener() { + b.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { @@ -132,7 +147,7 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { cacheRateSelect.addItem(new Integer(1)); cacheRateSelect.addItem(new Integer(2)); cacheRateSelect.setValue(2); - cacheRateSelect.addListener(new ValueChangeListener() { + cacheRateSelect.addValueChangeListener(new ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { @@ -143,7 +158,6 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { addComponent(cacheRateSelect); treeTable = new TreeTable(); treeTable.addStyleName("table-equal-rowheight"); - // treeTable.setPageLength(0); testBeanContainer = new BeanItemContainer(TestBean.class); Map hasChildren = new HashMap(); @@ -156,7 +170,8 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { hasChildren.put("99", 20); treeTable.setContainerDataSource(createContainer(100, hasChildren)); treeTable.addGeneratedColumn("col3", new Col3ColumnGenerator()); - treeTable.addListener(new ExpandListener() { + treeTable.addGeneratedColumn("col4", new Col4ColumnGenerator()); + treeTable.addExpandListener(new ExpandListener() { @Override public void nodeExpand(ExpandEvent event) { @@ -164,7 +179,7 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { } }); - treeTable.addListener(new CollapseListener() { + treeTable.addCollapseListener(new CollapseListener() { @Override public void nodeCollapse(CollapseEvent event) { @@ -172,12 +187,11 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { } }); - treeTable.setColumnHeaders(columnHeaders); treeTable.setVisibleColumns(visibleColumns); + treeTable.setColumnHeaders(columnHeaders); treeTable.setColumnWidth("col1", 150); treeTable.setColumnWidth("col2", 50); treeTable.setHeight("430px"); - // treeTable.setColumnWidth("col3", 150); addComponent(log); addComponent(treeTable); } diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.html b/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.html new file mode 100644 index 0000000000..1aa952d77e --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.html @@ -0,0 +1,61 @@ + + + + + + +TreeTableInternalError + + +
New Test
TreeTableCacheOnPartialUpdates
open
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[0]/VNativeButton[0]cacheTestButton-1 (children)-A 47,5
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[1]/VNativeButton[0]cacheTestButton-2-B 46,7
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[6]/VNativeButton[0]cacheTestButton-7 (children)-G 54,12
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]cacheTestButtonToggle-1 (children)-A 10,7
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[0]/VNativeButton[0]cacheTestButton-1 (children)-A 81,6
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[1]/VNativeButton[0]cacheTestButton-1.1-A.A 73,8
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[0]/VNativeButton[0]cacheTestButton-1 (children)-A 86,7
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[5]/VNativeButton[0]cacheTestButton-1.5-A.E 72,2
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[6]/VNativeButton[0]cacheTestButton-2-B 73,7
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]cacheTestButtonToggle-1 (children)-A 11,2
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[0]/VNativeButton[0]cacheTestButton-1 (children)-A 76,5
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[2]/VNativeButton[0]cacheTestButton-3 (children)-C 58,7
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[1]/VNativeButton[0]cacheTestButton-2-B 69,10
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[0]/VNativeButton[0]cacheTestButton-1 (children)-A 78,7
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[41]/VNativeButton[0]cacheTestButton-100-CV 53,-2462
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[34]/VNativeButton[0]cacheTestButton-93-CO 91,-2452
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[28]/VNativeButton[0]cacheTestButton-87-CI 84,-2461
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[41]/VNativeButton[0]cacheTestButton-100-CV 102,-2452
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[34]/domChild[0]/domChild[0]/domChild[0]cacheTestButtonToggle-40 (children)-AN 9,-995
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[34]/VNativeButton[0]cacheTestButton-40 (children)-AN 93,-991
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[35]/VNativeButton[0]cacheTestButton-40.1-AN.A 123,-991
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[40]/VNativeButton[0]cacheTestButton-40.6-AN.F 114,-1000
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[34]/VNativeButton[0]cacheTestButton-40 (children)-AN 118,-993
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[34]/domChild[0]/domChild[0]/domChild[0]cacheTestButtonToggle-40 (children)-AN 9,-998
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[34]/VNativeButton[0]cacheTestButton-40 (children)-AN 42,-990
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[29]/VNativeButton[0]cacheTestButton-35-AI 84,-990
waitForElementPresentvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[42]/VNativeButton[0]cacheTestButton-48-AV
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[42]/VNativeButton[0]cacheTestButton-48-AV 98,-998
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[35]/VNativeButton[0]cacheTestButton-41-AO 101,-994
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[30]/VNativeButton[0]cacheTestButton-86-CH 136,-2447
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[42]/VNativeButton[0]cacheTestButton-98-CT 131,-2462
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[43]/VNativeButton[0]cacheTestButton-99 (children)-CU 134,-2459
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[44]/VNativeButton[0]cacheTestButton-100-CV 144,-2454
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[43]/domChild[0]/domChild[0]/domChild[0]cacheTestButtonToggle-99 (children)-CU 10,-2461
waitForElementPresentvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[43]/VNativeButton[0]cacheTestButton-99 (children)-CU
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[43]/VNativeButton[0]cacheTestButton-99 (children)-CU 65,-2456
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[30]/VNativeButton[0]cacheTestButton-86-CH 109,-2455
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[44]/VNativeButton[0]cacheTestButton-99.1-CU.A 82,-2457
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[43]/VNativeButton[0]cacheTestButton-99 (children)-CU 85,-2792
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[42]/VNativeButton[0]cacheTestButton-98-CT 101,-2788
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[44]/VNativeButton[0]cacheTestButton-99.1-CU.A 111,-2794
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[56]/VNativeButton[0]cacheTestButton-99.13-CU.M 113,-2794
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[43]/domChild[0]/domChild[0]/domChild[0]cacheTestButtonToggle-99 (children)-CU 11,-2792
waitForElementPresentvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[40]/VNativeButton[0]cacheTestButton-99 (children)-CU
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[40]/VNativeButton[0]cacheTestButton-99 (children)-CU 71,-2465
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[41]/VNativeButton[0]cacheTestButton-100-CV 81,-2459
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[39]/VNativeButton[0]cacheTestButton-98-CT 80,-2458
mouseClickvaadin=runcomvaadintestscomponentstreetableTreeTableCacheOnPartialUpdates::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VTreeTable[0]/FocusableScrollPanel[0]/VTreeTable$VTreeTableScrollBody[0]/VTreeTable$VTreeTableScrollBody$VTreeTableRow[28]/VNativeButton[0]cacheTestButton-87-CI 86,-2462
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TreeTableInternalError
open/run/com.vaadin.tests.components.treetable.TreeTableInternalError?restartApplication
scrollvaadin=runcomvaadintestscomponentstreetableTreeTableInternalError::PID_Streetable/domChild[1]1320
waitForElementPresentcacheTestButtonToggle-57
clickresize
scrollvaadin=runcomvaadintestscomponentstreetableTreeTableInternalError::PID_Streetable/domChild[1]1500
waitForElementPresentcacheTestButtonToggle-55
mouseClickcacheTestButtonToggle-55-402,-1632
mouseClickcacheTestButtonToggle-58-402,-1660
screenCapture
+ + diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.java b/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.java new file mode 100644 index 0000000000..f6d7f11eb7 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.java @@ -0,0 +1,82 @@ +package com.vaadin.tests.components.treetable; + +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Component; +import com.vaadin.ui.NativeButton; +import com.vaadin.ui.Table.ColumnGenerator; +import com.vaadin.ui.TreeTable; +import com.vaadin.ui.VerticalLayout; + +public class TreeTableInternalError extends TestBase { + private TreeTable t; + + @Override + protected void setup() { + VerticalLayout content = getLayout(); + content.setSizeFull(); + + t = new TreeTable() { + { + setSizeFull(); + fillTreeTable(this); + } + }; + t.setId("treetable"); + content.addComponent(t); + content.setExpandRatio(t, 1); + + Button button = new Button("Resize") { + { + addClickListener(new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + t.setHeight("300px"); + } + }); + } + }; + button.setId("resize"); + content.addComponent(button); + } + + @Override + protected String getDescription() { + return "Internal Error when scrolling down enough that more rows are loaded (cache updated), then scrolling down just a few rows and expanding rows"; + } + + @Override + protected Integer getTicketNumber() { + return 10057; + } + + private void fillTreeTable(TreeTable t) { + t.addContainerProperty("name", String.class, null); + t.addGeneratedColumn("toggle", new ButtonColumnGenerator()); + for (int i = 0; i < 1000; i++) { + t.addItem(i); + t.getContainerProperty(i, "name").setValue("Item-" + i); + t.addItem(i + "c"); + t.getContainerProperty(i + "c", "name").setValue("Child-" + i); + t.setParent(i + "c", i); + t.setChildrenAllowed(i + "c", false); + } + } + + public class ButtonColumnGenerator implements ColumnGenerator { + public Component generateCell(final com.vaadin.ui.Table source, + final Object itemId, Object columnId) { + String identifier = "Expand/Collapse"; + Button btnCol = new NativeButton(identifier); + btnCol.setId("cacheTestButtonToggle-" + itemId); + btnCol.addClickListener(new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + t.setCollapsed(itemId, !t.isCollapsed(itemId)); + } + }); + return btnCol; + } + + } + +} -- 2.39.5