diff options
author | Denis Anisimov <denis@vaadin.com> | 2016-11-02 14:16:53 +0200 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2016-11-04 12:02:50 +0000 |
commit | b6960abff449a5cf6cb858f3102bc37dfab3fcc2 (patch) | |
tree | 5d6817b78db68ec377278126c5c205cc00cf3f44 /server/src | |
parent | 9e10d5d29cc091f7bab62091ebf955891ec28188 (diff) | |
download | vaadin-framework-b6960abff449a5cf6cb858f3102bc37dfab3fcc2.tar.gz vaadin-framework-b6960abff449a5cf6cb858f3102bc37dfab3fcc2.zip |
Create MultiSelect interface to replace Multi selection model.
Fixes vaadin/framework8-issues#423
Change-Id: Ifd252b8feed323708a7ae73af2b836832570d192
Diffstat (limited to 'server/src')
16 files changed, 721 insertions, 631 deletions
diff --git a/server/src/main/java/com/vaadin/data/Listing.java b/server/src/main/java/com/vaadin/data/Listing.java index 397272a677..422b3a1128 100644 --- a/server/src/main/java/com/vaadin/data/Listing.java +++ b/server/src/main/java/com/vaadin/data/Listing.java @@ -17,7 +17,6 @@ package com.vaadin.data; import java.io.Serializable; import java.util.Collection; -import java.util.Set; import com.vaadin.server.data.DataSource; @@ -28,12 +27,9 @@ import com.vaadin.server.data.DataSource; * * @param <T> * the item data type - * @param <SELECTIONMODEL> - * the selection logic supported by this listing * @since 8.0 */ -public interface Listing<T, SELECTIONMODEL extends SelectionModel<T>> - extends Serializable { +public interface Listing<T> extends Serializable { /** * Returns the source of data items used by this listing. @@ -52,13 +48,6 @@ public interface Listing<T, SELECTIONMODEL extends SelectionModel<T>> void setDataSource(DataSource<T> dataSource); /** - * Returns the selection model for this listing. - * - * @return the selection model, not null - */ - SELECTIONMODEL getSelectionModel(); - - /** * Sets the collection of data items of this listing. * * @param items @@ -79,54 +68,4 @@ public interface Listing<T, SELECTIONMODEL extends SelectionModel<T>> setDataSource(DataSource.create(items)); } - /* SelectionModel helper methods */ - - /** - * Returns an immutable set of the currently selected items. The iteration - * order of the items in the returned set is specified by the - * {@linkplain #getSelectionModel() selection model} used. - * - * @return the current selection - * - * @see SelectionModel#getSelectedItems - */ - default Set<T> getSelectedItems() { - return getSelectionModel().getSelectedItems(); - } - - /** - * Selects the given item. If the item is already selected, does nothing. - * - * @param item - * the item to select, not null - * - * @see SelectionModel#select - */ - default void select(T item) { - getSelectionModel().select(item); - } - - /** - * Deselects the given item. If the item is not currently selected, does - * nothing. - * - * @param item - * the item to deselect, not null - * - * @see SelectionModel#deselect - */ - default void deselect(T item) { - getSelectionModel().deselect(item); - } - - /** - * Returns whether the given item is currently selected. - * - * @param item - * the item to check, not null - * @return {@code true} if the item is selected, {@code false} otherwise - */ - default boolean isSelected(T item) { - return getSelectionModel().isSelected(item); - } } diff --git a/server/src/main/java/com/vaadin/event/selection/SingleSelectionChangeEvent.java b/server/src/main/java/com/vaadin/event/selection/SingleSelectionChangeEvent.java index 566facfd9c..7f1acff82b 100644 --- a/server/src/main/java/com/vaadin/event/selection/SingleSelectionChangeEvent.java +++ b/server/src/main/java/com/vaadin/event/selection/SingleSelectionChangeEvent.java @@ -67,8 +67,8 @@ public class SingleSelectionChangeEvent<T> extends ValueChangeEvent<T> @Override @SuppressWarnings("unchecked") - public AbstractListing<T, ?> getComponent() { - return (AbstractListing<T, ?>) super.getComponent(); + public AbstractListing<T> getComponent() { + return (AbstractListing<T>) super.getComponent(); } @Override diff --git a/server/src/main/java/com/vaadin/ui/AbstractListing.java b/server/src/main/java/com/vaadin/ui/AbstractListing.java index 3892e55662..40d3001ca6 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractListing.java +++ b/server/src/main/java/com/vaadin/ui/AbstractListing.java @@ -32,13 +32,11 @@ import com.vaadin.server.data.DataSource; * * @param <T> * the item data type - * @param <SELECTIONMODEL> - * the selection logic supported by this listing * * @since 8.0 */ -public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T>> - extends AbstractComponent implements Listing<T, SELECTIONMODEL> { +public abstract class AbstractListing<T> extends AbstractComponent + implements Listing<T> { /** * A helper base class for creating extensions for Listing components. This @@ -57,7 +55,7 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T * @param listing * the parent component to add to */ - public void extend(AbstractListing<T, ?> listing) { + public void extend(AbstractListing<T> listing) { super.extend(listing); listing.addDataGenerator(this); } @@ -82,8 +80,8 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T @Override @SuppressWarnings("unchecked") - public AbstractListing<T, ?> getParent() { - return (AbstractListing<T, ?>) super.getParent(); + public AbstractListing<T> getParent() { + return (AbstractListing<T>) super.getParent(); } /** @@ -100,8 +98,6 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T private final DataCommunicator<T> dataCommunicator; - private SELECTIONMODEL selectionModel; - /** * Creates a new {@code AbstractListing} with a default data communicator. * <p> @@ -146,29 +142,6 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T return getDataCommunicator().getDataSource(); } - @Override - public SELECTIONMODEL getSelectionModel() { - assert selectionModel != null : "No selection model set by " - + getClass().getName() + " constructor"; - return selectionModel; - } - - /** - * Sets the selection model for this listing. - * - * @param model - * the selection model to use, not null - */ - protected void setSelectionModel(SELECTIONMODEL model) { - if (selectionModel != null) { - throw new IllegalStateException( - "A selection model can't be changed."); - } - - Objects.requireNonNull(model, "selection model cannot be null"); - selectionModel = model; - } - /** * Adds the given data generator to this listing. If the generator was * already added, does nothing. diff --git a/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java b/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java index 779cd959dd..fc6ce4f0a4 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java +++ b/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java @@ -52,158 +52,16 @@ import elemental.json.JsonObject; * @author Vaadin Ltd * @since 8.0 */ -public abstract class AbstractMultiSelect<T> - extends AbstractListing<T, Multi<T>> implements HasValue<Set<T>> { +public abstract class AbstractMultiSelect<T> extends AbstractListing<T> + implements MultiSelect<T> { - /** - * Simple implementation of multiselectmodel. - */ - protected class SimpleMultiSelectModel implements SelectionModel.Multi<T> { - - private Set<T> selection = new LinkedHashSet<>(); - - @Override - public void select(T item) { - // Not user originated - select(item, false); - } - - /** - * Selects the given item. Depending on the implementation, may cause - * other items to be deselected. If the item is already selected, does - * nothing. - * - * @param item - * the item to select, not null - * @param userOriginated - * {@code true} if this was used originated, {@code false} if - * not - */ - protected void select(T item, boolean userOriginated) { - if (selection.contains(item)) { - return; - } - - updateSelection(set -> set.add(item), userOriginated); - } - - @Override - public void updateSelection(Set<T> addedItems, Set<T> removedItems) { - updateSelection(addedItems, removedItems, false); - } - - /** - * Updates the selection by adding and removing the given items. - * - * @param addedItems - * the items added to selection, not {@code} null - * @param removedItems - * the items removed from selection, not {@code} null - * @param userOriginated - * {@code true} if this was used originated, {@code false} if - * not - */ - protected void updateSelection(Set<T> addedItems, Set<T> removedItems, - boolean userOriginated) { - Objects.requireNonNull(addedItems); - Objects.requireNonNull(removedItems); - - // if there are duplicates, some item is both added & removed, just - // discard that and leave things as was before - addedItems.removeIf(item -> removedItems.remove(item)); - - if (selection.containsAll(addedItems) - && Collections.disjoint(selection, removedItems)) { - return; - } - - updateSelection(set -> { - // order of add / remove does not matter since no duplicates - set.removeAll(removedItems); - set.addAll(addedItems); - }, userOriginated); - } - - @Override - public Set<T> getSelectedItems() { - return Collections.unmodifiableSet(new LinkedHashSet<>(selection)); - } - - @Override - public void deselect(T item) { - // Not user originated - deselect(item, false); - } - - /** - * Deselects the given item. If the item is not currently selected, does - * nothing. - * - * @param item - * the item to deselect, not null - * @param userOriginated - * {@code true} if this was used originated, {@code false} if - * not - */ - protected void deselect(T item, boolean userOriginated) { - if (!selection.contains(item)) { - return; - } - - updateSelection(set -> set.remove(item), userOriginated); - } - - /** - * Removes the given items. Any item that is not currently selected, is - * ignored. If none of the items are selected, does nothing. - * - * @param items - * the items to deselect, not {@code null} - * @param userOriginated - * {@code true} if this was used originated, {@code false} if - * not - */ - protected void deselect(Set<T> items, boolean userOriginated) { - Objects.requireNonNull(items); - if (items.stream().noneMatch(i -> isSelected(i))) { - return; - } - - updateSelection(set -> set.removeAll(items), userOriginated); - } - - @Override - public void deselectAll() { - if (selection.isEmpty()) { - return; - } - - updateSelection(Set::clear, false); - } - - private void updateSelection(Consumer<Set<T>> handler, - boolean userOriginated) { - LinkedHashSet<T> oldSelection = new LinkedHashSet<>(selection); - handler.accept(selection); - LinkedHashSet<T> newSelection = new LinkedHashSet<>(selection); - - fireEvent(new MultiSelectionEvent<>(AbstractMultiSelect.this, - oldSelection, userOriginated)); - - getDataCommunicator().reset(); - } - - @Override - public boolean isSelected(T item) { - return selection.contains(item); - } - } + private Set<T> selection = new LinkedHashSet<>(); private class MultiSelectServerRpcImpl implements MultiSelectServerRpc { @Override public void updateSelection(Set<String> selectedItemKeys, Set<String> deselectedItemKeys) { - getSelectionModel().updateSelection( + AbstractMultiSelect.this.updateSelection( getItemsForSelectionChange(selectedItemKeys), getItemsForSelectionChange(deselectedItemKeys), true); } @@ -223,10 +81,6 @@ public abstract class AbstractMultiSelect<T> return Optional.of(item); } - private SimpleMultiSelectModel getSelectionModel() { - return (SimpleMultiSelectModel) AbstractMultiSelect.this - .getSelectionModel(); - } } private class MultiSelectDataGenerator implements DataGenerator<T> { @@ -250,7 +104,7 @@ public abstract class AbstractMultiSelect<T> true); } - if (getSelectionModel().isSelected(data)) { + if (isSelected(data)) { jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED, true); } @@ -287,8 +141,6 @@ public abstract class AbstractMultiSelect<T> * Creates a new multi select with an empty data source. */ protected AbstractMultiSelect() { - setSelectionModel(new SimpleMultiSelectModel()); - registerRpc(new MultiSelectServerRpcImpl()); // #FIXME it should be the responsibility of the SelectionModel @@ -304,6 +156,7 @@ public abstract class AbstractMultiSelect<T> * the value change listener, not {@code null} * @return a registration for the listener */ + @Override public Registration addSelectionListener( MultiSelectionListener<T> listener) { addListener(MultiSelectionEvent.class, listener, @@ -376,8 +229,7 @@ public abstract class AbstractMultiSelect<T> Set<T> copy = value.stream().map(Objects::requireNonNull) .collect(Collectors.toCollection(LinkedHashSet::new)); - getSelectionModel().updateSelection(copy, - new LinkedHashSet<>(getSelectionModel().getSelectedItems())); + updateSelection(copy, new LinkedHashSet<>(getSelectedItems())); } @Override @@ -502,4 +354,122 @@ public abstract class AbstractMultiSelect<T> public boolean isReadOnly() { return super.isReadOnly(); } + + @Override + public void updateSelection(Set<T> addedItems, Set<T> removedItems) { + updateSelection(addedItems, removedItems, false); + } + + /** + * Updates the selection by adding and removing the given items. + * + * @param addedItems + * the items added to selection, not {@code} null + * @param removedItems + * the items removed from selection, not {@code} null + * @param userOriginated + * {@code true} if this was used originated, {@code false} if not + */ + protected void updateSelection(Set<T> addedItems, Set<T> removedItems, + boolean userOriginated) { + Objects.requireNonNull(addedItems); + Objects.requireNonNull(removedItems); + + // if there are duplicates, some item is both added & removed, just + // discard that and leave things as was before + addedItems.removeIf(item -> removedItems.remove(item)); + + if (selection.containsAll(addedItems) + && Collections.disjoint(selection, removedItems)) { + return; + } + + updateSelection(set -> { + // order of add / remove does not matter since no duplicates + set.removeAll(removedItems); + set.addAll(addedItems); + }, userOriginated); + } + + @Override + public Set<T> getSelectedItems() { + return Collections.unmodifiableSet(new LinkedHashSet<>(selection)); + } + + @Override + public void deselectAll() { + if (selection.isEmpty()) { + return; + } + + updateSelection(Set::clear, false); + } + + @Override + public boolean isSelected(T item) { + return selection.contains(item); + } + + /** + * Deselects the given item. If the item is not currently selected, does + * nothing. + * + * @param item + * the item to deselect, not null + * @param userOriginated + * {@code true} if this was used originated, {@code false} if not + */ + protected void deselect(T item, boolean userOriginated) { + if (!selection.contains(item)) { + return; + } + + updateSelection(set -> set.remove(item), userOriginated); + } + + /** + * Removes the given items. Any item that is not currently selected, is + * ignored. If none of the items are selected, does nothing. + * + * @param items + * the items to deselect, not {@code null} + * @param userOriginated + * {@code true} if this was used originated, {@code false} if not + */ + protected void deselect(Set<T> items, boolean userOriginated) { + Objects.requireNonNull(items); + if (items.stream().noneMatch(i -> isSelected(i))) { + return; + } + + updateSelection(set -> set.removeAll(items), userOriginated); + } + + /** + * Selects the given item. Depending on the implementation, may cause other + * items to be deselected. If the item is already selected, does nothing. + * + * @param item + * the item to select, not null + * @param userOriginated + * {@code true} if this was used originated, {@code false} if not + */ + protected void select(T item, boolean userOriginated) { + if (selection.contains(item)) { + return; + } + + updateSelection(set -> set.add(item), userOriginated); + } + + private void updateSelection(Consumer<Set<T>> handler, + boolean userOriginated) { + LinkedHashSet<T> oldSelection = new LinkedHashSet<>(selection); + handler.accept(selection); + + fireEvent(new MultiSelectionEvent<>(AbstractMultiSelect.this, + oldSelection, userOriginated)); + + getDataCommunicator().reset(); + } } diff --git a/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java b/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java index cb72d52444..7125139d8a 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java +++ b/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java @@ -43,189 +43,9 @@ import com.vaadin.util.ReflectTools; * * @since 8.0 */ -public abstract class AbstractSingleSelect<T> extends - AbstractListing<T, AbstractSingleSelect<T>.AbstractSingleSelection> +public abstract class AbstractSingleSelect<T> extends AbstractListing<T> implements SingleSelect<T> { - /** - * A base class for single selection model implementations. Listens to - * {@code SelectionServerRpc} invocations to track selection requests by the - * client. Maintaining the selection state and communicating it from the - * server to the client is the responsibility of the implementing class. - */ - public abstract class AbstractSingleSelection - implements SelectionModel.Single<T> { - - /** - * Creates a new {@code SimpleSingleSelection} instance. - */ - public AbstractSingleSelection() { - registerRpc(new SelectionServerRpc() { - - @Override - public void select(String key) { - setSelectedFromClient(key); - } - - @Override - public void deselect(String key) { - if (isKeySelected(key)) { - setSelectedFromClient(null); - } - } - }); - } - - @Override - public void deselect(T item) { - Objects.requireNonNull(item, "deselected item cannot be null"); - if (isSelected(item)) { - setSelectedFromServer(null); - } - } - - @Override - public void select(T item) { - Objects.requireNonNull(item, "selected item cannot be null"); - setSelectedFromServer(item); - } - - /** - * Returns the communication key of the selected item or {@code null} if - * no item is selected. - * - * @return the key of the selected item if any, {@code null} otherwise. - */ - protected abstract String getSelectedKey(); - - /** - * Sets the selected item based on the given communication key. If the - * key is {@code null}, clears the current selection if any. - * - * @param key - * the key of the selected item or {@code null} to clear - * selection - */ - protected abstract void doSetSelectedKey(String key); - - /** - * Sets the selection based on a client request. Does nothing if the - * select component is {@linkplain Component#isReadOnly()} or if the - * selection would not change. Otherwise updates the selection and fires - * a selection change event with {@code isUserOriginated == true}. - * - * @param key - * the key of the item to select or {@code null} to clear - * selection - */ - protected void setSelectedFromClient(String key) { - if (isReadOnly()) { - return; - } - if (isKeySelected(key)) { - return; - } - - doSetSelectedKey(key); - fireEvent(new SingleSelectionChangeEvent<>( - AbstractSingleSelect.this, true)); - } - - /** - * Sets the selection based on server API call. Does nothing if the - * selection would not change; otherwise updates the selection and fires - * a selection change event with {@code isUserOriginated == false}. - * - * @param item - * the item to select or {@code null} to clear selection - */ - protected void setSelectedFromServer(T item) { - // TODO creates a key if item not in data source - String key = itemToKey(item); - - if (isKeySelected(key) || isSelected(item)) { - return; - } - - doSetSelectedKey(key); - fireEvent(new SingleSelectionChangeEvent<>( - AbstractSingleSelect.this, false)); - } - - /** - * Returns whether the given key maps to the currently selected item. - * - * @param key - * the key to test or {@code null} to test whether nothing is - * selected - * @return {@code true} if the key equals the key of the currently - * selected item (or {@code null} if no selection), - * {@code false} otherwise. - */ - protected boolean isKeySelected(String key) { - return Objects.equals(key, getSelectedKey()); - } - - /** - * Returns the communication key assigned to the given item. - * - * @param item - * the item whose key to return - * @return the assigned key - */ - protected String itemToKey(T item) { - if (item == null) { - return null; - } else { - // TODO creates a key if item not in data source - return getDataCommunicator().getKeyMapper().key(item); - } - } - - /** - * Returns the item that the given key is assigned to, or {@code null} - * if there is no such item. - * - * @param key - * the key whose item to return - * @return the associated item if any, {@code null} otherwise. - */ - protected T keyToItem(String key) { - return getDataCommunicator().getKeyMapper().get(key); - } - } - - /** - * A simple single selection model using the {@code AbstractSingleSelect} - * RPC and state to communicate with the client. Has no client-side - * counterpart; the listing connector is expected to handle selection. - * Client-to-server selection is passed via {@link SelectionServerRpc} and - * server-to-client via {@link AbstractSingleSelectState#selectedItemKey}. - */ - protected class SimpleSingleSelection extends AbstractSingleSelection { - - /** - * Creates a new {@code SimpleSingleSelection}. - */ - public SimpleSingleSelection() { - } - - @Override - public Optional<T> getSelectedItem() { - return Optional.ofNullable(keyToItem(getSelectedKey())); - } - - @Override - protected String getSelectedKey() { - return getState(false).selectedItemKey; - } - - @Override - protected void doSetSelectedKey(String key) { - getState().selectedItemKey = key; - } - } - @Deprecated private static final Method SELECTION_CHANGE_METHOD = ReflectTools .findMethod(SingleSelectionListener.class, "accept", @@ -240,6 +60,7 @@ public abstract class AbstractSingleSelect<T> extends * {@link AbstractSingleSelect.AbstractSingleSelection} . */ protected AbstractSingleSelect() { + init(); } /** @@ -260,6 +81,7 @@ public abstract class AbstractSingleSelect<T> extends */ protected AbstractSingleSelect(DataCommunicator<T> dataCommunicator) { super(dataCommunicator); + init(); } /** @@ -285,7 +107,7 @@ public abstract class AbstractSingleSelect<T> extends * otherwise */ public Optional<T> getSelectedItem() { - return getSelectionModel().getSelectedItem(); + return Optional.ofNullable(keyToItem(getSelectedKey())); } /** @@ -296,7 +118,7 @@ public abstract class AbstractSingleSelect<T> extends * the item to select or {@code null} to clear selection */ public void setSelectedItem(T item) { - getSelectionModel().setSelectedItem(item); + setSelectedFromServer(item); } /** @@ -369,4 +191,152 @@ public abstract class AbstractSingleSelect<T> extends public boolean isReadOnly() { return super.isReadOnly(); } + + public void deselect(T item) { + Objects.requireNonNull(item, "deselected item cannot be null"); + if (isSelected(item)) { + setSelectedFromServer(null); + } + } + + public void select(T item) { + Objects.requireNonNull(item, "selected item cannot be null"); + setSelectedFromServer(item); + } + + /** + * Returns the communication key of the selected item or {@code null} if no + * item is selected. + * + * @return the key of the selected item if any, {@code null} otherwise. + */ + protected String getSelectedKey() { + return getState(false).selectedItemKey; + } + + /** + * Sets the selected item based on the given communication key. If the key + * is {@code null}, clears the current selection if any. + * + * @param key + * the key of the selected item or {@code null} to clear + * selection + */ + protected void doSetSelectedKey(String key) { + getState().selectedItemKey = key; + } + + /** + * Sets the selection based on a client request. Does nothing if the select + * component is {@linkplain Component#isReadOnly()} or if the selection + * would not change. Otherwise updates the selection and fires a selection + * change event with {@code isUserOriginated == true}. + * + * @param key + * the key of the item to select or {@code null} to clear + * selection + */ + protected void setSelectedFromClient(String key) { + if (isReadOnly()) { + return; + } + if (isKeySelected(key)) { + return; + } + + doSetSelectedKey(key); + fireEvent(new SingleSelectionChangeEvent<>(AbstractSingleSelect.this, + true)); + } + + /** + * Sets the selection based on server API call. Does nothing if the + * selection would not change; otherwise updates the selection and fires a + * selection change event with {@code isUserOriginated == false}. + * + * @param item + * the item to select or {@code null} to clear selection + */ + protected void setSelectedFromServer(T item) { + // TODO creates a key if item not in data source + String key = itemToKey(item); + + if (isKeySelected(key) || isSelected(item)) { + return; + } + + doSetSelectedKey(key); + fireEvent(new SingleSelectionChangeEvent<>(AbstractSingleSelect.this, + false)); + } + + /** + * Returns whether the given key maps to the currently selected item. + * + * @param key + * the key to test or {@code null} to test whether nothing is + * selected + * @return {@code true} if the key equals the key of the currently selected + * item (or {@code null} if no selection), {@code false} otherwise. + */ + protected boolean isKeySelected(String key) { + return Objects.equals(key, getSelectedKey()); + } + + /** + * Returns the communication key assigned to the given item. + * + * @param item + * the item whose key to return + * @return the assigned key + */ + protected String itemToKey(T item) { + if (item == null) { + return null; + } else { + // TODO creates a key if item not in data source + return getDataCommunicator().getKeyMapper().key(item); + } + } + + /** + * Returns the item that the given key is assigned to, or {@code null} if + * there is no such item. + * + * @param key + * the key whose item to return + * @return the associated item if any, {@code null} otherwise. + */ + protected T keyToItem(String key) { + return getDataCommunicator().getKeyMapper().get(key); + } + + /** + * Returns whether the given item is currently selected. + * + * @param item + * the item to check, not null + * @return {@code true} if the item is selected, {@code false} otherwise + */ + public boolean isSelected(T item) { + return Objects.equals(getValue(), item); + } + + private void init() { + registerRpc(new SelectionServerRpc() { + + @Override + public void select(String key) { + setSelectedFromClient(key); + } + + @Override + public void deselect(String key) { + if (isKeySelected(key)) { + setSelectedFromClient(null); + } + } + }); + } + } diff --git a/server/src/main/java/com/vaadin/ui/ComboBox.java b/server/src/main/java/com/vaadin/ui/ComboBox.java index 8ceaec125c..097ae30bc7 100644 --- a/server/src/main/java/com/vaadin/ui/ComboBox.java +++ b/server/src/main/java/com/vaadin/ui/ComboBox.java @@ -56,24 +56,6 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>, FieldEvents.BlurNotifier, FieldEvents.FocusNotifier { /** - * Custom single selection model for ComboBox. - */ - protected class ComboBoxSelectionModel extends SimpleSingleSelection { - @Override - protected void doSetSelectedKey(String key) { - super.doSetSelectedKey(key); - - String selectedCaption = null; - T value = getDataCommunicator().getKeyMapper().get(key); - if (value != null) { - selectedCaption = getItemCaptionGenerator().apply(value); - } - getState().selectedItemCaption = selectedCaption; - } - - } - - /** * Handler that adds a new item based on user input when the new items * allowed mode is active. */ @@ -158,7 +140,6 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>, }; } }); - setSelectionModel(new ComboBoxSelectionModel()); init(); } @@ -540,24 +521,6 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>, this.filter = filter; } - /** - * Sets the value of this object. If the new value is not equal to - * {@code getValue()}, fires a {@link ValueChangeEvent}. - * - * @param value - * the new value, may be {@code null} - */ - @Override - public void setValue(T value) { - getSelectionModel().setSelectedFromServer(value); - - } - - @Override - public T getValue() { - return getSelectionModel().getSelectedItem().orElse(null); - } - @Override public Registration addValueChangeListener( HasValue.ValueChangeListener<T> listener) { @@ -577,4 +540,16 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>, return (ComboBoxState) super.getState(markAsDirty); } + @Override + protected void doSetSelectedKey(String key) { + super.doSetSelectedKey(key); + + String selectedCaption = null; + T value = getDataCommunicator().getKeyMapper().get(key); + if (value != null) { + selectedCaption = getItemCaptionGenerator().apply(value); + } + getState().selectedItemCaption = selectedCaption; + } + } diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index bf50d6a53d..b738b71323 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -37,9 +37,11 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import com.vaadin.data.SelectionModel.Single; import com.vaadin.event.ConnectorEvent; import com.vaadin.event.ContextClickEvent; import com.vaadin.event.EventListener; +import com.vaadin.event.selection.SingleSelectionChangeEvent; import com.vaadin.server.EncodeResult; import com.vaadin.server.JsonCodec; import com.vaadin.server.SerializableComparator; @@ -48,6 +50,7 @@ import com.vaadin.server.data.SortOrder; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.data.DataCommunicatorConstants; +import com.vaadin.shared.data.selection.SelectionServerRpc; import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.grid.ColumnState; import com.vaadin.shared.ui.grid.GridConstants; @@ -488,7 +491,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { extends AbstractListingExtension<T> { @Override - public void extend(AbstractListing<T, ?> grid) { + public void extend(AbstractListing<T> grid) { if (!(grid instanceof Grid)) { throw new IllegalArgumentException( getClass().getSimpleName() + " can only extend Grid"); @@ -1485,7 +1488,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { } } - private final class SingleSelection extends AbstractSingleSelection { + private final class SingleSelection implements Single<T> { private T selectedItem = null; SingleSelection() { @@ -1494,6 +1497,20 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { json.put(DataCommunicatorConstants.SELECTED, true); } }); + registerRpc(new SelectionServerRpc() { + + @Override + public void select(String key) { + setSelectedFromClient(key); + } + + @Override + public void deselect(String key) { + if (isKeySelected(key)) { + setSelectedFromClient(null); + } + } + }); } @Override @@ -1502,16 +1519,51 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { } @Override + public void deselect(T item) { + Objects.requireNonNull(item, "deselected item cannot be null"); + if (isSelected(item)) { + setSelectedFromServer(null); + } + } + + @Override + public void select(T item) { + Objects.requireNonNull(item, "selected item cannot be null"); + setSelectedFromServer(item); + } + + /** + * Returns whether the given key maps to the currently selected item. + * + * @param key + * the key to test or {@code null} to test whether nothing is + * selected + * @return {@code true} if the key equals the key of the currently + * selected item (or {@code null} if no selection), + * {@code false} otherwise. + */ protected boolean isKeySelected(String key) { return Objects.equals(key, getSelectedKey()); } - @Override + /** + * Returns the communication key of the selected item or {@code null} if + * no item is selected. + * + * @return the key of the selected item if any, {@code null} otherwise. + */ protected String getSelectedKey() { return itemToKey(selectedItem); } - @Override + /** + * Sets the selected item based on the given communication key. If the + * key is {@code null}, clears the current selection if any. + * + * @param key + * the key of the selected item or {@code null} to clear + * selection + */ protected void doSetSelectedKey(String key) { if (selectedItem != null) { getDataCommunicator().refresh(selectedItem); @@ -1521,6 +1573,76 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { getDataCommunicator().refresh(selectedItem); } } + + /** + * Sets the selection based on a client request. Does nothing if the + * select component is {@linkplain Component#isReadOnly()} or if the + * selection would not change. Otherwise updates the selection and fires + * a selection change event with {@code isUserOriginated == true}. + * + * @param key + * the key of the item to select or {@code null} to clear + * selection + */ + protected void setSelectedFromClient(String key) { + if (isReadOnly()) { + return; + } + if (isKeySelected(key)) { + return; + } + + doSetSelectedKey(key); + fireEvent(new SingleSelectionChangeEvent<>(Grid.this, true)); + } + + /** + * Sets the selection based on server API call. Does nothing if the + * selection would not change; otherwise updates the selection and fires + * a selection change event with {@code isUserOriginated == false}. + * + * @param item + * the item to select or {@code null} to clear selection + */ + protected void setSelectedFromServer(T item) { + // TODO creates a key if item not in data source + String key = itemToKey(item); + + if (isKeySelected(key) || isSelected(item)) { + return; + } + + doSetSelectedKey(key); + fireEvent(new SingleSelectionChangeEvent<>(Grid.this, false)); + } + + /** + * Returns the communication key assigned to the given item. + * + * @param item + * the item whose key to return + * @return the assigned key + */ + protected String itemToKey(T item) { + if (item == null) { + return null; + } else { + // TODO creates a key if item not in data source + return getDataCommunicator().getKeyMapper().key(item); + } + } + + /** + * Returns the item that the given key is assigned to, or {@code null} + * if there is no such item. + * + * @param key + * the key whose item to return + * @return the associated item if any, {@code null} otherwise. + */ + protected T keyToItem(String key) { + return getDataCommunicator().getKeyMapper().get(key); + } } /** @@ -1713,6 +1835,8 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { private int counter = 0; + private Single<T> selectionModel; + /** * Constructor for the {@link Grid} component. */ @@ -2561,6 +2685,38 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { fireColumnReorderEvent(false); } + /** + * Returns the selection model for this listing. + * + * @return the selection model, not null + */ + public Single<T> getSelectionModel() { + assert selectionModel != null : "No selection model set by " + + getClass().getName() + " constructor"; + return selectionModel; + } + + @Override + public Optional<T> getSelectedItem() { + return getSelectionModel().getSelectedItem(); + } + + /** + * Sets the selection model for this listing. + * + * @param model + * the selection model to use, not null + */ + protected void setSelectionModel(Single<T> model) { + if (selectionModel != null) { + throw new IllegalStateException( + "A selection model can't be changed."); + } + + Objects.requireNonNull(model, "selection model cannot be null"); + selectionModel = model; + } + @Override protected GridState getState() { return getState(true); diff --git a/server/src/main/java/com/vaadin/ui/MultiSelect.java b/server/src/main/java/com/vaadin/ui/MultiSelect.java new file mode 100644 index 0000000000..a3677d6d4e --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/MultiSelect.java @@ -0,0 +1,139 @@ +/* + * 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.ui; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; + +import com.vaadin.data.HasValue; +import com.vaadin.event.selection.MultiSelectionListener; +import com.vaadin.shared.Registration; + +/** + * Multi selection component which allows to select and deselect multiple items. + * + * @author Vaadin Ltd + * + * @param <T> + * the type of the items to select + * + * @since 8.0 + * + */ +public interface MultiSelect<T> extends HasValue<Set<T>>, Serializable { + + /** + * Adds the given items to the set of currently selected items. + * <p> + * By default this does not clear any previous selection. To do that, use + * {@link #deselectAll()}. + * <p> + * If the all the items were already selected, this is a NO-OP. + * <p> + * This is a short-hand for {@link #updateSelection(Set, Set)} with nothing + * to deselect. + * + * @param items + * to add to selection, not {@code null} + */ + public default void select(T... items) { + Objects.requireNonNull(items); + Stream.of(items).forEach(Objects::requireNonNull); + + updateSelection(new LinkedHashSet<>(Arrays.asList(items)), + Collections.emptySet()); + } + + /** + * Removes the given items from the set of currently selected items. + * <p> + * If the none of the items were selected, this is a NO-OP. + * <p> + * This is a short-hand for {@link #updateSelection(Set, Set)} with nothing + * to select. + * + * @param items + * to remove from selection, not {@code null} + */ + public default void deselect(T... items) { + Objects.requireNonNull(items); + Stream.of(items).forEach(Objects::requireNonNull); + + updateSelection(Collections.emptySet(), + new LinkedHashSet<>(Arrays.asList(items))); + } + + /** + * Updates the selection by adding and removing the given items from it. + * <p> + * If all the added items were already selected and the removed items were + * not selected, this is a NO-OP. + * <p> + * Duplicate items (in both add & remove sets) are ignored. + * + * @param addedItems + * the items to add, not {@code null} + * @param removedItems + * the items to remove, not {@code null} + */ + public void updateSelection(Set<T> addedItems, Set<T> removedItems); + + /** + * Returns an immutable set of the currently selected items. It is safe to + * invoke other {@code SelectionModel} methods while iterating over the set. + * <p> + * <em>Implementation note:</em> the iteration order of the items in the + * returned set should be well-defined and documented by the implementing + * class. + * + * @return the items in the current selection, not null + */ + public Set<T> getSelectedItems(); + + /** + * Deselects all currently selected items. + */ + public default void deselectAll() { + getSelectedItems().forEach(this::deselect); + } + + /** + * Returns whether the given item is currently selected. + * + * @param item + * the item to check, not null + * @return {@code true} if the item is selected, {@code false} otherwise + */ + public default boolean isSelected(T item) { + return getSelectedItems().contains(item); + } + + /** + * Adds a selection listener that will be called when the selection is + * changed either by the user or programmatically. + * + * @param listener + * the value change listener, not {@code null} + * @return a registration for the listener + */ + public Registration addSelectionListener( + MultiSelectionListener<T> listener); +} diff --git a/server/src/main/java/com/vaadin/ui/NativeSelect.java b/server/src/main/java/com/vaadin/ui/NativeSelect.java index bb3ae41df4..a1ec0dca40 100644 --- a/server/src/main/java/com/vaadin/ui/NativeSelect.java +++ b/server/src/main/java/com/vaadin/ui/NativeSelect.java @@ -52,8 +52,6 @@ public class NativeSelect<T> extends AbstractSingleSelect<T> registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); addDataGenerator((item, json) -> json .put(DataCommunicatorConstants.DATA, String.valueOf(item))); - - setSelectionModel(new SimpleSingleSelection()); } /** diff --git a/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java b/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java index 6f1191e8d4..4c5b814f22 100644 --- a/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java +++ b/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java @@ -103,7 +103,6 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T> */ public RadioButtonGroup() { registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); - setSelectionModel(new SimpleSingleSelection()); addDataGenerator(new DataGenerator<T>() { @Override @@ -127,7 +126,7 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T> true); } - if (getSelectionModel().isSelected(data)) { + if (isSelected(data)) { jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED, true); } diff --git a/server/src/test/java/com/vaadin/data/BinderMultiSelectTest.java b/server/src/test/java/com/vaadin/data/BinderMultiSelectTest.java index f8a4313b09..c4725471da 100644 --- a/server/src/test/java/com/vaadin/data/BinderMultiSelectTest.java +++ b/server/src/test/java/com/vaadin/data/BinderMultiSelectTest.java @@ -157,7 +157,7 @@ public class BinderMultiSelectTest converterBinder.readBean(new AtomicReference<>("TWO")); assertEquals(Collections.singleton(TestEnum.TWO), - select.getSelectionModel().getSelectedItems()); + select.getSelectedItems()); } @Test diff --git a/server/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java b/server/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java index 0522e26ac9..572413bb99 100644 --- a/server/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java +++ b/server/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java @@ -1,5 +1,7 @@ package com.vaadin.tests.components.grid; +import java.util.Optional; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -18,17 +20,17 @@ public class GridSelectionTest { @Test public void testGridWithSingleSelection() { - Assert.assertFalse(grid.isSelected("Foo")); - grid.select("Foo"); - Assert.assertTrue(grid.isSelected("Foo")); - Assert.assertEquals(1, grid.getSelectedItems().size()); - Assert.assertEquals("Foo", grid.getSelectedItems().iterator().next()); - grid.select("Bar"); - Assert.assertFalse(grid.isSelected("Foo")); - Assert.assertTrue(grid.isSelected("Bar")); - grid.deselect("Bar"); - Assert.assertFalse(grid.isSelected("Bar")); - Assert.assertEquals(0, grid.getSelectedItems().size()); + Assert.assertFalse(grid.getSelectionModel().isSelected("Foo")); + grid.getSelectionModel().select("Foo"); + Assert.assertTrue(grid.getSelectionModel().isSelected("Foo")); + Assert.assertEquals(Optional.of("Foo"), grid.getSelectedItem()); + grid.getSelectionModel().select("Bar"); + Assert.assertFalse(grid.getSelectionModel().isSelected("Foo")); + Assert.assertTrue(grid.getSelectionModel().isSelected("Bar")); + grid.getSelectionModel().deselect("Bar"); + Assert.assertFalse(grid.getSelectionModel().isSelected("Bar")); + Assert.assertFalse( + grid.getSelectionModel().getSelectedItem().isPresent()); } } diff --git a/server/src/test/java/com/vaadin/ui/AbstractListingTest.java b/server/src/test/java/com/vaadin/ui/AbstractListingTest.java index 585dce1f5e..494fc2e1c5 100644 --- a/server/src/test/java/com/vaadin/ui/AbstractListingTest.java +++ b/server/src/test/java/com/vaadin/ui/AbstractListingTest.java @@ -22,10 +22,6 @@ public class AbstractListingTest { private final class TestListing extends AbstractSingleSelect<String> { - protected TestListing() { - setSelectionModel(new SimpleSingleSelection()); - } - /** * Used to execute data generation */ diff --git a/server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java b/server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java index 6d748f076a..92c288fc34 100644 --- a/server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java +++ b/server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java @@ -37,7 +37,6 @@ import org.junit.runners.Parameterized.Parameters; import org.mockito.Mockito; import com.vaadin.data.HasValue.ValueChangeEvent; -import com.vaadin.data.SelectionModel.Multi; import com.vaadin.event.selection.MultiSelectionEvent; import com.vaadin.event.selection.MultiSelectionListener; import com.vaadin.server.data.DataSource; @@ -56,18 +55,16 @@ public class AbstractMultiSelectTest { @Parameter public AbstractMultiSelect<String> selectToTest; - private Multi<String> selectionModel; private MultiSelectServerRpc rpc; private Registration registration; @Before public void setUp() { - selectToTest.getSelectionModel().deselectAll(); + selectToTest.deselectAll(); // Intentional deviation from upcoming selection order selectToTest.setDataSource( DataSource.create("3", "2", "1", "5", "8", "7", "4", "6")); - selectionModel = selectToTest.getSelectionModel(); rpc = ComponentTest.getRpcProxy(selectToTest, MultiSelectServerRpc.class); } @@ -82,28 +79,28 @@ public class AbstractMultiSelectTest { @Test public void stableSelectionOrder() { - selectionModel.select("1"); - selectionModel.select("2"); - selectionModel.select("3"); + selectToTest.select("1"); + selectToTest.select("2"); + selectToTest.select("3"); - assertSelectionOrder(selectionModel, "1", "2", "3"); + assertSelectionOrder("1", "2", "3"); - selectionModel.deselect("1"); - assertSelectionOrder(selectionModel, "2", "3"); + selectToTest.deselect("1"); + assertSelectionOrder("2", "3"); - selectionModel.select("1"); - assertSelectionOrder(selectionModel, "2", "3", "1"); + selectToTest.select("1"); + assertSelectionOrder("2", "3", "1"); - selectionModel.selectItems("7", "8", "4"); - assertSelectionOrder(selectionModel, "2", "3", "1", "7", "8", "4"); + selectToTest.select("7", "8", "4"); + assertSelectionOrder("2", "3", "1", "7", "8", "4"); - selectionModel.deselectItems("2", "1", "4", "5"); - assertSelectionOrder(selectionModel, "3", "7", "8"); + selectToTest.deselect("2", "1", "4", "5"); + assertSelectionOrder("3", "7", "8"); - selectionModel.updateSelection( + selectToTest.updateSelection( new LinkedHashSet<>(Arrays.asList("5", "2")), new LinkedHashSet<>(Arrays.asList("3", "8"))); - assertSelectionOrder(selectionModel, "7", "5", "2"); + assertSelectionOrder("7", "5", "2"); } @Test @@ -120,27 +117,27 @@ public class AbstractMultiSelectTest { selectToTest.select("2"); selectToTest.deselect("2"); - selectToTest.getSelectionModel().deselectAll(); + selectToTest.deselectAll(); - selectToTest.getSelectionModel().selectItems("2", "3", "4"); - selectToTest.getSelectionModel().deselectItems("1", "4"); + selectToTest.select("2", "3", "4"); + selectToTest.deselect("1", "4"); Assert.assertEquals(6, listenerCount.get()); // select partly selected - selectToTest.getSelectionModel().selectItems("2", "3", "4"); + selectToTest.select("2", "3", "4"); Assert.assertEquals(7, listenerCount.get()); // select completely selected - selectToTest.getSelectionModel().selectItems("2", "3", "4"); + selectToTest.select("2", "3", "4"); Assert.assertEquals(7, listenerCount.get()); // deselect partly not selected - selectToTest.getSelectionModel().selectItems("1", "4"); + selectToTest.select("1", "4"); Assert.assertEquals(8, listenerCount.get()); // deselect completely not selected - selectToTest.getSelectionModel().selectItems("1", "4"); + selectToTest.select("1", "4"); Assert.assertEquals(8, listenerCount.get()); } @@ -154,88 +151,88 @@ public class AbstractMultiSelectTest { }); rpcSelect("1"); - assertSelectionOrder(selectionModel, "1"); + assertSelectionOrder("1"); rpcSelect("2"); - assertSelectionOrder(selectionModel, "1", "2"); - rpcDeselect("2"); - assertSelectionOrder(selectionModel, "1"); + assertSelectionOrder("1", "2"); + rpcDeselectItems("2"); + assertSelectionOrder("1"); rpcSelect("3", "6"); - assertSelectionOrder(selectionModel, "1", "3", "6"); - rpcDeselect("1", "3"); - assertSelectionOrder(selectionModel, "6"); + assertSelectionOrder("1", "3", "6"); + rpcDeselectItems("1", "3"); + assertSelectionOrder("6"); Assert.assertEquals(5, listenerCount.get()); // select partly selected rpcSelect("2", "3", "4"); Assert.assertEquals(6, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "2", "3", "4"); + assertSelectionOrder("6", "2", "3", "4"); // select completely selected rpcSelect("2", "3", "4"); Assert.assertEquals(6, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "2", "3", "4"); + assertSelectionOrder("6", "2", "3", "4"); // deselect partly not selected - rpcDeselect("1", "4"); + rpcDeselectItems("1", "4"); Assert.assertEquals(7, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "2", "3"); + assertSelectionOrder("6", "2", "3"); // deselect completely not selected - rpcDeselect("1", "4"); + rpcDeselectItems("1", "4"); Assert.assertEquals(7, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "2", "3"); + assertSelectionOrder("6", "2", "3"); // select completely selected and deselect completely not selected rpcUpdateSelection(new String[] { "3" }, new String[] { "1", "4" }); Assert.assertEquals(7, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "2", "3"); + assertSelectionOrder("6", "2", "3"); // select partly selected and deselect completely not selected rpcUpdateSelection(new String[] { "4", "2" }, new String[] { "1", "8" }); Assert.assertEquals(8, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "2", "3", "4"); + assertSelectionOrder("6", "2", "3", "4"); // select completely selected and deselect partly not selected rpcUpdateSelection(new String[] { "4", "3" }, new String[] { "1", "2" }); Assert.assertEquals(9, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "3", "4"); + assertSelectionOrder("6", "3", "4"); // duplicate case - ignored rpcUpdateSelection(new String[] { "2" }, new String[] { "2" }); Assert.assertEquals(9, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "3", "4"); + assertSelectionOrder("6", "3", "4"); // duplicate case - duplicate removed rpcUpdateSelection(new String[] { "2" }, new String[] { "2", "3" }); Assert.assertEquals(10, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "4"); + assertSelectionOrder("6", "4"); // duplicate case - duplicate removed rpcUpdateSelection(new String[] { "6", "8" }, new String[] { "6" }); Assert.assertEquals(11, listenerCount.get()); - assertSelectionOrder(selectionModel, "6", "4", "8"); + assertSelectionOrder("6", "4", "8"); } @Test public void getValue() { - selectionModel.selectItems("1"); + selectToTest.select("1"); Assert.assertEquals(Collections.singleton("1"), selectToTest.getValue()); - selectionModel.deselectAll(); + selectToTest.deselectAll(); LinkedHashSet<String> set = new LinkedHashSet<>(); set.add("1"); set.add("5"); - selectionModel.selectItems(set.toArray(new String[2])); + selectToTest.select(set.toArray(new String[2])); Assert.assertEquals(set, selectToTest.getValue()); set.add("3"); - selectionModel.selectItems("3"); + selectToTest.select("3"); Assert.assertEquals(set, selectToTest.getValue()); } @@ -259,26 +256,21 @@ public class AbstractMultiSelectTest { selectToTest.setValue(Collections.singleton("1")); Assert.assertEquals(Collections.singleton("1"), - selectionModel.getSelectedItems()); + selectToTest.getSelectedItems()); Set<String> set = new LinkedHashSet<>(); set.add("4"); set.add("3"); selectToTest.setValue(set); - Assert.assertEquals(set, selectionModel.getSelectedItems()); + Assert.assertEquals(set, selectToTest.getSelectedItems()); } @Test @SuppressWarnings({ "unchecked", "rawtypes", "serial" }) public void setValue_isDelegatedToDeselectAndUpdateSelection() { - Multi<?> model = Mockito.mock(Multi.class); - AbstractMultiSelect<String> select = new AbstractMultiSelect<String>() { - @Override - public Multi<String> getSelectionModel() { - return (Multi<String>) model; - } - }; + AbstractMultiSelect<String> select = Mockito + .mock(AbstractMultiSelect.class); Set set = new LinkedHashSet<>(); set.add("foo1"); @@ -287,11 +279,12 @@ public class AbstractMultiSelectTest { selected.add("bar1"); selected.add("bar"); selected.add("bar2"); - Mockito.when(model.getSelectedItems()).thenReturn(selected); + Mockito.when(select.getSelectedItems()).thenReturn(selected); + Mockito.doCallRealMethod().when(select).setValue(Mockito.anySet()); select.setValue(set); - Mockito.verify(model).updateSelection(set, selected); + Mockito.verify(select).updateSelection(set, selected); } @SuppressWarnings({ "unchecked", "serial" }) @@ -336,7 +329,7 @@ public class AbstractMultiSelectTest { rpcUpdateSelection(keysToSelect, new String[] {}); } - private void rpcDeselect(String... keysToDeselect) { + private void rpcDeselectItems(String... keysToDeselect) { rpcUpdateSelection(new String[] {}, keysToDeselect); } @@ -353,9 +346,8 @@ public class AbstractMultiSelectTest { .key(dataObject); } - private static void assertSelectionOrder(Multi<String> selectionModel, - String... selectionOrder) { + private void assertSelectionOrder(String... selectionOrder) { Assert.assertEquals(Arrays.asList(selectionOrder), - new ArrayList<>(selectionModel.getSelectedItems())); + new ArrayList<>(selectToTest.getSelectedItems())); } } diff --git a/server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java b/server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java index 6ae5c91b96..47f5fbc81d 100644 --- a/server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java +++ b/server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; @@ -34,13 +33,11 @@ import org.junit.Test; import org.mockito.Mockito; import com.vaadin.data.HasValue.ValueChangeEvent; -import com.vaadin.data.SelectionModel.Multi; import com.vaadin.event.selection.SingleSelectionChangeEvent; import com.vaadin.event.selection.SingleSelectionListener; import com.vaadin.server.data.datasource.bov.Person; import com.vaadin.shared.Registration; import com.vaadin.shared.data.DataCommunicatorClientRpc; -import com.vaadin.ui.AbstractSingleSelect.AbstractSingleSelection; /** * Test for {@link AbstractSingleSelect} and {@link AbstractSingleSelection} @@ -49,20 +46,15 @@ import com.vaadin.ui.AbstractSingleSelect.AbstractSingleSelection; */ public class AbstractSingleSelectTest { - private PersonListing.AbstractSingleSelection selectionModel; private List<Person> selectionChanges; private static class PersonListing extends AbstractSingleSelect<Person> { - public PersonListing() { - setSelectionModel(new SimpleSingleSelection()); - } } @Before public void initListing() { listing = new PersonListing(); listing.setItems(PERSON_A, PERSON_B, PERSON_C); - selectionModel = listing.getSelectionModel(); selectionChanges = new ArrayList<>(); listing.addSelectionListener(e -> selectionChanges.add(e.getValue())); } @@ -77,18 +69,17 @@ public class AbstractSingleSelectTest { @Test public void select() { - selectionModel.select(PERSON_B); + listing.select(PERSON_B); - assertTrue(selectionModel.getSelectedItem().isPresent()); + assertTrue(listing.getSelectedItem().isPresent()); - assertEquals(PERSON_B, selectionModel.getSelectedItem().orElse(null)); + assertEquals(PERSON_B, listing.getSelectedItem().orElse(null)); - assertFalse(selectionModel.isSelected(PERSON_A)); - assertTrue(selectionModel.isSelected(PERSON_B)); - assertFalse(selectionModel.isSelected(PERSON_C)); + assertFalse(listing.isSelected(PERSON_A)); + assertTrue(listing.isSelected(PERSON_B)); + assertFalse(listing.isSelected(PERSON_C)); - assertEquals(Collections.singleton(PERSON_B), - selectionModel.getSelectedItems()); + assertEquals(Optional.of(PERSON_B), listing.getSelectedItem()); assertEquals(Arrays.asList(PERSON_B), selectionChanges); } @@ -96,16 +87,16 @@ public class AbstractSingleSelectTest { @Test public void selectDeselect() { - selectionModel.select(PERSON_B); - selectionModel.deselect(PERSON_B); + listing.select(PERSON_B); + listing.deselect(PERSON_B); - assertFalse(selectionModel.getSelectedItem().isPresent()); + assertFalse(listing.getSelectedItem().isPresent()); - assertFalse(selectionModel.isSelected(PERSON_A)); - assertFalse(selectionModel.isSelected(PERSON_B)); - assertFalse(selectionModel.isSelected(PERSON_C)); + assertFalse(listing.isSelected(PERSON_A)); + assertFalse(listing.isSelected(PERSON_B)); + assertFalse(listing.isSelected(PERSON_C)); - assertTrue(selectionModel.getSelectedItems().isEmpty()); + assertFalse(listing.getSelectedItem().isPresent()); assertEquals(Arrays.asList(PERSON_B, null), selectionChanges); } @@ -113,17 +104,16 @@ public class AbstractSingleSelectTest { @Test public void reselect() { - selectionModel.select(PERSON_B); - selectionModel.select(PERSON_C); + listing.select(PERSON_B); + listing.select(PERSON_C); - assertEquals(PERSON_C, selectionModel.getSelectedItem().orElse(null)); + assertEquals(PERSON_C, listing.getSelectedItem().orElse(null)); - assertFalse(selectionModel.isSelected(PERSON_A)); - assertFalse(selectionModel.isSelected(PERSON_B)); - assertTrue(selectionModel.isSelected(PERSON_C)); + assertFalse(listing.isSelected(PERSON_A)); + assertFalse(listing.isSelected(PERSON_B)); + assertTrue(listing.isSelected(PERSON_C)); - assertEquals(Collections.singleton(PERSON_C), - selectionModel.getSelectedItems()); + assertEquals(Optional.of(PERSON_C), listing.getSelectedItem()); assertEquals(Arrays.asList(PERSON_B, PERSON_C), selectionChanges); } @@ -131,17 +121,16 @@ public class AbstractSingleSelectTest { @Test public void deselectNoOp() { - selectionModel.select(PERSON_C); - selectionModel.deselect(PERSON_B); + listing.select(PERSON_C); + listing.deselect(PERSON_B); - assertEquals(PERSON_C, selectionModel.getSelectedItem().orElse(null)); + assertEquals(PERSON_C, listing.getSelectedItem().orElse(null)); - assertFalse(selectionModel.isSelected(PERSON_A)); - assertFalse(selectionModel.isSelected(PERSON_B)); - assertTrue(selectionModel.isSelected(PERSON_C)); + assertFalse(listing.isSelected(PERSON_A)); + assertFalse(listing.isSelected(PERSON_B)); + assertTrue(listing.isSelected(PERSON_C)); - assertEquals(Collections.singleton(PERSON_C), - selectionModel.getSelectedItems()); + assertEquals(Optional.of(PERSON_C), listing.getSelectedItem()); assertEquals(Arrays.asList(PERSON_C), selectionChanges); } @@ -149,17 +138,16 @@ public class AbstractSingleSelectTest { @Test public void selectTwice() { - selectionModel.select(PERSON_C); - selectionModel.select(PERSON_C); + listing.select(PERSON_C); + listing.select(PERSON_C); - assertEquals(PERSON_C, selectionModel.getSelectedItem().orElse(null)); + assertEquals(PERSON_C, listing.getSelectedItem().orElse(null)); - assertFalse(selectionModel.isSelected(PERSON_A)); - assertFalse(selectionModel.isSelected(PERSON_B)); - assertTrue(selectionModel.isSelected(PERSON_C)); + assertFalse(listing.isSelected(PERSON_A)); + assertFalse(listing.isSelected(PERSON_B)); + assertTrue(listing.isSelected(PERSON_C)); - assertEquals(Collections.singleton(PERSON_C), - selectionModel.getSelectedItems()); + assertEquals(Optional.of(PERSON_C), listing.getSelectedItem()); assertEquals(Arrays.asList(PERSON_C), selectionChanges); } @@ -167,28 +155,28 @@ public class AbstractSingleSelectTest { @Test public void deselectTwice() { - selectionModel.select(PERSON_C); - selectionModel.deselect(PERSON_C); - selectionModel.deselect(PERSON_C); + listing.select(PERSON_C); + listing.deselect(PERSON_C); + listing.deselect(PERSON_C); - assertFalse(selectionModel.getSelectedItem().isPresent()); + assertFalse(listing.getSelectedItem().isPresent()); - assertFalse(selectionModel.isSelected(PERSON_A)); - assertFalse(selectionModel.isSelected(PERSON_B)); - assertFalse(selectionModel.isSelected(PERSON_C)); + assertFalse(listing.isSelected(PERSON_A)); + assertFalse(listing.isSelected(PERSON_B)); + assertFalse(listing.isSelected(PERSON_C)); - assertTrue(selectionModel.getSelectedItems().isEmpty()); + assertFalse(listing.getSelectedItem().isPresent()); assertEquals(Arrays.asList(PERSON_C, null), selectionChanges); } @Test public void getValue() { - selectionModel.setSelectedItem(PERSON_B); + listing.setSelectedItem(PERSON_B); Assert.assertEquals(PERSON_B, listing.getValue()); - selectionModel.deselectAll(); + listing.deselect(PERSON_B); Assert.assertNull(listing.getValue()); } @@ -211,11 +199,11 @@ public class AbstractSingleSelectTest { public void setValue() { listing.setValue(PERSON_C); - Assert.assertEquals(PERSON_C, selectionModel.getSelectedItem().get()); + Assert.assertEquals(PERSON_C, listing.getSelectedItem().get()); listing.setValue(null); - Assert.assertFalse(selectionModel.getSelectedItem().isPresent()); + Assert.assertFalse(listing.getSelectedItem().isPresent()); } @Test @@ -268,15 +256,10 @@ public class AbstractSingleSelectTest { } @Test - @SuppressWarnings({ "unchecked", "rawtypes", "serial" }) + @SuppressWarnings({ "unchecked", "rawtypes" }) public void setValue_isDelegatedToDeselectAndUpdateSelection() { - Multi<?> model = Mockito.mock(Multi.class); - AbstractMultiSelect<String> select = new AbstractMultiSelect<String>() { - @Override - public Multi<String> getSelectionModel() { - return (Multi<String>) model; - } - }; + AbstractMultiSelect<String> select = Mockito + .mock(AbstractMultiSelect.class); Set set = new LinkedHashSet<>(); set.add("foo1"); @@ -285,11 +268,12 @@ public class AbstractSingleSelectTest { selected.add("bar1"); selected.add("bar"); selected.add("bar2"); - Mockito.when(model.getSelectedItems()).thenReturn(selected); + Mockito.when(select.getSelectedItems()).thenReturn(selected); + Mockito.doCallRealMethod().when(select).setValue(Mockito.anySet()); select.setValue(set); - Mockito.verify(model).updateSelection(set, selected); + Mockito.verify(select).updateSelection(set, selected); } } diff --git a/server/src/test/java/com/vaadin/ui/RadioButtonGroupTest.java b/server/src/test/java/com/vaadin/ui/RadioButtonGroupTest.java index 31bc2de271..ea1cf39351 100644 --- a/server/src/test/java/com/vaadin/ui/RadioButtonGroupTest.java +++ b/server/src/test/java/com/vaadin/ui/RadioButtonGroupTest.java @@ -23,14 +23,12 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import com.vaadin.data.SelectionModel; import com.vaadin.data.SelectionModel.Multi; import com.vaadin.server.data.DataSource; import com.vaadin.shared.data.selection.SelectionServerRpc; public class RadioButtonGroupTest { private RadioButtonGroup<String> radioButtonGroup; - private SelectionModel.Single<String> selectionModel; @Before public void setUp() { @@ -38,7 +36,6 @@ public class RadioButtonGroupTest { // Intentional deviation from upcoming selection order radioButtonGroup .setDataSource(DataSource.create("Third", "Second", "First")); - selectionModel = radioButtonGroup.getSelectionModel(); } @Test @@ -54,7 +51,7 @@ public class RadioButtonGroupTest { radioButtonGroup.select("Second"); radioButtonGroup.deselect("Second"); - radioButtonGroup.getSelectionModel().deselectAll(); + radioButtonGroup.deselect("Second"); Assert.assertEquals(3, listenerCount.get()); } |