diff options
author | Denis Anisimov <denis@vaadin.com> | 2016-09-12 16:52:13 +0300 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2016-09-14 11:41:25 +0000 |
commit | 62d712fb4514b2ed118539f43967c9b24d906518 (patch) | |
tree | 4fe5dc00f8eac65a42c51ef893edeb397e62d093 /server/src/main/java | |
parent | 4aac5294bba6eff216cddaa826b7739494376ff7 (diff) | |
download | vaadin-framework-62d712fb4514b2ed118539f43967c9b24d906518.tar.gz vaadin-framework-62d712fb4514b2ed118539f43967c9b24d906518.zip |
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
Diffstat (limited to 'server/src/main/java')
7 files changed, 270 insertions, 16 deletions
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<T> implements DataSource<T> { + + 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 <code>object</code>'s methods that are + * registered to listen to events of type <code>eventType</code> generated + * by this component. + * + * @param eventType + * the exact event type the <code>object</code> listens to. + * @param listener + * the target object that has registered to listen to events of + * type <code>eventType</code> 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 <T> * data source data type */ -public class BackEndDataSource<T> implements DataSource<T> { +public class BackEndDataSource<T> extends AbstractDataSource<T> { private Function<Query, Stream<T>> request; private Function<Query, Integer> 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<T> 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<T> 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<T> extends AbstractExtension { public void setDataSource(DataSource<T> 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. @@ -56,6 +58,27 @@ public interface DataSource<T> 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. + * <p> + * 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 * the Collection. 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. + * <p> + * 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 <T> * data type */ -public class ListDataSource<T> implements DataSource<T> { +public class ListDataSource<T> extends AbstractDataSource<T> { - private Function<Query, Stream<T>> request; - private int size; + private Comparator<T> sortOrder; + private final Collection<T> backend; /** * Constructs a new ListDataSource. This method makes a protective copy of @@ -44,26 +42,33 @@ public class ListDataSource<T> implements DataSource<T> { */ public ListDataSource(Collection<T> items) { Objects.requireNonNull(items, "items cannot be null"); - final List<T> 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<Query, Stream<T>> request) { - this.request = request; + protected ListDataSource(Collection<T> items, Comparator<T> sortOrder) { + this(items); + this.sortOrder = sortOrder; } @Override public Stream<T> apply(Query query) { - return request.apply(query); + Stream<T> stream = backend.stream(); + if (sortOrder != null) { + stream = stream.sorted(sortOrder); + } + return stream; } /** @@ -77,7 +82,7 @@ public class ListDataSource<T> implements DataSource<T> { * @return new data source with modified sorting */ public ListDataSource<T> sortingBy(Comparator<T> sortOrder) { - return new ListDataSource<>(q -> request.apply(q).sorted(sortOrder)); + return new ListDataSource<>(backend, sortOrder); } /** @@ -113,6 +118,7 @@ public class ListDataSource<T> implements DataSource<T> { */ @Override public int size(Query t) { - return size; + return backend.size(); } + } |