/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.web.analytics; import java.util.Collections; import java.util.List; import com.google.common.collect.Lists; import com.opengamma.engine.ComputationTargetResolver; import com.opengamma.engine.depgraph.DependencyGraphExplorer; import com.opengamma.engine.depgraph.DependencyNode; import com.opengamma.engine.function.FunctionDefinition; import com.opengamma.engine.function.FunctionRepository; import com.opengamma.engine.management.ValueMappings; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.engine.view.compilation.CompiledViewDefinition; import com.opengamma.engine.view.compilation.CompiledViewDefinitionWithGraphs; import com.opengamma.engine.view.cycle.ViewCycle; /** * Builds the row and column structure of a dependency graph grid given the compiled view definition and the target at the root of the graph. */ /* package */class DependencyGraphStructureBuilder { /** {@link ValueSpecification}s for all rows in the grid in row index order. */ private final List<ValueSpecification> _valueSpecifications = Lists.newArrayList(); /** Function names for all rows in the grid in row index order. */ private final List<String> _fnNames = Lists.newArrayList(); /** The grid structure. */ private final DependencyGraphGridStructure _structure; private final FunctionRepository _functions; @SuppressWarnings("unused") private final ValueMappings _valueMappings; /** Mutable variable for keeping track of the index of the last row */ private int _lastRow; /** * @param compiledViewDef The compiled view definition containing the dependency graph * @param rootValueRequirement value requirement for the root cell * @param calcConfigName The calculation configuration used when calculating the value * @param targetResolver For looking up calculation targets given their specification * @param cycle The most recent view cycle */ /* package */DependencyGraphStructureBuilder(CompiledViewDefinition compiledViewDef, ValueRequirement rootValueRequirement, String calcConfigName, ComputationTargetResolver targetResolver, FunctionRepository functions, ViewCycle cycle, ValueMappings valueMappings) { // TODO see [PLAT-2478] this is a bit nasty // with this hack in place the user can open a dependency graph before the first set of results arrives // and see the graph structure with no values. without this hack the graph would be completely empty. // it only works if this class is running in the same VM as the engine // // if the engine and the web components are in a different VM then compiledViewDef won't be an instance of // CompiledViewDefinitionWithGraphs and the hack won't work. in that case the view cycle will be empty and the // user won't see a dependency graph if this is called before the first set of results arrives. // as soon as the first set of results arrives it will work the same as if all the components are in the same VM CompiledViewDefinitionWithGraphs viewDef; if (compiledViewDef instanceof CompiledViewDefinitionWithGraphs) { viewDef = (CompiledViewDefinitionWithGraphs) compiledViewDef; } else { viewDef = cycle.getCompiledViewDefinition(); } ValueSpecification rootValueSpecification = valueMappings.getValueSpecification(calcConfigName, rootValueRequirement); DependencyGraphExplorer depGraphExplorer = viewDef.getDependencyGraphExplorer(calcConfigName); DependencyNode rootNode = depGraphExplorer.getNodeProducing(rootValueSpecification); _functions = functions; _valueMappings = valueMappings; AnalyticsNode node = (rootNode != null) ? createNode(rootValueSpecification, rootNode, true) : null; _structure = new DependencyGraphGridStructure(node, calcConfigName, _valueSpecifications, _fnNames, targetResolver); } private String getFunctionName(final String functionId) { final FunctionDefinition function = _functions.getFunction(functionId); if (function != null) { return function.getShortName(); } else { return functionId; } } /** * Builds the tree structure of the graph starting at a node and working up the dependency graph through all the nodes it depends on. Recursively builds up the node structure representing whole the * dependency graph. * * @param valueSpecification The value specification of the target that is the current root * @param targetNode The node producing {@code valueSpec}, not null * @param rootNode Whether the value specification is for the root node of the dependency graph * @return Root node of the grid structure representing the dependency graph for the value */ private AnalyticsNode createNode(ValueSpecification valueSpecification, DependencyNode targetNode, boolean rootNode) { _valueSpecifications.add(valueSpecification); _fnNames.add(getFunctionName(targetNode.getFunction().getFunctionId())); int nodeStart = _lastRow; List<AnalyticsNode> nodes = Lists.newArrayList(); final int inputCount = targetNode.getInputCount(); if (inputCount == 0) { if (rootNode) { // the root node should never be null even if it has no children return new AnalyticsNode(nodeStart, _lastRow, Collections.<AnalyticsNode>emptyList(), false); } else { // non-root leaf nodes don't need a node of their own, their place in the structure is handled by their parent return null; } } else { for (int i = 0; i < inputCount; i++) { ++_lastRow; AnalyticsNode newNode = createNode(targetNode.getInputValue(i), targetNode.getInputNode(i), false); if (newNode != null) { nodes.add(newNode); } } return new AnalyticsNode(nodeStart, _lastRow, Collections.unmodifiableList(nodes), false); } } /** * @return The grid structure */ /* package */DependencyGraphGridStructure getStructure() { return _structure; } }