Change-Id: I16f9577ea4091fb4febe167d76e141b5945f53abtags/8.0.0.alpha1
@@ -28,8 +28,8 @@ import com.google.gwt.core.client.Duration; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.core.client.Scheduler.ScheduledCommand; | |||
import com.vaadin.client.Profiler; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.ui.grid.Range; | |||
/** | |||
* Base implementation for data sources that fetch data from a remote system. |
@@ -16,7 +16,7 @@ | |||
package com.vaadin.client.data; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.Range; | |||
/** | |||
* Determines what data an {@link AbstractRemoteDataSource} should fetch and |
@@ -16,6 +16,9 @@ | |||
package com.vaadin.client.data; | |||
import java.util.function.Consumer; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.Registration; | |||
/** | |||
@@ -104,7 +107,7 @@ public interface DataSource<T> { | |||
* override of an existing method, we're defining a new method for that | |||
* instead. | |||
* | |||
* @param rowHandle | |||
* @param obj | |||
* the reference object with which to compare | |||
* @return {@code true} if this object is the same as the obj argument; | |||
* {@code false} otherwise. | |||
@@ -182,10 +185,27 @@ public interface DataSource<T> { | |||
* | |||
* @param dataChangeHandler | |||
* the data change handler | |||
* | |||
* @return registration for removing the handler | |||
*/ | |||
public Registration addDataChangeHandler( | |||
DataChangeHandler dataChangeHandler); | |||
/** | |||
* Sets a simple data change handler for a widget without lazy loading. | |||
* Refresh method should reset all the data in the widget. | |||
* | |||
* @param refreshMethod | |||
* a method to refresh all data in the widget | |||
* | |||
* @return registration for removing the handler | |||
*/ | |||
public default Registration addDataChangeHandler( | |||
Consumer<Range> refreshMethod) { | |||
return addDataChangeHandler( | |||
new SimpleDataChangeHandler(this, refreshMethod)); | |||
} | |||
/** | |||
* Gets a {@link RowHandle} of a row object in the cache. | |||
* |
@@ -0,0 +1,103 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.client.data; | |||
import java.util.function.Consumer; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.vaadin.shared.Range; | |||
/** | |||
* Helper class for creating a {@link DataChangeHandler} for a Widget that does | |||
* not support lazy loading. | |||
* | |||
* @author Vaadin Ltd | |||
* @since | |||
*/ | |||
public class SimpleDataChangeHandler implements DataChangeHandler { | |||
/** | |||
* Class to request the data source to get the full data set. | |||
*/ | |||
private static class DelayedResetScheduler { | |||
private final DataSource<?> dataSource; | |||
private boolean scheduled = false; | |||
public DelayedResetScheduler(DataSource<?> dataSource) { | |||
this.dataSource = dataSource; | |||
} | |||
public void schedule() { | |||
if (scheduled) { | |||
return; | |||
} | |||
Scheduler.get().scheduleFinally(() -> { | |||
dataSource.ensureAvailability(0, dataSource.size()); | |||
scheduled = false; | |||
}); | |||
scheduled = true; | |||
} | |||
public int getExpectedSize() { | |||
return dataSource.size(); | |||
} | |||
public boolean isScheduled() { | |||
return scheduled; | |||
} | |||
} | |||
private final DelayedResetScheduler scheduler; | |||
private final Consumer<Range> refreshMethod; | |||
SimpleDataChangeHandler(DataSource<?> dataSource, | |||
Consumer<Range> refreshMethod) { | |||
scheduler = new DelayedResetScheduler(dataSource); | |||
this.refreshMethod = refreshMethod; | |||
} | |||
@Override | |||
public void dataUpdated(int firstRowIndex, int numberOfRows) { | |||
scheduler.schedule(); | |||
} | |||
@Override | |||
public void dataRemoved(int firstRowIndex, int numberOfRows) { | |||
scheduler.schedule(); | |||
} | |||
@Override | |||
public void dataAdded(int firstRowIndex, int numberOfRows) { | |||
scheduler.schedule(); | |||
} | |||
@Override | |||
public void dataAvailable(int firstRowIndex, int numberOfRows) { | |||
if (!scheduler.isScheduled() && firstRowIndex == 0 | |||
&& numberOfRows == scheduler.getExpectedSize()) { | |||
// All data should now be available. | |||
refreshMethod.accept(Range.withLength(firstRowIndex, numberOfRows)); | |||
} else { | |||
scheduler.schedule(); | |||
} | |||
} | |||
@Override | |||
public void resetDataAndSize(int newSize) { | |||
scheduler.schedule(); | |||
} | |||
} |
@@ -17,7 +17,7 @@ | |||
package com.vaadin.client.widget.escalator; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.Range; | |||
/** | |||
* Event fired when the range of visible rows changes e.g. because of scrolling. |
@@ -16,7 +16,7 @@ | |||
package com.vaadin.client.widget.grid; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.Range; | |||
/** | |||
* Event object describing a change of row availability in DataSource of a Grid. |
@@ -89,8 +89,8 @@ import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent; | |||
import com.vaadin.client.widget.grid.events.ScrollEvent; | |||
import com.vaadin.client.widget.grid.events.ScrollHandler; | |||
import com.vaadin.client.widgets.Escalator.JsniUtil.TouchHandlerBundle; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.ui.grid.HeightMode; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.ui.grid.ScrollDestination; | |||
import com.vaadin.shared.util.SharedUtil; | |||
@@ -174,13 +174,13 @@ import com.vaadin.client.widgets.Escalator.SubPartArguments; | |||
import com.vaadin.client.widgets.Grid.Editor.State; | |||
import com.vaadin.client.widgets.Grid.StaticSection.StaticCell; | |||
import com.vaadin.client.widgets.Grid.StaticSection.StaticRow; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.data.sort.SortDirection; | |||
import com.vaadin.shared.ui.grid.GridConstants; | |||
import com.vaadin.shared.ui.grid.GridConstants.Section; | |||
import com.vaadin.shared.ui.grid.GridStaticCellType; | |||
import com.vaadin.shared.ui.grid.HeightMode; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.ui.grid.ScrollDestination; | |||
import com.vaadin.shared.util.SharedUtil; | |||
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue; | |||
import org.junit.Test; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.Range; | |||
@SuppressWarnings("static-method") | |||
public class PartitioningTest { |
@@ -29,8 +29,8 @@ import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.annotations.OnStateChange; | |||
import com.vaadin.client.data.DataSource; | |||
import com.vaadin.client.data.DataSource.RowHandle; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.v7.client.renderers.ComplexRenderer; | |||
import com.vaadin.v7.client.renderers.Renderer; | |||
import com.vaadin.v7.client.widget.grid.DataAvailableEvent; |
@@ -23,10 +23,10 @@ import java.util.List; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.data.AbstractRemoteDataSource; | |||
import com.vaadin.client.extensions.AbstractExtensionConnector; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.data.DataProviderRpc; | |||
import com.vaadin.shared.data.DataRequestRpc; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.v7.shared.ui.grid.GridState; | |||
import elemental.json.Json; |
@@ -17,7 +17,7 @@ | |||
package com.vaadin.v7.client.widget.escalator; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.Range; | |||
/** | |||
* Event fired when the range of visible rows changes e.g. because of scrolling. |
@@ -16,7 +16,7 @@ | |||
package com.vaadin.v7.client.widget.grid; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.Range; | |||
/** | |||
* Event object describing a change of row availability in DataSource of a Grid. |
@@ -65,7 +65,7 @@ import com.vaadin.client.DeferredWorker; | |||
import com.vaadin.client.Profiler; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.client.ui.SubPartAware; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.util.SharedUtil; | |||
import com.vaadin.v7.client.widget.escalator.Cell; | |||
import com.vaadin.v7.client.widget.escalator.ColumnConfiguration; |
@@ -90,9 +90,9 @@ import com.vaadin.client.ui.dd.DragAndDropHandler.DragAndDropCallback; | |||
import com.vaadin.client.ui.dd.DragHandle; | |||
import com.vaadin.client.ui.dd.DragHandle.DragHandleCallback; | |||
import com.vaadin.client.widgets.Overlay; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.data.sort.SortDirection; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.util.SharedUtil; | |||
import com.vaadin.v7.client.renderers.ComplexRenderer; | |||
import com.vaadin.v7.client.renderers.Renderer; |
@@ -29,9 +29,9 @@ import java.util.Set; | |||
import com.vaadin.server.AbstractExtension; | |||
import com.vaadin.server.ClientConnector; | |||
import com.vaadin.server.KeyMapper; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.data.DataProviderRpc; | |||
import com.vaadin.shared.data.DataRequestRpc; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.v7.data.Container; | |||
import com.vaadin.v7.data.Container.Indexed; | |||
import com.vaadin.v7.data.Container.Indexed.ItemAddEvent; |
@@ -31,10 +31,10 @@ import java.util.stream.Stream; | |||
import com.vaadin.server.AbstractExtension; | |||
import com.vaadin.server.KeyMapper; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.data.DataCommunicatorClientRpc; | |||
import com.vaadin.shared.data.DataCommunicatorConstants; | |||
import com.vaadin.shared.data.DataRequestRpc; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import elemental.json.Json; | |||
import elemental.json.JsonArray; |
@@ -14,7 +14,7 @@ | |||
* the License. | |||
*/ | |||
package com.vaadin.shared.ui.grid; | |||
package com.vaadin.shared; | |||
import java.io.Serializable; | |||
@@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue; | |||
import org.junit.Test; | |||
import com.vaadin.shared.Range; | |||
@SuppressWarnings("static-method") | |||
public class RangeTest { | |||
@@ -0,0 +1,117 @@ | |||
package com.vaadin.tests.data; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Set; | |||
import java.util.stream.Stream; | |||
import com.vaadin.annotations.Widgetset; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.server.data.DataCommunicator; | |||
import com.vaadin.server.data.ListDataSource; | |||
import com.vaadin.server.data.Query; | |||
import com.vaadin.shared.data.DataCommunicatorConstants; | |||
import com.vaadin.shared.data.selection.SelectionModel; | |||
import com.vaadin.tests.components.AbstractTestUIWithLog; | |||
import com.vaadin.tests.widgetset.TestingWidgetSet; | |||
import com.vaadin.ui.AbstractListing; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.HorizontalLayout; | |||
@Widgetset(TestingWidgetSet.NAME) | |||
public class DummyData extends AbstractTestUIWithLog { | |||
/** | |||
* DataSource that keeps track on how often the data is requested. | |||
*/ | |||
private class LoggingDataSource extends ListDataSource<String> { | |||
private int count = 0; | |||
private LoggingDataSource(Collection<String> collection) { | |||
super(collection); | |||
} | |||
@Override | |||
public Stream<String> apply(Query query) { | |||
log("Backend request #" + (count++)); | |||
return super.apply(query); | |||
} | |||
} | |||
/** | |||
* Simplified server only selection model. Selection state passed in data, | |||
* shown as bold text. | |||
*/ | |||
private static class DummySelectionModel implements SelectionModel<String> { | |||
private String selected; | |||
private DataCommunicator<String> communicator; | |||
@Override | |||
public Set<String> getSelectedItems() { | |||
if (selected != null) { | |||
return Collections.singleton(selected); | |||
} | |||
return Collections.emptySet(); | |||
} | |||
@Override | |||
public void select(String item) { | |||
if (selected != null) { | |||
communicator.refresh(selected); | |||
} | |||
selected = item; | |||
if (selected != null) { | |||
communicator.refresh(selected); | |||
} | |||
} | |||
@Override | |||
public void deselect(String item) { | |||
if (item == selected) { | |||
select(null); | |||
} | |||
} | |||
private void setCommunicator(DataCommunicator<String> dataComm) { | |||
communicator = dataComm; | |||
} | |||
} | |||
public static class DummyComponent | |||
extends AbstractListing<String, DummySelectionModel> { | |||
private DummyComponent() { | |||
setSelectionModel(new DummySelectionModel()); | |||
addDataGenerator((str, json) -> { | |||
json.put(DataCommunicatorConstants.DATA, str); | |||
if (isSelected(str)) { | |||
json.put(DataCommunicatorConstants.SELECTED, true); | |||
} | |||
}); | |||
getSelectionModel().setCommunicator(getDataCommunicator()); | |||
} | |||
} | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
DummyComponent dummy = new DummyComponent(); | |||
List<String> items = new ArrayList<>(); | |||
for (int i = 0; i < 300; ++i) { | |||
items.add("Foo " + i); | |||
} | |||
dummy.setDataSource(new LoggingDataSource(items)); | |||
dummy.select("Foo 200"); | |||
HorizontalLayout controls = new HorizontalLayout(); | |||
addComponent(controls); | |||
controls.addComponent(new Button("Select Foo 20", e -> { | |||
dummy.select("Foo " + 20); | |||
})); | |||
controls.addComponent(new Button("Reset data source", e -> { | |||
dummy.setDataSource(new LoggingDataSource(items)); | |||
})); | |||
addComponent(dummy); | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
package com.vaadin.tests.widgetset.client.data; | |||
import com.google.gwt.user.client.ui.FlowPanel; | |||
import com.vaadin.client.connectors.AbstractListingConnector; | |||
import com.vaadin.client.data.DataSource; | |||
import com.vaadin.client.ui.VLabel; | |||
import com.vaadin.shared.data.DataCommunicatorConstants; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.tests.data.DummyData.DummyComponent; | |||
import elemental.json.JsonObject; | |||
@Connect(DummyComponent.class) | |||
public class DummyComponentConnector extends AbstractListingConnector { | |||
@Override | |||
public FlowPanel getWidget() { | |||
return (FlowPanel) super.getWidget(); | |||
} | |||
@Override | |||
public void setDataSource(DataSource<JsonObject> dataSource) { | |||
super.setDataSource(dataSource); | |||
dataSource.addDataChangeHandler(range -> { | |||
assert range.getStart() == 0 && range.getEnd() == dataSource | |||
.size() : "Widget only supports full updates."; | |||
getWidget().clear(); | |||
for (int i = range.getStart(); i < range.getEnd(); ++i) { | |||
VLabel label = new VLabel(); | |||
getWidget().add(label); | |||
JsonObject row = dataSource.getRow(i); | |||
String text = row.getString(DataCommunicatorConstants.DATA); | |||
if (row.hasKey(DataCommunicatorConstants.SELECTED) | |||
&& row.getBoolean(DataCommunicatorConstants.SELECTED)) { | |||
text = "<b>" + text + "</b>"; | |||
label.addStyleName("selected"); | |||
} | |||
label.setHTML(text); | |||
} | |||
}); | |||
} | |||
} |
@@ -30,7 +30,7 @@ import org.junit.Test; | |||
import org.openqa.selenium.NoSuchElementException; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.testbench.By; | |||
import com.vaadin.testbench.ElementQuery; | |||
import com.vaadin.testbench.TestBenchElement; |
@@ -33,7 +33,7 @@ import org.openqa.selenium.Keys; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.testbench.TestBenchElement; | |||
import com.vaadin.testbench.elements.NotificationElement; | |||
import com.vaadin.testbench.parallel.BrowserUtil; |
@@ -0,0 +1,60 @@ | |||
package com.vaadin.tests.data; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import java.util.List; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.testbench.By; | |||
import com.vaadin.testbench.elements.ButtonElement; | |||
import com.vaadin.tests.tb3.SingleBrowserTest; | |||
public class DummyDataTest extends SingleBrowserTest { | |||
@Before | |||
public void setUp() { | |||
setDebug(true); | |||
openTestURL(); | |||
} | |||
@Test | |||
public void testCorrectRowSelectedOnInit() { | |||
List<WebElement> selected = findElements(By.className("selected")); | |||
assertTrue("Only one should be selected", 1 == selected.size()); | |||
assertEquals("Wrong item selected", "Foo 200", | |||
selected.get(0).getText()); | |||
} | |||
@Test | |||
public void testServerSelectionUpdatesSelected() { | |||
$(ButtonElement.class).first().click(); | |||
List<WebElement> selected = findElements(By.className("selected")); | |||
assertTrue("Only one should be selected", 1 == selected.size()); | |||
assertEquals("Wrong item selected", "Foo 20", | |||
selected.get(0).getText()); | |||
} | |||
@Test | |||
public void testDataUpdateDoesNotCauseBackEndRequest() { | |||
assertEquals("Unexpected backend requests", "2. Backend request #1", | |||
getLogRow(0)); | |||
assertEquals("Unexpected backend requests", "1. Backend request #0", | |||
getLogRow(1)); | |||
// Select a row on the server-side, triggers an update | |||
$(ButtonElement.class).first().click(); | |||
assertEquals("No requests should have happened", | |||
"2. Backend request #1", getLogRow(0)); | |||
} | |||
@Test | |||
public void testDataSourceChangeOnlyOneRequest() { | |||
// Change to a new logging data source | |||
$(ButtonElement.class).get(1).click(); | |||
assertEquals("DataSource change should only cause 1 request", | |||
"3. Backend request #0", getLogRow(0)); | |||
} | |||
} |