import java.io.Serializable;
import java.util.Collection;
-import java.util.Set;
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.
*/
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.
*
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);
- }
}
@Override
@SuppressWarnings("unchecked")
- public AbstractListing<T, ?> getComponent() {
- return (AbstractListing<T, ?>) super.getComponent();
+ public AbstractListing<T> getComponent() {
+ return (AbstractListing<T>) super.getComponent();
}
@Override
*
* @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
* @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);
}
@Override
@SuppressWarnings("unchecked")
- public AbstractListing<T, ?> getParent() {
- return (AbstractListing<T, ?>) super.getParent();
+ public AbstractListing<T> getParent() {
+ return (AbstractListing<T>) super.getParent();
}
/**
private final DataCommunicator<T> dataCommunicator;
- private SELECTIONMODEL selectionModel;
-
/**
* Creates a new {@code AbstractListing} with a default data communicator.
* <p>
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.
* @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);
}
return Optional.of(item);
}
- private SimpleMultiSelectModel getSelectionModel() {
- return (SimpleMultiSelectModel) AbstractMultiSelect.this
- .getSelectionModel();
- }
}
private class MultiSelectDataGenerator implements DataGenerator<T> {
true);
}
- if (getSelectionModel().isSelected(data)) {
+ if (isSelected(data)) {
jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED,
true);
}
* 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
* the value change listener, not {@code null}
* @return a registration for the listener
*/
+ @Override
public Registration addSelectionListener(
MultiSelectionListener<T> listener) {
addListener(MultiSelectionEvent.class, listener,
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
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();
+ }
}
*
* @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",
* {@link AbstractSingleSelect.AbstractSingleSelection} .
*/
protected AbstractSingleSelect() {
+ init();
}
/**
*/
protected AbstractSingleSelect(DataCommunicator<T> dataCommunicator) {
super(dataCommunicator);
+ init();
}
/**
* otherwise
*/
public Optional<T> getSelectedItem() {
- return getSelectionModel().getSelectedItem();
+ return Optional.ofNullable(keyToItem(getSelectedKey()));
}
/**
* the item to select or {@code null} to clear selection
*/
public void setSelectedItem(T item) {
- getSelectionModel().setSelectedItem(item);
+ setSelectedFromServer(item);
}
/**
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);
+ }
+ }
+ });
+ }
+
}
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.
};
}
});
- setSelectionModel(new ComboBoxSelectionModel());
init();
}
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) {
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;
+ }
+
}
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;
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;
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");
}
}
- private final class SingleSelection extends AbstractSingleSelection {
+ private final class SingleSelection implements Single<T> {
private T selectedItem = null;
SingleSelection() {
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
}
@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);
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);
+ }
}
/**
private int counter = 0;
+ private Single<T> selectionModel;
+
/**
* Constructor for the {@link Grid} component.
*/
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);
--- /dev/null
+/*
+ * 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);
+}
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
addDataGenerator((item, json) -> json
.put(DataCommunicatorConstants.DATA, String.valueOf(item)));
-
- setSelectionModel(new SimpleSingleSelection());
}
/**
*/
public RadioButtonGroup() {
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
- setSelectionModel(new SimpleSingleSelection());
addDataGenerator(new DataGenerator<T>() {
@Override
true);
}
- if (getSelectionModel().isSelected(data)) {
+ if (isSelected(data)) {
jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED,
true);
}
converterBinder.readBean(new AtomicReference<>("TWO"));
assertEquals(Collections.singleton(TestEnum.TWO),
- select.getSelectionModel().getSelectedItems());
+ select.getSelectedItems());
}
@Test
package com.vaadin.tests.components.grid;
+import java.util.Optional;
+
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@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());
}
}
private final class TestListing extends AbstractSingleSelect<String> {
- protected TestListing() {
- setSelectionModel(new SimpleSingleSelection());
- }
-
/**
* Used to execute data generation
*/
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;
@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);
}
@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
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());
}
});
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());
}
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");
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" })
rpcUpdateSelection(keysToSelect, new String[] {});
}
- private void rpcDeselect(String... keysToDeselect) {
+ private void rpcDeselectItems(String... keysToDeselect) {
rpcUpdateSelection(new String[] {}, keysToDeselect);
}
.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()));
}
}
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
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}
*/
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()));
}
@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);
}
@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);
}
@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);
}
@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);
}
@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);
}
@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());
}
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
}
@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");
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);
}
}
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() {
// Intentional deviation from upcoming selection order
radioButtonGroup
.setDataSource(DataSource.create("Third", "Second", "First"));
- selectionModel = radioButtonGroup.getSelectionModel();
}
@Test
radioButtonGroup.select("Second");
radioButtonGroup.deselect("Second");
- radioButtonGroup.getSelectionModel().deselectAll();
+ radioButtonGroup.deselect("Second");
Assert.assertEquals(3, listenerCount.get());
}
import java.util.stream.IntStream;
import com.googlecode.gentyref.GenericTypeReflector;
-import com.vaadin.data.SelectionModel;
import com.vaadin.event.FieldEvents.BlurNotifier;
import com.vaadin.event.FieldEvents.FocusNotifier;
import com.vaadin.server.VaadinRequest;
* @author Vaadin Ltd
*
*/
-public abstract class AbstractListingFocusBlurTest<T extends AbstractListing<Integer, S> & FocusNotifier & BlurNotifier, S extends SelectionModel<Integer>>
+public abstract class AbstractListingFocusBlurTest<T extends AbstractListing<Integer> & FocusNotifier & BlurNotifier>
extends AbstractTestUIWithLog {
@Override
if (valueType instanceof Class<?>) {
Class<?> clazz = (Class<?>) valueType;
try {
- AbstractListing<Integer, ?> select = (AbstractListing<Integer, ?>) clazz
+ AbstractListing<Integer> select = (AbstractListing<Integer>) clazz
.newInstance();
select.setItems(
IntStream.range(1, 10).mapToObj(Integer::valueOf)
import com.vaadin.ui.AbstractListing;
@Widgetset("com.vaadin.DefaultWidgetSet")
-public abstract class AbstractListingTestUI<T extends AbstractListing<Object, ?>>
+public abstract class AbstractListingTestUI<T extends AbstractListing<Object>>
extends AbstractComponentTest<T> {
@Override
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import com.vaadin.data.SelectionModel.Multi;
import com.vaadin.ui.AbstractMultiSelect;
import com.vaadin.ui.ItemCaptionGenerator;
}
protected void createSelectionMenu() {
- createClickAction(
- "Clear selection", selectionCategory, (component, item,
- data) -> component.getSelectionModel().deselectAll(),
- "");
+ createClickAction("Clear selection", selectionCategory,
+ (component, item, data) -> component.deselectAll(), "");
Command<MULTISELECT, String> toggleSelection = (component, item,
data) -> toggleSelection(item);
}
private void toggleSelection(String item) {
- Multi<Object> selectionModel = getComponent().getSelectionModel();
- if (selectionModel.isSelected(item)) {
- selectionModel.deselect(item);
+ if (getComponent().isSelected(item)) {
+ getComponent().deselect(item);
} else {
- selectionModel.select(item);
+ getComponent().select(item);
}
}
private void toggleMultiSelection(boolean add, List<String> items) {
- Multi<Object> selectionModel = getComponent().getSelectionModel();
if (add) {
- selectionModel.selectItems(items.toArray());
+ getComponent().select(items.toArray());
} else {
- selectionModel.deselectItems(items.toArray());
+ getComponent().deselect(items.toArray());
}
}
options.put("Item 100", "Item 100");
createSelectAction("Select", "Selection", options, "None",
- (c, selected, data) -> {
+ (component, selected, data) -> {
if (selected != null) {
- c.select(selected);
+ component.select(selected);
} else {
- c.getSelectedItems().forEach(c::deselect);
+ component.getSelectedItem()
+ .ifPresent(component::deselect);
}
});
}
*/
package com.vaadin.tests.components.checkboxgroup;
-import com.vaadin.data.SelectionModel.Multi;
import com.vaadin.tests.components.AbstractListingFocusBlurTest;
import com.vaadin.ui.CheckBoxGroup;
* @author Vaadin Ltd
*
*/
-public class CheckBoxGroupFocusBlur extends
- AbstractListingFocusBlurTest<CheckBoxGroup<Integer>, Multi<Integer>> {
+public class CheckBoxGroupFocusBlur
+ extends AbstractListingFocusBlurTest<CheckBoxGroup<Integer>> {
}
MenuItem headerTypeMenu = columnMenu.addItem("Header Type", null);
headerTypeMenu.addItem("Text Header", selectedItem -> grid
.getDefaultHeaderRow().getCell(col).setText("Text Header"));
- headerTypeMenu
- .addItem("HTML Header",
- selectedItem -> grid.getDefaultHeaderRow()
- .getCell(col)
- .setHtml("<b>HTML Header</b>"));
+ headerTypeMenu.addItem("HTML Header",
+ selectedItem -> grid.getDefaultHeaderRow().getCell(col)
+ .setHtml("<b>HTML Header</b>"));
headerTypeMenu.addItem("Widget Header", selectedItem -> {
final Button button = new Button("Button Header");
button.addClickListener(clickEvent -> log("Button clicked!"));
: null))
.setCheckable(true);
stateMenu
- .addItem("Cell description generator",
- item -> grid.getColumns().stream().findFirst()
- .ifPresent(
- c -> c.setDescriptionGenerator(
- item.isChecked()
- ? t -> "Cell tooltip for row "
- + t.getRowNumber()
- + ", Column 0"
- : null)))
+ .addItem("Cell description generator", item -> grid.getColumns()
+ .stream().findFirst()
+ .ifPresent(c -> c.setDescriptionGenerator(
+ item.isChecked() ? t -> "Cell tooltip for row "
+ + t.getRowNumber() + ", Column 0"
+ : null)))
.setCheckable(true);
stateMenu.addItem("Item click listener", new Command() {
private void createBodyMenu(MenuItem rowMenu) {
rowMenu.addItem("Toggle first row selection", menuItem -> {
DataObject item = data.get(0);
- if (grid.isSelected(item)) {
- grid.deselect(item);
+ if (grid.getSelectionModel().isSelected(item)) {
+ grid.getSelectionModel().deselect(item);
} else {
- grid.select(item);
+ grid.getSelectionModel().select(item);
}
});
}
private void createFooterMenu(MenuItem footerMenu) {
footerMenu.addItem("Add default footer row", menuItem -> {
FooterRow defaultFooter = grid.appendFooterRow();
- grid.getColumns()
- .forEach(column -> defaultFooter.getCell(column)
- .setText(grid.getDefaultHeaderRow().getCell(column)
- .getText()));
+ grid.getColumns().forEach(
+ column -> defaultFooter.getCell(column).setText(grid
+ .getDefaultHeaderRow().getCell(column).getText()));
footerMenu.removeChild(menuItem);
});
footerMenu.addItem("Append footer row", menuItem -> {
package com.vaadin.tests.components.nativeselect;
import com.vaadin.tests.components.AbstractListingFocusBlurTest;
-import com.vaadin.ui.AbstractSingleSelect;
import com.vaadin.ui.NativeSelect;
/**
* @author Vaadin Ltd
*
*/
-public class NativeSelectFocusBlur extends
- AbstractListingFocusBlurTest<NativeSelect<Integer>, AbstractSingleSelect<Integer>.AbstractSingleSelection> {
+public class NativeSelectFocusBlur
+ extends AbstractListingFocusBlurTest<NativeSelect<Integer>> {
}
*/
package com.vaadin.tests.components.radiobutton;
-import com.vaadin.data.SelectionModel;
+import java.util.LinkedHashMap;
+import java.util.stream.IntStream;
+
import com.vaadin.server.FontAwesome;
import com.vaadin.tests.components.abstractlisting.AbstractListingTestUI;
import com.vaadin.ui.ItemCaptionGenerator;
import com.vaadin.ui.RadioButtonGroup;
-import java.util.LinkedHashMap;
-import java.util.stream.IntStream;
-
/**
* Test UI for RadioButtonGroup component
*
}
protected void createSelectionMenu() {
- createClickAction(
- "Clear selection", selectionCategory, (component, item,
- data) -> component.getSelectionModel().deselectAll(),
+ createClickAction("Clear selection", selectionCategory,
+ (component, item, data) -> component.getSelectedItem()
+ .ifPresent(component::deselect),
"");
Command<RadioButtonGroup<Object>, String> toggleSelection = (component,
.forEach(item -> createClickAction("Toggle " + item,
selectionCategory, toggleSelection, item));
}
+
private void createItemIconGeneratorMenu() {
- createBooleanAction("Use Item Icon Generator", "Item Icon Generator", false,
- this::useItemIconGenerator);
+ createBooleanAction("Use Item Icon Generator", "Item Icon Generator",
+ false, this::useItemIconGenerator);
}
private void useItemIconGenerator(RadioButtonGroup<Object> group,
- boolean activate, Object data) {
+ boolean activate, Object data) {
if (activate) {
group.setItemIconGenerator(
item -> FontAwesome.values()[getIndex(item) + 1]);
options.put("Custom Caption Generator",
item -> item.toString() + " Caption");
- createSelectAction("Item Caption Generator", "Item Caption Generator", options,
- "None", (radioButtonGroup, captionGenerator, data) -> {
+ createSelectAction("Item Caption Generator", "Item Caption Generator",
+ options, "None", (radioButtonGroup, captionGenerator, data) -> {
radioButtonGroup.setItemCaptionGenerator(captionGenerator);
radioButtonGroup.getDataSource().refreshAll();
}, true);
}
private void toggleSelection(String item) {
- SelectionModel.Single<Object> selectionModel = getComponent()
- .getSelectionModel();
- if (selectionModel.isSelected(item)) {
- selectionModel.deselect(item);
+ if (getComponent().isSelected(item)) {
+ getComponent().deselect(item);
} else {
- selectionModel.select(item);
+ getComponent().select(item);
}
}
e -> log("Selected: " + e.getSelectedItem())));
}
-
private int getIndex(Object item) {
int index = item.toString().indexOf(' ');
if (index < 0) {
package com.vaadin.tests.components.radiobuttongroup;
import com.vaadin.tests.components.AbstractListingFocusBlurTest;
-import com.vaadin.ui.AbstractSingleSelect;
import com.vaadin.ui.RadioButtonGroup;
/**
* @author Vaadin Ltd
*
*/
-public class RadioButtonGroupFocusBlur extends
- AbstractListingFocusBlurTest<RadioButtonGroup<Integer>, AbstractSingleSelect<Integer>.AbstractSingleSelection> {
+public class RadioButtonGroupFocusBlur
+ extends AbstractListingFocusBlurTest<RadioButtonGroup<Integer>> {
}
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Set;
+import java.util.Optional;
import java.util.stream.Stream;
import com.vaadin.annotations.Widgetset;
-import com.vaadin.data.SelectionModel;
import com.vaadin.server.VaadinRequest;
-import com.vaadin.server.data.DataCommunicator;
import com.vaadin.server.data.ListDataSource;
import com.vaadin.server.data.Query;
import com.vaadin.shared.data.DataCommunicatorConstants;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.tests.widgetset.TestingWidgetSet;
-import com.vaadin.ui.AbstractListing;
+import com.vaadin.ui.AbstractSingleSelect;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;
* Simplified server only selection model. Selection state passed in data,
* shown as bold text.
*/
- private static class DummySelectionModel implements SelectionModel<String> {
+ public static class DummyComponent extends AbstractSingleSelect<String> {
+
private String selected;
- private DataCommunicator<String> communicator;
+
+ private DummyComponent() {
+ addDataGenerator((str, json) -> {
+ json.put(DataCommunicatorConstants.DATA, str);
+ if (isSelected(str)) {
+ json.put(DataCommunicatorConstants.SELECTED, true);
+ }
+ });
+ }
@Override
- public Set<String> getSelectedItems() {
- if (selected != null) {
- return Collections.singleton(selected);
- }
- return Collections.emptySet();
+ public Optional<String> getSelectedItem() {
+ return Optional.ofNullable(selected);
}
@Override
public void select(String item) {
if (selected != null) {
- communicator.refresh(selected);
+ getDataCommunicator().refresh(selected);
}
selected = item;
if (selected != null) {
- communicator.refresh(selected);
+ getDataCommunicator().refresh(selected);
}
}
select(null);
}
}
-
- private void setCommunicator(DataCommunicator<String> dataComm) {
- communicator = dataComm;
- }
- }
-
- public static class DummyComponent
- extends AbstractListing<String, DummySelectionModel> {
-
- private DummyComponent() {
- setSelectionModel(new DummySelectionModel());
- addDataGenerator((str, json) -> {
- json.put(DataCommunicatorConstants.DATA, str);
- if (isSelected(str)) {
- json.put(DataCommunicatorConstants.SELECTED, true);
- }
- });
- getSelectionModel().setCommunicator(getDataCommunicator());
- }
}
@Override