From 62d712fb4514b2ed118539f43967c9b24d906518 Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Mon, 12 Sep 2016 16:52:13 +0300 Subject: DataSource.refreshAll() for notifying components to reload (#233). The method refreshAll() fires an event which is handled by UI components to reload their content. Change-Id: Ibbbe1f24b08ed883f0dda93c3ff6f05f380e9eaa --- .../com/vaadin/server/data/AbstractDataSource.java | 104 +++++++++++++++++++++ .../com/vaadin/server/data/BackEndDataSource.java | 2 +- .../com/vaadin/server/data/DataChangeEvent.java | 48 ++++++++++ .../com/vaadin/server/data/DataCommunicator.java | 31 ++++++ .../java/com/vaadin/server/data/DataSource.java | 23 +++++ .../com/vaadin/server/data/DataSourceListener.java | 42 +++++++++ .../com/vaadin/server/data/ListDataSource.java | 36 ++++--- 7 files changed, 270 insertions(+), 16 deletions(-) create mode 100644 server/src/main/java/com/vaadin/server/data/AbstractDataSource.java create mode 100644 server/src/main/java/com/vaadin/server/data/DataChangeEvent.java create mode 100644 server/src/main/java/com/vaadin/server/data/DataSourceListener.java (limited to 'server/src/main/java') diff --git a/server/src/main/java/com/vaadin/server/data/AbstractDataSource.java b/server/src/main/java/com/vaadin/server/data/AbstractDataSource.java new file mode 100644 index 0000000000..9dba0bd35e --- /dev/null +++ b/server/src/main/java/com/vaadin/server/data/AbstractDataSource.java @@ -0,0 +1,104 @@ +/* + * 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.server.data; + +import java.lang.reflect.Method; +import java.util.EventObject; +import java.util.Objects; + +import com.vaadin.event.EventRouter; +import com.vaadin.shared.Registration; + +/** + * Abstract data source implementation which takes care of refreshing data from + * the underlying data provider. + * + * @author Vaadin Ltd + * @since 8.0 + * + */ +public abstract class AbstractDataSource implements DataSource { + + private EventRouter eventRouter; + + @Override + public Registration addDataSourceListener(DataSourceListener listener) { + Objects.requireNonNull(listener, "listener cannot be null"); + addListener(DataChangeEvent.class, listener, + DataSourceListener.class.getMethods()[0]); + return () -> removeListener(DataChangeEvent.class, listener); + } + + @Override + public void refreshAll() { + fireEvent(new DataChangeEvent(this)); + } + + /** + * Registers a new listener with the specified activation method to listen + * events generated by this component. If the activation method does not + * have any arguments the event object will not be passed to it when it's + * called. + * + * @param eventType + * the type of the listened event. Events of this type or its + * subclasses activate the listener. + * @param listener + * the object instance who owns the activation method. + * @param method + * the activation method. + * + */ + protected void addListener(Class eventType, DataSourceListener listener, + Method method) { + if (eventRouter == null) { + eventRouter = new EventRouter(); + } + eventRouter.addListener(eventType, listener, method); + } + + /** + * Removes all registered listeners matching the given parameters. Since + * this method receives the event type and the listener object as + * parameters, it will unregister all object's methods that are + * registered to listen to events of type eventType generated + * by this component. + * + * @param eventType + * the exact event type the object listens to. + * @param listener + * the target object that has registered to listen to events of + * type eventType with one or more methods. + */ + protected void removeListener(Class eventType, + DataSourceListener listener) { + if (eventRouter != null) { + eventRouter.removeListener(eventType, listener); + } + } + + /** + * Sends the event to all listeners. + * + * @param event + * the Event to be sent to all listeners. + */ + protected void fireEvent(EventObject event) { + if (eventRouter != null) { + eventRouter.fireEvent(event); + } + } +} diff --git a/server/src/main/java/com/vaadin/server/data/BackEndDataSource.java b/server/src/main/java/com/vaadin/server/data/BackEndDataSource.java index 34379deb60..e0cfc8f48b 100644 --- a/server/src/main/java/com/vaadin/server/data/BackEndDataSource.java +++ b/server/src/main/java/com/vaadin/server/data/BackEndDataSource.java @@ -27,7 +27,7 @@ import java.util.stream.Stream; * @param * data source data type */ -public class BackEndDataSource implements DataSource { +public class BackEndDataSource extends AbstractDataSource { private Function> request; private Function sizeCallback; diff --git a/server/src/main/java/com/vaadin/server/data/DataChangeEvent.java b/server/src/main/java/com/vaadin/server/data/DataChangeEvent.java new file mode 100644 index 0000000000..cf2e9621de --- /dev/null +++ b/server/src/main/java/com/vaadin/server/data/DataChangeEvent.java @@ -0,0 +1,48 @@ +/* + * 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.server.data; + +import java.util.EventObject; + +/** + * An event fired when the data of a {@code DataSource} changes. + * + * + * @see DataSourceListener + * + * @author Vaadin Ltd + * @since 8.0 + * + */ +public class DataChangeEvent extends EventObject { + + /** + * Creates a new {@code DataChangeEvent} event originating from the given + * data source. + * + * @param source + * the data source, not null + */ + public DataChangeEvent(DataSource source) { + super(source); + } + + @Override + public DataSource getSource() { + return (DataSource) super.getSource(); + } + +} diff --git a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java index b0ade744de..5fc11bb15d 100644 --- a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java +++ b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java @@ -32,6 +32,7 @@ import java.util.stream.Stream; import com.vaadin.server.AbstractExtension; import com.vaadin.server.KeyMapper; import com.vaadin.shared.Range; +import com.vaadin.shared.Registration; import com.vaadin.shared.data.DataCommunicatorClientRpc; import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.data.DataRequestRpc; @@ -50,6 +51,8 @@ import elemental.json.JsonObject; */ public class DataCommunicator extends AbstractExtension { + private Registration dataSourceUpdateRegistration; + /** * Simple implementation of collection data provider communication. All data * is sent by server automatically and no data is requested by client. @@ -195,6 +198,18 @@ public class DataCommunicator extends AbstractExtension { keyMapper = createKeyMapper(); } + @Override + public void attach() { + super.attach(); + attachDataSourceListener(); + } + + @Override + public void detach() { + super.detach(); + detachDataSourceListener(); + } + /** * Initially and in the case of a reset all data should be pushed to the * client. @@ -459,6 +474,22 @@ public class DataCommunicator extends AbstractExtension { public void setDataSource(DataSource dataSource) { Objects.requireNonNull(dataSource, "data source cannot be null"); this.dataSource = dataSource; + detachDataSourceListener(); + if (isAttached()) { + attachDataSourceListener(); + } reset(); } + + private void attachDataSourceListener() { + dataSourceUpdateRegistration = getDataSource() + .addDataSourceListener(event -> reset()); + } + + private void detachDataSourceListener() { + if (dataSourceUpdateRegistration != null) { + dataSourceUpdateRegistration.remove(); + dataSourceUpdateRegistration = null; + } + } } diff --git a/server/src/main/java/com/vaadin/server/data/DataSource.java b/server/src/main/java/com/vaadin/server/data/DataSource.java index 25679a3c89..f767f60cf0 100644 --- a/server/src/main/java/com/vaadin/server/data/DataSource.java +++ b/server/src/main/java/com/vaadin/server/data/DataSource.java @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.function.Function; import java.util.stream.Stream; +import com.vaadin.shared.Registration; + /** * Minimal DataSource API for communication between the DataProvider and a back * end service. @@ -55,6 +57,27 @@ public interface DataSource */ int size(Query t); + /** + * Refreshes all data based on currently available data in the underlying + * provider. + */ + void refreshAll(); + + /** + * Adds a data source listener. The listener is called when some piece of + * data is updated. + *

+ * The {@link #refreshAll()} method fires {@link DataChangeEvent} each time + * when it's called. It allows to update UI components when user changes + * something in the underlying data. + * + * @see #refreshAll() + * @param listener + * the data change listener, not null + * @return a registration for the listener + */ + Registration addDataSourceListener(DataSourceListener listener); + /** * This method creates a new {@link ListDataSource} from a given Collection. * The ListDataSource creates a protective List copy of all the contents in diff --git a/server/src/main/java/com/vaadin/server/data/DataSourceListener.java b/server/src/main/java/com/vaadin/server/data/DataSourceListener.java new file mode 100644 index 0000000000..c9c486cfd1 --- /dev/null +++ b/server/src/main/java/com/vaadin/server/data/DataSourceListener.java @@ -0,0 +1,42 @@ +/* + * 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.server.data; + +import java.io.Serializable; + +/** + * Interface for listening for a data change events fired by a + * {@link DataSource}. + * + * @author Vaadin Ltd + * @since 8.0 + */ +public interface DataSourceListener extends Serializable { + + /** + * Invoked when this listener receives a data change event from a data + * source to which it has been added. + *

+ * This event is fired when something has changed in the underlying data. It + * doesn't allow to distinguish different kind of events + * (add/remove/update). It means that the method implementation normally + * just reloads the whole data to refresh. + * + * @param event + * the received event, not null + */ + void onDataChange(DataChangeEvent event); +} diff --git a/server/src/main/java/com/vaadin/server/data/ListDataSource.java b/server/src/main/java/com/vaadin/server/data/ListDataSource.java index ecac378182..64b7c03cb1 100644 --- a/server/src/main/java/com/vaadin/server/data/ListDataSource.java +++ b/server/src/main/java/com/vaadin/server/data/ListDataSource.java @@ -15,10 +15,8 @@ */ package com.vaadin.server.data; -import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; -import java.util.List; import java.util.Objects; import java.util.function.Function; import java.util.stream.Stream; @@ -30,10 +28,10 @@ import java.util.stream.Stream; * @param * data type */ -public class ListDataSource implements DataSource { +public class ListDataSource extends AbstractDataSource { - private Function> request; - private int size; + private Comparator sortOrder; + private final Collection backend; /** * Constructs a new ListDataSource. This method makes a protective copy of @@ -44,26 +42,33 @@ public class ListDataSource implements DataSource { */ public ListDataSource(Collection items) { Objects.requireNonNull(items, "items cannot be null"); - final List backend = new ArrayList<>(items); - request = query -> backend.stream(); - size = backend.size(); + backend = items; + sortOrder = null; } /** * Chaining constructor for making modified {@link ListDataSource}s. This * Constructor is used internally for making sorted and filtered variants of * a base data source with actual data. + * + * @param items + * the backend data from the original list data source + * @param sortOrder + * a {@link Comparator} providing the needed sorting order * - * @param request - * request for the new data source */ - protected ListDataSource(Function> request) { - this.request = request; + protected ListDataSource(Collection items, Comparator sortOrder) { + this(items); + this.sortOrder = sortOrder; } @Override public Stream apply(Query query) { - return request.apply(query); + Stream stream = backend.stream(); + if (sortOrder != null) { + stream = stream.sorted(sortOrder); + } + return stream; } /** @@ -77,7 +82,7 @@ public class ListDataSource implements DataSource { * @return new data source with modified sorting */ public ListDataSource sortingBy(Comparator sortOrder) { - return new ListDataSource<>(q -> request.apply(q).sorted(sortOrder)); + return new ListDataSource<>(backend, sortOrder); } /** @@ -113,6 +118,7 @@ public class ListDataSource implements DataSource { */ @Override public int size(Query t) { - return size; + return backend.size(); } + } -- cgit v1.2.3