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.

AbstractRemoteDataSource.java 27KB

Migrating 7.7.1, 7.7.2, 7.7.3 to V8. commit 11c3f8bd9ea65f7a7b8da9a282c31a127bd475a6 - Test and its UI class are added (both V8 and V7). Required functionality should be available via modern GWT version. commit 729dbf96fe76e7627168ab2c9d1d71c4eb7214c8 - About update release notes. No need to be included. commit 675f38349c43ac45dae40cf33a7b1fd0f8f261ca - V8 already contains correct Import-Packages section which uses osgi.javax.servlet.version variable whise version is 3.0.0 at the moment. commit 5da7c052f55cb4703b74f38f5bb19fc3f3fa2a76 - Use Vaadin plugin 7.7.0 from 7.7.0.alpha1. Is not applicable. commit 1df80001ab6c916effa917781dba652d09d01056 - Updated tutorial to Vaadin 7.7.0. Is not applicable. The tutorial already contains correct links and updated source code snippets. commit 8b4f0ed8a894b04902a5d4258119dcdc8e76d1e0 - set-property-fallback name="user.agent" value="safari". Is already there. commit 28ed04e827669cc4dd329331dac9699bd93f70bc - Fix animation end listeners so they are always removed. Is already there. commit 408253bc3f8bd3975f0525ce6832be214a3552e9 - Use servlet context classloader when finding servlet class for websockets. Is already there. commit 7a6f250d89474849648ed2ee96a6bfb78c3b9ca8 - Fire actions before removing menu from the DOM. Is already there. commit 9b66c6eb9bebf657d3f2def8c767e0e9d51cc92c - Do not run test on IE8 as IE8 is broken. Transplanted. commit 3faa43ff39ecda56587b93f0c5e262a2907871a7 - Discard for DateField when the data source contains null. It is not applicable for V8 (There is no anymore discard method in DateField (and no datasource suport in field)). Transplanted for DateField in compatibility. commit e0c1f91a3d6d1884e07ce8d1ba957aff6a9bf29a - Fix ComboBox paging when number of items equals page length. It's already done by another fix which replaced ComboBox in compatibility package to the V7 version. commit 83a1b8a0961cc9b2d43e01757530cefd035b0a22 - Update DOM and update escalator row count in the correct order. Transplanted. commit 45f2fba8ff7a4b62680618a325d4afcebfb7a1e9 - Prevent editor from being canceled while it is being saved. Transplanted to compatibility package. Is not applicable to modern Grid. commit ad67f7f43afb0feec5e029aea90297f2abe4f2c1 - Delete broken stylesheet and revert to default style until a new stylesheet is created. Is already there. commit c970a78d42a2d8f1745df7a11a74f3731f8be9a5 - Always show loading indicator for JavaScript RPC. Transplanted. commit 2aad3416061586f7e2649160bd832eefe03702ad - Make test independent of any converters present in the factory. It's already there. commit c9ad48430be135d18fe9f30868e091dd51c57b94 - Do not include yuicompressor for Sass compiler. Transplanted. Exclusion is added into vaadin/pom.xml commit 52d01a68e91ce73306b3a1747af97e928048ecdf - Test for Firefox download disconnecting push channel. Transplanted. commit 4bc375d1d21f468e6433da3a183150e0bfe0cae4 - Handle encoded URL characters correctly when constructing widget set name. Transplanted. commit 17ba88eaf87e15e6f3c729e5c7f8e875d5f86d8d - Update version to 7.7-SNAPSHOT. Is not applicable. commit 47b7b13e5c959de3bd925693b074d85e7625a87e - Ensure Firefox always updates the grid scrollbar. Transplanted. Made changes in the logic to the test for modern Grid component. commit 4d851ba21d1b8f35685b631d2845731f8fb33252 - Calculate column widths immediately if there is data. Transplanted to both client side modules. commit 8f0b1a1dd026a756912c9f21bd2b34ea46897c7f - Skip Maven enforcer plugin during demo validation. Transplanted (one build file is affected). commit 62815353e1b9d3cd126809f5c818ad35bf913807 - Build demos from 7.7 branch (now for master branch). FW8 demos are added (one build file is affected). commit 815d72115d5aaf3676daefd5642115577e4151ef - Make test pass on all browsers. Transplanted to both V7 and V8 version tests. commit 516c428ca127e3c31b7b4d74220e4b7eed4571be - Use widget set specified by init parameter. Transplanted to the one UIProvider class. commit b00c580ed70f682a42afbfa91f978921bb86c2cd - Use correct column index when calculating min width during resize. Transplanted into both client side classes (main and compatibility) as is. Test for V7 is transplanted as is. Test for V8 is written from scratch based on V7 version. commit 7dd91cf057eb06a09009096a8278f34aad9bd8d9 - Fix regression that broke widget set compilation in 7.7.1. It's already there. commit c665731b0b97b697e80c47955d3558c19f0c81cb - Ensure temporary layout manager state is cleared at the end of a layout phase. Transplanted to the one LayoutManager class. commit 57a965251afdb5ee9ac1913a0101d854d8215aa6 - Fix assertion error when column widths are calculated. Transplanted to both versions of the client Grid widget. commit c5c52684eb30d924cb75a632b526a0f879d5a33c - Format Java files using Eclipse Neon and Vaadin settings. Only formatting changes. Is not transplanted. f5d06d877165bf413ec71c4fc88cf46c8c57a372 - Change javadoc to a style Eclipse formatter can handle. Transplanted to both versions of the client Grid widget. commit 6033e13c20b3d6e8b6f5add0f786d5ab2e1bb3fe - Make initially disabled grid work when enabled. Transplanted to both client side modules. commit a2d6e4fb4b1fd13e9a1b88f2ab1b78d14d8b64a9 - Use requestAnimationFrame when scrolling in Grid. Transplanted to both client side modules. commit fe9438e7b77c606855cfd739dd7e30b3f8cd4165 - Specify branch also for Sampler. Is not applicable for master branch. commit 1ec5d8ef7cb8bbd82bae1c9b79a376a5dca28f48 - Update to Chrome 53. Is already there. commit 961851bfbc4844474299433c34af6c9e4323d891 - Updated link to new step 1 video in tutorial. Is already there. commit 41dc2fe1611adc70d00e6f77debb2a6d4dcdefb0 - Revert "Use widget set specified by init parameter. Transplanted to the one UIProvider class. commit 092b4f7f3192555fe3ae22ac03a89ac2ada2a2dd - Use widget set specified by init parameter. Transplanted to the common server side classes. commit 977cec7e3107c2da306d46449dbf32f6544313be - Fix widget set builder to create widget set in correct location. Transplanted to the one ClassPathExplorer class file. commit 6c12ad89ea1064cd4cc0456baca5ee00ae76d032 - Format project pom files using correct settings. Is not transplanted: only formatting changes for POM files. commit 0aad93ecc1ce743dffc093ce7ae2ef88831f6073 - Add tests for widgetset compilation in different modes. Transplanted. New test projects. commit 0a3a1ef8321ed421be2337034fdb1cae2c434c3d - Use versions-maven-plugin 2.3 to avoid NPE while setting project version. Is already there. Change-Id: Ie3a5088f25de1772f01ea30c4a5eba0b169ee0ab
7 years ago
Migrating 7.7.1, 7.7.2, 7.7.3 to V8. commit 11c3f8bd9ea65f7a7b8da9a282c31a127bd475a6 - Test and its UI class are added (both V8 and V7). Required functionality should be available via modern GWT version. commit 729dbf96fe76e7627168ab2c9d1d71c4eb7214c8 - About update release notes. No need to be included. commit 675f38349c43ac45dae40cf33a7b1fd0f8f261ca - V8 already contains correct Import-Packages section which uses osgi.javax.servlet.version variable whise version is 3.0.0 at the moment. commit 5da7c052f55cb4703b74f38f5bb19fc3f3fa2a76 - Use Vaadin plugin 7.7.0 from 7.7.0.alpha1. Is not applicable. commit 1df80001ab6c916effa917781dba652d09d01056 - Updated tutorial to Vaadin 7.7.0. Is not applicable. The tutorial already contains correct links and updated source code snippets. commit 8b4f0ed8a894b04902a5d4258119dcdc8e76d1e0 - set-property-fallback name="user.agent" value="safari". Is already there. commit 28ed04e827669cc4dd329331dac9699bd93f70bc - Fix animation end listeners so they are always removed. Is already there. commit 408253bc3f8bd3975f0525ce6832be214a3552e9 - Use servlet context classloader when finding servlet class for websockets. Is already there. commit 7a6f250d89474849648ed2ee96a6bfb78c3b9ca8 - Fire actions before removing menu from the DOM. Is already there. commit 9b66c6eb9bebf657d3f2def8c767e0e9d51cc92c - Do not run test on IE8 as IE8 is broken. Transplanted. commit 3faa43ff39ecda56587b93f0c5e262a2907871a7 - Discard for DateField when the data source contains null. It is not applicable for V8 (There is no anymore discard method in DateField (and no datasource suport in field)). Transplanted for DateField in compatibility. commit e0c1f91a3d6d1884e07ce8d1ba957aff6a9bf29a - Fix ComboBox paging when number of items equals page length. It's already done by another fix which replaced ComboBox in compatibility package to the V7 version. commit 83a1b8a0961cc9b2d43e01757530cefd035b0a22 - Update DOM and update escalator row count in the correct order. Transplanted. commit 45f2fba8ff7a4b62680618a325d4afcebfb7a1e9 - Prevent editor from being canceled while it is being saved. Transplanted to compatibility package. Is not applicable to modern Grid. commit ad67f7f43afb0feec5e029aea90297f2abe4f2c1 - Delete broken stylesheet and revert to default style until a new stylesheet is created. Is already there. commit c970a78d42a2d8f1745df7a11a74f3731f8be9a5 - Always show loading indicator for JavaScript RPC. Transplanted. commit 2aad3416061586f7e2649160bd832eefe03702ad - Make test independent of any converters present in the factory. It's already there. commit c9ad48430be135d18fe9f30868e091dd51c57b94 - Do not include yuicompressor for Sass compiler. Transplanted. Exclusion is added into vaadin/pom.xml commit 52d01a68e91ce73306b3a1747af97e928048ecdf - Test for Firefox download disconnecting push channel. Transplanted. commit 4bc375d1d21f468e6433da3a183150e0bfe0cae4 - Handle encoded URL characters correctly when constructing widget set name. Transplanted. commit 17ba88eaf87e15e6f3c729e5c7f8e875d5f86d8d - Update version to 7.7-SNAPSHOT. Is not applicable. commit 47b7b13e5c959de3bd925693b074d85e7625a87e - Ensure Firefox always updates the grid scrollbar. Transplanted. Made changes in the logic to the test for modern Grid component. commit 4d851ba21d1b8f35685b631d2845731f8fb33252 - Calculate column widths immediately if there is data. Transplanted to both client side modules. commit 8f0b1a1dd026a756912c9f21bd2b34ea46897c7f - Skip Maven enforcer plugin during demo validation. Transplanted (one build file is affected). commit 62815353e1b9d3cd126809f5c818ad35bf913807 - Build demos from 7.7 branch (now for master branch). FW8 demos are added (one build file is affected). commit 815d72115d5aaf3676daefd5642115577e4151ef - Make test pass on all browsers. Transplanted to both V7 and V8 version tests. commit 516c428ca127e3c31b7b4d74220e4b7eed4571be - Use widget set specified by init parameter. Transplanted to the one UIProvider class. commit b00c580ed70f682a42afbfa91f978921bb86c2cd - Use correct column index when calculating min width during resize. Transplanted into both client side classes (main and compatibility) as is. Test for V7 is transplanted as is. Test for V8 is written from scratch based on V7 version. commit 7dd91cf057eb06a09009096a8278f34aad9bd8d9 - Fix regression that broke widget set compilation in 7.7.1. It's already there. commit c665731b0b97b697e80c47955d3558c19f0c81cb - Ensure temporary layout manager state is cleared at the end of a layout phase. Transplanted to the one LayoutManager class. commit 57a965251afdb5ee9ac1913a0101d854d8215aa6 - Fix assertion error when column widths are calculated. Transplanted to both versions of the client Grid widget. commit c5c52684eb30d924cb75a632b526a0f879d5a33c - Format Java files using Eclipse Neon and Vaadin settings. Only formatting changes. Is not transplanted. f5d06d877165bf413ec71c4fc88cf46c8c57a372 - Change javadoc to a style Eclipse formatter can handle. Transplanted to both versions of the client Grid widget. commit 6033e13c20b3d6e8b6f5add0f786d5ab2e1bb3fe - Make initially disabled grid work when enabled. Transplanted to both client side modules. commit a2d6e4fb4b1fd13e9a1b88f2ab1b78d14d8b64a9 - Use requestAnimationFrame when scrolling in Grid. Transplanted to both client side modules. commit fe9438e7b77c606855cfd739dd7e30b3f8cd4165 - Specify branch also for Sampler. Is not applicable for master branch. commit 1ec5d8ef7cb8bbd82bae1c9b79a376a5dca28f48 - Update to Chrome 53. Is already there. commit 961851bfbc4844474299433c34af6c9e4323d891 - Updated link to new step 1 video in tutorial. Is already there. commit 41dc2fe1611adc70d00e6f77debb2a6d4dcdefb0 - Revert "Use widget set specified by init parameter. Transplanted to the one UIProvider class. commit 092b4f7f3192555fe3ae22ac03a89ac2ada2a2dd - Use widget set specified by init parameter. Transplanted to the common server side classes. commit 977cec7e3107c2da306d46449dbf32f6544313be - Fix widget set builder to create widget set in correct location. Transplanted to the one ClassPathExplorer class file. commit 6c12ad89ea1064cd4cc0456baca5ee00ae76d032 - Format project pom files using correct settings. Is not transplanted: only formatting changes for POM files. commit 0aad93ecc1ce743dffc093ce7ae2ef88831f6073 - Add tests for widgetset compilation in different modes. Transplanted. New test projects. commit 0a3a1ef8321ed421be2337034fdb1cae2c434c3d - Use versions-maven-plugin 2.3 to avoid NPE while setting project version. Is already there. Change-Id: Ie3a5088f25de1772f01ea30c4a5eba0b169ee0ab
7 years ago
Migrating 7.7.1, 7.7.2, 7.7.3 to V8. commit 11c3f8bd9ea65f7a7b8da9a282c31a127bd475a6 - Test and its UI class are added (both V8 and V7). Required functionality should be available via modern GWT version. commit 729dbf96fe76e7627168ab2c9d1d71c4eb7214c8 - About update release notes. No need to be included. commit 675f38349c43ac45dae40cf33a7b1fd0f8f261ca - V8 already contains correct Import-Packages section which uses osgi.javax.servlet.version variable whise version is 3.0.0 at the moment. commit 5da7c052f55cb4703b74f38f5bb19fc3f3fa2a76 - Use Vaadin plugin 7.7.0 from 7.7.0.alpha1. Is not applicable. commit 1df80001ab6c916effa917781dba652d09d01056 - Updated tutorial to Vaadin 7.7.0. Is not applicable. The tutorial already contains correct links and updated source code snippets. commit 8b4f0ed8a894b04902a5d4258119dcdc8e76d1e0 - set-property-fallback name="user.agent" value="safari". Is already there. commit 28ed04e827669cc4dd329331dac9699bd93f70bc - Fix animation end listeners so they are always removed. Is already there. commit 408253bc3f8bd3975f0525ce6832be214a3552e9 - Use servlet context classloader when finding servlet class for websockets. Is already there. commit 7a6f250d89474849648ed2ee96a6bfb78c3b9ca8 - Fire actions before removing menu from the DOM. Is already there. commit 9b66c6eb9bebf657d3f2def8c767e0e9d51cc92c - Do not run test on IE8 as IE8 is broken. Transplanted. commit 3faa43ff39ecda56587b93f0c5e262a2907871a7 - Discard for DateField when the data source contains null. It is not applicable for V8 (There is no anymore discard method in DateField (and no datasource suport in field)). Transplanted for DateField in compatibility. commit e0c1f91a3d6d1884e07ce8d1ba957aff6a9bf29a - Fix ComboBox paging when number of items equals page length. It's already done by another fix which replaced ComboBox in compatibility package to the V7 version. commit 83a1b8a0961cc9b2d43e01757530cefd035b0a22 - Update DOM and update escalator row count in the correct order. Transplanted. commit 45f2fba8ff7a4b62680618a325d4afcebfb7a1e9 - Prevent editor from being canceled while it is being saved. Transplanted to compatibility package. Is not applicable to modern Grid. commit ad67f7f43afb0feec5e029aea90297f2abe4f2c1 - Delete broken stylesheet and revert to default style until a new stylesheet is created. Is already there. commit c970a78d42a2d8f1745df7a11a74f3731f8be9a5 - Always show loading indicator for JavaScript RPC. Transplanted. commit 2aad3416061586f7e2649160bd832eefe03702ad - Make test independent of any converters present in the factory. It's already there. commit c9ad48430be135d18fe9f30868e091dd51c57b94 - Do not include yuicompressor for Sass compiler. Transplanted. Exclusion is added into vaadin/pom.xml commit 52d01a68e91ce73306b3a1747af97e928048ecdf - Test for Firefox download disconnecting push channel. Transplanted. commit 4bc375d1d21f468e6433da3a183150e0bfe0cae4 - Handle encoded URL characters correctly when constructing widget set name. Transplanted. commit 17ba88eaf87e15e6f3c729e5c7f8e875d5f86d8d - Update version to 7.7-SNAPSHOT. Is not applicable. commit 47b7b13e5c959de3bd925693b074d85e7625a87e - Ensure Firefox always updates the grid scrollbar. Transplanted. Made changes in the logic to the test for modern Grid component. commit 4d851ba21d1b8f35685b631d2845731f8fb33252 - Calculate column widths immediately if there is data. Transplanted to both client side modules. commit 8f0b1a1dd026a756912c9f21bd2b34ea46897c7f - Skip Maven enforcer plugin during demo validation. Transplanted (one build file is affected). commit 62815353e1b9d3cd126809f5c818ad35bf913807 - Build demos from 7.7 branch (now for master branch). FW8 demos are added (one build file is affected). commit 815d72115d5aaf3676daefd5642115577e4151ef - Make test pass on all browsers. Transplanted to both V7 and V8 version tests. commit 516c428ca127e3c31b7b4d74220e4b7eed4571be - Use widget set specified by init parameter. Transplanted to the one UIProvider class. commit b00c580ed70f682a42afbfa91f978921bb86c2cd - Use correct column index when calculating min width during resize. Transplanted into both client side classes (main and compatibility) as is. Test for V7 is transplanted as is. Test for V8 is written from scratch based on V7 version. commit 7dd91cf057eb06a09009096a8278f34aad9bd8d9 - Fix regression that broke widget set compilation in 7.7.1. It's already there. commit c665731b0b97b697e80c47955d3558c19f0c81cb - Ensure temporary layout manager state is cleared at the end of a layout phase. Transplanted to the one LayoutManager class. commit 57a965251afdb5ee9ac1913a0101d854d8215aa6 - Fix assertion error when column widths are calculated. Transplanted to both versions of the client Grid widget. commit c5c52684eb30d924cb75a632b526a0f879d5a33c - Format Java files using Eclipse Neon and Vaadin settings. Only formatting changes. Is not transplanted. f5d06d877165bf413ec71c4fc88cf46c8c57a372 - Change javadoc to a style Eclipse formatter can handle. Transplanted to both versions of the client Grid widget. commit 6033e13c20b3d6e8b6f5add0f786d5ab2e1bb3fe - Make initially disabled grid work when enabled. Transplanted to both client side modules. commit a2d6e4fb4b1fd13e9a1b88f2ab1b78d14d8b64a9 - Use requestAnimationFrame when scrolling in Grid. Transplanted to both client side modules. commit fe9438e7b77c606855cfd739dd7e30b3f8cd4165 - Specify branch also for Sampler. Is not applicable for master branch. commit 1ec5d8ef7cb8bbd82bae1c9b79a376a5dca28f48 - Update to Chrome 53. Is already there. commit 961851bfbc4844474299433c34af6c9e4323d891 - Updated link to new step 1 video in tutorial. Is already there. commit 41dc2fe1611adc70d00e6f77debb2a6d4dcdefb0 - Revert "Use widget set specified by init parameter. Transplanted to the one UIProvider class. commit 092b4f7f3192555fe3ae22ac03a89ac2ada2a2dd - Use widget set specified by init parameter. Transplanted to the common server side classes. commit 977cec7e3107c2da306d46449dbf32f6544313be - Fix widget set builder to create widget set in correct location. Transplanted to the one ClassPathExplorer class file. commit 6c12ad89ea1064cd4cc0456baca5ee00ae76d032 - Format project pom files using correct settings. Is not transplanted: only formatting changes for POM files. commit 0aad93ecc1ce743dffc093ce7ae2ef88831f6073 - Add tests for widgetset compilation in different modes. Transplanted. New test projects. commit 0a3a1ef8321ed421be2337034fdb1cae2c434c3d - Use versions-maven-plugin 2.3 to avoid NPE while setting project version. Is already there. Change-Id: Ie3a5088f25de1772f01ea30c4a5eba0b169ee0ab
7 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  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.client.data;
  17. import java.util.HashMap;
  18. import java.util.LinkedHashSet;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.Objects;
  22. import java.util.Set;
  23. import java.util.stream.Stream;
  24. import com.google.gwt.core.client.Duration;
  25. import com.google.gwt.core.client.Scheduler;
  26. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  27. import com.vaadin.client.Profiler;
  28. import com.vaadin.shared.Range;
  29. import com.vaadin.shared.Registration;
  30. /**
  31. * Base implementation for data sources that fetch data from a remote system.
  32. * This class takes care of caching data and communicating with the data source
  33. * user. An implementation of this class should override
  34. * {@link #requestRows(int, int, RequestRowsCallback)} to trigger asynchronously
  35. * loading of data and then pass the loaded data into the provided callback.
  36. *
  37. * @since 7.4
  38. * @author Vaadin Ltd
  39. * @param <T>
  40. * the row type
  41. */
  42. public abstract class AbstractRemoteDataSource<T> implements DataSource<T> {
  43. /**
  44. * Callback used by
  45. * {@link AbstractRemoteDataSource#requestRows(int, int, RequestRowsCallback)}
  46. * to pass data to the underlying implementation when data has been fetched.
  47. */
  48. public static class RequestRowsCallback<T> {
  49. private final Range requestedRange;
  50. private final double requestStart;
  51. private final AbstractRemoteDataSource<T> source;
  52. /**
  53. * Creates a new callback
  54. *
  55. * @param source
  56. * the data source for which the request is made
  57. * @param requestedRange
  58. * the requested row range
  59. */
  60. protected RequestRowsCallback(AbstractRemoteDataSource<T> source,
  61. Range requestedRange) {
  62. this.source = source;
  63. this.requestedRange = requestedRange;
  64. requestStart = Duration.currentTimeMillis();
  65. }
  66. /**
  67. * Called by the
  68. * {@link AbstractRemoteDataSource#requestRows(int, int, RequestRowsCallback)}
  69. * implementation when data has been received.
  70. *
  71. * @param rowData
  72. * a list of row objects starting at the requested offset
  73. * @param totalSize
  74. * the total number of rows available at the remote end
  75. */
  76. public void onResponse(List<T> rowData, int totalSize) {
  77. if (source.size != totalSize) {
  78. source.resetDataAndSize(totalSize);
  79. }
  80. source.setRowData(requestedRange.getStart(), rowData);
  81. }
  82. /**
  83. * Gets the range of rows that was requested.
  84. *
  85. * @return the requsted row range
  86. */
  87. public Range getRequestedRange() {
  88. return requestedRange;
  89. }
  90. }
  91. protected class RowHandleImpl extends RowHandle<T> {
  92. private T row;
  93. private final Object key;
  94. public RowHandleImpl(final T row, final Object key) {
  95. this.row = row;
  96. this.key = key;
  97. }
  98. /**
  99. * A method for the data source to update the row data.
  100. *
  101. * @param row
  102. * the updated row object
  103. */
  104. public void setRow(final T row) {
  105. this.row = row;
  106. assert getRowKey(row).equals(key) : "The old key does not "
  107. + "equal the new key for the given row (old: " + key
  108. + ", new :" + getRowKey(row) + ")";
  109. }
  110. @Override
  111. public T getRow() throws IllegalStateException {
  112. return row;
  113. }
  114. public boolean isPinned() {
  115. return pinnedRows.containsKey(key);
  116. }
  117. @Override
  118. public void pin() {
  119. pinHandle(this);
  120. }
  121. @Override
  122. public void unpin() throws IllegalStateException {
  123. unpinHandle(this);
  124. }
  125. @Override
  126. protected boolean equalsExplicit(final Object obj) {
  127. if (obj instanceof AbstractRemoteDataSource.RowHandleImpl) {
  128. /*
  129. * Java prefers AbstractRemoteDataSource<?>.RowHandleImpl. I
  130. * like the @SuppressWarnings more (keeps the line length in
  131. * check.)
  132. */
  133. @SuppressWarnings("unchecked")
  134. final RowHandleImpl rhi = (RowHandleImpl) obj;
  135. return key.equals(rhi.key);
  136. } else {
  137. return false;
  138. }
  139. }
  140. @Override
  141. protected int hashCodeExplicit() {
  142. return key.hashCode();
  143. }
  144. @Override
  145. public void updateRow() {
  146. int index = indexOf(row);
  147. if (index >= 0) {
  148. getHandlers().forEach(dch -> dch.dataUpdated(index, 1));
  149. }
  150. }
  151. }
  152. private RequestRowsCallback<T> currentRequestCallback;
  153. private boolean coverageCheckPending = false;
  154. private Range requestedAvailability = Range.between(0, 0);
  155. private Range cached = Range.between(0, 0);
  156. private final HashMap<Integer, T> indexToRowMap = new HashMap<>();
  157. private final HashMap<Object, Integer> keyToIndexMap = new HashMap<>();
  158. private Set<DataChangeHandler> dataChangeHandlers = new LinkedHashSet<>();
  159. private CacheStrategy cacheStrategy = new CacheStrategy.DefaultCacheStrategy();
  160. private final ScheduledCommand coverageChecker = new ScheduledCommand() {
  161. @Override
  162. public void execute() {
  163. coverageCheckPending = false;
  164. checkCacheCoverage();
  165. }
  166. };
  167. private Map<Object, Integer> pinnedCounts = new HashMap<>();
  168. private Map<Object, RowHandleImpl> pinnedRows = new HashMap<>();
  169. // Size not yet known
  170. private int size = -1;
  171. private void ensureCoverageCheck() {
  172. if (!coverageCheckPending) {
  173. coverageCheckPending = true;
  174. Scheduler.get().scheduleDeferred(coverageChecker);
  175. }
  176. }
  177. /**
  178. * Pins a row with given handle. This function can be overridden to do
  179. * specific logic related to pinning rows.
  180. *
  181. * @param handle
  182. * row handle to pin
  183. */
  184. protected void pinHandle(RowHandleImpl handle) {
  185. Object key = handle.key;
  186. Integer count = pinnedCounts.get(key);
  187. if (count == null) {
  188. count = Integer.valueOf(0);
  189. pinnedRows.put(key, handle);
  190. }
  191. pinnedCounts.put(key, Integer.valueOf(count.intValue() + 1));
  192. }
  193. /**
  194. * Unpins a previously pinned row with given handle. This function can be
  195. * overridden to do specific logic related to unpinning rows.
  196. *
  197. * @param handle
  198. * row handle to unpin
  199. *
  200. * @throws IllegalStateException
  201. * if given row handle has not been pinned before
  202. */
  203. protected void unpinHandle(RowHandleImpl handle)
  204. throws IllegalStateException {
  205. Object key = handle.key;
  206. final Integer count = pinnedCounts.get(key);
  207. if (count == null) {
  208. throw new IllegalStateException("Row " + handle.getRow()
  209. + " with key " + key + " was not pinned to begin with");
  210. } else if (count.equals(Integer.valueOf(1))) {
  211. pinnedRows.remove(key);
  212. pinnedCounts.remove(key);
  213. } else {
  214. pinnedCounts.put(key, Integer.valueOf(count.intValue() - 1));
  215. }
  216. }
  217. @Override
  218. public void ensureAvailability(int firstRowIndex, int numberOfRows) {
  219. requestedAvailability = Range.withLength(firstRowIndex, numberOfRows);
  220. /*
  221. * Don't request any data right away since the data might be included in
  222. * a message that has been received but not yet fully processed.
  223. */
  224. ensureCoverageCheck();
  225. }
  226. /**
  227. * Gets the row index range that was requested by the previous call to
  228. * {@link #ensureAvailability(int, int)}.
  229. *
  230. * @return the requested availability range
  231. */
  232. public Range getRequestedAvailability() {
  233. return requestedAvailability;
  234. }
  235. private void checkCacheCoverage() {
  236. if (isWaitingForData()) {
  237. // Anyone clearing the waiting status should run this method again
  238. return;
  239. }
  240. Profiler.enter("AbstractRemoteDataSource.checkCacheCoverage");
  241. Range minCacheRange = getMinCacheRange();
  242. if (!minCacheRange.intersects(cached) || cached.isEmpty()) {
  243. /*
  244. * Simple case: no overlap between cached data and needed data.
  245. * Clear the cache and request new data
  246. */
  247. dropFromCache(cached);
  248. cached = Range.between(0, 0);
  249. Range maxCacheRange = getMaxCacheRange();
  250. if (!maxCacheRange.isEmpty()) {
  251. handleMissingRows(maxCacheRange);
  252. } else {
  253. // There is nothing to fetch. We're done here.
  254. getHandlers().forEach(dch -> dch
  255. .dataAvailable(cached.getStart(), cached.length()));
  256. }
  257. } else {
  258. discardStaleCacheEntries();
  259. // Might need more rows -> request them
  260. if (!minCacheRange.isSubsetOf(cached)) {
  261. Range[] missingCachePartition = getMaxCacheRange()
  262. .partitionWith(cached);
  263. handleMissingRows(missingCachePartition[0]);
  264. handleMissingRows(missingCachePartition[2]);
  265. } else {
  266. getHandlers().forEach(dch -> dch
  267. .dataAvailable(cached.getStart(), cached.length()));
  268. }
  269. }
  270. Profiler.leave("AbstractRemoteDataSource.checkCacheCoverage");
  271. }
  272. /**
  273. * Checks whether this data source is currently waiting for more rows to
  274. * become available.
  275. *
  276. * @return <code>true</code> if waiting for data; otherwise
  277. * <code>false</code>
  278. */
  279. @Override
  280. public boolean isWaitingForData() {
  281. return currentRequestCallback != null;
  282. }
  283. private void discardStaleCacheEntries() {
  284. Range[] cacheParition = cached.partitionWith(getMaxCacheRange());
  285. dropFromCache(cacheParition[0]);
  286. cached = cacheParition[1];
  287. dropFromCache(cacheParition[2]);
  288. }
  289. private void dropFromCache(Range range) {
  290. for (int i = range.getStart(); i < range.getEnd(); i++) {
  291. // Called after dropping from cache. Dropped row is passed as a
  292. // parameter, but is no longer present in the DataSource
  293. T removed = indexToRowMap.remove(Integer.valueOf(i));
  294. if (removed != null) {
  295. onDropFromCache(i, removed);
  296. keyToIndexMap.remove(getRowKey(removed));
  297. }
  298. }
  299. }
  300. /**
  301. * A hook that can be overridden to do something whenever a row has been
  302. * dropped from the cache. DataSource no longer has anything in the given
  303. * index.
  304. * <p>
  305. * NOTE: This method has been replaced. Override
  306. * {@link #onDropFromCache(int, Object)} instead of this method.
  307. *
  308. * @since 7.5.0
  309. * @param rowIndex
  310. * the index of the dropped row
  311. * @deprecated replaced by {@link #onDropFromCache(int, Object)}
  312. */
  313. @Deprecated
  314. protected void onDropFromCache(int rowIndex) {
  315. // noop
  316. }
  317. /**
  318. * A hook that can be overridden to do something whenever a row has been
  319. * dropped from the cache. DataSource no longer has anything in the given
  320. * index.
  321. *
  322. * @since 7.6
  323. * @param rowIndex
  324. * the index of the dropped row
  325. * @param removed
  326. * the removed row object
  327. */
  328. protected void onDropFromCache(int rowIndex, T removed) {
  329. // Call old version as a fallback (someone might have used it)
  330. onDropFromCache(rowIndex);
  331. }
  332. private void handleMissingRows(Range range) {
  333. if (range.isEmpty() || !canFetchData()) {
  334. return;
  335. }
  336. currentRequestCallback = new RequestRowsCallback<>(this, range);
  337. requestRows(range.getStart(), range.length(), currentRequestCallback);
  338. }
  339. /**
  340. * Triggers fetching rows from the remote data source. The provided callback
  341. * should be informed when the requested rows have been received.
  342. *
  343. * @param firstRowIndex
  344. * the index of the first row to fetch
  345. * @param numberOfRows
  346. * the number of rows to fetch
  347. * @param callback
  348. * callback to inform when the requested rows are available
  349. */
  350. protected abstract void requestRows(int firstRowIndex, int numberOfRows,
  351. RequestRowsCallback<T> callback);
  352. @Override
  353. public T getRow(int rowIndex) {
  354. return indexToRowMap.get(Integer.valueOf(rowIndex));
  355. }
  356. /**
  357. * Retrieves the index for given row object.
  358. * <p>
  359. * <em>Note:</em> This method does not verify that the given row object
  360. * exists at all in this DataSource.
  361. *
  362. * @param row
  363. * the row object
  364. * @return index of the row; or <code>-1</code> if row is not available
  365. */
  366. public int indexOf(T row) {
  367. Object key = getRowKey(row);
  368. if (keyToIndexMap.containsKey(key)) {
  369. return keyToIndexMap.get(key);
  370. }
  371. return -1;
  372. }
  373. @Override
  374. public Registration addDataChangeHandler(
  375. final DataChangeHandler dataChangeHandler) {
  376. Objects.requireNonNull(dataChangeHandler,
  377. "DataChangeHandler can't be null");
  378. dataChangeHandlers.add(dataChangeHandler);
  379. if (!cached.isEmpty()) {
  380. // Push currently cached data to the implementation
  381. dataChangeHandler.dataUpdated(cached.getStart(), cached.length());
  382. dataChangeHandler.dataAvailable(cached.getStart(), cached.length());
  383. }
  384. return () -> dataChangeHandlers.remove(dataChangeHandler);
  385. }
  386. /**
  387. * Informs this data source that updated data has been sent from the server.
  388. *
  389. * @param firstRowIndex
  390. * the index of the first received row
  391. * @param rowData
  392. * a list of rows, starting from <code>firstRowIndex</code>
  393. */
  394. protected void setRowData(int firstRowIndex, List<T> rowData) {
  395. assert firstRowIndex + rowData.size() <= size();
  396. Profiler.enter("AbstractRemoteDataSource.setRowData");
  397. Range received = Range.withLength(firstRowIndex, rowData.size());
  398. if (isWaitingForData()) {
  399. cacheStrategy.onDataArrive(
  400. Duration.currentTimeMillis()
  401. - currentRequestCallback.requestStart,
  402. received.length());
  403. currentRequestCallback = null;
  404. }
  405. Range maxCacheRange = getMaxCacheRange(received);
  406. Range[] partition = received.partitionWith(maxCacheRange);
  407. Range newUsefulData = partition[1];
  408. if (!newUsefulData.isEmpty()) {
  409. // Update the parts that are actually inside
  410. int start = newUsefulData.getStart();
  411. for (int i = start; i < newUsefulData.getEnd(); i++) {
  412. final T row = rowData.get(i - firstRowIndex);
  413. indexToRowMap.put(Integer.valueOf(i), row);
  414. keyToIndexMap.put(getRowKey(row), Integer.valueOf(i));
  415. }
  416. Profiler.enter(
  417. "AbstractRemoteDataSource.setRowData notify dataChangeHandler");
  418. int length = newUsefulData.length();
  419. getHandlers().forEach(dch -> dch.dataUpdated(start, length));
  420. Profiler.leave(
  421. "AbstractRemoteDataSource.setRowData notify dataChangeHandler");
  422. // Potentially extend the range
  423. if (cached.isEmpty()) {
  424. cached = newUsefulData;
  425. } else {
  426. discardStaleCacheEntries();
  427. /*
  428. * everything might've become stale so we need to re-check for
  429. * emptiness.
  430. */
  431. if (!cached.isEmpty()) {
  432. cached = cached.combineWith(newUsefulData);
  433. } else {
  434. cached = newUsefulData;
  435. }
  436. }
  437. getHandlers().forEach(dch -> dch.dataAvailable(cached.getStart(),
  438. cached.length()));
  439. updatePinnedRows(rowData);
  440. }
  441. if (!partition[0].isEmpty() || !partition[2].isEmpty()) {
  442. /*
  443. * FIXME
  444. *
  445. * Got data that we might need in a moment if the container is
  446. * updated before the widget settings. Support for this will be
  447. * implemented later on.
  448. */
  449. // Run a dummy drop from cache for unused rows.
  450. for (int i = 0; i < partition[0].length(); ++i) {
  451. onDropFromCache(i + partition[0].getStart(), rowData.get(i));
  452. }
  453. for (int i = 0; i < partition[2].length(); ++i) {
  454. onDropFromCache(i + partition[2].getStart(), rowData.get(i));
  455. }
  456. }
  457. // Eventually check whether all needed rows are now available
  458. ensureCoverageCheck();
  459. Profiler.leave("AbstractRemoteDataSource.setRowData");
  460. }
  461. private Stream<DataChangeHandler> getHandlers() {
  462. Set<DataChangeHandler> copy = new LinkedHashSet<>(dataChangeHandlers);
  463. return copy.stream();
  464. }
  465. private void updatePinnedRows(final List<T> rowData) {
  466. for (final T row : rowData) {
  467. final Object key = getRowKey(row);
  468. final RowHandleImpl handle = pinnedRows.get(key);
  469. if (handle != null) {
  470. handle.setRow(row);
  471. }
  472. }
  473. }
  474. /**
  475. * Informs this data source that the server has removed data.
  476. *
  477. * @param firstRowIndex
  478. * the index of the first removed row
  479. * @param count
  480. * the number of removed rows, starting from
  481. * <code>firstRowIndex</code>
  482. */
  483. protected void removeRowData(int firstRowIndex, int count) {
  484. Profiler.enter("AbstractRemoteDataSource.removeRowData");
  485. size -= count;
  486. Range removedRange = Range.withLength(firstRowIndex, count);
  487. dropFromCache(removedRange);
  488. // shift indices to fill the cache correctly
  489. int firstMoved = Math.max(firstRowIndex + count, cached.getStart());
  490. for (int i = firstMoved; i < cached.getEnd(); i++) {
  491. moveRowFromIndexToIndex(i, i - count);
  492. }
  493. if (cached.isSubsetOf(removedRange)) {
  494. // Whole cache is part of the removal. Empty cache
  495. cached = Range.withLength(0, 0);
  496. } else if (removedRange.intersects(cached)) {
  497. // Removal and cache share some indices. fix accordingly.
  498. Range[] partitions = cached.partitionWith(removedRange);
  499. Range remainsBefore = partitions[0];
  500. Range transposedRemainsAfter = partitions[2]
  501. .offsetBy(-removedRange.length());
  502. cached = remainsBefore.combineWith(transposedRemainsAfter);
  503. } else if (removedRange.getEnd() <= cached.getStart()) {
  504. // Removal was before the cache. offset the cache.
  505. cached = cached.offsetBy(-removedRange.length());
  506. }
  507. getHandlers().forEach(dch -> dch.dataRemoved(firstRowIndex, count));
  508. ensureCoverageCheck();
  509. Profiler.leave("AbstractRemoteDataSource.removeRowData");
  510. }
  511. /**
  512. * Informs this data source that new data has been inserted from the server.
  513. *
  514. * @param firstRowIndex
  515. * the destination index of the new row data
  516. * @param count
  517. * the number of rows inserted
  518. */
  519. protected void insertRowData(int firstRowIndex, int count) {
  520. Profiler.enter("AbstractRemoteDataSource.insertRowData");
  521. size += count;
  522. if (firstRowIndex <= cached.getStart()) {
  523. Range oldCached = cached;
  524. cached = cached.offsetBy(count);
  525. for (int i = 1; i <= cached.length(); i++) {
  526. int oldIndex = oldCached.getEnd() - i;
  527. int newIndex = cached.getEnd() - i;
  528. moveRowFromIndexToIndex(oldIndex, newIndex);
  529. }
  530. } else if (cached.contains(firstRowIndex)) {
  531. int oldCacheEnd = cached.getEnd();
  532. /*
  533. * We need to invalidate the cache from the inserted row onwards,
  534. * since the cache wants to be a contiguous range. It doesn't
  535. * support holes.
  536. *
  537. * If holes were supported, we could shift the higher part of
  538. * "cached" and leave a hole the size of "count" in the middle.
  539. */
  540. cached = cached.splitAt(firstRowIndex)[0];
  541. for (int i = firstRowIndex; i < oldCacheEnd; i++) {
  542. T row = indexToRowMap.remove(Integer.valueOf(i));
  543. keyToIndexMap.remove(getRowKey(row));
  544. }
  545. }
  546. getHandlers().forEach(dch -> dch.dataAdded(firstRowIndex, count));
  547. ensureCoverageCheck();
  548. Profiler.leave("AbstractRemoteDataSource.insertRowData");
  549. }
  550. @SuppressWarnings("boxing")
  551. private void moveRowFromIndexToIndex(int oldIndex, int newIndex) {
  552. T row = indexToRowMap.remove(oldIndex);
  553. if (indexToRowMap.containsKey(newIndex)) {
  554. // Old row is about to be overwritten. Remove it from keyCache.
  555. T row2 = indexToRowMap.remove(newIndex);
  556. if (row2 != null) {
  557. keyToIndexMap.remove(getRowKey(row2));
  558. }
  559. }
  560. indexToRowMap.put(newIndex, row);
  561. if (row != null) {
  562. keyToIndexMap.put(getRowKey(row), newIndex);
  563. }
  564. }
  565. /**
  566. * Gets the current range of cached rows
  567. *
  568. * @return the range of currently cached rows
  569. */
  570. public Range getCachedRange() {
  571. return cached;
  572. }
  573. /**
  574. * Sets the cache strategy that is used to determine how much data is
  575. * fetched and cached.
  576. * <p>
  577. * The new strategy is immediately used to evaluate whether currently cached
  578. * rows should be discarded or new rows should be fetched.
  579. *
  580. * @param cacheStrategy
  581. * a cache strategy implementation, not <code>null</code>
  582. */
  583. public void setCacheStrategy(CacheStrategy cacheStrategy) {
  584. if (cacheStrategy == null) {
  585. throw new IllegalArgumentException();
  586. }
  587. if (this.cacheStrategy != cacheStrategy) {
  588. this.cacheStrategy = cacheStrategy;
  589. checkCacheCoverage();
  590. }
  591. }
  592. private Range getMinCacheRange() {
  593. Range availableDataRange = getAvailableRangeForCache();
  594. Range minCacheRange = cacheStrategy.getMinCacheRange(
  595. requestedAvailability, cached, availableDataRange);
  596. assert minCacheRange.isSubsetOf(availableDataRange);
  597. return minCacheRange;
  598. }
  599. private Range getMaxCacheRange() {
  600. return getMaxCacheRange(getRequestedAvailability());
  601. }
  602. private Range getMaxCacheRange(Range range) {
  603. Range availableDataRange = getAvailableRangeForCache();
  604. Range maxCacheRange = cacheStrategy.getMaxCacheRange(range, cached,
  605. availableDataRange);
  606. assert maxCacheRange.isSubsetOf(availableDataRange);
  607. return maxCacheRange;
  608. }
  609. private Range getAvailableRangeForCache() {
  610. int upperBound = size();
  611. if (upperBound == -1) {
  612. upperBound = requestedAvailability.length();
  613. }
  614. return Range.withLength(0, upperBound);
  615. }
  616. @Override
  617. public RowHandle<T> getHandle(T row) throws IllegalStateException {
  618. Object key = getRowKey(row);
  619. if (key == null) {
  620. throw new NullPointerException(
  621. "key may not be null (row: " + row + ")");
  622. }
  623. if (pinnedRows.containsKey(key)) {
  624. return pinnedRows.get(key);
  625. } else if (keyToIndexMap.containsKey(key)) {
  626. return new RowHandleImpl(row, key);
  627. } else {
  628. throw new IllegalStateException("The cache of this DataSource "
  629. + "does not currently contain the row " + row);
  630. }
  631. }
  632. /**
  633. * Gets a stable key for the row object.
  634. * <p>
  635. * This method is a workaround for the fact that there is no means to force
  636. * proper implementations for {@link #hashCode()} and
  637. * {@link #equals(Object)} methods.
  638. * <p>
  639. * Since the same row object will be created several times for the same
  640. * logical data, the DataSource needs a mechanism to be able to compare two
  641. * objects, and figure out whether or not they represent the same data. Even
  642. * if all the fields of an entity would be changed, it still could represent
  643. * the very same thing (say, a person changes all of her names.)
  644. * <p>
  645. * A very usual and simple example what this could be, is an unique ID for
  646. * this object that would also be stored in a database.
  647. *
  648. * @param row
  649. * the row object for which to get the key
  650. * @return a non-null object that uniquely and consistently represents the
  651. * row object
  652. */
  653. abstract public Object getRowKey(T row);
  654. @Override
  655. public int size() {
  656. return size;
  657. }
  658. /**
  659. * Updates the size, discarding all cached data. This method is used when
  660. * the size of the container is changed without any information about the
  661. * structure of the change. In this case, all cached data is discarded to
  662. * avoid cache offset issues.
  663. * <p>
  664. * If you have information about the structure of the change, use
  665. * {@link #insertRowData(int, int)} or {@link #removeRowData(int, int)} to
  666. * indicate where the inserted or removed rows are located.
  667. *
  668. * @param newSize
  669. * the new size of the container
  670. */
  671. protected void resetDataAndSize(int newSize) {
  672. size = newSize;
  673. dropFromCache(getCachedRange());
  674. cached = Range.withLength(0, 0);
  675. getHandlers().forEach(dch -> dch.resetDataAndSize(newSize));
  676. }
  677. protected int indexOfKey(Object rowKey) {
  678. if (!keyToIndexMap.containsKey(rowKey)) {
  679. return -1;
  680. } else {
  681. return keyToIndexMap.get(rowKey);
  682. }
  683. }
  684. protected boolean isPinned(T row) {
  685. return pinnedRows.containsKey(getRowKey(row));
  686. }
  687. /**
  688. * Checks if it is possible to currently fetch data from the remote data
  689. * source.
  690. *
  691. * @return <code>true</code> if it is ok to try to fetch data,
  692. * <code>false</code> if it is known that fetching data will fail
  693. * and should not be tried right now.
  694. * @since 7.7.2
  695. */
  696. protected boolean canFetchData() {
  697. return true;
  698. }
  699. }