aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <teemusa@vaadin.com>2016-02-04 15:30:31 +0200
committerTeemu Suo-Anttila <teemusa@vaadin.com>2016-02-04 15:30:31 +0200
commited3fd8a20855d4bdbaa3f0e54534c93e649a8e50 (patch)
tree3f36cc325a3304a884dab22678d366438ab27d9c
parent607e17afe508e6da32086e513c863b192d4e2baa (diff)
downloadvaadin-framework-ed3fd8a20855d4bdbaa3f0e54534c93e649a8e50.tar.gz
vaadin-framework-ed3fd8a20855d4bdbaa3f0e54534c93e649a8e50.zip
Introduce DataSource API and DataChangeHandler for it
Change-Id: I3b24bca46ffc136884e163c94f3f4c304c1e12b2
-rw-r--r--server/src/com/vaadin/server/communication/data/typed/AbstractDataSource.java64
-rw-r--r--server/src/com/vaadin/server/communication/data/typed/CollectionDataSource.java69
-rw-r--r--server/src/com/vaadin/server/communication/data/typed/DataProvider.java8
-rw-r--r--server/src/com/vaadin/server/communication/data/typed/DataSource.java117
-rw-r--r--server/src/com/vaadin/server/communication/data/typed/SimpleDataProvider.java36
-rw-r--r--uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java52
6 files changed, 304 insertions, 42 deletions
diff --git a/server/src/com/vaadin/server/communication/data/typed/AbstractDataSource.java b/server/src/com/vaadin/server/communication/data/typed/AbstractDataSource.java
new file mode 100644
index 0000000000..bf16b47aa5
--- /dev/null
+++ b/server/src/com/vaadin/server/communication/data/typed/AbstractDataSource.java
@@ -0,0 +1,64 @@
+/*
+ * 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.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Base class for AbstractDataSource. Provides tracking for
+ * {@link DataChangeHandler}s and helper methods to call them.
+ *
+ * @since
+ */
+public abstract class AbstractDataSource<T> implements DataSource<T> {
+
+ protected Set<DataChangeHandler<T>> handlers = new LinkedHashSet<DataChangeHandler<T>>();
+
+ @Override
+ public void addDataChangeHandler(DataChangeHandler<T> handler) {
+ handlers.add(handler);
+ }
+
+ @Override
+ public void removeDataChangeHandler(DataChangeHandler<T> handler) {
+ handlers.remove(handler);
+ }
+
+ protected void fireDataChange() {
+ for (DataChangeHandler<T> handler : handlers) {
+ handler.onDataChange();
+ }
+ }
+
+ protected void fireDataAdd(T data) {
+ for (DataChangeHandler<T> handler : handlers) {
+ handler.onDataAdd(data);
+ }
+ }
+
+ protected void fireDataRemove(T data) {
+ for (DataChangeHandler<T> handler : handlers) {
+ handler.onDataRemove(data);
+ }
+ }
+
+ protected void fireDataUpdate(T data) {
+ for (DataChangeHandler<T> handler : handlers) {
+ handler.onDataUpdate(data);
+ }
+ }
+}
diff --git a/server/src/com/vaadin/server/communication/data/typed/CollectionDataSource.java b/server/src/com/vaadin/server/communication/data/typed/CollectionDataSource.java
new file mode 100644
index 0000000000..3a29befccf
--- /dev/null
+++ b/server/src/com/vaadin/server/communication/data/typed/CollectionDataSource.java
@@ -0,0 +1,69 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * DataSource that stores its data in a List. This DataSource does not support
+ * paging.
+ *
+ * @since
+ */
+public class CollectionDataSource<T> extends AbstractDataSource<T> {
+
+ protected List<T> backend;
+
+ public CollectionDataSource() {
+ backend = new ArrayList<T>();
+ }
+
+ public CollectionDataSource(Collection<T> data) {
+ this();
+ backend.addAll(data);
+ }
+
+ @Override
+ public void save(T data) {
+ if (backend.contains(data)) {
+ fireDataUpdate(data);
+ } else {
+ backend.add(data);
+ fireDataAdd(data);
+ }
+ }
+
+ @Override
+ public void remove(T data) {
+ if (backend.contains(data)) {
+ backend.remove(data);
+ fireDataRemove(data);
+ }
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return backend.iterator();
+ }
+
+ @Override
+ public long size() {
+ return backend.size();
+ }
+}
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 b47af1c033..d2fa859aba 100644
--- a/server/src/com/vaadin/server/communication/data/typed/DataProvider.java
+++ b/server/src/com/vaadin/server/communication/data/typed/DataProvider.java
@@ -59,7 +59,7 @@ public abstract class DataProvider<T> extends AbstractExtension {
* component to extend with the data provider
* @return created data provider
*/
- public static <V> SimpleDataProvider<V> create(Collection<V> data,
+ public static <V> SimpleDataProvider<V> create(DataSource<V> data,
AbstractComponent component) {
SimpleDataProvider<V> dataProvider = new SimpleDataProvider<V>(data);
dataProvider.extend(component);
@@ -98,7 +98,7 @@ public abstract class DataProvider<T> extends AbstractExtension {
* @param dataObjects
* collection of new active data objects
*/
- public void addActiveData(Collection<T> dataObjects) {
+ public void addActiveData(Iterable<T> dataObjects) {
for (T data : dataObjects) {
if (!activeData.contains(getKeyMapper().key(data))) {
activeData.add(getKeyMapper().key(data));
@@ -119,7 +119,7 @@ public abstract class DataProvider<T> extends AbstractExtension {
* @param dataObjects
* collection of most recently sent data to the client
*/
- public void cleanUp(Collection<T> dataObjects) {
+ public void cleanUp(Iterable<T> dataObjects) {
Collection<String> keys = new HashSet<String>();
for (T data : dataObjects) {
keys.add(getKeyMapper().key(data));
@@ -213,7 +213,7 @@ public abstract class DataProvider<T> extends AbstractExtension {
* @param data
* data objects to send as an iterable
*/
- protected void pushData(long firstIndex, Collection<T> data) {
+ protected void pushData(long firstIndex, Iterable<T> data) {
JsonArray dataArray = Json.createArray();
int i = 0;
diff --git a/server/src/com/vaadin/server/communication/data/typed/DataSource.java b/server/src/com/vaadin/server/communication/data/typed/DataSource.java
new file mode 100644
index 0000000000..d39286a028
--- /dev/null
+++ b/server/src/com/vaadin/server/communication/data/typed/DataSource.java
@@ -0,0 +1,117 @@
+/*
+ * 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;
+
+/**
+ * Minimal DataSource API for communication between the DataProvider and a back
+ * end service.
+ *
+ * @since
+ * @param <T>
+ * data type
+ */
+public interface DataSource<T> extends Iterable<T>, Serializable {
+
+ /**
+ * Gets the data object count from the back end.
+ *
+ * @return back end size
+ */
+ long size();
+
+ /**
+ * Saves a data object to the back end. If it's a new object, it should be
+ * created in the back end. Existing objects with changes should be stored.
+ *
+ * @param data
+ * data object to save
+ */
+ void save(T data);
+
+ /**
+ * Removes the given data object from the back end.
+ *
+ * @param data
+ * data object to remove
+ */
+ void remove(T data);
+
+ /**
+ * Adds a new DataChangeHandler to this DataSource. DataChangeHandler is
+ * called when changes occur in DataSource.
+ *
+ * @param handler
+ * data change handler
+ */
+ void addDataChangeHandler(DataChangeHandler<T> handler);
+
+ /**
+ * Removed a DataChangeHandler from this DataSource.
+ *
+ * @param handler
+ * data change handler
+ */
+ void removeDataChangeHandler(DataChangeHandler<T> handler);
+
+ /**
+ * Interface for DataSources to inform of various changes in the back end to
+ * anyone interested.
+ *
+ * @param <T>
+ * data type
+ */
+ interface DataChangeHandler<T> extends Serializable {
+
+ /**
+ * This method is called when a generic change in the DataSource. All
+ * cached data should be considered invalid.
+ * <p>
+ * <strong>Note: </strong> This method usually does an expensive full
+ * refresh of everything. Even though it makes everything up to date,
+ * you should only use this when really needed.
+ */
+ void onDataChange();
+
+ /**
+ * This method is called when a data object has been added as the last
+ * object in the back end.
+ *
+ * @param data
+ * new data object
+ */
+ void onDataAdd(T data);
+
+ /**
+ * This method is called when a data object has been removed from the
+ * back end.
+ *
+ * @param data
+ * removed data object
+ */
+ void onDataRemove(T data);
+
+ /**
+ * This method is called when a data object has been updated in the back
+ * end.
+ *
+ * @param data
+ * updated data object
+ */
+ void onDataUpdate(T data);
+ }
+}
diff --git a/server/src/com/vaadin/server/communication/data/typed/SimpleDataProvider.java b/server/src/com/vaadin/server/communication/data/typed/SimpleDataProvider.java
index ebf3734f51..9fba28e59a 100644
--- a/server/src/com/vaadin/server/communication/data/typed/SimpleDataProvider.java
+++ b/server/src/com/vaadin/server/communication/data/typed/SimpleDataProvider.java
@@ -15,10 +15,10 @@
*/
package com.vaadin.server.communication.data.typed;
-import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
+import com.vaadin.server.communication.data.typed.DataSource.DataChangeHandler;
import com.vaadin.shared.data.DataProviderClientRpc;
import com.vaadin.shared.data.DataRequestRpc;
@@ -64,7 +64,7 @@ public class SimpleDataProvider<T> extends DataProvider<T> {
private boolean reset = false;
private final Set<T> updatedData = new HashSet<T>();
- private Collection<T> data;
+ private DataSource<T> data;
// TODO: Allow customizing the used key mapper
private DataKeyMapper<T> keyMapper = new KeyMapper<T>();
@@ -74,8 +74,30 @@ public class SimpleDataProvider<T> extends DataProvider<T> {
* @param data
* collection of data to use
*/
- protected SimpleDataProvider(Collection<T> data) {
+ protected SimpleDataProvider(DataSource<T> data) {
this.data = data;
+ this.data.addDataChangeHandler(new DataChangeHandler<T>() {
+
+ @Override
+ public void onDataChange() {
+ reset();
+ }
+
+ @Override
+ public void onDataAdd(T data) {
+ add(data);
+ }
+
+ @Override
+ public void onDataRemove(T data) {
+ remove(data);
+ }
+
+ @Override
+ public void onDataUpdate(T data) {
+ refresh(data);
+ }
+ });
}
/**
@@ -114,7 +136,7 @@ public class SimpleDataProvider<T> extends DataProvider<T> {
* @param data
* data object added to collection
*/
- public void add(T data) {
+ protected void add(T data) {
rpc.add(getDataObject(data));
}
@@ -124,7 +146,7 @@ public class SimpleDataProvider<T> extends DataProvider<T> {
* @param data
* data object removed from collection
*/
- public void remove(T data) {
+ protected void remove(T data) {
if (handler.getActiveData().contains(data)) {
rpc.drop(getKeyMapper().key(data));
}
@@ -133,7 +155,7 @@ public class SimpleDataProvider<T> extends DataProvider<T> {
/**
* Informs the DataProvider that the collection has changed.
*/
- public void reset() {
+ protected void reset() {
if (reset) {
return;
}
@@ -148,7 +170,7 @@ public class SimpleDataProvider<T> extends DataProvider<T> {
* @param data
* updated data object
*/
- public void refresh(T data) {
+ protected void refresh(T data) {
if (updatedData.isEmpty()) {
markAsDirty();
}
diff --git a/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java b/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java
index c5e2dfedb2..0cd45f1994 100644
--- a/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java
+++ b/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java
@@ -24,7 +24,9 @@ import java.util.Random;
import com.vaadin.annotations.Widgetset;
import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.communication.data.typed.CollectionDataSource;
import com.vaadin.server.communication.data.typed.DataProvider;
+import com.vaadin.server.communication.data.typed.DataSource;
import com.vaadin.server.communication.data.typed.SimpleDataProvider;
import com.vaadin.server.communication.data.typed.TypedDataGenerator;
import com.vaadin.tests.components.AbstractTestUI;
@@ -44,14 +46,8 @@ public class DummyDataProviderUI extends AbstractTestUI {
public static class DummyDataComponent extends AbstractComponent {
private SimpleDataProvider<ComplexPerson> dataProvider;
- private List<ComplexPerson> data;
- public DummyDataComponent(Collection<ComplexPerson> data) {
- if (data instanceof List) {
- this.data = (List<ComplexPerson>) data;
- } else {
- this.data = new ArrayList<ComplexPerson>(data);
- }
+ public DummyDataComponent(DataSource<ComplexPerson> data) {
dataProvider = DataProvider.create(data, this);
dataProvider
.addDataGenerator(new TypedDataGenerator<ComplexPerson>() {
@@ -69,29 +65,22 @@ public class DummyDataProviderUI extends AbstractTestUI {
}
});
}
+ }
- void addItem(ComplexPerson person) {
- // TODO: This should be in the back end implementation
- if (data.add(person)) {
- dataProvider.add(person);
- }
- }
+ private static class MyDataSource extends
+ CollectionDataSource<ComplexPerson> {
- void removeItem(ComplexPerson person) {
- // TODO: This should be in the back end implementation
- if (data.remove(person)) {
- dataProvider.remove(person);
- }
+ public MyDataSource(Collection<ComplexPerson> data) {
+ super(data);
}
- public void sort(Comparator<ComplexPerson> comparator) {
- // TODO: This should be in the back end implementation
- Collections.sort(data, comparator);
- dataProvider.reset();
+ public void sort(Comparator<ComplexPerson> c) {
+ Collections.sort(backend, c);
+ fireDataChange();
}
- public void update(ComplexPerson p) {
- dataProvider.refresh(p);
+ public List<ComplexPerson> getData() {
+ return Collections.unmodifiableList(backend);
}
}
@@ -108,41 +97,42 @@ public class DummyDataProviderUI extends AbstractTestUI {
};
private Random r = new Random(RANDOM_SEED);
- private List<ComplexPerson> persons = createPersons(PERSON_COUNT, r);
+ private MyDataSource dataSource;
private DummyDataComponent dummy;
@Override
protected void setup(VaadinRequest request) {
- dummy = new DummyDataComponent(persons);
+ dataSource = new MyDataSource(createPersons(PERSON_COUNT, r));
+ dummy = new DummyDataComponent(dataSource);
Button remove = new Button("Remove third", new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
- dummy.removeItem(persons.get(2));
+ dataSource.remove(dataSource.getData().get(2));
}
});
Button add = new Button("Add new", new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
- dummy.addItem(ComplexPerson.create(r));
+ dataSource.save(ComplexPerson.create(r));
}
});
Button sort = new Button("Sort content", new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
- dummy.sort(nameComparator);
+ dataSource.sort(nameComparator);
}
});
Button edit = new Button("Edit first", new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
- ComplexPerson p = persons.get(0);
+ ComplexPerson p = dataSource.iterator().next();
p.setFirstName("Foo");
- dummy.update(p);
+ dataSource.save(p);
}
});