Procházet zdrojové kódy

Simplify DataSource to be a function that provides Stream of values

This patch also removes the old automatic update logic.

Change-Id: Idb50137eee2592c1acd14ff67f577ad5edbbd2ff
feature/vaadin8-book
Teemu Suo-Anttila před 7 roky
rodič
revize
ba228972ae

+ 0
- 97
server/src/main/java/com/vaadin/tokka/server/communication/data/AbstractDataSource.java Zobrazit soubor

@@ -1,97 +0,0 @@
/*
* 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.tokka.server.communication.data;

import java.util.LinkedHashSet;
import java.util.Set;

import com.vaadin.tokka.event.Registration;

/**
* 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 final Set<DataChangeHandler<T>> handlers = new LinkedHashSet<DataChangeHandler<T>>();

@Override
public Registration addDataChangeHandler(DataChangeHandler<T> handler) {
if (handler != null) {
handlers.add(handler);
return () -> handlers.remove(handler);
}
return () -> { /* NO-OP */ };
}

/**
* Informs all handlers of a generic change in the data. This usually
* triggers a full cache invalidation and refresh of all data, which is
* usually an expensive operation. Should be called when none of the other
* methods help.
*
* @see #fireDataAppend(Object)
* @see #fireDataRemove(Object)
* @see #fireDataUpdate(Object)
*/
protected void fireDataChange() {
for (DataChangeHandler<T> handler : handlers) {
handler.onDataChange();
}
}

/**
* Informs all handlers of an added data object in the back end. This method
* should only be called when the newly added data object is the last object
* in the back end. Other additions can be handled with
* {@link #fireDataChange()}.
*
* @param data
* added data object
*/
protected void fireDataAppend(T data) {
for (DataChangeHandler<T> handler : handlers) {
handler.onDataAppend(data);
}
}

/**
* Informs all handlers of a data object that was removed from the back end.
*
* @param data
* removed data object
*/
protected void fireDataRemove(T data) {
for (DataChangeHandler<T> handler : handlers) {
handler.onDataRemove(data);
}
}

/**
* Informs all handlers of an existing data object that was updated in the
* back end.
*
* @param data
* updated data object
*/
protected void fireDataUpdate(T data) {
for (DataChangeHandler<T> handler : handlers) {
handler.onDataUpdate(data);
}
}
}

+ 7
- 84
server/src/main/java/com/vaadin/tokka/server/communication/data/DataCommunicator.java Zobrazit soubor

@@ -25,7 +25,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.vaadin.server.AbstractExtension;
import com.vaadin.server.ClientConnector;
import com.vaadin.shared.data.DataRequestRpc;
import com.vaadin.shared.data.typed.DataCommunicatorClientRpc;
import com.vaadin.shared.data.typed.DataProviderConstants;
@@ -72,8 +71,8 @@ public class DataCommunicator<T> extends AbstractExtension {
* longer needed. Data tracking is based on key string provided by
* {@link DataKeyMapper}.
* <p>
* When the {@link DataCommunicator} is pushing new data to the client-side via
* {@link DataCommunicator#pushData(long, Collection)},
* When the {@link DataCommunicator} is pushing new data to the client-side
* via {@link DataCommunicator#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
@@ -173,8 +172,6 @@ public class DataCommunicator<T> extends AbstractExtension {
protected DataCommunicatorClientRpc rpc;

protected DataSource<T> dataSource;
private Registration dataChangeHandler;
private DetachListener detachListener;
private DataKeyMapper<T> keyMapper;

private boolean reset = false;
@@ -186,8 +183,6 @@ public class DataCommunicator<T> extends AbstractExtension {
this.dataSource = dataSource;
rpc = getRpcProxy(DataCommunicatorClientRpc.class);
registerRpc(createRpc());
dataChangeHandler = this.dataSource
.addDataChangeHandler(createDataChangeHandler());
keyMapper = createKeyMapper();
}

@@ -200,11 +195,13 @@ public class DataCommunicator<T> extends AbstractExtension {
super.beforeClientResponse(initial);

if (initial || reset) {
rpc.reset(dataSource.size());
// FIXME: Rethink the size question.
rpc.reset((int) dataSource.apply(null).count());
}

if (!pushRows.isEmpty()) {
Stream<T> rowsToPush = dataSource.request()
// FIXME: Query object
Stream<T> rowsToPush = dataSource.apply(null)
.skip(pushRows.getStart()).limit(pushRows.length());
pushData(pushRows.getStart(), rowsToPush);
}
@@ -223,32 +220,6 @@ public class DataCommunicator<T> extends AbstractExtension {
updatedData.clear();
}

@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 DataCommunicator}.
*
@@ -340,22 +311,6 @@ public class DataCommunicator<T> extends AbstractExtension {
}
}

/**
* Clean up method for removing all listeners attached by the
* {@link DataCommunicator}. This method is called from {@link #remove()} or
* when the UI gets detached.
*/
protected void cleanUp() {
if (dataSource != null) {
dataChangeHandler.removeHandler();
dataChangeHandler = null;
}
if (detachListener != null) {
getUI().removeDetachListener(detachListener);
detachListener = null;
}
}

/**
* Informs the DataProvider that a data object has been added. It is assumed
* to be the last object in the collection.
@@ -406,7 +361,7 @@ public class DataCommunicator<T> extends AbstractExtension {
}

/**
* Creates a {@link DataKeyMapper} to use with this {@link DataCommunicator}.
* Creates a {@link DataKeyMapper} to use with this DataCommunicator.
* <p>
* This method is called from the constructor.
*
@@ -426,36 +381,4 @@ public class DataCommunicator<T> extends AbstractExtension {
protected DataRequestRpc createRpc() {
return new SimpleDataRequestRpc();
}

/**
* Creates a {@link DataChangeHandler} to use with the {@link DataSource}.
* <p>
* This method is called from the constructor.
*
* @return data change handler
*/
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);
}
};
}
}

+ 18
- 51
server/src/main/java/com/vaadin/tokka/server/communication/data/DataSource.java Zobrazit soubor

@@ -18,6 +18,7 @@ package com.vaadin.tokka.server.communication.data;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Function;
import java.util.stream.Stream;

import com.vaadin.tokka.event.Registration;
@@ -25,74 +26,40 @@ import com.vaadin.tokka.event.Registration;
/**
* Minimal DataSource API for communication between the DataProvider and a back
* end service.
* <p>
* FIXME: Missing Query class
*
* @since
* @param <T>
* data type
*/
public interface DataSource<T> extends Serializable {
public interface DataSource<T> extends Function<Object, Stream<T>>,
Serializable {

/**
* Requests data from the back end.
*
* @return stream of results
*/
Stream<T> request(/* Query query */);

/**
* 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);

/**
* TODO: Decide the fate of the size method.
*
* @return size of the data source
*/
int size();

/**
* Adds a new DataChangeHandler to this DataSource. DataChangeHandler is
* called when changes occur in DataSource.
*
* @param handler
* data change handler
*/
Registration addDataChangeHandler(DataChangeHandler<T> handler);

/**
* 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.
* This method creates a new {@link InMemoryDataSource} from a given
* Collection. The InMemoryDataSource creates a protective List copy of all
* the contents in the Collection.
*
* @param data
* collection of data
* @return list data source
* @return in-memory data source
*/
public static <T> ListDataSource<T> create(Collection<T> data) {
return new ListDataSource<>(data);
public static <T> InMemoryDataSource<T> create(Collection<T> data) {
return new InMemoryDataSource<>(data);
}

/**
* This method creates a new {@link ListDataSource} from given objects.
* This method creates a new {@link InMemoryDataSource} from given
* objects.The InMemoryDataSource creates a protective List copy of all the
* contents in the array.
*
* @param data
* data objects
* @return list data source
* @return in-memory data source
*/
public static <T> ListDataSource<T> create(T... data) {
return new ListDataSource<>(Arrays.asList(data));
@SafeVarargs
public static <T> InMemoryDataSource<T> create(T... data) {
return new InMemoryDataSource<>(Arrays.asList(data));
}
}

server/src/main/java/com/vaadin/tokka/server/communication/data/ListDataSource.java → server/src/main/java/com/vaadin/tokka/server/communication/data/InMemoryDataSource.java Zobrazit soubor

@@ -18,6 +18,7 @@ package com.vaadin.tokka.server.communication.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

/**
@@ -26,9 +27,10 @@ import java.util.stream.Stream;
* @param <T>
* data type
*/
public class ListDataSource<T> extends AbstractDataSource<T> {
public class InMemoryDataSource<T> implements DataSource<T> {

private List<T> backend;
// FIXME: Missing Query object
private Function<Object, Stream<T>> request;

/**
* Constructs a new ListDataSource. This method makes a protective copy of
@@ -37,35 +39,13 @@ public class ListDataSource<T> extends AbstractDataSource<T> {
* @param collection
* initial data
*/
public ListDataSource(Collection<T> collection) {
backend = new ArrayList<T>(collection);
public InMemoryDataSource(Collection<T> collection) {
final List<T> backend = new ArrayList<T>(collection);
request = query -> backend.stream();
}

@Override
public void save(T data) {
if (!backend.contains(data)) {
backend.add(data);
fireDataAppend(data);
} else {
fireDataUpdate(data);
}
}

@Override
public void remove(T data) {
if (backend.contains(data)) {
backend.remove(data);
fireDataRemove(data);
}
}

@Override
public Stream<T> request() {
return backend.stream();
}

@Override
public int size() {
return backend.size();
public Stream<T> apply(Object query) {
return request.apply(query);
}
}

+ 55
- 0
server/src/test/java/com/vaadin/tokka/data/datasource/InMemoryDataSourceTest.java Zobrazit soubor

@@ -0,0 +1,55 @@
package com.vaadin.tokka.data.datasource;

import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import org.junit.Before;
import org.junit.Test;

import com.vaadin.tokka.server.communication.data.DataSource;
import com.vaadin.tokka.server.communication.data.InMemoryDataSource;

public class InMemoryDataSourceTest {

private static class StrBean {

private String value;

public StrBean(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

}

private InMemoryDataSource<StrBean> dataSource;
private List<StrBean> data;

@Before
public void setUp() {
data = createData();
dataSource = DataSource.create(data);
}

@Test
public void testListContainsAllData() {
dataSource.apply(null).forEach(str -> assertTrue(data.contains(str)));
}

private List<StrBean> createData() {
List<StrBean> list = new ArrayList<>();
Stream.of("Foo", "Bar", "Baz").map(StrBean::new).forEach(list::add);
return list;
}

}

+ 0
- 100
server/src/test/java/com/vaadin/tokka/data/datasource/ListDataSourceTest.java Zobrazit soubor

@@ -1,100 +0,0 @@
package com.vaadin.tokka.data.datasource;

import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.tokka.server.communication.data.DataChangeHandler;
import com.vaadin.tokka.server.communication.data.DataSource;
import com.vaadin.tokka.server.communication.data.ListDataSource;

public class ListDataSourceTest {

private static class StrBean {

private String value;

public StrBean(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

}

private ListDataSource<StrBean> dataSource;
private List<StrBean> data;
private DataChangeHandler<StrBean> handler;

@Before
public void setUp() {
data = createData();
dataSource = DataSource.create(data);
handler = EasyMock.mock(DataChangeHandler.class);
}

@Test
public void testListContainsAllData() {
EasyMock.replay(handler);
dataSource.request().forEach(str -> assertTrue(data.contains(str)));
}

@Test
public void testListFiresDataAddEvent() {
StrBean strBean = new StrBean("Spam");
handler.onDataAppend(strBean);
EasyMock.expectLastCall().times(1);
EasyMock.replay(handler);

dataSource.addDataChangeHandler(handler);
dataSource.save(strBean);
}

@Test
public void testListFiresDataRemoveEvent() {
StrBean strBean = data.get(0);
handler.onDataRemove(strBean);
EasyMock.expectLastCall().times(1);
EasyMock.replay(handler);

dataSource.addDataChangeHandler(handler);
dataSource.remove(strBean);
}

@Test
public void testListFiresDataUpdateEvent() {
StrBean strBean = data.get(0);
handler.onDataUpdate(strBean);
EasyMock.expectLastCall().times(1);
EasyMock.replay(handler);

dataSource.addDataChangeHandler(handler);
strBean.setValue("Fuu");
dataSource.save(strBean);
}

@After
public void tearDown() {
EasyMock.verify(handler);
}

private List<StrBean> createData() {
List<StrBean> list = new ArrayList<>();
Stream.of("Foo", "Bar", "Baz").map(StrBean::new).forEach(list::add);
return list;
}

}

+ 1
- 24
server/src/test/java/com/vaadin/tokka/ui/components/AbstractListingTest.java Zobrazit soubor

@@ -2,15 +2,11 @@ package com.vaadin.tokka.ui.components;

import static org.junit.Assert.assertEquals;

import java.util.stream.Stream;

import org.junit.Before;
import org.junit.Test;

import com.vaadin.tokka.server.communication.data.AbstractDataSource;
import com.vaadin.tokka.server.communication.data.DataCommunicator;
import com.vaadin.tokka.server.communication.data.DataSource;
import com.vaadin.tokka.ui.components.AbstractListing;
import com.vaadin.tokka.ui.components.AbstractListing.AbstractListingExtension;

import elemental.json.JsonObject;
@@ -49,26 +45,7 @@ public class AbstractListingTest {

@Before
public void setUp() {
testComponent.setDataSource(new AbstractDataSource<String>() {

@Override
public void save(String data) {
}

@Override
public void remove(String data) {
}

@Override
public Stream<String> request() {
return Stream.of("Foo");
}

@Override
public int size() {
return 1;
}
});
testComponent.setDataSource(DataSource.create("Foo"));
}

@Test

+ 2
- 3
uitest/src/main/java/com/vaadin/tokka/tests/components/ListingTestUI.java Zobrazit soubor

@@ -62,7 +62,6 @@ public class ListingTestUI extends AbstractTestUI {
.nextInt(100)));
}
return beans;

}
}

@@ -82,8 +81,8 @@ public class ListingTestUI extends AbstractTestUI {
.forEach(s -> Notification.show(s))));
hLayout.addComponent(new Button("Random select", e -> {
DataSource<String> ds = select.getDataSource();
int skip = r.nextInt(ds.size());
ds.request().skip(skip).findFirst()
int skip = r.nextInt((int) ds.apply(null).count());
ds.apply(null).skip(skip).findFirst()
.ifPresent(select.getSelectionModel()::select);
}));


Načítá se…
Zrušit
Uložit