aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/main/java/com/vaadin/ui
diff options
context:
space:
mode:
authorPekka Hyvönen <pekka@vaadin.com>2016-11-07 09:16:37 +0200
committerIlia Motornyi <elmot@vaadin.com>2016-11-10 07:49:08 +0000
commit39c15034076015a16cd056adf9dc422335985543 (patch)
tree8bb235b285e69cb48b83de067152f06d7c076e8d /server/src/main/java/com/vaadin/ui
parent0f42869ce1f81270141e94f169f1447febaff446 (diff)
downloadvaadin-framework-39c15034076015a16cd056adf9dc422335985543.tar.gz
vaadin-framework-39c15034076015a16cd056adf9dc422335985543.zip
Remove HasValue from Grid
Extracts grid single selection into separate class, which is an extension like in V7. Using an extension makes it possible to easily add multiselect and no-select modes back, and support custom selection models. Adds Grid:asSingleSelect() SingleSelect so that grid can be used as a Select in a binder. Removes all remaining references to SelectionModels in Listings. Renames SingleSelectionChangeEvent to SingleSelectionEvent, because then it is unified with selection listener and MultiSelectionEvent. Fixes vaadin/framework8-issues#424 Fixes vaadin/framework8-issues#425 Change-Id: Ie22bef29cfd4336c3f65d4e63531c578b8dd76a3
Diffstat (limited to 'server/src/main/java/com/vaadin/ui')
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java28
-rw-r--r--server/src/main/java/com/vaadin/ui/ComboBox.java2
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java229
-rw-r--r--server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerSelect.java24
-rw-r--r--server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModel.java299
5 files changed, 371 insertions, 211 deletions
diff --git a/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java b/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java
index 7125139d8a..2ffcedde15 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java
@@ -22,7 +22,7 @@ import java.util.Optional;
import com.vaadin.data.HasValue;
import com.vaadin.data.SelectionModel;
import com.vaadin.data.SelectionModel.Single;
-import com.vaadin.event.selection.SingleSelectionChangeEvent;
+import com.vaadin.event.selection.SingleSelectionEvent;
import com.vaadin.event.selection.SingleSelectionListener;
import com.vaadin.server.data.DataCommunicator;
import com.vaadin.shared.Registration;
@@ -49,7 +49,7 @@ public abstract class AbstractSingleSelect<T> extends AbstractListing<T>
@Deprecated
private static final Method SELECTION_CHANGE_METHOD = ReflectTools
.findMethod(SingleSelectionListener.class, "accept",
- SingleSelectionChangeEvent.class);
+ SingleSelectionEvent.class);
/**
* Creates a new {@code AbstractListing} with a default data communicator.
@@ -92,11 +92,11 @@ public abstract class AbstractSingleSelect<T> extends AbstractListing<T>
* the value change listener, not null
* @return a registration for the listener
*/
- public Registration addSelectionListener(
+ public Registration addSelectionChangeListener(
SingleSelectionListener<T> listener) {
- addListener(SingleSelectionChangeEvent.class, listener,
+ addListener(SingleSelectionEvent.class, listener,
SELECTION_CHANGE_METHOD);
- return () -> removeListener(SingleSelectionChangeEvent.class, listener);
+ return () -> removeListener(SingleSelectionEvent.class, listener);
}
/**
@@ -158,7 +158,7 @@ public abstract class AbstractSingleSelect<T> extends AbstractListing<T>
@Override
public Registration addValueChangeListener(
HasValue.ValueChangeListener<T> listener) {
- return addSelectionListener(event -> listener.accept(
+ return addSelectionChangeListener(event -> listener.accept(
new ValueChangeEvent<>(this, event.isUserOriginated())));
}
@@ -192,18 +192,6 @@ public abstract class AbstractSingleSelect<T> extends AbstractListing<T>
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.
@@ -245,7 +233,7 @@ public abstract class AbstractSingleSelect<T> extends AbstractListing<T>
}
doSetSelectedKey(key);
- fireEvent(new SingleSelectionChangeEvent<>(AbstractSingleSelect.this,
+ fireEvent(new SingleSelectionEvent<>(AbstractSingleSelect.this,
true));
}
@@ -266,7 +254,7 @@ public abstract class AbstractSingleSelect<T> extends AbstractListing<T>
}
doSetSelectedKey(key);
- fireEvent(new SingleSelectionChangeEvent<>(AbstractSingleSelect.this,
+ fireEvent(new SingleSelectionEvent<>(AbstractSingleSelect.this,
false));
}
diff --git a/server/src/main/java/com/vaadin/ui/ComboBox.java b/server/src/main/java/com/vaadin/ui/ComboBox.java
index 097ae30bc7..d4df2236f7 100644
--- a/server/src/main/java/com/vaadin/ui/ComboBox.java
+++ b/server/src/main/java/com/vaadin/ui/ComboBox.java
@@ -524,7 +524,7 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
@Override
public Registration addValueChangeListener(
HasValue.ValueChangeListener<T> listener) {
- return addSelectionListener(event -> {
+ return addSelectionChangeListener(event -> {
listener.accept(new ValueChangeEvent<>(event.getComponent(), this,
event.isUserOriginated()));
});
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
index 63c8fe3e6f..42aab6a7e4 100644
--- a/server/src/main/java/com/vaadin/ui/Grid.java
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -30,19 +30,19 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.Set;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import com.vaadin.data.SelectionModel.Single;
+import com.vaadin.data.Binder;
+import com.vaadin.data.SelectionModel;
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.Extension;
import com.vaadin.server.JsonCodec;
import com.vaadin.server.SerializableComparator;
import com.vaadin.server.SerializableFunction;
@@ -50,7 +50,6 @@ 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;
@@ -65,6 +64,7 @@ import com.vaadin.ui.Grid.FooterRow;
import com.vaadin.ui.components.grid.Footer;
import com.vaadin.ui.components.grid.Header;
import com.vaadin.ui.components.grid.Header.Row;
+import com.vaadin.ui.components.grid.SingleSelectionModel;
import com.vaadin.ui.renderers.AbstractRenderer;
import com.vaadin.ui.renderers.Renderer;
import com.vaadin.ui.renderers.TextRenderer;
@@ -83,7 +83,7 @@ import elemental.json.JsonValue;
* @param <T>
* the grid bean type
*/
-public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
+public class Grid<T> extends AbstractListing<T> implements HasComponents {
@Deprecated
private static final Method COLUMN_REORDER_METHOD = ReflectTools.findMethod(
@@ -121,6 +121,27 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
}
/**
+ * The server-side interface that controls Grid's selection state.
+ * SelectionModel should extend {@link AbstractGridExtension}.
+ *
+ * @param <T>
+ * the grid bean type
+ */
+ public interface GridSelectionModel<T>
+ extends SelectionModel<T>, Extension {
+
+ /**
+ * Removes this selection model from the grid.
+ * <p>
+ * Must call super {@link Extension#remove()} to detach the extension,
+ * and fire an selection change event for the selection model (with an
+ * empty selection).
+ */
+ @Override
+ void remove();
+ }
+
+ /**
* An event listener for column resize events in the Grid.
*/
@FunctionalInterface
@@ -602,8 +623,8 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
.hasKey(diffStateKey) : "Field name has changed";
Type type = null;
try {
- type = (getState(false).getClass()
- .getDeclaredField(diffStateKey).getGenericType());
+ type = getState(false).getClass()
+ .getDeclaredField(diffStateKey).getGenericType();
} catch (NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
@@ -1488,163 +1509,6 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
}
}
- private final class SingleSelection implements Single<T> {
- private T selectedItem = null;
-
- SingleSelection() {
- addDataGenerator((item, json) -> {
- if (isSelected(item)) {
- 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
- public Optional<T> getSelectedItem() {
- return Optional.ofNullable(selectedItem);
- }
-
- @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());
- }
-
- /**
- * 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);
- }
-
- /**
- * 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);
- }
- selectedItem = keyToItem(key);
- if (selectedItem != null) {
- 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);
- }
- }
-
/**
* A header row in a Grid.
*/
@@ -1835,17 +1699,18 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
private int counter = 0;
- private Single<T> selectionModel;
+ private GridSelectionModel<T> selectionModel;
/**
* Constructor for the {@link Grid} component.
*/
public Grid() {
- setSelectionModel(new SingleSelection());
registerRpc(new GridServerRpcImpl());
setDefaultHeaderRow(appendHeaderRow());
+ selectionModel = new SingleSelectionModel<>(this);
+
detailsManager = new DetailsManager<>();
addExtension(detailsManager);
addDataGenerator(detailsManager);
@@ -2690,30 +2555,40 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
*
* @return the selection model, not null
*/
- public Single<T> getSelectionModel() {
+ public GridSelectionModel<T> getSelectionModel() {
assert selectionModel != null : "No selection model set by "
+ getClass().getName() + " constructor";
return selectionModel;
}
- @Override
- public Optional<T> getSelectedItem() {
- return getSelectionModel().getSelectedItem();
+ /**
+ * Use this grid as a single select in {@link Binder}.
+ * <p>
+ * Sets the grid to single select mode, if not yet so.
+ *
+ * @return the single select wrapper that can be used in binder
+ */
+ public SingleSelect<T> asSingleSelect() {
+ GridSelectionModel<T> model = getSelectionModel();
+ if (!(model instanceof SingleSelectionModel)) {
+ model = new SingleSelectionModel<>(this);
+ setSelectionModel(model);
+ }
+
+ return ((SingleSelectionModel<T>) model).asSingleSelect();
}
/**
* Sets the selection model for this listing.
+ * <p>
+ * The default selection model is {@link SingleSelectionModel}.
*
* @param model
- * the selection model to use, not null
+ * the selection model to use, not {@code null}
*/
- protected void setSelectionModel(Single<T> model) {
- if (selectionModel != null) {
- throw new IllegalStateException(
- "A selection model can't be changed.");
- }
-
+ protected void setSelectionModel(GridSelectionModel<T> model) {
Objects.requireNonNull(model, "selection model cannot be null");
+ selectionModel.remove();
selectionModel = model;
}
diff --git a/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerSelect.java b/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerSelect.java
index ec200082ab..f762aae827 100644
--- a/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerSelect.java
+++ b/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerSelect.java
@@ -62,7 +62,7 @@ public class ColorPickerSelect extends CustomField<Color> {
range.setWidth("100%");
range.addValueChangeListener(this::valueChange);
- range.select(ColorRange.ALL);
+ range.setValue(ColorRange.ALL);
layout.addComponent(range);
@@ -90,20 +90,20 @@ public class ColorPickerSelect extends CustomField<Color> {
for (int row = 0; row < rows; row++) {
for (int col = 0; col < columns; col++) {
- if (row < (rows - 1)) {
+ if (row < rows - 1) {
// Create the color grid by varying the saturation and value
// Calculate new hue value
- float hue = ((float) col / (float) columns);
+ float hue = (float) col / (float) columns;
float saturation = 1f;
float value = 1f;
// For the upper half use value=1 and variable
// saturation
- if (row < (rows / 2)) {
- saturation = ((row + 1f) / (rows / 2f));
+ if (row < rows / 2) {
+ saturation = (row + 1f) / (rows / 2f);
} else {
- value = 1f - ((row - (rows / 2f)) / (rows / 2f));
+ value = 1f - (row - rows / 2f) / (rows / 2f);
}
colors[row][col] = new Color(
@@ -112,7 +112,7 @@ public class ColorPickerSelect extends CustomField<Color> {
// The last row should have the black&white gradient
float hue = 0f;
float saturation = 0f;
- float value = 1f - ((float) col / (float) columns);
+ float value = 1f - (float) col / (float) columns;
colors[row][col] = new Color(
Color.HSVtoRGB(hue, saturation, value));
@@ -151,13 +151,11 @@ public class ColorPickerSelect extends CustomField<Color> {
saturation = 1f;
value = 1f;
- if (index <= ((rows * columns) / 2)) {
- saturation = index
- / (((float) rows * (float) columns) / 2f);
+ if (index <= rows * columns / 2) {
+ saturation = index / ((float) rows * (float) columns / 2f);
} else {
- index -= ((rows * columns) / 2);
- value = 1f
- - index / (((float) rows * (float) columns) / 2f);
+ index -= rows * columns / 2;
+ value = 1f - index / ((float) rows * (float) columns / 2f);
}
colors[row][col] = new Color(
diff --git a/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModel.java b/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModel.java
new file mode 100644
index 0000000000..bf2f55648e
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModel.java
@@ -0,0 +1,299 @@
+/*
+ * 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.components.grid;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import com.vaadin.data.SelectionModel;
+import com.vaadin.event.selection.SingleSelectionEvent;
+import com.vaadin.event.selection.SingleSelectionListener;
+import com.vaadin.shared.Registration;
+import com.vaadin.shared.data.DataCommunicatorConstants;
+import com.vaadin.shared.data.selection.SelectionServerRpc;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.Grid.AbstractGridExtension;
+import com.vaadin.ui.Grid.GridSelectionModel;
+import com.vaadin.ui.SingleSelect;
+import com.vaadin.util.ReflectTools;
+
+import elemental.json.JsonObject;
+
+/**
+ * Single selection model for grid.
+ *
+ * @author Vaadin Ltd.
+ * @since 8.0
+ *
+ * @param <T>
+ * the type of the selected item in grid.
+ */
+public class SingleSelectionModel<T> extends AbstractGridExtension<T>
+ implements GridSelectionModel<T>, SelectionModel.Single<T> {
+
+ private static final Method SELECTION_CHANGE_METHOD = ReflectTools
+ .findMethod(SingleSelectionListener.class, "accept",
+ SingleSelectionEvent.class);
+
+ private final Grid<T> grid;
+ private T selectedItem = null;
+
+ /**
+ * Constructs a new single selection model for the given grid.
+ *
+ * @param grid
+ * the grid to bind the selection model into
+ */
+ public SingleSelectionModel(Grid<T> grid) {
+ this.grid = grid;
+ extend(grid);
+ registerRpc(new SelectionServerRpc() {
+
+ @Override
+ public void select(String key) {
+ setSelectedFromClient(key);
+ }
+
+ @Override
+ public void deselect(String key) {
+ if (isKeySelected(key)) {
+ setSelectedFromClient(null);
+ }
+ }
+ });
+ }
+
+ /**
+ * Adds a selection change listener to this select. The listener is called
+ * when the value of this select is changed either by the user or
+ * programmatically.
+ *
+ * @param listener
+ * the value change listener, not null
+ * @return a registration for the listener
+ */
+ public Registration addSelectionChangeListener(
+ SingleSelectionListener<T> listener) {
+ addListener(SingleSelectionEvent.class, listener,
+ SELECTION_CHANGE_METHOD);
+ return () -> removeListener(SingleSelectionEvent.class, listener);
+ }
+
+ @Override
+ public Optional<T> getSelectedItem() {
+ return Optional.ofNullable(selectedItem);
+ }
+
+ @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());
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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) {
+ grid.getDataCommunicator().refresh(selectedItem);
+ }
+ selectedItem = getData(key);
+ if (selectedItem != null) {
+ grid.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 (isKeySelected(key)) {
+ return;
+ }
+
+ doSetSelectedKey(key);
+ fireEvent(
+ new SingleSelectionEvent<>(grid, asSingleSelect(), 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 (isSelected(item) || isKeySelected(key)) {
+ return;
+ }
+
+ doSetSelectedKey(key);
+ fireEvent(new SingleSelectionEvent<>(grid, asSingleSelect(),
+ 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 grid.getDataCommunicator().getKeyMapper().key(item);
+ }
+ }
+
+ @Override
+ public Set<T> getSelectedItems() {
+ if (selectedItem != null) {
+ return new HashSet<>(Arrays.asList(selectedItem));
+ } else {
+ return Collections.emptySet();
+ }
+ }
+
+ @Override
+ public void generateData(T item, JsonObject jsonObject) {
+ if (isSelected(item)) {
+ jsonObject.put(DataCommunicatorConstants.SELECTED, true);
+ }
+ }
+
+ @Override
+ public void remove() {
+ // when selection model changes, firing an event for selection change
+ // event fired before removing so that parent is still intact (in case
+ // needed)
+ selectedItem = null;
+ fireEvent(new SingleSelectionEvent<>(grid, asSingleSelect(),
+ false));
+
+ super.remove();
+ }
+
+ /**
+ * Gets a wrapper for using this grid as a single select in a binder.
+ *
+ * @return a single select wrapper for grid
+ */
+ public SingleSelect<T> asSingleSelect() {
+ return new SingleSelect<T>() {
+
+ @Override
+ public void setValue(T value) {
+ SingleSelectionModel.this.setSelectedFromServer(value);
+ }
+
+ @Override
+ public T getValue() {
+ return SingleSelectionModel.this.getSelectedItem().orElse(null);
+ }
+
+ @Override
+ public Registration addValueChangeListener(
+ com.vaadin.data.HasValue.ValueChangeListener<T> listener) {
+ return SingleSelectionModel.this.addSelectionChangeListener(
+ event -> listener.accept(event));
+ }
+
+ @Override
+ public void setRequiredIndicatorVisible(
+ boolean requiredIndicatorVisible) {
+ // TODO support required indicator when grid is used in binder ?
+ throw new UnsupportedOperationException(
+ "Required indicator is not supported for Grid.");
+ }
+
+ @Override
+ public boolean isRequiredIndicatorVisible() {
+ throw new UnsupportedOperationException(
+ "Required indicator is not supported for Grid.");
+ }
+
+ @Override
+ public void setReadOnly(boolean readOnly) {
+ // TODO support read only when grid is used in binder ?
+ throw new UnsupportedOperationException(
+ "Read only is not supported for Grid.");
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ throw new UnsupportedOperationException(
+ "Read only is not supported for Grid.");
+ }
+ };
+ }
+} \ No newline at end of file