/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.view.cycle; import java.util.HashSet; import java.util.Set; import org.apache.commons.lang.ObjectUtils; import com.opengamma.engine.cache.CacheSelectHint; import com.opengamma.engine.cache.ViewComputationCache; import com.opengamma.engine.depgraph.DependencyGraph; import com.opengamma.engine.depgraph.DependencyNode; import com.opengamma.engine.function.MarketDataSourcingFunction; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.util.ArgumentChecker; /** * Determines which nodes in a graph have changed. A node has 'changed' if and only if its subtree contains a node for which PreviousLiveDataInput != CurrentLiveDataInput. Note that this excludes * changes due to passage of the system clock. */ public class LiveDataDeltaCalculator { private final DependencyGraph _graph; private final ViewComputationCache _cache; private final ViewComputationCache _previousCache; private final Set<ValueSpecification> _changedSpecifications; private final Set<DependencyNode> _changedNodes = new HashSet<DependencyNode>(); private final Set<DependencyNode> _unchangedNodes = new HashSet<DependencyNode>(); private boolean _done; // = false /** * For the delta calculation to be meaningful, the caches should be populated with LiveData inputs required to compute the given dependency graph. See {@link DependencyNode#getRequiredLiveData()} * and {@link ViewComputationCache#getValue(ValueSpecification)}. * * @param graph Dependency graph * @param cache Contains CurrentLiveDataInputs (for the given graph) * @param previousCache Contains PreviousLiveDataInputs (for the given graph) * @param dirtySpecifications Value specifications that are to be considered "changed" */ public LiveDataDeltaCalculator(final DependencyGraph graph, final ViewComputationCache cache, final ViewComputationCache previousCache, final Set<ValueSpecification> dirtySpecifications) { ArgumentChecker.notNull(graph, "Graph"); ArgumentChecker.notNull(cache, "Cache"); ArgumentChecker.notNull(previousCache, "Previous cache"); ArgumentChecker.notNull(dirtySpecifications, "dirtySpecifications"); _graph = graph; _cache = cache; _previousCache = previousCache; _changedSpecifications = dirtySpecifications.isEmpty() ? null : dirtySpecifications; } public Set<DependencyNode> getChangedNodes() { if (!_done) { throw new IllegalStateException("Call computeDelta() first"); } return _changedNodes; } public Set<DependencyNode> getUnchangedNodes() { if (!_done) { throw new IllegalStateException("Call computeDelta() first"); } return _unchangedNodes; } public void computeDelta() { if (_done) { throw new IllegalStateException("Cannot determine delta twice"); } final int count = _graph.getRootCount(); for (int i = 0; i < count; i++) { computeDelta(_graph.getRootNode(i)); } _done = true; } private boolean computeDelta(final DependencyNode node) { if (_changedNodes.contains(node)) { return true; } if (_unchangedNodes.contains(node)) { return false; } boolean hasChanged = false; int count = node.getInputCount(); if (count == 0) { if (MarketDataSourcingFunction.UNIQUE_ID.equals(node.getFunction().getFunctionId())) { // This is a graph leaf, but market data changes may affect the function of the node. count = node.getOutputCount(); for (int i = 0; i < count; i++) { final ValueSpecification liveData = node.getOutputValue(i); // Market data is always in the shared cache final Object oldValue = _previousCache.getValue(liveData, CacheSelectHint.allShared()); final Object newValue = _cache.getValue(liveData, CacheSelectHint.allShared()); if (!ObjectUtils.equals(oldValue, newValue)) { hasChanged = true; break; } } } } else { for (int i = 0; i < count; i++) { // if any children changed, this node requires recalculation hasChanged |= computeDelta(node.getInputNode(i)); } if (!hasChanged && (_changedSpecifications != null)) { count = node.getOutputCount(); for (int i = 0; i < count; i++) { if (_changedSpecifications.contains(node.getOutputValue(i))) { hasChanged = true; break; } } } } if (hasChanged) { _changedNodes.add(node); } else { _unchangedNodes.add(node); } return hasChanged; } }