/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.view.worker; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.threeten.bp.Instant; import com.google.common.collect.Maps; import com.opengamma.engine.marketdata.MarketDataSnapshot; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.id.UniqueId; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.tuple.Pair; /** * Snapshot of market data which aggregates data from multiple underlying snapshots. */ /* package */class CompositeMarketDataSnapshot implements MarketDataSnapshot { /** The underlying snapshots. */ private final List<MarketDataSnapshot> _snapshots; /** The object that can map the value specifications to/from an underlying provider. */ private final SnapshottingViewExecutionDataProvider.ValueSpecificationProvider _valueMap; /** * @param snapshots The underlying snapshots */ /* package */CompositeMarketDataSnapshot(final List<MarketDataSnapshot> snapshots, final SnapshottingViewExecutionDataProvider.ValueSpecificationProvider valueMap) { ArgumentChecker.notEmpty(snapshots, "snapshots"); ArgumentChecker.notNull(valueMap, "valueMap"); _snapshots = snapshots; _valueMap = valueMap; } @Override public UniqueId getUniqueId() { // TODO is this unique enough? same as in the existing impls return UniqueId.of(MARKET_DATA_SNAPSHOT_ID_SCHEME, "CompositeMarketDataSnapshot:" + getSnapshotTime()); } /** * @return The first non-null indication of snapshot time from the underlying snapshots * @throws IllegalStateException If none of the underlying snapshots return a value */ @Override public Instant getSnapshotTimeIndication() { for (final MarketDataSnapshot snapshot : _snapshots) { final Instant snapshotTimeIndication = snapshot.getSnapshotTimeIndication(); if (snapshotTimeIndication != null) { return snapshotTimeIndication; } } throw new IllegalStateException("None of the underlying snapshots returned a snapshot time indication"); } /** * Initializes all of the underlying snapshots. */ @Override public void init() { for (final MarketDataSnapshot snapshot : _snapshots) { snapshot.init(); } } /** * Initializes the underlying snapshots. * * @param values the values required in the snapshot, not null * @param timeout the maximum time to wait for the required values * @param unit the timeout unit, not null */ @Override public void init(final Set<ValueSpecification> values, final long timeout, final TimeUnit unit) { ArgumentChecker.notNull(values, "values"); ArgumentChecker.notNull(unit, "unit"); final List<Set<ValueSpecification>> valuesBySnapshot = _valueMap.getUnderlyingSpecifications(values); for (int i = 0; i < _snapshots.size(); i++) { final MarketDataSnapshot snapshot = _snapshots.get(i); final Set<ValueSpecification> snapshotSubscriptions = valuesBySnapshot.get(i); if (snapshotSubscriptions.isEmpty()) { snapshot.init(); } else { // TODO whole timeout? or divide timeout between all delegate snapshots and keep track of how much is left? // the combined snapshot does this but that seems broken to me snapshot.init(snapshotSubscriptions, timeout, unit); } } } @Override public boolean isInitialized() { for (MarketDataSnapshot snapshot : _snapshots) { if (!snapshot.isInitialized()) { return false; } } return true; } @Override public boolean isEmpty() { if (!isInitialized()) { throw new IllegalStateException("Market data snapshot is not initialized"); } for (MarketDataSnapshot snapshot : _snapshots) { if (!snapshot.isEmpty()) { return false; } } return true; } /** * @return The first non-null snapshot time from the underlying snapshots * @throws IllegalStateException If none of the underlying snapshots return a value */ @Override public Instant getSnapshotTime() { for (final MarketDataSnapshot snapshot : _snapshots) { final Instant snapshotTime = snapshot.getSnapshotTime(); if (snapshotTime != null) { return snapshotTime; } } throw new IllegalStateException("None of the underlying snapshots returned a snapshot time"); } /** * Returns the value from one of the underlying snapshots or null if it isn't available in any of them * * @param value the value required, not null * @return The value from one of the underlying snapshots or null if it isn't available in any of them */ @Override public Object query(final ValueSpecification value) { ArgumentChecker.notNull(value, "value"); final Pair<Integer, ValueSpecification> lookup = _valueMap.getUnderlyingAndSpecification(value); if (lookup == null) { return null; } return _snapshots.get(lookup.getFirst()).query(lookup.getSecond()); } /** * Returns the values from the underlying snapshots if they are available * * @param values the values required, not null * @return The values from the underlying snapshots if they are available, values that aren't available will be missing from the results map */ @Override public Map<ValueSpecification, Object> query(final Set<ValueSpecification> values) { ArgumentChecker.notNull(values, "values"); final Map<ValueSpecification, Object> results = Maps.newHashMapWithExpectedSize(values.size()); final List<Set<ValueSpecification>> valuesBySnapshot = _valueMap.getUnderlyingSpecifications(values); for (int i = 0; i < _snapshots.size(); i++) { final MarketDataSnapshot snapshot = _snapshots.get(i); final Set<ValueSpecification> snapshotSubscriptions = valuesBySnapshot.get(i); if (!snapshotSubscriptions.isEmpty()) { final Map<ValueSpecification, Object> snapshotResults = snapshot.query(snapshotSubscriptions); for (final Map.Entry<ValueSpecification, Object> snapshotResult : snapshotResults.entrySet()) { results.put(_valueMap.convertUnderlyingSpecification(i, snapshotResult.getKey()), snapshotResult.getValue()); } } } return results; } }