Change-Id: I8fba190a905a4dac6bfef5693064218672e23ba4feature/vaadin8-book
@@ -0,0 +1,192 @@ | |||
/* | |||
* Copyright 2000-2014 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.connectors.data; | |||
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.extensions.AbstractExtensionConnector; | |||
import com.vaadin.client.widget.grid.datasources.ListDataSource; | |||
import com.vaadin.server.communication.data.typed.SimpleDataProvider; | |||
import com.vaadin.shared.data.DataRequestRpc; | |||
import com.vaadin.shared.data.typed.DataProviderClientRpc; | |||
import com.vaadin.shared.data.typed.DataProviderConstants; | |||
import com.vaadin.shared.ui.Connect; | |||
import elemental.json.Json; | |||
import elemental.json.JsonArray; | |||
import elemental.json.JsonObject; | |||
/** | |||
* A simple connector for DataProvider class. Based on {@link ListDataSource} | |||
* and does not support lazy loading or paging. | |||
* | |||
* @since | |||
*/ | |||
@Connect(SimpleDataProvider.class) | |||
public class DataSourceConnector extends AbstractExtensionConnector { | |||
private Map<String, JsonObject> keyToJson = new HashMap<String, JsonObject>(); | |||
private Set<String> droppedKeys = new HashSet<String>(); | |||
private ListDataSource<JsonObject> ds = new ListDataSource<JsonObject>(); | |||
private boolean pendingDrop = false; | |||
@Override | |||
protected void extend(ServerConnector target) { | |||
registerRpc(DataProviderClientRpc.class, new DataProviderClientRpc() { | |||
@Override | |||
public void reset() { | |||
ds.asList().clear(); | |||
// Inform the server-side that all keys are now dropped. | |||
Set<String> keySet = new HashSet<String>(keyToJson.keySet()); | |||
for (String key : keySet) { | |||
dropKey(key); | |||
} | |||
sendDroppedKeys(); | |||
} | |||
@Override | |||
public void setData(long firstIndex, JsonArray data) { | |||
List<JsonObject> l = ds.asList(); | |||
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(object); | |||
} else if (i + firstIndex < l.size()) { | |||
int index = (int) (i + firstIndex); | |||
dropKey(getKey(l.get(index))); | |||
l.set(index, object); | |||
} | |||
keyToJson.put(getKey(object), object); | |||
} | |||
sendDroppedKeys(); | |||
} | |||
@Override | |||
public void add(JsonObject dataObject) { | |||
ds.asList().add(dataObject); | |||
keyToJson.put(getKey(dataObject), dataObject); | |||
} | |||
@Override | |||
public void drop(String key) { | |||
if (keyToJson.containsKey(key)) { | |||
ds.asList().remove(keyToJson.get(key)); | |||
dropKey(key); | |||
sendDroppedKeys(); | |||
} | |||
} | |||
@Override | |||
public void updateData(JsonArray data) { | |||
List<JsonObject> list = ds.asList(); | |||
for (int i = 0; i < data.length(); ++i) { | |||
JsonObject json = data.getObject(i); | |||
String key = getKey(json); | |||
if (keyToJson.containsKey(key)) { | |||
int index = list.indexOf(keyToJson.get(key)); | |||
list.set(index, json); | |||
keyToJson.put(key, json); | |||
} else { | |||
dropKey(key); | |||
} | |||
} | |||
sendDroppedKeys(); | |||
} | |||
}); | |||
ServerConnector parent = getParent(); | |||
if (parent instanceof HasDataSource) { | |||
((HasDataSource) parent).setDataSource(ds); | |||
} else { | |||
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) { | |||
droppedKeys.add(key); | |||
if (keyToJson.containsKey(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; | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
/* | |||
* Copyright 2000-2014 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.connectors.data; | |||
import com.vaadin.client.data.DataSource; | |||
import elemental.json.JsonObject; | |||
/** | |||
* Marker interface for Connectors that have a {@link DataSource}. | |||
*/ | |||
public interface HasDataSource { | |||
/** | |||
* Sets the data source for this Conenctor. | |||
* | |||
* @param dataSource | |||
* new data source | |||
*/ | |||
public void setDataSource(DataSource<JsonObject> dataSource); | |||
} |
@@ -0,0 +1,62 @@ | |||
/* | |||
* Copyright 2000-2014 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.server.communication.data.typed; | |||
import java.io.Serializable; | |||
/** | |||
* DataKeyMapper to map data objects to key strings. | |||
* | |||
* @since | |||
* @param <T> | |||
* data type | |||
*/ | |||
public interface DataKeyMapper<T> extends Serializable { | |||
/** | |||
* Gets the key for data object. If no key exists beforehand, a new key is | |||
* created. | |||
* | |||
* @param dataObject | |||
* data object for key mapping | |||
* @return key for given data object | |||
*/ | |||
String key(T dataObject); | |||
/** | |||
* Gets the data object identified by given key. | |||
* | |||
* @param key | |||
* key of a data object | |||
* @return identified data object; <code>null</code> if invalid key | |||
*/ | |||
T get(String key); | |||
/** | |||
* Removes a data object from the key mapping. The key is also dropped. | |||
* Dropped keys are not reused. | |||
* | |||
* @param dataObject | |||
* dropped data object | |||
*/ | |||
void remove(T dataObject); | |||
/** | |||
* Removes all data objects from the key mapping. The keys are also dropped. | |||
* Dropped keys are not reused. | |||
*/ | |||
void removeAll(); | |||
} |
@@ -0,0 +1,353 @@ | |||
/* | |||
* Copyright 2000-2014 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.server.communication.data.typed; | |||
import java.io.Serializable; | |||
import java.util.Collection; | |||
import java.util.HashSet; | |||
import java.util.LinkedHashSet; | |||
import java.util.Set; | |||
import com.vaadin.server.AbstractExtension; | |||
import com.vaadin.server.ClientConnector; | |||
import com.vaadin.shared.data.DataRequestRpc; | |||
import com.vaadin.shared.data.typed.DataProviderClientRpc; | |||
import com.vaadin.shared.data.typed.DataProviderConstants; | |||
import com.vaadin.ui.AbstractComponent; | |||
import elemental.json.Json; | |||
import elemental.json.JsonArray; | |||
import elemental.json.JsonObject; | |||
/** | |||
* DataProvider base class. This class is the base for all DataProvider | |||
* communication implementations. It uses {@link TypedDataGenerator}s to write | |||
* {@link JsonObject}s representing each data object to be sent to the | |||
* client-side. | |||
* | |||
* @since | |||
*/ | |||
public abstract class DataProvider<T> extends AbstractExtension { | |||
/** | |||
* Creates the appropriate type of DataProvider based on the type of | |||
* Collection provided to the method. | |||
* <p> | |||
* <strong>Note:</strong> this method will also extend the given component | |||
* with the newly created DataProvider. The user should <strong>not</strong> | |||
* call the {@link #extend(com.vaadin.server.AbstractClientConnector)} | |||
* method explicitly. | |||
* <p> | |||
* TODO: Actually use different DataProviders and provide an API for the | |||
* back end to inform changes back. | |||
* | |||
* @param data | |||
* collection of data objects | |||
* @param component | |||
* component to extend with the data provider | |||
* @return created data provider | |||
*/ | |||
public static <V> SimpleDataProvider<V> create(DataSource<V> data, | |||
AbstractComponent component) { | |||
SimpleDataProvider<V> dataProvider = new SimpleDataProvider<V>(data); | |||
dataProvider.extend(component); | |||
return dataProvider; | |||
} | |||
/** | |||
* A class for handling currently active data and dropping data that is no | |||
* longer needed. Data tracking is based on key string provided by | |||
* {@link DataKeyMapper}. | |||
* <p> | |||
* When the {@link DataProvider} is pushing new data to the client-side via | |||
* {@link DataProvider#pushData(long, Collection)}, | |||
* {@link #addActiveData(Collection)} and {@link #cleanUp(Collection)} are | |||
* called with the same parameter. In the clean up method any dropped data | |||
* objects that are not in the given collection will be cleaned up and | |||
* {@link TypedDataGenerator#destroyData(Object)} will be called for them. | |||
*/ | |||
protected class ActiveDataHandler implements Serializable, | |||
TypedDataGenerator<T> { | |||
/** | |||
* Set of key strings for currently active data objects | |||
*/ | |||
private final Set<String> activeData = new HashSet<String>(); | |||
/** | |||
* Set of key strings for data objects dropped on the client. This set | |||
* is used to clean up old data when it's no longer needed. | |||
*/ | |||
private final Set<String> droppedData = new HashSet<String>(); | |||
/** | |||
* Adds given objects as currently active objects. | |||
* | |||
* @param dataObjects | |||
* collection of new active data objects | |||
*/ | |||
public void addActiveData(Iterable<T> dataObjects) { | |||
for (T data : dataObjects) { | |||
if (!activeData.contains(getKeyMapper().key(data))) { | |||
activeData.add(getKeyMapper().key(data)); | |||
} | |||
} | |||
} | |||
/** | |||
* Executes the data destruction for dropped data that is not sent to | |||
* the client. This method takes most recently sent data objects in a | |||
* collection. Doing the clean up like this prevents the | |||
* {@link ActiveDataHandler} from creating new keys for rows that were | |||
* dropped but got re-requested by the client-side. In the case of | |||
* having all data at the client, the collection should be all the data | |||
* in the back end. | |||
* | |||
* @see DataProvider#pushData(long, Collection) | |||
* @param dataObjects | |||
* collection of most recently sent data to the client | |||
*/ | |||
public void cleanUp(Iterable<T> dataObjects) { | |||
Collection<String> keys = new HashSet<String>(); | |||
for (T data : dataObjects) { | |||
keys.add(getKeyMapper().key(data)); | |||
} | |||
// Remove still active rows that were dropped by the client | |||
droppedData.removeAll(keys); | |||
// Do data clean up for object no longer needed. | |||
dropData(droppedData); | |||
droppedData.clear(); | |||
} | |||
/** | |||
* Marks a data object identified by given key string to be dropped. | |||
* | |||
* @param key | |||
* key string | |||
*/ | |||
public void dropActiveData(String key) { | |||
if (activeData.contains(key)) { | |||
droppedData.add(key); | |||
} | |||
} | |||
/** | |||
* Returns the collection of all currently active data. | |||
* | |||
* @return collection of active data objects | |||
*/ | |||
public Collection<T> getActiveData() { | |||
HashSet<T> hashSet = new HashSet<T>(); | |||
for (String key : activeData) { | |||
hashSet.add(getKeyMapper().get(key)); | |||
} | |||
return hashSet; | |||
} | |||
@Override | |||
public void generateData(T data, JsonObject jsonObject) { | |||
// Write the key string for given data object | |||
jsonObject.put(DataProviderConstants.KEY, getKeyMapper().key(data)); | |||
} | |||
@Override | |||
public void destroyData(T data) { | |||
// Remove from active data set | |||
activeData.remove(getKeyMapper().key(data)); | |||
// Drop the registered key | |||
getKeyMapper().remove(data); | |||
} | |||
} | |||
private Collection<TypedDataGenerator<T>> generators = new LinkedHashSet<TypedDataGenerator<T>>(); | |||
protected ActiveDataHandler handler = new ActiveDataHandler(); | |||
protected DataProviderClientRpc rpc; | |||
protected DataSource<T> dataSource; | |||
private DataChangeHandler<T> dataChangeHandler; | |||
private DetachListener detachListener; | |||
private DataKeyMapper<T> keyMapper; | |||
protected DataProvider(DataSource<T> dataSource) { | |||
addDataGenerator(handler); | |||
this.dataSource = dataSource; | |||
rpc = getRpcProxy(DataProviderClientRpc.class); | |||
registerRpc(createRpc()); | |||
dataChangeHandler = createDataChangeHandler(); | |||
this.dataSource.addDataChangeHandler(dataChangeHandler); | |||
keyMapper = createKeyMapper(); | |||
} | |||
@Override | |||
public void attach() { | |||
super.attach(); | |||
if (detachListener == null) { | |||
detachListener = new DetachListener() { | |||
@Override | |||
public void detach(DetachEvent event) { | |||
cleanUp(); | |||
} | |||
}; | |||
getUI().addDetachListener(detachListener); | |||
} | |||
} | |||
@Override | |||
public void setParent(ClientConnector parent) { | |||
if (getParent() != null && parent == null) { | |||
// Removing from parent, clean up. | |||
cleanUp(); | |||
} | |||
super.setParent(parent); | |||
} | |||
/** | |||
* Adds a {@link TypedDataGenerator} to this {@link DataProvider}. | |||
* | |||
* @param generator | |||
* typed data generator | |||
*/ | |||
public void addDataGenerator(TypedDataGenerator<T> generator) { | |||
generators.add(generator); | |||
} | |||
/** | |||
* Removes a {@link TypedDataGenerator} from this {@link DataProvider}. | |||
* | |||
* @param generator | |||
* typed data generator | |||
*/ | |||
public void removeDataGenerator(TypedDataGenerator<T> generator) { | |||
generators.remove(generator); | |||
} | |||
/** | |||
* Gets the {@link DataKeyMapper} used by this {@link DataProvider}. Key | |||
* mapper can be used to map keys sent to the client-side back to their | |||
* respective data objects. | |||
* | |||
* @return key mapper | |||
*/ | |||
public DataKeyMapper<T> getKeyMapper() { | |||
return keyMapper; | |||
} | |||
/** | |||
* Sends given collection of data objects to the client-side. | |||
* | |||
* @param firstIndex | |||
* first index of pushed data | |||
* @param data | |||
* data objects to send as an iterable | |||
*/ | |||
protected void pushData(long firstIndex, Iterable<T> data) { | |||
JsonArray dataArray = Json.createArray(); | |||
int i = 0; | |||
for (T item : data) { | |||
dataArray.set(i++, getDataObject(item)); | |||
} | |||
rpc.setData(firstIndex, dataArray); | |||
handler.addActiveData(data); | |||
handler.cleanUp(data); | |||
} | |||
/** | |||
* Creates the JsonObject for given data object. This method calls all data | |||
* generators for it. | |||
* | |||
* @param data | |||
* data object to be made into a json object | |||
* @return json object representing the data object | |||
*/ | |||
protected JsonObject getDataObject(T data) { | |||
JsonObject dataObject = Json.createObject(); | |||
for (TypedDataGenerator<T> generator : generators) { | |||
generator.generateData(data, dataObject); | |||
} | |||
return dataObject; | |||
} | |||
/** | |||
* Drops data objects identified by given keys from memory. This will invoke | |||
* {@link TypedDataGenerator#destroyData} for each of those objects. | |||
* | |||
* @param droppedKeys | |||
* collection of dropped keys | |||
*/ | |||
private void dropData(Collection<String> droppedKeys) { | |||
for (String key : droppedKeys) { | |||
assert key != null : "Bookkeepping failure. Dropping a null key"; | |||
T data = getKeyMapper().get(key); | |||
assert data != null : "Bookkeepping failure. No data object to match key"; | |||
for (TypedDataGenerator<T> g : generators) { | |||
g.destroyData(data); | |||
} | |||
} | |||
} | |||
/** | |||
* Clean up method for removing all listeners attached by the | |||
* {@link DataProvider}. This method is called from {@link #remove()} or | |||
* when the UI gets detached. | |||
*/ | |||
protected void cleanUp() { | |||
if (dataSource != null) { | |||
dataSource.removeDataChangeHandler(dataChangeHandler); | |||
dataChangeHandler = null; | |||
} | |||
if (detachListener != null) { | |||
getUI().removeDetachListener(detachListener); | |||
detachListener = null; | |||
} | |||
} | |||
/** | |||
* Creates a {@link DataKeyMapper} to use with this {@link DataProvider}. | |||
* <p> | |||
* This method is called from the constructor. | |||
* | |||
* @return key mapper | |||
*/ | |||
protected abstract DataKeyMapper<T> createKeyMapper(); | |||
/** | |||
* Creates a {@link DataRequestRpc} used with this {@link DataProvider}. | |||
* <p> | |||
* This method is called from the constructor. | |||
* | |||
* @return data request rpc implementation | |||
*/ | |||
protected abstract DataRequestRpc createRpc(); | |||
/** | |||
* Creates a {@link DataChangeHandler} to use with the {@link DataSource}. | |||
* <p> | |||
* This method is called from the constructor. | |||
* | |||
* @return data change handler | |||
*/ | |||
protected abstract DataChangeHandler<T> createDataChangeHandler(); | |||
} |
@@ -0,0 +1,29 @@ | |||
/* | |||
* Copyright 2000-2014 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.server.communication.data.typed; | |||
/** | |||
* Generic {@link DataKeyMapper} implementation based on | |||
* {@link com.vaadin.server.KeyMapper}. Provides the interface on top of super | |||
* class. | |||
* | |||
* @since | |||
* @param <T> | |||
* data type | |||
*/ | |||
public class KeyMapper<T> extends com.vaadin.server.KeyMapper<T> implements | |||
DataKeyMapper<T> { | |||
} |
@@ -0,0 +1,189 @@ | |||
/* | |||
* Copyright 2000-2014 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.server.communication.data.typed; | |||
import java.util.Collections; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import com.vaadin.shared.data.DataRequestRpc; | |||
import com.vaadin.shared.data.typed.DataProviderClientRpc; | |||
import elemental.json.Json; | |||
import elemental.json.JsonArray; | |||
/** | |||
* DataProvider for Collections. This class takes care of sending data objects | |||
* stored in a Collection from the server-side to the client-side. | |||
* <p> | |||
* This is an implementation that does not provide any kind of lazy loading. All | |||
* data is sent to the client-side on the initial client response. | |||
* | |||
* @since | |||
*/ | |||
public class SimpleDataProvider<T> extends DataProvider<T> { | |||
/** | |||
* Simple implementation of collection data provider communication. All data | |||
* is sent by server automatically and no data is requested by client. | |||
*/ | |||
protected class SimpleDataRequestRpc implements DataRequestRpc { | |||
@Override | |||
public void requestRows(int firstRowIndex, int numberOfRows, | |||
int firstCachedRowIndex, int cacheSize) { | |||
throw new UnsupportedOperationException( | |||
"Collection data provider sends all data from server." | |||
+ " It does not expect client to request anything."); | |||
} | |||
@Override | |||
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(dataSource); | |||
} | |||
} | |||
private boolean reset = false; | |||
private final Set<T> updatedData = new HashSet<T>(); | |||
/** | |||
* Creates a new DataProvider with the given Collection. | |||
* | |||
* @param data | |||
* collection of data to use | |||
*/ | |||
protected SimpleDataProvider(DataSource<T> data) { | |||
super(data); | |||
} | |||
/** | |||
* Initially and in the case of a reset all data should be pushed to the | |||
* client. | |||
*/ | |||
@Override | |||
public void beforeClientResponse(boolean initial) { | |||
super.beforeClientResponse(initial); | |||
if (reset) { | |||
getRpcProxy(DataProviderClientRpc.class).reset(); | |||
} | |||
if (initial || reset) { | |||
pushData(0, dataSource); | |||
} else if (!updatedData.isEmpty()) { | |||
JsonArray dataArray = Json.createArray(); | |||
int i = 0; | |||
for (T data : updatedData) { | |||
dataArray.set(i++, getDataObject(data)); | |||
} | |||
rpc.updateData(dataArray); | |||
} | |||
reset = false; | |||
updatedData.clear(); | |||
} | |||
/** | |||
* Informs the DataProvider that a data object has been added. It is assumed | |||
* to be the last object in the collection. | |||
* | |||
* @param data | |||
* data object added to collection | |||
*/ | |||
protected void add(T data) { | |||
rpc.add(getDataObject(data)); | |||
handler.addActiveData(Collections.singleton(data)); | |||
} | |||
/** | |||
* Informs the DataProvider that a data object has been removed. | |||
* | |||
* @param data | |||
* data object removed from collection | |||
*/ | |||
protected void remove(T data) { | |||
if (handler.getActiveData().contains(data)) { | |||
rpc.drop(getKeyMapper().key(data)); | |||
} | |||
} | |||
/** | |||
* Informs the DataProvider that the collection has changed. | |||
*/ | |||
protected void reset() { | |||
if (reset) { | |||
return; | |||
} | |||
reset = true; | |||
markAsDirty(); | |||
} | |||
/** | |||
* Informs the DataProvider that a data object has been updated. | |||
* | |||
* @param data | |||
* updated data object | |||
*/ | |||
protected void refresh(T data) { | |||
if (updatedData.isEmpty()) { | |||
markAsDirty(); | |||
} | |||
updatedData.add(data); | |||
} | |||
@Override | |||
protected DataKeyMapper<T> createKeyMapper() { | |||
return new KeyMapper<T>(); | |||
} | |||
@Override | |||
protected DataRequestRpc createRpc() { | |||
return new SimpleDataRequestRpc(); | |||
} | |||
@Override | |||
protected DataChangeHandler<T> createDataChangeHandler() { | |||
return new DataChangeHandler<T>() { | |||
@Override | |||
public void onDataChange() { | |||
reset(); | |||
} | |||
@Override | |||
public void onDataAppend(T data) { | |||
add(data); | |||
} | |||
@Override | |||
public void onDataRemove(T data) { | |||
remove(data); | |||
} | |||
@Override | |||
public void onDataUpdate(T data) { | |||
refresh(data); | |||
} | |||
}; | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
/* | |||
* Copyright 2000-2014 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.server.communication.data.typed; | |||
import java.io.Serializable; | |||
import elemental.json.JsonObject; | |||
/** | |||
* Simple typed data generator for {@link DataProvider}. | |||
* | |||
* @since | |||
*/ | |||
public interface TypedDataGenerator<T> extends Serializable { | |||
/** | |||
* Adds data for given object to {@link JsonObject}. This JsonObject will be | |||
* sent to client-side DataSource. | |||
* | |||
* @param data | |||
* data object | |||
* @param jsonObject | |||
* json object being sent to the client | |||
*/ | |||
void generateData(T data, JsonObject jsonObject); | |||
/** | |||
* Informs the {@link TypedDataGenerator} that given data has been dropped | |||
* and is no longer needed. This method should clean up any unneeded | |||
* information stored for this data. | |||
* | |||
* @param data | |||
* dropped data | |||
*/ | |||
public void destroyData(T data); | |||
} |
@@ -0,0 +1,75 @@ | |||
/* | |||
* Copyright 2000-2014 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.shared.data.typed; | |||
import java.util.List; | |||
import com.vaadin.shared.communication.ClientRpc; | |||
import elemental.json.JsonArray; | |||
import elemental.json.JsonObject; | |||
/** | |||
* RPC interface used by DataProvider to send data to the client-side. | |||
* | |||
* @since | |||
*/ | |||
public interface DataProviderClientRpc extends ClientRpc { | |||
/** | |||
* Informs the client-side DataSource that all data has been invalidated. | |||
*/ | |||
void reset(); | |||
/** | |||
* Sets the data of the client-side DataSource to match the given data | |||
* starting from given index. | |||
* <p> | |||
* <strong>Note:</strong> This method will override any existing data in the | |||
* range starting from first index with the length of the data array. | |||
* | |||
* @param firstIndex | |||
* first index to update | |||
* @param data | |||
* array of new data | |||
*/ | |||
void setData(long firstIndex, JsonArray data); | |||
/** | |||
* Adds a new data object to the client-side DataSource. The new data object | |||
* is added at the end of the data source. | |||
* | |||
* @param dataObject | |||
* single added data object | |||
*/ | |||
void add(JsonObject dataObject); | |||
/** | |||
* Removes data identified by given key from the client-side DataSource. | |||
* | |||
* @param key | |||
* key identifying the object to be removed | |||
*/ | |||
void drop(String key); | |||
/** | |||
* Updates an array of objects based on their identifying key. | |||
* | |||
* @param data | |||
* array of updated data | |||
*/ | |||
void updateData(JsonArray data); | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2014 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.shared.data.typed; | |||
import java.io.Serializable; | |||
/** | |||
* Set of contants used by DataProvider. These are commonly used JsonObject keys | |||
* which are considered to be reserved for internal use. | |||
* | |||
* @since | |||
*/ | |||
public final class DataProviderConstants implements Serializable { | |||
public static final String KEY = "k"; | |||
} |