diff options
author | Teemu Suo-Anttila <teemusa@vaadin.com> | 2016-06-15 18:36:39 +0300 |
---|---|---|
committer | Teemu Suo-Anttila <teemusa@vaadin.com> | 2016-06-15 18:38:08 +0300 |
commit | 2b8eca955d6583d85254652d33a7d954e327379b (patch) | |
tree | f00535a410e81132d9038a8045167fd886c739d4 | |
parent | 7d31b7132f701008a34aa3d968273f8b5037797b (diff) | |
download | vaadin-framework-2b8eca955d6583d85254652d33a7d954e327379b.tar.gz vaadin-framework-2b8eca955d6583d85254652d33a7d954e327379b.zip |
Extract a base class from typed NativeSelect
This patch adds some helpers for extensions aimed at Listing components.
Change-Id: I7ac2ee56ca7e44ac0300c94d02d30533aea11f9a
7 files changed, 341 insertions, 120 deletions
diff --git a/server/src/main/java/com/vaadin/server/ListingExtension.java b/server/src/main/java/com/vaadin/server/ListingExtension.java new file mode 100644 index 0000000000..72799e2662 --- /dev/null +++ b/server/src/main/java/com/vaadin/server/ListingExtension.java @@ -0,0 +1,23 @@ +/* + * 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; + +import com.vaadin.ui.components.Listing; + +public interface ListingExtension<T> extends Extension { + + public void extend(Listing<T> listing); +} diff --git a/server/src/main/java/com/vaadin/server/communication/data/typed/AbstractSelectionModel.java b/server/src/main/java/com/vaadin/server/communication/data/typed/AbstractSelectionModel.java index f45720d60e..f132d85f8b 100644 --- a/server/src/main/java/com/vaadin/server/communication/data/typed/AbstractSelectionModel.java +++ b/server/src/main/java/com/vaadin/server/communication/data/typed/AbstractSelectionModel.java @@ -16,10 +16,10 @@ package com.vaadin.server.communication.data.typed; import com.vaadin.server.AbstractClientConnector; -import com.vaadin.server.AbstractExtension; import com.vaadin.server.Extension; import com.vaadin.shared.data.typed.DataProviderConstants; import com.vaadin.ui.Component; +import com.vaadin.ui.components.AbstractListing.AbstractListingExtension; import com.vaadin.ui.components.Listing; import elemental.json.JsonObject; @@ -30,8 +30,8 @@ import elemental.json.JsonObject; * @param <T> * type of selected data */ -public abstract class AbstractSelectionModel<T> extends AbstractExtension - implements SelectionModel<T>, TypedDataGenerator<T> { +public abstract class AbstractSelectionModel<T> extends + AbstractListingExtension<T> implements SelectionModel<T> { private Listing<T> parent; @@ -47,42 +47,6 @@ public abstract class AbstractSelectionModel<T> extends AbstractExtension } } - // TODO: Following methods should be somewhere else being less weird. - - protected final Listing<T> getParentListing() { - return parent; - } - - protected final DataProvider<T> getDataProvider() { - for (Extension e : ((Component) parent).getExtensions()) { - if (e instanceof DataProvider) { - return ((DataProvider<T>) e); - } - } - return null; - } - - protected final DataKeyMapper<T> getKeyMapper() { - for (Extension e : ((Component) parent).getExtensions()) { - if (e instanceof DataProvider) { - return ((DataProvider<T>) e).getKeyMapper(); - } - } - return null; - } - - protected final T getData(String key) { - DataKeyMapper<T> keyMapper = getKeyMapper(); - if (keyMapper != null) { - return keyMapper.get(key); - } - return null; - } - - protected final void refresh(T value) { - getDataProvider().refresh(value); - } - @Override public void generateData(T data, JsonObject jsonObject) { if (getSelected().contains(data)) { diff --git a/server/src/main/java/com/vaadin/server/communication/data/typed/DataProvider.java b/server/src/main/java/com/vaadin/server/communication/data/typed/DataProvider.java index 348e785920..0a47462eb2 100644 --- a/server/src/main/java/com/vaadin/server/communication/data/typed/DataProvider.java +++ b/server/src/main/java/com/vaadin/server/communication/data/typed/DataProvider.java @@ -46,11 +46,6 @@ 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. * @@ -63,7 +58,6 @@ public abstract class DataProvider<T> extends AbstractExtension { public static <V> SimpleDataProvider<V> create(DataSource<V> data, AbstractComponent component) { SimpleDataProvider<V> dataProvider = new SimpleDataProvider<V>(data); - dataProvider.extend(component); return dataProvider; } diff --git a/server/src/main/java/com/vaadin/server/communication/data/typed/SelectionModel.java b/server/src/main/java/com/vaadin/server/communication/data/typed/SelectionModel.java index ef3bf69523..e16bb7b2a6 100644 --- a/server/src/main/java/com/vaadin/server/communication/data/typed/SelectionModel.java +++ b/server/src/main/java/com/vaadin/server/communication/data/typed/SelectionModel.java @@ -22,7 +22,7 @@ import java.util.List; import com.vaadin.event.handler.Handler; import com.vaadin.event.handler.Registration; -import com.vaadin.server.Extension; +import com.vaadin.server.ListingExtension; import com.vaadin.ui.Component; import com.vaadin.ui.components.HasValue; import com.vaadin.ui.components.Listing; @@ -34,7 +34,7 @@ import com.vaadin.ui.components.Listing; * @param <T> * type of selected values */ -public interface SelectionModel<T> extends Serializable, Extension { +public interface SelectionModel<T> extends Serializable, ListingExtension<T> { /** * Selection model for selection a single value. diff --git a/server/src/main/java/com/vaadin/ui/components/AbstractListing.java b/server/src/main/java/com/vaadin/ui/components/AbstractListing.java new file mode 100644 index 0000000000..3964d31e28 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/components/AbstractListing.java @@ -0,0 +1,206 @@ +/* + * 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.ui.components; + +import java.util.LinkedHashSet; +import java.util.Set; + +import com.vaadin.server.AbstractClientConnector; +import com.vaadin.server.AbstractExtension; +import com.vaadin.server.ListingExtension; +import com.vaadin.server.communication.data.typed.AbstractSelectionModel; +import com.vaadin.server.communication.data.typed.DataProvider; +import com.vaadin.server.communication.data.typed.SelectionModel; +import com.vaadin.server.communication.data.typed.TypedDataGenerator; +import com.vaadin.ui.AbstractComponent; + +/** + * Base class for Listing components. Provides common handling for + * {@link DataProvider}, {@link SelectionModel} and {@link TypedDataGenerator}s. + * + * @param <T> + * listing data type + */ +public abstract class AbstractListing<T> extends AbstractComponent implements + Listing<T> { + + /** + * Helper base class for creating extensions for Listing components. This + * class provides helpers for accessing the underlying parts of the + * component and it's communicational mechanism. + * + * @param <T> + * listing data type + */ + public abstract static class AbstractListingExtension<T> extends + AbstractExtension implements ListingExtension<T>, + TypedDataGenerator<T> { + + /** + * {@inheritDoc} + * <p> + * Note: AbstractListingExtensions need parent to be of type + * AbstractListing. + * + * @throws IllegalArgument + * if parent is not an AbstractListing + */ + @Override + public void extend(Listing<T> listing) { + if (listing instanceof AbstractListing) { + AbstractListing<T> parent = (AbstractListing<T>) listing; + super.extend(parent); + parent.addDataGenerator(this); + } else { + throw new IllegalArgumentException( + "Parent needs to extend AbstractListing"); + } + } + + @Override + public void remove() { + getParent().removeDataGenerator(this); + + super.remove(); + } + + /** + * Gets a data object based on it's client-side identifier key. + * + * @param key + * key for data object + * @return the data object + */ + protected T getData(String key) { + DataProvider<T> dataProvider = getParent().getDataProvider(); + if (dataProvider != null) { + return dataProvider.getKeyMapper().get(key); + } + return null; + } + + @Override + @SuppressWarnings("unchecked") + public AbstractListing<T> getParent() { + return (AbstractListing<T>) super.getParent(); + } + + /** + * Helper method for refreshing a single data object. + * + * @param data + * data object to refresh + */ + protected void refresh(T data) { + DataProvider<T> dataProvider = getParent().getDataProvider(); + if (dataProvider != null) { + dataProvider.refresh(data); + } + } + } + + /* DataProvider for this Listing component */ + private DataProvider<T> dataProvider; + /* TypedDataGenerators used by this Listing */ + private Set<TypedDataGenerator<T>> generators = new LinkedHashSet<>(); + /* SelectionModel for this Listing */ + private SelectionModel<T> selectionModel; + + /** + * Adds a {@link TypedDataGenerator} for the {@link DataProvider} of this + * Listing component. + * + * @param generator + * typed data generator + */ + protected void addDataGenerator(TypedDataGenerator<T> generator) { + generators.add(generator); + + if (dataProvider != null) { + dataProvider.addDataGenerator(generator); + } + } + + /** + * Removed a {@link TypedDataGenerator} from the {@link DataProvider} of + * this Listing component. + * + * @param generator + * typed data generator + */ + protected void removeDataGenerator(TypedDataGenerator<T> generator) { + generators.remove(generator); + + if (dataProvider != null) { + dataProvider.removeDataGenerator(generator); + } + } + + /** + * Extends this listing component with a data provider. This method + * reapplies all data generators to the new data provider. + * + * @param dataProvider + * new data provider + */ + protected void setDataProvider(DataProvider<T> dataProvider) { + if (this.dataProvider == dataProvider) { + return; + } + + if (this.dataProvider != null) { + this.dataProvider.remove(); + } + + this.dataProvider = dataProvider; + if (dataProvider != null) { + addExtension(dataProvider); + + if (dataProvider != null) { + // Reapply all data generators to the new data provider. + for (TypedDataGenerator<T> generator : generators) { + dataProvider.addDataGenerator(generator); + } + } + } + } + + /** + * Get the {@link DataProvider} of this Listing component. + * + * @return data provider + */ + protected DataProvider<T> getDataProvider() { + return dataProvider; + } + + @SuppressWarnings("unchecked") + @Override + public void setSelectionModel(SelectionModel<T> model) { + if (selectionModel != null) { + selectionModel.remove(); + } + selectionModel = model; + if (model != null) { + model.extend(this); + } + } + + @Override + public SelectionModel<T> getSelectionModel() { + return selectionModel; + } +} diff --git a/server/src/main/java/com/vaadin/ui/components/nativeselect/NativeSelect.java b/server/src/main/java/com/vaadin/ui/components/nativeselect/NativeSelect.java index 8fecf96ebb..b6bd45de0f 100644 --- a/server/src/main/java/com/vaadin/ui/components/nativeselect/NativeSelect.java +++ b/server/src/main/java/com/vaadin/ui/components/nativeselect/NativeSelect.java @@ -17,62 +17,46 @@ package com.vaadin.ui.components.nativeselect; import java.util.function.Function; -import com.vaadin.server.Extension; import com.vaadin.server.communication.data.typed.DataProvider; import com.vaadin.server.communication.data.typed.DataSource; -import com.vaadin.server.communication.data.typed.SelectionModel; import com.vaadin.server.communication.data.typed.SingleSelection; import com.vaadin.server.communication.data.typed.TypedDataGenerator; -import com.vaadin.ui.AbstractComponent; -import com.vaadin.ui.components.Listing; +import com.vaadin.ui.components.AbstractListing; import elemental.json.JsonObject; -public class NativeSelect<T> extends AbstractComponent implements Listing<T> { +public class NativeSelect<T> extends AbstractListing<T> { private DataSource<T> dataSource; - private DataProvider<T> dataProvider; - private SelectionModel<T> selectionModel; private Function<T, String> nameProvider = T::toString; public NativeSelect() { - internalSetSelectionModel(new SingleSelection<>()); + setSelectionModel(new SingleSelection<>()); + addDataGenerator(new TypedDataGenerator<T>() { + + @Override + public void generateData(T data, JsonObject jsonObject) { + jsonObject.put("n", nameProvider.apply(data)); + } + + @Override + public void destroyData(T data) { + } + }); } public NativeSelect(DataSource<T> dataSource) { this(); - internalSetDataSource(dataSource); + setDataSource(dataSource); } @Override public void setDataSource(DataSource<T> data) { - internalSetDataSource(data); - } - - private void internalSetDataSource(DataSource<T> data) { - if (dataProvider != null) { - dataProvider.remove(); - dataProvider = null; - } dataSource = data; if (dataSource != null) { - dataProvider = DataProvider.create(dataSource, this); - dataProvider.addDataGenerator(new TypedDataGenerator<T>() { - - @Override - public void generateData(T data, JsonObject jsonObject) { - jsonObject.put("n", nameProvider.apply(data)); - } - - @Override - public void destroyData(T data) { - } - }); - for (Extension e : getExtensions()) { - if (e instanceof TypedDataGenerator) { - dataProvider.addDataGenerator((TypedDataGenerator<T>) e); - } - } + setDataProvider(DataProvider.create(dataSource, this)); + } else { + setDataProvider(null); } } @@ -80,43 +64,4 @@ public class NativeSelect<T> extends AbstractComponent implements Listing<T> { public DataSource<T> getDataSource() { return dataSource; } - - @Override - public SelectionModel<T> getSelectionModel() { - return selectionModel; - } - - @Override - public void setSelectionModel(SelectionModel<T> model) { - internalSetSelectionModel(model); - } - - private void internalSetSelectionModel(SelectionModel<T> model) { - if (selectionModel != null) { - selectionModel.remove(); - } - selectionModel = model; - if (model != null) { - model.setParentListing(this); - } - } - - @Override - protected void addExtension(Extension extension) { - super.addExtension(extension); - - if (dataProvider != null && extension instanceof TypedDataGenerator) { - dataProvider.addDataGenerator((TypedDataGenerator<T>) extension); - } - } - - @Override - public void removeExtension(Extension extension) { - super.removeExtension(extension); - - if (dataProvider != null && extension instanceof TypedDataGenerator) { - dataProvider.removeDataGenerator((TypedDataGenerator<T>) extension); - } - } - } diff --git a/server/src/test/java/com/vaadin/ui/components/AbstractListingTest.java b/server/src/test/java/com/vaadin/ui/components/AbstractListingTest.java new file mode 100644 index 0000000000..d5c6471db4 --- /dev/null +++ b/server/src/test/java/com/vaadin/ui/components/AbstractListingTest.java @@ -0,0 +1,89 @@ +package com.vaadin.ui.components; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Iterator; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.server.communication.data.typed.AbstractDataSource; +import com.vaadin.server.communication.data.typed.DataProvider; +import com.vaadin.server.communication.data.typed.DataSource; +import com.vaadin.ui.components.AbstractListing.AbstractListingExtension; + +import elemental.json.JsonObject; + +public class AbstractListingTest { + + private final class CountGenerator extends AbstractListingExtension<String> { + + int callCount = 0; + + @Override + public void generateData(String data, JsonObject jsonObject) { + ++callCount; + } + + @Override + public void destroyData(String data) { + } + } + + AbstractListing<String> testComponent = new AbstractListing<String>() { + + DataSource<String> data; + + @Override + public void setDataSource(DataSource<String> data) { + this.data = data; + setDataProvider(DataProvider.create(data, this)); + } + + @Override + public DataSource<String> getDataSource() { + return data; + } + }; + + @Before + public void setUp() { + testComponent.setDataSource(new AbstractDataSource<String>() { + + @Override + public void save(String data) { + } + + @Override + public void remove(String data) { + } + + @Override + public Iterator<String> iterator() { + return Arrays.asList("Foo").iterator(); + } + }); + } + + @Test + public void testAddDataGenerator() { + CountGenerator countGenerator = new CountGenerator(); + + countGenerator.extend(testComponent); + testComponent.getDataProvider().beforeClientResponse(true); + + assertEquals("Generator was not called.", 1, countGenerator.callCount); + } + + @Test + public void testAddAndRemoveDataGenerator() { + CountGenerator countGenerator = new CountGenerator(); + + countGenerator.extend(testComponent); + countGenerator.remove(); + testComponent.getDataProvider().beforeClientResponse(true); + + assertEquals("Generator was called.", 0, countGenerator.callCount); + } +} |