From 38396def32f9c58ce8b4c4d2df42310ef9b0605e Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Mon, 1 Feb 2016 17:44:56 +0200 Subject: [PATCH] Use key mapping to keep track of data on the client-side This method fixes data removing to be based on the key mapping instead of data content. Also the data clean up is now properly called when dropping data objects. Change-Id: I76a917968026f6c3b2693b52848448de92145fb1 --- .../data/typed/DataSourceConnector.java | 108 ++++++++++++++++-- .../data/typed/DataProvider.java | 33 +++--- .../shared/data/DataProviderClientRpc.java | 8 +- 3 files changed, 120 insertions(+), 29 deletions(-) diff --git a/client/src/com/vaadin/client/connectors/data/typed/DataSourceConnector.java b/client/src/com/vaadin/client/connectors/data/typed/DataSourceConnector.java index a2fc1db8e6..59d72baa67 100644 --- a/client/src/com/vaadin/client/connectors/data/typed/DataSourceConnector.java +++ b/client/src/com/vaadin/client/connectors/data/typed/DataSourceConnector.java @@ -15,28 +15,41 @@ */ package com.vaadin.client.connectors.data.typed; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.vaadin.client.ServerConnector; import com.vaadin.client.data.HasDataSource; import com.vaadin.client.extensions.AbstractExtensionConnector; import com.vaadin.client.widget.grid.datasources.ListDataSource; import com.vaadin.server.communication.data.typed.DataProvider; import com.vaadin.shared.data.DataProviderClientRpc; +import com.vaadin.shared.data.DataProviderConstants; +import com.vaadin.shared.data.DataRequestRpc; import com.vaadin.shared.ui.Connect; +import elemental.json.Json; import elemental.json.JsonArray; import elemental.json.JsonObject; /** - * A simple connector for DataProvider class. + * A simple connector for DataProvider class. Based on {@link ListDataSource} + * and does not support lazy loading or paging. * * @since */ @Connect(DataProvider.class) public class DataSourceConnector extends AbstractExtensionConnector { - ListDataSource ds = new ListDataSource(); + private Map keyToJson = new HashMap(); + private Set droppedKeys = new HashSet(); + private ListDataSource ds = new ListDataSource(); + private boolean pendingDrop = false; @Override protected void extend(ServerConnector target) { @@ -44,8 +57,12 @@ public class DataSourceConnector extends AbstractExtensionConnector { @Override public void resetSize(long size) { - // Server will provide the data we need. ds.asList().clear(); + // Inform the server-side that all keys are now dropped. + for (String key : keyToJson.keySet()) { + dropKey(key); + } + sendDroppedKeys(); } @Override @@ -54,12 +71,17 @@ public class DataSourceConnector extends AbstractExtensionConnector { assert firstIndex <= l.size() : "Gap in data. First Index: " + firstIndex + ", Size: " + l.size(); for (long i = 0; i < data.length(); ++i) { + JsonObject object = data.getObject((int) i); if (i + firstIndex == l.size()) { - l.add(data.getObject((int) i)); + l.add(object); } else if (i + firstIndex < l.size()) { - l.set((int) (i + firstIndex), data.getObject((int) i)); + int index = (int) (i + firstIndex); + dropKey(getKey(l.get(index))); + l.set(index, object); } + keyToJson.put(getKey(object), object); } + sendDroppedKeys(); } @Override @@ -68,13 +90,11 @@ public class DataSourceConnector extends AbstractExtensionConnector { } @Override - public void drop(JsonObject dataObject) { - List l = ds.asList(); - for (int i = 0; i < l.size(); ++i) { - if (l.get(i).toJson().equals(dataObject.toJson())) { - l.remove(i); - return; - } + public void drop(String key) { + if (keyToJson.containsKey(key)) { + ds.asList().remove(keyToJson.get(key)); + dropKey(key); + sendDroppedKeys(); } } }); @@ -86,4 +106,68 @@ public class DataSourceConnector extends AbstractExtensionConnector { assert false : "Parent not implementing HasDataSource"; } } + + /** + * Marks a key as dropped. Call to + * {@link DataSourceConnector#sendDroppedKeys()} should be called to make + * sure the information is sent to the server-side. + * + * @param key + * dropped key + */ + private void dropKey(String key) { + if (keyToJson.containsKey(key)) { + droppedKeys.add(key); + keyToJson.remove(key); + } + } + + /** + * Sends dropped keys to the server-side with a deferred scheduled command. + * Multiple calls to this method will only result to one command being + * executed. + */ + private void sendDroppedKeys() { + if (pendingDrop) { + return; + } + + pendingDrop = true; + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + + @Override + public void execute() { + pendingDrop = false; + if (droppedKeys.isEmpty()) { + return; + } + + JsonArray keyArray = Json.createArray(); + int i = 0; + for (String key : droppedKeys) { + keyArray.set(i++, key); + } + + getRpcProxy(DataRequestRpc.class).dropRows(keyArray); + + // Force RPC since it's delayed. + getConnection().getServerRpcQueue().flush(); + + droppedKeys.clear(); + } + }); + } + + /** + * Gets the mapping key from given {@link JsonObject}. + * + * @param jsonObject + * json object to get the key from + */ + protected String getKey(JsonObject jsonObject) { + if (jsonObject.hasKey(DataProviderConstants.KEY)) { + return jsonObject.getString(DataProviderConstants.KEY); + } + return null; + } } diff --git a/server/src/com/vaadin/server/communication/data/typed/DataProvider.java b/server/src/com/vaadin/server/communication/data/typed/DataProvider.java index a4e937cbdb..e975792713 100644 --- a/server/src/com/vaadin/server/communication/data/typed/DataProvider.java +++ b/server/src/com/vaadin/server/communication/data/typed/DataProvider.java @@ -187,8 +187,13 @@ public class DataProvider extends AbstractExtension { } @Override - public void dropRows(JsonArray rowKeys) { - // FIXME: What should I do with these? + public void dropRows(JsonArray keys) { + for (int i = 0; i < keys.length(); ++i) { + handler.dropActiveData(keys.getString(i)); + } + + // Use the whole data as the ones sent to the client. + handler.cleanUp(data); } } @@ -328,24 +333,26 @@ public class DataProvider extends AbstractExtension { } /** - * Informs the DataProvider that an item has been added. It is assumed to be - * the last item in the collection. + * Informs the DataProvider that a data object has been added. It is assumed + * to be the last object in the collection. * - * @param item - * item added to collection + * @param data + * data object added to collection */ - public void add(T item) { - rpc.add(getDataObject(item)); + public void add(T data) { + rpc.add(getDataObject(data)); } /** - * Informs the DataProvider that an item has been removed. + * Informs the DataProvider that a data object has been removed. * - * @param item - * item removed from collection + * @param data + * data object removed from collection */ - public void remove(T item) { - rpc.drop(getDataObject(item)); + public void remove(T data) { + if (handler.getActiveData().contains(data)) { + rpc.drop(getKeyMapper().key(data)); + } } } diff --git a/shared/src/com/vaadin/shared/data/DataProviderClientRpc.java b/shared/src/com/vaadin/shared/data/DataProviderClientRpc.java index de23e1c3ed..c96a2a64fa 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderClientRpc.java +++ b/shared/src/com/vaadin/shared/data/DataProviderClientRpc.java @@ -59,11 +59,11 @@ public interface DataProviderClientRpc extends ClientRpc { void add(JsonObject dataObject); /** - * Removes data from the client-side DataSource. + * Removes data identified by given key from the client-side DataSource. * - * @param dataObject - * single removed data object + * @param key + * key identifying the object to be removed */ - void drop(JsonObject dataObject); + void drop(String key); } -- 2.39.5