You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SingleSelectionModelImpl.java 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui.components.grid;
  17. import java.util.Arrays;
  18. import java.util.Collections;
  19. import java.util.HashSet;
  20. import java.util.Objects;
  21. import java.util.Optional;
  22. import java.util.Set;
  23. import com.vaadin.event.selection.SingleSelectionEvent;
  24. import com.vaadin.event.selection.SingleSelectionListener;
  25. import com.vaadin.shared.Registration;
  26. import com.vaadin.shared.data.selection.SelectionServerRpc;
  27. import com.vaadin.shared.ui.grid.SingleSelectionModelState;
  28. import com.vaadin.ui.SingleSelect;
  29. /**
  30. * Single selection model for grid.
  31. *
  32. * @author Vaadin Ltd.
  33. * @since 8.0
  34. *
  35. * @param <T>
  36. * the type of the selected item in grid.
  37. */
  38. public class SingleSelectionModelImpl<T> extends AbstractSelectionModel<T>
  39. implements SingleSelectionModel<T> {
  40. private T selectedItem = null;
  41. @Override
  42. protected void init() {
  43. registerRpc(new SelectionServerRpc() {
  44. @Override
  45. public void select(String key) {
  46. setSelectedFromClient(key);
  47. }
  48. @Override
  49. public void deselect(String key) {
  50. if (isKeySelected(key)) {
  51. setSelectedFromClient(null);
  52. }
  53. }
  54. });
  55. }
  56. @Override
  57. protected SingleSelectionModelState getState() {
  58. return (SingleSelectionModelState) super.getState();
  59. }
  60. @Override
  61. protected SingleSelectionModelState getState(boolean markAsDirty) {
  62. return (SingleSelectionModelState) super.getState(markAsDirty);
  63. }
  64. @Override
  65. public Registration addSingleSelectionListener(
  66. SingleSelectionListener<T> listener) {
  67. return addListener(SingleSelectionEvent.class, listener,
  68. SingleSelectionListener.SELECTION_CHANGE_METHOD);
  69. }
  70. @Override
  71. public Optional<T> getSelectedItem() {
  72. return Optional.ofNullable(selectedItem);
  73. }
  74. @Override
  75. public void deselect(T item) {
  76. Objects.requireNonNull(item, "deselected item cannot be null");
  77. if (isSelected(item)) {
  78. setSelectedFromServer(null);
  79. }
  80. }
  81. @Override
  82. public void select(T item) {
  83. Objects.requireNonNull(item, "selected item cannot be null");
  84. setSelectedFromServer(item);
  85. }
  86. /**
  87. * Returns whether the given key maps to the currently selected item.
  88. *
  89. * @param key
  90. * the key to test or {@code null} to test whether nothing is
  91. * selected
  92. * @return {@code true} if the key equals the key of the currently selected
  93. * item (or {@code null} if no selection), {@code false} otherwise.
  94. */
  95. protected boolean isKeySelected(String key) {
  96. return Objects.equals(key, getSelectedKey());
  97. }
  98. /**
  99. * Returns the communication key of the selected item or {@code null} if no
  100. * item is selected.
  101. *
  102. * @return the key of the selected item if any, {@code null} otherwise.
  103. */
  104. protected String getSelectedKey() {
  105. return itemToKey(selectedItem);
  106. }
  107. /**
  108. * Sets the selected item based on the given communication key. If the key
  109. * is {@code null}, clears the current selection if any.
  110. *
  111. * @param key
  112. * the key of the selected item or {@code null} to clear
  113. * selection
  114. */
  115. protected void doSetSelectedKey(String key) {
  116. if (getParent() == null) {
  117. throw new IllegalStateException(
  118. "Trying to update selection for grid selection model that has been detached from the grid.");
  119. }
  120. if (selectedItem != null) {
  121. getGrid().getDataCommunicator().refresh(selectedItem);
  122. }
  123. selectedItem = getData(key);
  124. if (selectedItem != null) {
  125. getGrid().getDataCommunicator().refresh(selectedItem);
  126. }
  127. }
  128. /**
  129. * Sets the selection based on a client request. Does nothing if the select
  130. * component is {@linkplain SingleSelect#isReadOnly()} or if the selection
  131. * would not change. Otherwise updates the selection and fires a selection
  132. * change event with {@code isUserOriginated == true}.
  133. *
  134. * @param key
  135. * the key of the item to select or {@code null} to clear
  136. * selection
  137. */
  138. protected void setSelectedFromClient(String key) {
  139. if (!isUserSelectionAllowed()) {
  140. throw new IllegalStateException("Client tried to update selection"
  141. + " although user selection is disallowed");
  142. }
  143. if (isKeySelected(key)) {
  144. return;
  145. }
  146. T oldSelection = this.getSelectedItem().orElse(null);
  147. doSetSelectedKey(key);
  148. fireEvent(new SingleSelectionEvent<>(getGrid(), asSingleSelect(),
  149. oldSelection, true));
  150. }
  151. /**
  152. * Sets the selection based on server API call. Does nothing if the
  153. * selection would not change; otherwise updates the selection and fires a
  154. * selection change event with {@code isUserOriginated == false}.
  155. *
  156. * @param item
  157. * the item to select or {@code null} to clear selection
  158. */
  159. protected void setSelectedFromServer(T item) {
  160. // TODO creates a key if item not in data provider
  161. String key = itemToKey(item);
  162. if (isSelected(item) || isKeySelected(key)) {
  163. return;
  164. }
  165. T oldSelection = this.getSelectedItem()
  166. .orElse(asSingleSelect().getEmptyValue());
  167. doSetSelectedKey(key);
  168. fireEvent(new SingleSelectionEvent<>(getGrid(), asSingleSelect(),
  169. oldSelection, false));
  170. }
  171. /**
  172. * Returns the communication key assigned to the given item.
  173. *
  174. * @param item
  175. * the item whose key to return
  176. * @return the assigned key
  177. */
  178. protected String itemToKey(T item) {
  179. if (item == null) {
  180. return null;
  181. } else {
  182. // TODO creates a key if item not in data provider
  183. return getGrid().getDataCommunicator().getKeyMapper().key(item);
  184. }
  185. }
  186. @Override
  187. public Set<T> getSelectedItems() {
  188. if (selectedItem != null) {
  189. return new HashSet<>(Arrays.asList(selectedItem));
  190. } else {
  191. return Collections.emptySet();
  192. }
  193. }
  194. @Override
  195. public void setDeselectAllowed(boolean deselectAllowed) {
  196. getState().deselectAllowed = deselectAllowed;
  197. }
  198. @Override
  199. public boolean isDeselectAllowed() {
  200. return getState().deselectAllowed;
  201. }
  202. /**
  203. * Gets a wrapper for using this grid as a single select in a binder.
  204. *
  205. * @return a single select wrapper for grid
  206. */
  207. @Override
  208. public SingleSelect<T> asSingleSelect() {
  209. return new SingleSelect<T>() {
  210. @Override
  211. public void setValue(T value) {
  212. SingleSelectionModelImpl.this.setSelectedFromServer(value);
  213. }
  214. @Override
  215. public T getValue() {
  216. return SingleSelectionModelImpl.this.getSelectedItem()
  217. .orElse(null);
  218. }
  219. @Override
  220. public Registration addValueChangeListener(
  221. com.vaadin.data.HasValue.ValueChangeListener<T> listener) {
  222. return SingleSelectionModelImpl.this.addSingleSelectionListener(
  223. (SingleSelectionListener<T>) event -> listener
  224. .valueChange(event));
  225. }
  226. @Override
  227. public void setRequiredIndicatorVisible(
  228. boolean requiredIndicatorVisible) {
  229. // TODO support required indicator when grid is used in binder ?
  230. throw new UnsupportedOperationException(
  231. "Required indicator is not supported for Grid.");
  232. }
  233. @Override
  234. public boolean isRequiredIndicatorVisible() {
  235. throw new UnsupportedOperationException(
  236. "Required indicator is not supported for Grid.");
  237. }
  238. @Override
  239. public void setReadOnly(boolean readOnly) {
  240. setUserSelectionAllowed(!readOnly);
  241. }
  242. @Override
  243. public boolean isReadOnly() {
  244. return !isUserSelectionAllowed();
  245. }
  246. };
  247. }
  248. @Override
  249. public void refreshData(T item) {
  250. if (isSelected(item)) {
  251. selectedItem = item;
  252. }
  253. }
  254. @Override
  255. public boolean isSelected(T item) {
  256. return item != null && selectedItem != null
  257. && getGrid().getDataProvider().getId(selectedItem)
  258. .equals(getGrid().getDataProvider().getId(item));
  259. }
  260. }