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.

RpcDataSourceConnector.java 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. * Copyright 2000-2014 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.client.connectors;
  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.List;
  20. import com.vaadin.client.ServerConnector;
  21. import com.vaadin.client.data.AbstractRemoteDataSource;
  22. import com.vaadin.client.data.HasDataSource;
  23. import com.vaadin.client.extensions.AbstractExtensionConnector;
  24. import com.vaadin.shared.data.DataProviderRpc;
  25. import com.vaadin.shared.data.DataRequestRpc;
  26. import com.vaadin.shared.ui.Connect;
  27. import com.vaadin.shared.ui.grid.GridState;
  28. import com.vaadin.shared.ui.grid.Range;
  29. import elemental.json.Json;
  30. import elemental.json.JsonArray;
  31. import elemental.json.JsonObject;
  32. /**
  33. * Connects a Vaadin server-side container data source to a Grid. This is
  34. * currently implemented as an Extension hardcoded to support a specific
  35. * connector type. This will be changed once framework support for something
  36. * more flexible has been implemented.
  37. *
  38. * @since 7.4
  39. * @author Vaadin Ltd
  40. */
  41. @Connect(com.vaadin.server.communication.data.RpcDataProviderExtension.class)
  42. public class RpcDataSourceConnector extends AbstractExtensionConnector {
  43. /**
  44. * A callback interface to let {@link GridConnector} know that detail
  45. * visibilities might have changed.
  46. *
  47. * @since 7.5.0
  48. * @author Vaadin Ltd
  49. */
  50. interface DetailsListener {
  51. /**
  52. * A request to verify (and correct) the visibility for a row, given
  53. * updated metadata.
  54. *
  55. * @param rowIndex
  56. * the index of the row that should be checked
  57. * @param row
  58. * the row object to check visibility for
  59. * @see GridState#JSONKEY_DETAILS_VISIBLE
  60. */
  61. void reapplyDetailsVisibility(int rowIndex, JsonObject row);
  62. }
  63. public class RpcDataSource extends AbstractRemoteDataSource<JsonObject> {
  64. protected RpcDataSource() {
  65. registerRpc(DataProviderRpc.class, new DataProviderRpc() {
  66. @Override
  67. public void setRowData(int firstRow, JsonArray rowArray) {
  68. ArrayList<JsonObject> rows = new ArrayList<JsonObject>(
  69. rowArray.length());
  70. for (int i = 0; i < rowArray.length(); i++) {
  71. JsonObject rowObject = rowArray.getObject(i);
  72. rows.add(rowObject);
  73. }
  74. RpcDataSource.this.setRowData(firstRow, rows);
  75. }
  76. @Override
  77. public void removeRowData(int firstRow, int count) {
  78. RpcDataSource.this.removeRowData(firstRow, count);
  79. }
  80. @Override
  81. public void insertRowData(int firstRow, int count) {
  82. RpcDataSource.this.insertRowData(firstRow, count);
  83. }
  84. @Override
  85. public void resetDataAndSize(int size) {
  86. RpcDataSource.this.resetDataAndSize(size);
  87. }
  88. @Override
  89. public void updateRowData(JsonArray rowArray) {
  90. for (int i = 0; i < rowArray.length(); ++i) {
  91. RpcDataSource.this.updateRowData(rowArray.getObject(i));
  92. }
  93. }
  94. });
  95. }
  96. private DataRequestRpc rpcProxy = getRpcProxy(DataRequestRpc.class);
  97. private DetailsListener detailsListener;
  98. private JsonArray droppedRowKeys = Json.createArray();
  99. @Override
  100. protected void requestRows(int firstRowIndex, int numberOfRows,
  101. RequestRowsCallback<JsonObject> callback) {
  102. if (droppedRowKeys.length() > 0) {
  103. rpcProxy.dropRows(droppedRowKeys);
  104. droppedRowKeys = Json.createArray();
  105. }
  106. /*
  107. * If you're looking at this code because you want to learn how to
  108. * use AbstactRemoteDataSource, please look somewhere else instead.
  109. *
  110. * We're not doing things in the conventional way with the callback
  111. * here since Vaadin doesn't directly support RPC with return
  112. * values. We're instead asking the server to push us some data, and
  113. * when we receive pushed data, we just push it along to the
  114. * underlying cache in the same way no matter if it was a genuine
  115. * push or just a result of us requesting rows.
  116. */
  117. Range cached = getCachedRange();
  118. rpcProxy.requestRows(firstRowIndex, numberOfRows,
  119. cached.getStart(), cached.length());
  120. /*
  121. * Show the progress indicator if there is a pending data request
  122. * and some of the visible rows are being requested. The RPC in
  123. * itself will not trigger the indicator since it might just fetch
  124. * some rows in the background to fill the cache.
  125. *
  126. * The indicator will be hidden by the framework when the response
  127. * is received (unless another request is already on its way at that
  128. * point).
  129. */
  130. if (getRequestedAvailability().intersects(
  131. Range.withLength(firstRowIndex, numberOfRows))) {
  132. getConnection().getLoadingIndicator().ensureTriggered();
  133. }
  134. }
  135. @Override
  136. public void ensureAvailability(int firstRowIndex, int numberOfRows) {
  137. super.ensureAvailability(firstRowIndex, numberOfRows);
  138. /*
  139. * We trigger the indicator already at this point since the actual
  140. * RPC will not be sent right away when waiting for the response to
  141. * a previous request.
  142. *
  143. * Only triggering here would not be enough since the check that
  144. * sets isWaitingForData is deferred. We don't want to trigger the
  145. * loading indicator here if we don't know that there is actually a
  146. * request going on since some other bug might then cause the
  147. * loading indicator to not be hidden.
  148. */
  149. if (isWaitingForData()
  150. && !Range.withLength(firstRowIndex, numberOfRows)
  151. .isSubsetOf(getCachedRange())) {
  152. getConnection().getLoadingIndicator().ensureTriggered();
  153. }
  154. }
  155. @Override
  156. public String getRowKey(JsonObject row) {
  157. if (row.hasKey(GridState.JSONKEY_ROWKEY)) {
  158. return row.getString(GridState.JSONKEY_ROWKEY);
  159. } else {
  160. return null;
  161. }
  162. }
  163. public RowHandle<JsonObject> getHandleByKey(Object key) {
  164. JsonObject row = Json.createObject();
  165. row.put(GridState.JSONKEY_ROWKEY, (String) key);
  166. return new RowHandleImpl(row, key);
  167. }
  168. @Override
  169. protected void unpinHandle(RowHandleImpl handle) {
  170. // Row data is no longer available after it has been unpinned.
  171. String key = getRowKey(handle.getRow());
  172. super.unpinHandle(handle);
  173. if (!handle.isPinned()) {
  174. if (indexOfKey(key) == -1) {
  175. // Row out of view has been unpinned. drop it
  176. droppedRowKeys.set(droppedRowKeys.length(), key);
  177. }
  178. }
  179. }
  180. void setDetailsListener(DetailsListener detailsListener) {
  181. this.detailsListener = detailsListener;
  182. }
  183. @Override
  184. protected void setRowData(int firstRowIndex, List<JsonObject> rowData) {
  185. super.setRowData(firstRowIndex, rowData);
  186. /*
  187. * Intercepting details information from the data source, rerouting
  188. * them back to the GridConnector (as a details listener)
  189. */
  190. for (int i = 0; i < rowData.size(); i++) {
  191. detailsListener.reapplyDetailsVisibility(firstRowIndex + i,
  192. rowData.get(i));
  193. }
  194. }
  195. /**
  196. * Updates row data based on row key.
  197. *
  198. * @since 7.6
  199. * @param row
  200. * new row object
  201. */
  202. protected void updateRowData(JsonObject row) {
  203. int index = indexOfKey(getRowKey(row));
  204. if (index >= 0) {
  205. setRowData(index, Collections.singletonList(row));
  206. }
  207. }
  208. @Override
  209. protected void onDropFromCache(int rowIndex, JsonObject row) {
  210. if (!isPinned(row)) {
  211. droppedRowKeys.set(droppedRowKeys.length(), getRowKey(row));
  212. }
  213. }
  214. }
  215. private final RpcDataSource dataSource = new RpcDataSource();
  216. @Override
  217. protected void extend(ServerConnector target) {
  218. if (target instanceof HasDataSource) {
  219. ((HasDataSource<JsonObject>) target).setDataSource(dataSource);
  220. } else {
  221. throw new IllegalArgumentException(
  222. "Parent connector does not implement HasDataSource");
  223. }
  224. if (target instanceof GridConnector) {
  225. dataSource.setDetailsListener(((GridConnector) target)
  226. .getDetailsListener());
  227. }
  228. }
  229. }