public synchronized void gatherLatestDatapointsLocking(long catId, int history) {
    waitForLock();
    mDatapointCache.populateLatest(catId, history);
    TimeSeries ts = getSeriesByIdNonlocking(catId);
    if (ts == null) {
      unlock();
      return;
    }

    if (ts.getDbRow().getSynthetic() == false) {
      ts.clearSeries();

      EntryDbTable.Row entry = mDbh.fetchLastCategoryEntry(catId);
      if (entry != null) {
        ArrayList<Datapoint> l = mDatapointCache.getLast(catId, history);
        if (l == null
            || l.size() < 1
            || entry.getTimestamp() > l.get(0).mMillis
            || entry.getValue() != l.get(0).mValue.y) {
          mDatapointCache.clearCache(catId);
          mDatapointCache.populateLatest(catId, history);
          l = mDatapointCache.getLast(catId, history);
        }

        l = aggregateDatapoints(l, ts.getDbRow().getType());
        ts.setDatapoints(null, l, null, true);
      }
    }

    unlock();
  }
  public synchronized void gatherSeries(long milliStart, long milliEnd) {
    ArrayList<Datapoint> pre, range, post;
    boolean has_data;
    long oldAggregationMs = mAggregationMs;

    mQueryStart = milliStart;
    mQueryEnd = milliEnd;

    setCollectionTimes(milliStart, milliEnd);
    for (int i = 0; i < mSeries.size(); i++) {
      has_data = false;

      TimeSeries ts = mSeries.get(i);
      if (ts == null || ts.getDbRow().getSynthetic()) continue;

      if (ts.isEnabled() == false) {
        boolean skip = true;
        for (int j = 0; j < ts.getDependees().size(); j++) {
          if (ts.getDependees().get(j).isEnabled() == true) {
            skip = false;
            break;
          }
        }
        if (skip == true) continue;
      }

      mDatapointCache.populateRange(
          ts.getDbRow().getId(), mCollectionStart, mCollectionEnd, mAggregationMs);

      pre = mDatapointCache.getDataBefore(ts.getDbRow().getId(), mHistory, mCollectionStart);
      if (pre != null && pre.size() > 0) has_data = true;

      range =
          mDatapointCache.getDataInRange(ts.getDbRow().getId(), mCollectionStart, mCollectionEnd);
      if (range != null && range.size() > 0) has_data = true;

      post = mDatapointCache.getDataAfter(ts.getDbRow().getId(), 1, mCollectionEnd);
      if (post != null && range.size() > 0) has_data = true;

      if (has_data == true) ts.setDatapoints(pre, range, post, true);
    }

    generateSynthetics();

    ArrayList<TimeSeries> enabledSeries = getAllEnabledSeries();
    for (int i = 0; i < enabledSeries.size(); i++) {
      aggregateDatapoints(enabledSeries.get(i));
    }

    mAggregationMs = oldAggregationMs;

    return;
  }
  private void updateTimeSeriesMeta(CategoryDbTable.Row row, boolean disable) {
    TimeSeries ts = getSeriesByIdNonlocking(row.getId());

    if (ts == null) {
      if (mDefaultPainter == null) {
        TimeSeriesPainter p = new TimeSeriesPainter.Default();
        ts = new TimeSeries(row, mHistory, mSmoothing, p);
      } else {
        ts = new TimeSeries(row, mHistory, mSmoothing, mDefaultPainter);
      }
      mSeries.add(ts);
      mDatapointCache.addCacheableCategory(row.getId(), mHistory);
    }

    ts.setDbRow(row);
    setSeriesInterpolator(ts, row.getInterpolation());

    if (row.getSynthetic() == true) {
      Formula formula = mFormulaCache.getFormula(Long.valueOf(row.getId()));
      if (formula == null) formula = new Formula();
      formula.setFormula(row.getFormula());
      mFormulaCache.setFormula(row.getId(), formula);
    }

    if (disable) ts.setEnabled(false);

    setDependents(ts);
    setDependees(ts);
  }
  private void updateTimeSeriesDataLocking(long catId, long start, long end, boolean flushCache) {
    if (flushCache == true) {
      waitForLock();
      mDatapointCache.refresh(catId);
      unlock();
    }

    gatherSeriesLocking(start, end);
  }
 public Datapoint getLastDatapoint(long catId) {
   ArrayList<Datapoint> list = mDatapointCache.getLast(catId, 1);
   if (list == null || list.size() < 1) return null;
   return list.get(0);
 }
 public void clearCache() {
   mDatapointCache.clearCache();
   clearSeriesLocking();
 }
  private void updateTimeSeriesData(long catId, long start, long end, boolean flushCache) {
    if (flushCache == true) mDatapointCache.refresh(catId);

    gatherSeries(start, end);
  }