/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.integration.marketdata.manipulator.dsl; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import com.google.common.collect.ArrayTable; import com.google.common.collect.ContiguousSet; import com.google.common.collect.DiscreteDomain; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Range; import com.google.common.collect.Sets; import com.google.common.collect.Table; import com.opengamma.core.position.PortfolioNode; import com.opengamma.core.position.Position; import com.opengamma.core.position.Trade; import com.opengamma.core.position.impl.PortfolioMapper; import com.opengamma.core.position.impl.PortfolioMapperFunction; import com.opengamma.engine.value.ComputedValueResult; import com.opengamma.engine.value.ValueProperties; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.engine.view.ViewCalculationConfiguration; import com.opengamma.engine.view.ViewResultEntry; import com.opengamma.engine.view.ViewResultModel; import com.opengamma.engine.view.compilation.CompiledViewCalculationConfiguration; import com.opengamma.engine.view.compilation.CompiledViewDefinition; import com.opengamma.id.ObjectId; import com.opengamma.id.UniqueIdentifiable; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.tuple.Pair; /** * Builds {@link SimpleResultModel} instances from results calculated by the engine. */ public class SimpleResultBuilder { private final Map<ObjectId, Integer> _idToIndex; private final Map<ColumnSpec, Integer> _colToIndex; private final CompiledViewDefinition _compiledViewDef; private final List<String> _columnNames; private final List<UniqueIdentifiable> _targets; public SimpleResultBuilder(CompiledViewDefinition compiledViewDef) { _compiledViewDef = ArgumentChecker.notNull(compiledViewDef, "compiledViewDef"); PortfolioMapperFunction<List<UniqueIdentifiable>> mapperFn = new PortfolioMapperFunction<List<UniqueIdentifiable>>() { @Override public List<UniqueIdentifiable> apply(PortfolioNode node) { return Collections.<UniqueIdentifiable>singletonList(node); } @Override public List<UniqueIdentifiable> apply(PortfolioNode parent, Position position) { List<UniqueIdentifiable> targets = Lists.<UniqueIdentifiable>newArrayList(position); for (Trade trade : position.getTrades()) { targets.add(trade); } return targets; } }; List<UniqueIdentifiable> targets = PortfolioMapper.flatMap(compiledViewDef.getPortfolio().getRootNode(), mapperFn); _idToIndex = Maps.newHashMapWithExpectedSize(targets.size()); int rowIndex = 0; for (UniqueIdentifiable target : targets) { _idToIndex.put(target.getUniqueId().getObjectId(), rowIndex++); } //--------------------------------------------------- Collection<ViewCalculationConfiguration> calcConfigs = compiledViewDef.getViewDefinition().getAllCalculationConfigurations(); Set<ColumnSpec> columns = Sets.newLinkedHashSet(); for (ViewCalculationConfiguration calcConfig : calcConfigs) { for (ViewCalculationConfiguration.Column column : calcConfig.getColumns()) { columns.add(new ColumnSpec(calcConfig.getName(), column.getValueName(), column.getProperties(), column.getHeader())); } for (Pair<String, ValueProperties> output : calcConfig.getAllPortfolioRequirements()) { String header; if (calcConfigs.size() == 1) { // if there's only 1 calc config then use the value name as the column header header = output.getFirst(); } else { // if there are multiple calc configs need to include the calc config name header = calcConfig.getName() + "/" + output.getFirst(); } columns.add(new ColumnSpec(calcConfig.getName(), output.getFirst(), output.getSecond(), header)); } } _colToIndex = Maps.newHashMapWithExpectedSize(columns.size()); List<String> columnNames = Lists.newArrayListWithCapacity(columns.size()); int colIndex = 0; for (ColumnSpec column : columns) { _colToIndex.put(column, colIndex++); columnNames.add(column._header); } _targets = Collections.unmodifiableList(targets); _columnNames = Collections.unmodifiableList(columnNames); } /** * Builds a {@link ScenarioResultModel} from the data calculated in a single cycle. * @param resultModel The results calculated by the engine in a single calculation cycle * @return A simple result model built from the results */ public SimpleResultModel build(ViewResultModel resultModel) { return build(resultModel, _columnNames); } /** * Builds a {@link ScenarioResultModel} from the data calculated in a single cycle. * @param resultModel the results calculated by the engine in a single calculation cycle * @param columnNames column name overrides * @return A simple result model built from the results * @throws IllegalArgumentException if the number of column names doesn't match the number of columns */ public SimpleResultModel build(ViewResultModel resultModel, List<String> columnNames) { ArgumentChecker.notNull(columnNames, "columnNames"); ArgumentChecker.notNull(resultModel, "resultModel"); if (columnNames.size() != _columnNames.size()) { throw new IllegalArgumentException("Wrong number of column names. expected: " + _columnNames.size() + ", actual: " + columnNames.size()); } int rowCount = _idToIndex.size(); int colCount = columnNames.size(); ContiguousSet<Integer> rowIndices = ContiguousSet.create(Range.closedOpen(0, rowCount), DiscreteDomain.integers()); ContiguousSet<Integer> colIndices = ContiguousSet.create(Range.closedOpen(0, colCount), DiscreteDomain.integers()); Table<Integer, Integer, Object> table = ArrayTable.create(rowIndices, colIndices); for (ViewResultEntry entry : resultModel.getAllResults()) { String calcConfigName = entry.getCalculationConfiguration(); ComputedValueResult value = entry.getComputedValue(); ValueSpecification valueSpec = value.getSpecification(); CompiledViewCalculationConfiguration calcConfig = _compiledViewDef.getCompiledCalculationConfiguration(calcConfigName); Set<ValueRequirement> valueReqs = calcConfig.getTerminalOutputSpecifications().get(valueSpec); for (ValueRequirement valueReq : valueReqs) { ColumnSpec colSpec = new ColumnSpec(calcConfigName, valueReq.getValueName(), valueReq.getConstraints()); Integer colIndex = _colToIndex.get(colSpec); Integer rowIndex = _idToIndex.get(valueReq.getTargetReference().getSpecification().getUniqueId().getObjectId()); // there won't be a row or column index for specific outputs (e.g. curves) if (colIndex != null && rowIndex != null) { table.put(rowIndex, colIndex, value); } // TODO handle specific outputs } } return new SimpleResultModel(_targets, columnNames, table, resultModel.getViewCycleExecutionOptions()); } private static final class ColumnSpec { /** Name of the calculation configuration that produces the column data. */ private final String _calcConfigName; /** Value name of the column's data. */ private final String _valueName; /** Value properties used when calculating the column's data. */ private final ValueProperties _valueProperties; /** Column header. */ private final String _header; private ColumnSpec(String calcConfigName, String valueName, ValueProperties valueProperties, String header) { _calcConfigName = calcConfigName; _valueName = valueName; _valueProperties = valueProperties; _header = header; } public ColumnSpec(String calcConfigName, String valueName, ValueProperties properties) { this(calcConfigName, valueName, properties, null); } // header is deliberately ignored for the purposes of equals and hashCode @Override public int hashCode() { return Objects.hash(_calcConfigName, _valueName, _valueProperties); } // header is deliberately ignored for the purposes of equals and hashCode @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final ColumnSpec other = (ColumnSpec) obj; return Objects.equals(this._calcConfigName, other._calcConfigName) && Objects.equals(this._valueName, other._valueName) && Objects.equals(this._valueProperties, other._valueProperties); } } }