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.

DataCommunicator.java 25KB

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