/**
  * Caches result locally for the given storage under given hash.
  *
  * @param localStorageData {@link LocalStorageData}
  * @param results Results to cache
  * @param hash Hash to use
  */
 private void cacheQueryResultLocally(
     LocalStorageData localStorageData, List<E> results, int hash) {
   try {
     storageManager.cacheStorageData(localStorageData, results, hash);
   } catch (IOException | SerializationException e) { // NOPMD NOCHK
     // ignore also if caching fails
   }
 }
  /**
   * This method executes the query in way that it first checks if wanted data is already cached. If
   * not method has the ability to load the data via the HTTP or locally and aggregate the data if
   * the {@link IAggregator} is provided. If the {@link IAggregator} is not provided, the data will
   * be returned not aggregated.
   *
   * <p>In addition it will try to cache the results if they are not yet cached.
   *
   * <p>This method should be used by all subclasses, because it guards against massive data loading
   * that can make out of memory exceptions on the UI.
   *
   * @param storageIndexQuery Query.
   * @param aggregator {@link IAggregator}
   * @param comparator If supplied the final result list will be sorted by this comparator.
   * @param limit Limit the number of results by given number. Value <code>-1</code> means no limit.
   * @return Return results of a query.
   */
  protected List<E> executeQuery(
      StorageIndexQuery storageIndexQuery,
      IAggregator<E> aggregator,
      Comparator<? super E> comparator,
      int limit) {
    List<E> returnList = null;
    // check if this can be cached
    if (storageManager.canBeCached(storageIndexQuery, aggregator)) {
      int hash = storageManager.getCachedDataHash(storageIndexQuery, aggregator);
      if (!localStorageData.isFullyDownloaded()) {
        // check if it s cached on the CMR
        StorageData storageData = new StorageData(localStorageData);
        try {
          returnList =
              dataRetriever.getCachedDataViaHttp(getCmrRepositoryDefinition(), storageData, hash);
        } catch (BusinessException | IOException | SerializationException e) { // NOPMD // NOCHK
          // ignore cause we can still load results in other way
        }

        if (null == returnList) {
          // if not we load data regular way
          returnList = loadData(storageIndexQuery, aggregator);

          // and cache it on the CMR if we get something
          if (CollectionUtils.isNotEmpty(returnList)) {
            cacheQueryResultOnCmr(getCmrRepositoryDefinition(), storageData, returnList, hash);
          }
        }
      } else {
        try {
          returnList = dataRetriever.getCachedDataLocally(localStorageData, hash);
        } catch (IOException | SerializationException e) { // NOPMD NOCHK
          // ignore cause we can still load results in other way
        }

        if (null == returnList) {
          // if not we load data regular way
          returnList = loadData(storageIndexQuery, aggregator);

          // and cache it locally if we get something
          if (CollectionUtils.isNotEmpty(returnList)) {
            cacheQueryResultLocally(localStorageData, returnList, hash);
          }
        }
      }
    } else {
      returnList = loadData(storageIndexQuery, aggregator);
    }

    // sort if needed
    if (null != comparator) {
      Collections.sort(returnList, comparator);
    }

    // limit the size if needed
    if ((limit > -1) && (returnList.size() > limit)) {
      returnList = returnList.subList(0, limit);
    }

    return returnList;
  }