Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

DataCommunicator.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. /*
  2. * Copyright 2000-2018 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.data.provider;
  17. import java.util.ArrayList;
  18. import java.util.Collection;
  19. import java.util.Collections;
  20. import java.util.Comparator;
  21. import java.util.HashSet;
  22. import java.util.LinkedHashSet;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Objects;
  26. import java.util.Set;
  27. import java.util.function.Function;
  28. import java.util.stream.Collectors;
  29. import java.util.stream.Stream;
  30. import com.vaadin.data.ValueProvider;
  31. import com.vaadin.data.provider.DataChangeEvent.DataRefreshEvent;
  32. import com.vaadin.server.AbstractExtension;
  33. import com.vaadin.server.KeyMapper;
  34. import com.vaadin.server.SerializableConsumer;
  35. import com.vaadin.shared.Range;
  36. import com.vaadin.shared.Registration;
  37. import com.vaadin.shared.data.DataCommunicatorClientRpc;
  38. import com.vaadin.shared.data.DataCommunicatorConstants;
  39. import com.vaadin.shared.data.DataRequestRpc;
  40. import com.vaadin.shared.extension.datacommunicator.DataCommunicatorState;
  41. import elemental.json.Json;
  42. import elemental.json.JsonArray;
  43. import elemental.json.JsonObject;
  44. /**
  45. * DataProvider base class. This class is the base for all DataProvider
  46. * communication implementations. It uses {@link DataGenerator}s to write
  47. * {@link JsonObject}s representing each data object to be sent to the
  48. * client-side.
  49. *
  50. * @param <T>
  51. * the bean type
  52. *
  53. * @since 8.0
  54. */
  55. public class DataCommunicator<T> extends AbstractExtension {
  56. private Registration dataProviderUpdateRegistration;
  57. /**
  58. * Simple implementation of collection data provider communication. All data
  59. * is sent by server automatically and no data is requested by client.
  60. */
  61. protected class SimpleDataRequestRpc implements DataRequestRpc {
  62. @Override
  63. public void requestRows(int firstRowIndex, int numberOfRows,
  64. int firstCachedRowIndex, int cacheSize) {
  65. onRequestRows(firstRowIndex, numberOfRows, firstCachedRowIndex,
  66. cacheSize);
  67. }
  68. @Override
  69. public void dropRows(JsonArray keys) {
  70. onDropRows(keys);
  71. }
  72. }
  73. /**
  74. * A class for handling currently active data and dropping data that is no
  75. * longer needed. Data tracking is based on key string provided by
  76. * {@link DataKeyMapper}.
  77. * <p>
  78. * When the {@link DataCommunicator} is pushing new data to the client-side
  79. * via {@link DataCommunicator#pushData(int, List)},
  80. * {@link #addActiveData(Stream)} and {@link #cleanUp(Stream)} are called
  81. * with the same parameter. In the clean up method any dropped data objects
  82. * that are not in the given collection will be cleaned up and
  83. * {@link DataGenerator#destroyData(Object)} will be called for them.
  84. */
  85. protected class ActiveDataHandler implements DataGenerator<T> {
  86. /**
  87. * Set of key strings for currently active data objects
  88. */
  89. private final Set<String> activeData = new HashSet<>();
  90. /**
  91. * Set of key strings for data objects dropped on the client. This set
  92. * is used to clean up old data when it's no longer needed.
  93. */
  94. private final Set<String> droppedData = new HashSet<>();
  95. /**
  96. * Adds given objects as currently active objects.
  97. *
  98. * @param dataObjects
  99. * collection of new active data objects
  100. */
  101. public void addActiveData(Stream<T> dataObjects) {
  102. dataObjects.map(getKeyMapper()::key)
  103. .filter(key -> !activeData.contains(key))
  104. .forEach(activeData::add);
  105. }
  106. /**
  107. * Executes the data destruction for dropped data that is not sent to
  108. * the client. This method takes most recently sent data objects in a
  109. * collection. Doing the clean up like this prevents the
  110. * {@link ActiveDataHandler} from creating new keys for rows that were
  111. * dropped but got re-requested by the client-side. In the case of
  112. * having all data at the client, the collection should be all the data
  113. * in the back end.
  114. *
  115. * @param dataObjects
  116. * collection of most recently sent data to the client
  117. */
  118. public void cleanUp(Stream<T> dataObjects) {
  119. Collection<String> keys = dataObjects.map(getKeyMapper()::key)
  120. .collect(Collectors.toSet());
  121. // Remove still active rows that were dropped by the client
  122. droppedData.removeAll(keys);
  123. // Do data clean up for object no longer needed.
  124. dropData(droppedData);
  125. droppedData.clear();
  126. }
  127. /**
  128. * Marks all currently active data objects to be dropped.
  129. *
  130. * @since 8.6.0
  131. */
  132. public void dropAllActiveData() {
  133. activeData.forEach(this::dropActiveData);
  134. }
  135. /**
  136. * Marks a data object identified by given key string to be dropped.
  137. *
  138. * @param key
  139. * key string
  140. */
  141. public void dropActiveData(String key) {
  142. if (activeData.contains(key)) {
  143. droppedData.add(key);
  144. }
  145. }
  146. /**
  147. * Returns all dropped data mapped by their id from DataProvider.
  148. *
  149. * @return map of ids to dropped data objects
  150. *
  151. * @since 8.6.0
  152. */
  153. protected Map<Object, T> getDroppedData() {
  154. Function<T, Object> getId = getDataProvider()::getId;
  155. return droppedData.stream().map(getKeyMapper()::get)
  156. .collect(Collectors.toMap(getId, i -> i));
  157. }
  158. /**
  159. * Returns all currently active data mapped by their id from
  160. * DataProvider.
  161. *
  162. * @return map of ids to active data objects
  163. */
  164. public Map<Object, T> getActiveData() {
  165. Function<T, Object> getId = getDataProvider()::getId;
  166. return activeData.stream().map(getKeyMapper()::get)
  167. .collect(Collectors.toMap(getId, i -> i));
  168. }
  169. @Override
  170. public void generateData(T data, JsonObject jsonObject) {
  171. // Make sure KeyMapper is up to date
  172. getKeyMapper().refresh(data);
  173. // Write the key string for given data object
  174. jsonObject.put(DataCommunicatorConstants.KEY,
  175. getKeyMapper().key(data));
  176. }
  177. @Override
  178. public void destroyData(T data) {
  179. // Remove from active data set
  180. activeData.remove(getKeyMapper().key(data));
  181. // Drop the registered key
  182. getKeyMapper().remove(data);
  183. }
  184. @Override
  185. public void destroyAllData() {
  186. droppedData.clear();
  187. activeData.clear();
  188. updatedData.clear();
  189. getKeyMapper().removeAll();
  190. }
  191. }
  192. private final Collection<DataGenerator<T>> generators = new LinkedHashSet<>();
  193. private final ActiveDataHandler handler = new ActiveDataHandler();
  194. /** Empty default data provider. */
  195. private DataProvider<T, ?> dataProvider = new CallbackDataProvider<>(
  196. q -> Stream.empty(), q -> 0);
  197. private final DataKeyMapper<T> keyMapper;
  198. /** Boolean for pending hard reset. */
  199. protected boolean reset = true;
  200. private final Set<T> updatedData = new HashSet<>();
  201. private int minPushSize = 40;
  202. private Range pushRows = Range.withLength(0, minPushSize);
  203. private Object filter;
  204. private Comparator<T> inMemorySorting;
  205. private final List<QuerySortOrder> backEndSorting = new ArrayList<>();
  206. private final DataCommunicatorClientRpc rpc;
  207. public DataCommunicator() {
  208. addDataGenerator(handler);
  209. rpc = getRpcProxy(DataCommunicatorClientRpc.class);
  210. registerRpc(createRpc());
  211. keyMapper = createKeyMapper(dataProvider::getId);
  212. }
  213. @Override
  214. public void attach() {
  215. super.attach();
  216. attachDataProviderListener();
  217. }
  218. @Override
  219. public void detach() {
  220. super.detach();
  221. detachDataProviderListener();
  222. }
  223. /**
  224. * Set the range of rows to push for next response.
  225. *
  226. * @param pushRows
  227. * @since 8.0.6
  228. */
  229. protected void setPushRows(Range pushRows) {
  230. this.pushRows = pushRows;
  231. }
  232. /**
  233. * Get the current range of rows to push in the next response.
  234. *
  235. * @return the range of rows to push
  236. * @since 8.0.6
  237. */
  238. protected Range getPushRows() {
  239. return pushRows;
  240. }
  241. /**
  242. * Get the object used for filtering in this data communicator.
  243. *
  244. * @return the filter object of this data communicator
  245. * @since 8.0.6
  246. */
  247. protected Object getFilter() {
  248. return filter;
  249. }
  250. /**
  251. * Get the client rpc interface for this data communicator.
  252. *
  253. * @return the client rpc interface for this data communicator
  254. * @since 8.0.6
  255. */
  256. protected DataCommunicatorClientRpc getClientRpc() {
  257. return rpc;
  258. }
  259. /**
  260. * Request the given rows to be available on the client side.
  261. *
  262. * @param firstRowIndex
  263. * the index of the first requested row
  264. * @param numberOfRows
  265. * the number of requested rows
  266. * @param firstCachedRowIndex
  267. * the index of the first cached row
  268. * @param cacheSize
  269. * the number of cached rows
  270. * @since 8.0.6
  271. */
  272. protected void onRequestRows(int firstRowIndex, int numberOfRows,
  273. int firstCachedRowIndex, int cacheSize) {
  274. setPushRows(Range.withLength(firstRowIndex, numberOfRows));
  275. markAsDirty();
  276. }
  277. /**
  278. * Triggered when rows have been dropped from the client side cache.
  279. *
  280. * @param keys
  281. * the keys of the rows that have been dropped
  282. * @since 8.0.6
  283. */
  284. protected void onDropRows(JsonArray keys) {
  285. for (int i = 0; i < keys.length(); ++i) {
  286. handler.dropActiveData(keys.getString(i));
  287. }
  288. }
  289. /**
  290. * Initially and in the case of a reset all data should be pushed to the
  291. * client.
  292. */
  293. @Override
  294. public void beforeClientResponse(boolean initial) {
  295. super.beforeClientResponse(initial);
  296. if (initial && getPushRows().isEmpty()) {
  297. // Make sure rows are pushed when component is attached.
  298. setPushRows(Range.withLength(0, getMinPushSize()));
  299. }
  300. sendDataToClient(initial);
  301. }
  302. /**
  303. * Send the needed data and updates to the client side.
  304. *
  305. * @param initial
  306. * {@code true} if initial data load, {@code false} if not
  307. * @since 8.0.6
  308. */
  309. protected void sendDataToClient(boolean initial) {
  310. if (getDataProvider() == null) {
  311. return;
  312. }
  313. if (initial || reset) {
  314. if (reset) {
  315. handler.dropAllActiveData();
  316. }
  317. rpc.reset(getDataProviderSize());
  318. }
  319. if (!updatedData.isEmpty()) {
  320. JsonArray dataArray = Json.createArray();
  321. int i = 0;
  322. for (T data : updatedData) {
  323. dataArray.set(i++, getDataObject(data));
  324. }
  325. rpc.updateData(dataArray);
  326. }
  327. Range requestedRows = getPushRows();
  328. boolean triggerReset = false;
  329. if (!requestedRows.isEmpty()) {
  330. int offset = requestedRows.getStart();
  331. int limit = requestedRows.length();
  332. List<T> rowsToPush = fetchItemsWithRange(offset, limit);
  333. if (!initial && !reset && rowsToPush.isEmpty()) {
  334. triggerReset = true;
  335. }
  336. pushData(offset, rowsToPush);
  337. }
  338. setPushRows(Range.withLength(0, 0));
  339. reset = triggerReset;
  340. updatedData.clear();
  341. }
  342. /**
  343. * Fetches a list of items from the DataProvider.
  344. *
  345. * @param offset
  346. * the starting index of the range
  347. * @param limit
  348. * the max number of results
  349. * @return the list of items in given range
  350. *
  351. * @since 8.1
  352. */
  353. @SuppressWarnings({ "rawtypes", "unchecked" })
  354. public List<T> fetchItemsWithRange(int offset, int limit) {
  355. return (List<T>) getDataProvider().fetch(new Query(offset, limit,
  356. backEndSorting, inMemorySorting, filter))
  357. .collect(Collectors.toList());
  358. }
  359. /**
  360. * Adds a data generator to this data communicator. Data generators can be
  361. * used to insert custom data to the rows sent to the client. If the data
  362. * generator is already added, does nothing.
  363. *
  364. * @param generator
  365. * the data generator to add, not null
  366. */
  367. public void addDataGenerator(DataGenerator<T> generator) {
  368. Objects.requireNonNull(generator, "generator cannot be null");
  369. generators.add(generator);
  370. // Make sure data gets generated when adding data generators.
  371. reset();
  372. }
  373. /**
  374. * Removes a data generator from this data communicator. If there is no such
  375. * data generator, does nothing.
  376. *
  377. * @param generator
  378. * the data generator to remove, not null
  379. */
  380. public void removeDataGenerator(DataGenerator<T> generator) {
  381. Objects.requireNonNull(generator, "generator cannot be null");
  382. generators.remove(generator);
  383. }
  384. /**
  385. * Gets the {@link DataKeyMapper} used by this {@link DataCommunicator}. Key
  386. * mapper can be used to map keys sent to the client-side back to their
  387. * respective data objects.
  388. *
  389. * @return key mapper
  390. */
  391. public DataKeyMapper<T> getKeyMapper() {
  392. return keyMapper;
  393. }
  394. /**
  395. * Sends given collection of data objects to the client-side.
  396. *
  397. * @param firstIndex
  398. * first index of pushed data
  399. * @param data
  400. * data objects to send as an iterable
  401. */
  402. protected void pushData(int firstIndex, List<T> data) {
  403. JsonArray dataArray = Json.createArray();
  404. int i = 0;
  405. for (T item : data) {
  406. dataArray.set(i++, getDataObject(item));
  407. }
  408. rpc.setData(firstIndex, dataArray);
  409. handler.addActiveData(data.stream());
  410. handler.cleanUp(data.stream());
  411. }
  412. /**
  413. * Creates the JsonObject for given data object. This method calls all data
  414. * generators for it.
  415. *
  416. * @param data
  417. * data object to be made into a json object
  418. * @return json object representing the data object
  419. */
  420. protected JsonObject getDataObject(T data) {
  421. JsonObject dataObject = Json.createObject();
  422. for (DataGenerator<T> generator : generators) {
  423. generator.generateData(data, dataObject);
  424. }
  425. return dataObject;
  426. }
  427. /**
  428. * Returns the active data handler.
  429. *
  430. * @return the active data handler
  431. * @since 8.0.6
  432. */
  433. protected ActiveDataHandler getActiveDataHandler() {
  434. return handler;
  435. }
  436. /**
  437. * Drops data objects identified by given keys from memory. This will invoke
  438. * {@link DataGenerator#destroyData} for each of those objects.
  439. *
  440. * @param droppedKeys
  441. * collection of dropped keys
  442. */
  443. private void dropData(Collection<String> droppedKeys) {
  444. for (String key : droppedKeys) {
  445. assert key != null : "Bookkeepping failure. Dropping a null key";
  446. T data = getKeyMapper().get(key);
  447. assert data != null : "Bookkeepping failure. No data object to match key";
  448. for (DataGenerator<T> g : generators) {
  449. g.destroyData(data);
  450. }
  451. }
  452. }
  453. /**
  454. * Drops all data associated with this data communicator.
  455. */
  456. protected void dropAllData() {
  457. for (DataGenerator<T> g : generators) {
  458. g.destroyAllData();
  459. }
  460. handler.destroyAllData();
  461. }
  462. /**
  463. * Method for internal reset from a change in the component, requiring a
  464. * full data update.
  465. */
  466. public void reset() {
  467. // Only needed if a full reset is not pending.
  468. if (!reset) {
  469. // Soft reset through client-side re-request.
  470. getClientRpc().reset(getDataProviderSize());
  471. }
  472. }
  473. /**
  474. * Informs the DataProvider that a data object has been updated.
  475. *
  476. * @param data
  477. * updated data object; not {@code null}
  478. */
  479. public void refresh(T data) {
  480. Objects.requireNonNull(data,
  481. "DataCommunicator can not refresh null object");
  482. Object id = getDataProvider().getId(data);
  483. // ActiveDataHandler has always the latest data through KeyMapper.
  484. Map<Object, T> activeData = getActiveDataHandler().getActiveData();
  485. if (activeData.containsKey(id)) {
  486. // Item is currently available at the client-side
  487. if (updatedData.isEmpty()) {
  488. markAsDirty();
  489. }
  490. updatedData.add(activeData.get(id));
  491. }
  492. }
  493. /**
  494. * Returns the currently set updated data.
  495. *
  496. * @return the set of data that should be updated on the next response
  497. * @since 8.0.6
  498. */
  499. protected Set<T> getUpdatedData() {
  500. return updatedData;
  501. }
  502. /**
  503. * Sets the {@link Comparator} to use with in-memory sorting.
  504. *
  505. * @param comparator
  506. * comparator used to sort data
  507. */
  508. public void setInMemorySorting(Comparator<T> comparator) {
  509. inMemorySorting = comparator;
  510. reset();
  511. }
  512. /**
  513. * Returns the {@link Comparator} to use with in-memory sorting.
  514. *
  515. * @return comparator used to sort data
  516. * @since 8.0.6
  517. */
  518. public Comparator<T> getInMemorySorting() {
  519. return inMemorySorting;
  520. }
  521. /**
  522. * Sets the {@link QuerySortOrder}s to use with backend sorting.
  523. *
  524. * @param sortOrder
  525. * list of sort order information to pass to a query
  526. */
  527. public void setBackEndSorting(List<QuerySortOrder> sortOrder) {
  528. backEndSorting.clear();
  529. backEndSorting.addAll(sortOrder);
  530. reset();
  531. }
  532. /**
  533. * Returns the {@link QuerySortOrder} to use with backend sorting.
  534. *
  535. * @return an unmodifiable list of sort order information to pass to a query
  536. * @since 8.0.6
  537. */
  538. public List<QuerySortOrder> getBackEndSorting() {
  539. return Collections.unmodifiableList(backEndSorting);
  540. }
  541. /**
  542. * Creates a {@link DataKeyMapper} to use with this DataCommunicator.
  543. * <p>
  544. * This method is called from the constructor.
  545. *
  546. * @param identifierGetter
  547. * has to return a unique key for every bean, and the returned
  548. * key has to follow general {@code hashCode()} and
  549. * {@code equals()} contract, see {@link Object#hashCode()} for
  550. * details.
  551. * @return key mapper
  552. *
  553. * @since 8.1
  554. *
  555. */
  556. protected DataKeyMapper<T> createKeyMapper(
  557. ValueProvider<T, Object> identifierGetter) {
  558. return new KeyMapper<T>(identifierGetter);
  559. }
  560. /**
  561. * Creates a {@link DataRequestRpc} used with this {@link DataCommunicator}.
  562. * <p>
  563. * This method is called from the constructor.
  564. *
  565. * @return data request rpc implementation
  566. */
  567. protected DataRequestRpc createRpc() {
  568. return new SimpleDataRequestRpc();
  569. }
  570. /**
  571. * Gets the current data provider from this DataCommunicator.
  572. *
  573. * @return the data provider
  574. */
  575. public DataProvider<T, ?> getDataProvider() {
  576. return dataProvider;
  577. }
  578. /**
  579. * Sets the current data provider for this DataCommunicator.
  580. * <p>
  581. * The returned consumer can be used to set some other filter value that
  582. * should be included in queries sent to the data provider. It is only valid
  583. * until another data provider is set.
  584. *
  585. * @param dataProvider
  586. * the data provider to set, not <code>null</code>
  587. * @param initialFilter
  588. * the initial filter value to use, or <code>null</code> to not
  589. * use any initial filter value
  590. *
  591. * @param <F>
  592. * the filter type
  593. *
  594. * @return a consumer that accepts a new filter value to use
  595. */
  596. public <F> SerializableConsumer<F> setDataProvider(
  597. DataProvider<T, F> dataProvider, F initialFilter) {
  598. Objects.requireNonNull(dataProvider, "data provider cannot be null");
  599. filter = initialFilter;
  600. setDataProvider(dataProvider);
  601. /*
  602. * This introduces behavior which influence on the client-server
  603. * communication: now the very first response to the client will always
  604. * contain some data. If data provider has been set already then {@code
  605. * pushRows} is empty at this point. So without the next line the very
  606. * first response will be without data. And the client will request more
  607. * data in the next request after the response. The next line allows to
  608. * send some data (in the {@code pushRows} range) to the client even in
  609. * the very first response. This is necessary for disabled component
  610. * (and theoretically allows to the client doesn't request more data in
  611. * a happy path).
  612. */
  613. setPushRows(Range.between(0, getMinPushSize()));
  614. if (isAttached()) {
  615. attachDataProviderListener();
  616. }
  617. reset = true;
  618. markAsDirty();
  619. return filter -> {
  620. if (this.dataProvider != dataProvider) {
  621. throw new IllegalStateException(
  622. "Filter slot is no longer valid after data provider has been changed");
  623. }
  624. if (!Objects.equals(this.filter, filter)) {
  625. setFilter(filter);
  626. reset();
  627. // Make sure filter change causes data to be sent again.
  628. markAsDirty();
  629. }
  630. };
  631. }
  632. /**
  633. * Sets the filter for this DataCommunicator. This method is used by user
  634. * through the consumer method from {@link #setDataProvider} and should not
  635. * be called elsewhere.
  636. *
  637. * @param filter
  638. * the filter
  639. *
  640. * @param <F>
  641. * the filter type
  642. *
  643. * @since 8.1
  644. */
  645. protected <F> void setFilter(F filter) {
  646. this.filter = filter;
  647. }
  648. /**
  649. * Set minimum size of data which will be sent to the client when data
  650. * source is set.
  651. * <p>
  652. * Server doesn't send all data from data source to the client. It sends
  653. * some initial chunk of data (whose size is determined as minimum between
  654. * {@code size} parameter of this method and data size). Client decides
  655. * whether it is able to show more data and request server to send more data
  656. * (next chunk).
  657. * <p>
  658. * When component is disabled then client cannot communicate to the server
  659. * side (by design, because of security reasons). It means that client will
  660. * get <b>only</b> initial chunk of data whose size is set here.
  661. *
  662. * @param size
  663. * the size of initial data to send to the client
  664. */
  665. public void setMinPushSize(int size) {
  666. if (size < 0) {
  667. throw new IllegalArgumentException("Value cannot be negative");
  668. }
  669. minPushSize = size;
  670. }
  671. /**
  672. * Get minimum size of data which will be sent to the client when data
  673. * source is set.
  674. *
  675. * @see #setMinPushSize(int)
  676. *
  677. * @return current minimum push size of initial data chunk which is sent to
  678. * the client when data source is set
  679. */
  680. public int getMinPushSize() {
  681. return minPushSize;
  682. }
  683. /**
  684. * Getter method for finding the size of DataProvider. Can be overridden by
  685. * a subclass that uses a specific type of DataProvider and/or query.
  686. *
  687. * @return the size of data provider with current filter
  688. */
  689. @SuppressWarnings({ "unchecked", "rawtypes" })
  690. public int getDataProviderSize() {
  691. return getDataProvider().size(new Query(getFilter()));
  692. }
  693. @Override
  694. protected DataCommunicatorState getState(boolean markAsDirty) {
  695. return (DataCommunicatorState) super.getState(markAsDirty);
  696. }
  697. @Override
  698. protected DataCommunicatorState getState() {
  699. return (DataCommunicatorState) super.getState();
  700. }
  701. private void attachDataProviderListener() {
  702. dataProviderUpdateRegistration = getDataProvider()
  703. .addDataProviderListener(event -> {
  704. if (event instanceof DataRefreshEvent) {
  705. T item = ((DataRefreshEvent<T>) event).getItem();
  706. getKeyMapper().refresh(item);
  707. generators.forEach(g -> g.refreshData(item));
  708. getUI().access(() -> refresh(item));
  709. } else {
  710. reset = true;
  711. getUI().access(() -> markAsDirty());
  712. }
  713. });
  714. }
  715. private void detachDataProviderListener() {
  716. if (dataProviderUpdateRegistration != null) {
  717. dataProviderUpdateRegistration.remove();
  718. dataProviderUpdateRegistration = null;
  719. }
  720. }
  721. /**
  722. * Sets a new {@code DataProvider} and refreshes all the internal
  723. * structures.
  724. *
  725. * @param dataProvider
  726. * @since 8.1
  727. */
  728. protected void setDataProvider(DataProvider<T, ?> dataProvider) {
  729. detachDataProviderListener();
  730. dropAllData();
  731. this.dataProvider = dataProvider;
  732. getKeyMapper().setIdentifierGetter(dataProvider::getId);
  733. }
  734. }