Package com.opengamma.core.historicaltimeseries.impl

Source Code of com.opengamma.core.historicaltimeseries.impl.EHCachingHistoricalTimeSeriesSource$MissHTS

/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.core.historicaltimeseries.impl;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Clock;
import org.threeten.bp.LocalDate;

import com.google.common.collect.Maps;
import com.opengamma.core.change.BasicChangeManager;
import com.opengamma.core.change.ChangeEvent;
import com.opengamma.core.change.ChangeListener;
import com.opengamma.core.change.ChangeManager;
import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries;
import com.opengamma.core.historicaltimeseries.HistoricalTimeSeriesSource;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.ObjectId;
import com.opengamma.id.UniqueId;
import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries;
import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.OpenGammaClock;
import com.opengamma.util.ehcache.EHCacheUtils;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.tuple.ObjectsPair;
import com.opengamma.util.tuple.Pair;

/**
* A cache decorating a {@code HistoricalTimeSeriesSource}.
* <p>
* The cache is implemented using {@code EHCache}.
*/
public class EHCachingHistoricalTimeSeriesSource implements HistoricalTimeSeriesSource {

  /** Logger. */
  private static final Logger s_logger = LoggerFactory.getLogger(EHCachingHistoricalTimeSeriesSource.class);

  /**
   * The cache prefix.
   */
  /*package*/static final String CACHE_PREFIX = "HistoricalTimeSeries";

  /**
   * The cache name.
   */
  private static final String DATA_CACHE_NAME = CACHE_PREFIX + "DataCache";

  /**
   * Id bundle cache name.
   */
  private static final String ID_BUNDLE_CACHE_NAME = CACHE_PREFIX + "IdBundleCache";

  /**
   * Listens for changes in the underlying security source.
   */
  private ChangeListener _changeListener;
  /**
   * The local change manager.
   */
  private final ChangeManager _changeManager;

  @Override
  public ChangeManager changeManager() {
    return _changeManager;
  }

  private static class MissHTS implements HistoricalTimeSeries, Serializable {

    private static final long serialVersionUID = 1L;

    @Override
    public UniqueId getUniqueId() {
      throw new UnsupportedOperationException();
    }

    @Override
    public LocalDateDoubleTimeSeries getTimeSeries() {
      throw new UnsupportedOperationException();
    }

    @Override
    public boolean equals(final Object o) {
      return o instanceof MissHTS;
    }

    @Override
    public int hashCode() {
      return 0;
    }

  };

  private static final MissHTS MISS = new MissHTS();

  /**
   * The underlying source.
   */
  private final HistoricalTimeSeriesSource _underlying;
  /**
   * The cache.
   */
  private final Cache _dataCache;
  /**
   * The identifier bundle cache
   */
  private final Cache _identifierBundleCache;
  /**
   * The clock.
   */
  private final Clock _clock = OpenGammaClock.getInstance();

  /**
   * Creates an instance.
   *
   * @param underlying the underlying source, not null
   * @param cacheManager the cache manager, not null
   */
  public EHCachingHistoricalTimeSeriesSource(HistoricalTimeSeriesSource underlying, CacheManager cacheManager) {
    ArgumentChecker.notNull(underlying, "underlying");
    ArgumentChecker.notNull(cacheManager, "Cache Manager");
    _underlying = underlying;
    EHCacheUtils.addCache(cacheManager, DATA_CACHE_NAME);
    _dataCache = EHCacheUtils.getCacheFromManager(cacheManager, DATA_CACHE_NAME);
    EHCacheUtils.addCache(cacheManager, ID_BUNDLE_CACHE_NAME);
    _identifierBundleCache = EHCacheUtils.getCacheFromManager(cacheManager, ID_BUNDLE_CACHE_NAME);

    _changeListener = createChangeListener();
    _underlying.changeManager().addChangeListener(_changeListener);
    _changeManager = new BasicChangeManager();
  }

  private ChangeListener createChangeListener() {
    return new ChangeListener() {

      @Override
      public void entityChanged(ChangeEvent event) {
        cleanCaches(event.getObjectId());
        changeManager().entityChanged(event.getType(), event.getObjectId(), event.getVersionFrom(), event.getVersionTo(), event.getVersionInstant());
      }

    };
  }

  private void cleanCaches(ObjectId oid) {
    // Only care where the unversioned ID has been cached since it now represents something else
    _dataCache.remove(oid);
    _identifierBundleCache.remove(oid);
    // Destroy all version/correction cached values for the object
    _dataCache.remove(oid);
    _identifierBundleCache.remove(oid);
  }

  //-------------------------------------------------------------------------
  /**
   * Gets the underlying source.
   *
   * @return the underlying source, not null
   */
  public HistoricalTimeSeriesSource getUnderlying() {
    return _underlying;
  }

  /**
   * Gets the id cache manager.
   *
   * @return the id cache manager, not null
   */
  public CacheManager getIdentifierBundleCacheManager() {
    return _identifierBundleCache.getCacheManager();
  }

  /**
   * Gets the clock.
   *
   * @return the clock, not null
   */
  public Clock getClock() {
    return _clock;
  }

  //-------------------------------------------------------------------------
  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(UniqueId uniqueId) {
    ArgumentChecker.notNull(uniqueId, "uniqueId");
    HistoricalTimeSeries hts = getFromDataCache(uniqueId);
    if (hts != null) {
      if (MISS.equals(hts)) {
        hts = null;
      }
    } else {
      hts = _underlying.getHistoricalTimeSeries(uniqueId);
      if (hts != null) {
        s_logger.debug("Caching time-series {}", hts);
        _dataCache.put(new Element(uniqueId, hts));
      } else {
        s_logger.debug("Caching miss on {}", uniqueId);
        _dataCache.put(new Element(uniqueId, MISS));
      }
    }
    return hts;
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      UniqueId uniqueId, LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    return doGetHistoricalTimeSeries(uniqueId, start, includeStart, end, includeEnd, null);
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      UniqueId uniqueId, LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd, int maxPoints) {
    return doGetHistoricalTimeSeries(uniqueId, start, includeStart, end, includeEnd, maxPoints);
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(UniqueId uniqueId) {
    HistoricalTimeSeries hts = doGetHistoricalTimeSeries(uniqueId, null, true, null, true, -1);
    if (hts == null || hts.getTimeSeries() == null || hts.getTimeSeries().isEmpty()) {
      return null;
    } else {
      return new ObjectsPair<LocalDate, Double>(hts.getTimeSeries().getLatestTime(), hts.getTimeSeries().getLatestValue());
    }
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(UniqueId uniqueId, LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    HistoricalTimeSeries hts = doGetHistoricalTimeSeries(uniqueId, start, includeStart, end, includeEnd, -1);
    if (hts == null || hts.getTimeSeries() == null || hts.getTimeSeries().isEmpty()) {
      return null;
    } else {
      return new ObjectsPair<LocalDate, Double>(hts.getTimeSeries().getLatestTime(), hts.getTimeSeries().getLatestValue());
    }
  }

  private HistoricalTimeSeries doGetHistoricalTimeSeries(
      UniqueId uniqueId, LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd, Integer maxPoints) {
    SubSeriesKey subseriesKey = new SubSeriesKey(start, end, maxPoints);
    ObjectsPair<UniqueId, SubSeriesKey> key = Pair.of(uniqueId, subseriesKey);
    Element element = _dataCache.get(key);
    HistoricalTimeSeries hts;
    if (element != null) {
      hts = (HistoricalTimeSeries) element.getObjectValue();
      if (MISS.equals(hts)) {
        hts = null;
      } else if (!subseriesKey.isMatch(start, includeStart, end, includeEnd, maxPoints)) {
        // Pick out the sub-series requested
        hts = getSubSeries(hts, start, includeStart, end, includeEnd, maxPoints);
      }
    } else {
      // If we have the full series cached computing a sub-series could be faster
      Element fullHtsElement = _dataCache.get(uniqueId);
      if (fullHtsElement != null) {
        hts = getSubSeries((HistoricalTimeSeries) fullHtsElement.getObjectValue(), start, includeStart, end, includeEnd, maxPoints);
      } else {
        if (maxPoints == null) {
          hts = _underlying.getHistoricalTimeSeries(uniqueId, subseriesKey.getStart(), true, subseriesKey.getEnd(), subseriesKey.getIncludeEnd());
        } else {
          hts = _underlying
              .getHistoricalTimeSeries(uniqueId, subseriesKey.getStart(), true, subseriesKey.getEnd(), subseriesKey.getIncludeEnd(), subseriesKey.getMaxPoints());
        }
        if (hts != null) {
          s_logger.debug("Caching sub time-series {}", hts);
          _dataCache.put(new Element(key, hts));
          if (!subseriesKey.isMatch(start, includeStart, end, includeEnd, maxPoints)) {
            // Pick out the sub-series requested
            hts = getSubSeries(hts, start, includeStart, end, includeEnd, maxPoints);
          }
        } else {
          s_logger.debug("Caching miss {}", key);
          _dataCache.put(new Element(key, MISS));
        }
      }
    }
    return hts;
  }

  //-------------------------------------------------------------------------

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      ExternalIdBundle identifiers, String dataSource, String dataProvider, String dataField) {
    return getHistoricalTimeSeries(identifiers, LocalDate.now(getClock()), dataSource, dataProvider, dataField);
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      ExternalIdBundle identifiers, LocalDate identifierValidityDate, String dataSource, String dataProvider, String dataField) {
    ArgumentChecker.notNull(identifiers, "identifiers");
    HistoricalTimeSeriesKey key = new HistoricalTimeSeriesKey(null, identifierValidityDate, identifiers, dataSource, dataProvider, dataField);
    HistoricalTimeSeries hts = getFromDataCache(key);
    if (hts != null) {
      if (MISS.equals(hts)) {
        hts = null;
      }
    } else {
      hts = _underlying.getHistoricalTimeSeries(identifiers, identifierValidityDate, dataSource, dataProvider, dataField);
      if (hts != null) {
        s_logger.debug("Caching time-series {}", hts);
        _dataCache.put(new Element(key, hts));
        _dataCache.put(new Element(hts.getUniqueId(), hts));
      } else {
        s_logger.debug("Caching miss on {}", key);
        _dataCache.put(new Element(key, MISS));
      }
    }
    return hts;
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      ExternalIdBundle identifiers, String dataSource, String dataProvider, String dataField,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    return getHistoricalTimeSeries(
        identifiers, LocalDate.now(getClock()), dataSource, dataProvider, dataField,
        start, includeStart, end, includeEnd);
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      ExternalIdBundle identifiers, LocalDate currentDate, String dataSource, String dataProvider, String dataField,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    return doGetHistoricalTimeSeries(identifiers, currentDate, dataSource, dataProvider, dataField, start, includeStart, end, includeEnd, null);
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      ExternalIdBundle identifierBundle, String dataSource, String dataProvider, String dataField, LocalDate start, boolean includeStart,
      LocalDate end, boolean includeEnd, int maxPoints) {
    return getHistoricalTimeSeries(identifierBundle, LocalDate.now(getClock()), dataSource, dataProvider, dataField, start, includeStart, end, includeEnd, maxPoints);
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      ExternalIdBundle identifiers, LocalDate currentDate, String dataSource, String dataProvider, String dataField,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd, int maxPoints) {
    return doGetHistoricalTimeSeries(identifiers, currentDate, dataSource, dataProvider, dataField, start, includeStart, end, includeEnd, maxPoints);
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(
      ExternalIdBundle identifiers, LocalDate currentDate, String dataSource, String dataProvider, String dataField) {
    HistoricalTimeSeries hts = doGetHistoricalTimeSeries(identifiers, currentDate, dataSource, dataProvider, dataField, null, true, null, true, -1);
    if (hts == null || hts.getTimeSeries() == null || hts.getTimeSeries().isEmpty()) {
      return null;
    } else {
      return new ObjectsPair<LocalDate, Double>(hts.getTimeSeries().getLatestTime(), hts.getTimeSeries().getLatestValue());
    }
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(
      ExternalIdBundle identifiers, LocalDate currentDate, String dataSource, String dataProvider, String dataField,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    HistoricalTimeSeries hts = doGetHistoricalTimeSeries(identifiers, currentDate, dataSource, dataProvider, dataField, start, includeStart, end, includeEnd, -1);
    if (hts == null || hts.getTimeSeries() == null || hts.getTimeSeries().isEmpty()) {
      return null;
    } else {
      return new ObjectsPair<LocalDate, Double>(hts.getTimeSeries().getLatestTime(), hts.getTimeSeries().getLatestValue());
    }
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(
      ExternalIdBundle identifierBundle, String dataSource, String dataProvider, String dataField) {
    return getLatestDataPoint(identifierBundle, LocalDate.now(getClock()), dataSource, dataProvider, dataField);
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(
      ExternalIdBundle identifierBundle, String dataSource, String dataProvider, String dataField,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    return getLatestDataPoint(identifierBundle, LocalDate.now(getClock()), dataSource, dataProvider, dataField,
        start, includeStart, end, includeEnd);
  }

  private HistoricalTimeSeries doGetHistoricalTimeSeries(
      ExternalIdBundle identifiers, LocalDate currentDate, String dataSource, String dataProvider, String dataField,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd, Integer maxPoints) {
    HistoricalTimeSeriesKey seriesKey = new HistoricalTimeSeriesKey(null, currentDate, identifiers, dataSource, dataProvider, dataField);
    SubSeriesKey subseriesKey = new SubSeriesKey(start, end, maxPoints);
    ObjectsPair<HistoricalTimeSeriesKey, SubSeriesKey> key = Pair.of(seriesKey, subseriesKey);
    Element element = _dataCache.get(key);
    HistoricalTimeSeries hts;
    if (element != null) {
      hts = (HistoricalTimeSeries) element.getObjectValue();
      if (MISS.equals(hts)) {
        hts = null;
      } else if (!subseriesKey.isMatch(start, includeStart, end, includeEnd, maxPoints)) {
        // Pick out the sub-series requested
        hts = getSubSeries(hts, start, includeStart, end, includeEnd, maxPoints);
      }
    } else {
      // If we have the full series cached computing a sub-series could be faster
      Element fullHtsElement = _dataCache.get(seriesKey);
      if (fullHtsElement != null) {
        hts = getSubSeries((HistoricalTimeSeries) fullHtsElement.getObjectValue(), start, includeStart, end, includeEnd, maxPoints);
      } else {
        if (maxPoints == null) {
          hts = _underlying.getHistoricalTimeSeries(identifiers, currentDate, dataSource, dataProvider, dataField,
              subseriesKey.getStart(), true, subseriesKey.getEnd(), subseriesKey.getIncludeEnd());
        } else {
          hts = _underlying.getHistoricalTimeSeries(identifiers, currentDate, dataSource, dataProvider, dataField,
              subseriesKey.getStart(), true, subseriesKey.getEnd(), subseriesKey.getIncludeEnd(), subseriesKey.getMaxPoints());
        }
        if (hts != null) {
          s_logger.debug("Caching sub time-series {}", hts);
          _dataCache.put(new Element(key, hts));
          if (!subseriesKey.isMatch(start, includeStart, end, includeEnd, maxPoints)) {
            // Pick out the sub-series requested
            hts = getSubSeries(hts, start, includeStart, end, includeEnd, maxPoints);
          }
        } else {
          s_logger.debug("Caching miss {}", key);
          _dataCache.put(new Element(key, MISS));
        }
      }
    }
    return hts;
  }

  //-------------------------------------------------------------------------
  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      String dataField, ExternalIdBundle identifierBundle, String resolutionKey) {
    return getHistoricalTimeSeries(dataField, identifierBundle, LocalDate.now(getClock()), resolutionKey);
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      String dataField, ExternalIdBundle identifierBundle, LocalDate identifierValidityDate, String resolutionKey) {
    ArgumentChecker.notNull(dataField, "dataField");
    ArgumentChecker.notEmpty(identifierBundle, "identifierBundle");
    HistoricalTimeSeriesKey key = new HistoricalTimeSeriesKey(resolutionKey, identifierValidityDate, identifierBundle, null, null, dataField);
    HistoricalTimeSeries hts = getFromDataCache(key);
    if (hts != null) {
      if (MISS.equals(hts)) {
        hts = null;
      }
    } else {
      hts = _underlying.getHistoricalTimeSeries(dataField, identifierBundle, identifierValidityDate, resolutionKey);
      if (hts != null) {
        s_logger.debug("Caching time-series {}", hts);
        _dataCache.put(new Element(key, hts));
        _dataCache.put(new Element(hts.getUniqueId(), hts));
      } else {
        s_logger.debug("Caching miss on {}", key);
        _dataCache.put(new Element(key, MISS));
      }
    }
    return hts;
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      String dataField, ExternalIdBundle identifierBundle, String resolutionKey,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    return doGetHistoricalTimeSeries(
        dataField, identifierBundle, LocalDate.now(getClock()), resolutionKey, start, includeStart, end, includeEnd, null);
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(
      String dataField, ExternalIdBundle identifierBundle, String resolutionKey,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd, int maxPoints) {
    return doGetHistoricalTimeSeries(
        dataField, identifierBundle, LocalDate.now(getClock()), resolutionKey, start, includeStart, end, includeEnd, maxPoints);
  }

  /*
   * PLAT-1589
   */
  private static final class SubSeriesKey implements Serializable {
    private static final long serialVersionUID = 2L;
    private final LocalDate _start;
    private final LocalDate _end;
    private final Integer _maxPoints;

    public SubSeriesKey(LocalDate start, LocalDate end, Integer maxPoints) {
      super();
      this._start = (start != null) ? start.withDayOfMonth(1).withMonth(1) : null;
      this._end = (end != null) ? end.plusYears(1).withMonth(1).withDayOfMonth(1) : null;
      if (maxPoints != null) {
        int mp = maxPoints;
        if (mp < 0) {
          int amp = -mp;
          if (end != null) {
            amp += DateUtils.getDaysBetween(end, _end);
          }
          this._maxPoints = -(amp + 1024 - (amp & 1023));
        } else if (mp > 0) {
          if (start != null) {
            mp += DateUtils.getDaysBetween(_start, start);
          }
          this._maxPoints = mp + 1024 - (mp & 1023);
        } else {
          this._maxPoints = maxPoints;
        }
      } else {
        this._maxPoints = null;
      }
    }

    public LocalDate getStart() {
      return _start;
    }

    public LocalDate getEnd() {
      return _end;
    }

    public Integer getMaxPoints() {
      return _maxPoints;
    }

    public boolean getIncludeEnd() {
      return getEnd() == null;
    }

    /**
     * Tests whether this key exactly matches the user request, or if it would be a larger time-series that needs to be cut down to match.
     *
     * @param true if an exact match, false if it needs trimming
     */
    public boolean isMatch(final LocalDate start, final boolean includeStart, final LocalDate end, final boolean includeEnd, final Integer maxPoints) {
      return ObjectUtils.equals(start, _start)
          && ObjectUtils.equals(end, _end)
          && includeStart
          && (includeEnd == getIncludeEnd())
          && ObjectUtils.equals(maxPoints, _maxPoints);
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ObjectUtils.hashCode(_end);
      result = prime * result + ObjectUtils.hashCode(_start);
      result = prime * result + ObjectUtils.hashCode(_maxPoints);
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      }
      if (obj == null) {
        return false;
      }
      SubSeriesKey other = (SubSeriesKey) obj;
      if (!ObjectUtils.equals(_end, other._end)) {
        return false;
      }
      if (!ObjectUtils.equals(_start, other._start)) {
        return false;
      }
      if (!ObjectUtils.equals(_maxPoints, other._maxPoints)) {
        return false;
      }
      return true;
    }
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(String dataField, ExternalIdBundle identifierBundle,
      LocalDate identifierValidityDate, String resolutionKey,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    return doGetHistoricalTimeSeries(
        dataField, identifierBundle, identifierValidityDate, resolutionKey, start, includeStart, end, includeEnd, null);
  }

  @Override
  public HistoricalTimeSeries getHistoricalTimeSeries(String dataField, ExternalIdBundle identifierBundle,
      LocalDate identifierValidityDate, String resolutionKey,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd, int maxPoints) {
    return doGetHistoricalTimeSeries(
        dataField, identifierBundle, identifierValidityDate, resolutionKey, start, includeStart, end, includeEnd, maxPoints);
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(
      String dataField, ExternalIdBundle identifierBundle, String resolutionKey) {
    return getLatestDataPoint(dataField, identifierBundle, LocalDate.now(getClock()), resolutionKey);
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(
      String dataField, ExternalIdBundle identifierBundle, String resolutionKey,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    return getLatestDataPoint(dataField, identifierBundle, LocalDate.now(getClock()), resolutionKey,
        start, includeStart, end, includeEnd);
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(
      String dataField, ExternalIdBundle identifierBundle, LocalDate identifierValidityDate, String resolutionKey) {
    return getLatestDataPoint(dataField, identifierBundle, identifierValidityDate, resolutionKey,
        (LocalDate) null, true, (LocalDate) null, true);
  }

  @Override
  public Pair<LocalDate, Double> getLatestDataPoint(
      String dataField, ExternalIdBundle identifierBundle, LocalDate identifierValidityDate, String resolutionKey,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd) {
    HistoricalTimeSeries hts = getHistoricalTimeSeries(
        dataField, identifierBundle, identifierValidityDate, resolutionKey,
        start, includeStart, end, includeEnd, -1);
    if (hts == null || hts.getTimeSeries() == null || hts.getTimeSeries().isEmpty()) {
      return null;
    } else {
      return new ObjectsPair<LocalDate, Double>(hts.getTimeSeries().getLatestTime(), hts.getTimeSeries().getLatestValue());
    }
  }

  private HistoricalTimeSeries doGetHistoricalTimeSeries(String dataField, ExternalIdBundle identifierBundle,
      LocalDate identifierValidityDate, String resolutionKey,
      LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd, Integer maxPoints) {
    HistoricalTimeSeriesKey seriesKey = new HistoricalTimeSeriesKey(resolutionKey, identifierValidityDate, identifierBundle, null, null, dataField);
    SubSeriesKey subseriesKey = new SubSeriesKey(start, end, maxPoints);
    ObjectsPair<HistoricalTimeSeriesKey, SubSeriesKey> key = Pair.of(seriesKey, subseriesKey);
    Element element = _dataCache.get(key);
    HistoricalTimeSeries hts;
    if (element != null) {
      hts = (HistoricalTimeSeries) element.getObjectValue();
      if (MISS.equals(hts)) {
        hts = null;
      } else if (!subseriesKey.isMatch(start, includeStart, end, includeEnd, maxPoints)) {
        // Pick out the sub-series requested
        hts = getSubSeries(hts, start, includeStart, end, includeEnd, maxPoints);
      }
    } else {
      // If we have the full series cached computing a sub-series could be faster
      Element fullHtsElement = _dataCache.get(seriesKey);
      if (fullHtsElement != null) {
        hts = getSubSeries((HistoricalTimeSeries) fullHtsElement.getObjectValue(), start, includeStart, end, includeEnd, maxPoints);
      } else {
        if (maxPoints == null) {
          hts = _underlying.getHistoricalTimeSeries(dataField, identifierBundle, identifierValidityDate, resolutionKey, subseriesKey.getStart(), true, subseriesKey.getEnd(),
              subseriesKey.getIncludeEnd());
        } else {
          hts = _underlying.getHistoricalTimeSeries(dataField, identifierBundle, identifierValidityDate, resolutionKey, subseriesKey.getStart(), true, subseriesKey.getEnd(),
              subseriesKey.getIncludeEnd(), subseriesKey.getMaxPoints());
        }
        if (hts != null) {
          s_logger.debug("Caching sub time-series {}", hts);
          _dataCache.put(new Element(key, hts));
          _dataCache.put(new Element(new ObjectsPair<UniqueId, SubSeriesKey>(hts.getUniqueId(), subseriesKey), hts));
          if (!subseriesKey.isMatch(start, includeStart, end, includeEnd, maxPoints)) {
            // Pick out the sub-series requested
            hts = getSubSeries(hts, start, includeStart, end, includeEnd, maxPoints);
          }
        } else {
          s_logger.debug("Caching miss {}", key);
          _dataCache.put(new Element(key, MISS));
        }
      }
    }
    return hts;
  }

  //-------------------------------------------------------------------------
  @Override
  public Map<ExternalIdBundle, HistoricalTimeSeries> getHistoricalTimeSeries(
      Set<ExternalIdBundle> identifierSet, String dataSource, String dataProvider, String dataField, LocalDate start,
      boolean includeStart, LocalDate end, boolean includeEnd) {
    ArgumentChecker.notNull(identifierSet, "identifierSet");
    Map<ExternalIdBundle, HistoricalTimeSeries> result = Maps.newHashMap();
    Set<ExternalIdBundle> remainingIds = new HashSet<ExternalIdBundle>();
    // caching works individually but all misses can be passed to underlying as one request
    for (ExternalIdBundle identifiers : identifierSet) {
      HistoricalTimeSeriesKey key = new HistoricalTimeSeriesKey(null, null, identifiers, dataSource, dataProvider, dataField);
      HistoricalTimeSeries hts = getFromDataCache(key);
      if (hts != null) {
        if (!MISS.equals(hts)) {
          hts = getSubSeries(hts, start, includeStart, end, includeEnd, null);
          result.put(identifiers, hts);
        } else {
          result.put(identifiers, null);
        }
      } else {
        remainingIds.add(identifiers);
      }
    }
    if (remainingIds.size() > 0) {
      Map<ExternalIdBundle, HistoricalTimeSeries> remainingTsResults =
          _underlying.getHistoricalTimeSeries(remainingIds, dataSource, dataProvider, dataField, start, includeStart, end, includeEnd);
      for (Map.Entry<ExternalIdBundle, HistoricalTimeSeries> tsResult : remainingTsResults.entrySet()) {
        ExternalIdBundle identifiers = tsResult.getKey();
        HistoricalTimeSeries hts = tsResult.getValue();
        HistoricalTimeSeriesKey key = new HistoricalTimeSeriesKey(null, null, identifiers, dataSource, dataProvider, dataField);
        if (hts != null) {
          s_logger.debug("Caching time-series {}", hts);
          _dataCache.put(new Element(key, hts));
          _dataCache.put(new Element(hts.getUniqueId(), hts));
          hts = getSubSeries(hts, start, includeStart, end, includeEnd, null);
        } else {
          s_logger.debug("Caching miss {}", key);
          _dataCache.put(new Element(key, MISS));
        }
        result.put(identifiers, hts);
      }
    }
    return result;
  }

  //-------------------------------------------------------------------------
  /**
   * Attempts to retrieve the time-series with the given key from the cache.
   *
   * @param key the key, not null
   * @return the time-series, null if no match
   */
  private HistoricalTimeSeries getFromDataCache(HistoricalTimeSeriesKey key) {
    Element element = _dataCache.get(key);
    if (element == null) {
      s_logger.debug("Cache miss on {}", key);
      return null;
    }
    s_logger.debug("Cache hit on {}", key);
    return (HistoricalTimeSeries) element.getObjectValue();
  }

  /**
   * Attempts to retrieve the time-series with the given unique identifier from the cache.
   *
   * @param uniqueId the unique identifier, not null
   * @return the time-series, null if no match
   */
  private HistoricalTimeSeries getFromDataCache(UniqueId uniqueId) {
    Element element = _dataCache.get(uniqueId);
    if (element == null) {
      s_logger.debug("Cache miss on {}", uniqueId);
      return null;
    }
    s_logger.debug("Cache hit on {}", uniqueId);
    return (HistoricalTimeSeries) element.getObjectValue();
  }

  /**
   * Gets a sub-series based on the supplied dates.
   *
   * @param hts the time-series, null returns null
   * @param start the start date, null will load the earliest date
   * @param includeStart whether or not the start date is included in the result
   * @param end the end date, null will load the latest date
   * @param includeEnd whether or not the end date is included in the result
   * @return the historical time-series, null if null input
   */
  private HistoricalTimeSeries getSubSeries(
      HistoricalTimeSeries hts, LocalDate start, boolean includeStart, LocalDate end, boolean includeEnd, Integer maxPoints) {

    if (hts == null) {
      return null;
    }
    LocalDateDoubleTimeSeries timeSeries = (LocalDateDoubleTimeSeries) hts.getTimeSeries();
    if (timeSeries == null || timeSeries.isEmpty()) {
      return hts;
    }
    if (start == null) {
      start = timeSeries.getEarliestTime();
    } else {
      if (!includeStart) {
        start = start.plusDays(1);
      }
      if (start.isBefore(timeSeries.getEarliestTime())) {
        start = timeSeries.getEarliestTime();
      }
    }
    if (end == null) {
      end = timeSeries.getLatestTime();
    } else {
      if (!includeEnd) {
        end = end.minusDays(1);
      }
      if (end.isAfter(timeSeries.getLatestTime())) {
        end = timeSeries.getLatestTime();
      }
    }
    if (start.isAfter(timeSeries.getLatestTime()) || end.isBefore(timeSeries.getEarliestTime())) {
      return new SimpleHistoricalTimeSeries(hts.getUniqueId(), ImmutableLocalDateDoubleTimeSeries.EMPTY_SERIES);
    }
    timeSeries = timeSeries.subSeries(start, true, end, true);
    if (((maxPoints != null) && (Math.abs(maxPoints) < timeSeries.size()))) {
      timeSeries = maxPoints >= 0 ? timeSeries.head(maxPoints) : timeSeries.tail(-maxPoints);
    }
    return new SimpleHistoricalTimeSeries(hts.getUniqueId(), timeSeries);
  }

  //-------------------------------------------------------------------------
  @Override
  public String toString() {
    return getClass().getSimpleName() + "[" + getUnderlying() + "]";
  }

  @Override
  public ExternalIdBundle getExternalIdBundle(UniqueId uniqueId) {
    Element idBundleCacheElement = _identifierBundleCache.get(uniqueId);
    if (idBundleCacheElement == null) {
      ExternalIdBundle idBundle = _underlying.getExternalIdBundle(uniqueId);
      _identifierBundleCache.put(new Element(uniqueId, idBundle));
      return idBundle;
    } else {
      return (ExternalIdBundle) idBundleCacheElement.getObjectValue();
    }
  }

  /**
   * Call this at the end of a unit test run to clear the state of EHCache. It should not be part of a generic lifecycle method.
   */
  protected void shutdown() {
    _dataCache.getCacheManager().removeCache(DATA_CACHE_NAME);
    _identifierBundleCache.getCacheManager().removeCache(ID_BUNDLE_CACHE_NAME);
  }

}
TOP

Related Classes of com.opengamma.core.historicaltimeseries.impl.EHCachingHistoricalTimeSeriesSource$MissHTS

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.