Change-Id: I8fba190a905a4dac6bfef5693064218672e23ba4feature/vaadin8-book
/* | |||||
* 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; | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} |
/* | |||||
* 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(); | |||||
} |
/* | |||||
* 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(); | |||||
} |
/* | |||||
* 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> { | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
}; | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} |
/* | |||||
* 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); | |||||
} |
/* | |||||
* 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"; | |||||
} |