/**
* 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.Collection;
import java.util.Collections;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetResolver;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.target.ComputationTargetTypeMap;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.engine.view.AggregatedExecutionLog;
import com.opengamma.id.UniqueId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Pairs;
import com.opengamma.web.analytics.formatting.TypeFormatter;
/**
* Row and column structure for a grid that displays the dependency graph used when calculating a value.
* Each row contains one calculated value from the results, all other columns in the row contain metadata about
* the value.
*/
public class DependencyGraphGridStructure implements GridStructure {
/** Index of the target column */
private static final int TARGET_COL = 0;
/** Index of the target type column */
private static final int TARGET_TYPE_COL = 1;
/** Index of the value name column */
private static final int VALUE_NAME_COL = 2;
/** Index of the value column */
private static final int VALUE_COL = 3;
/** Index of the function name column */
private static final int FUNCTION_NAME_COL = 4;
/** Index of the value properties column */
private static final int PROPERTIES_COL = 5;
/** Map of target types to displayable names. */
private static final ComputationTargetTypeMap<String> TARGET_TYPE_NAMES = createTargetTypeNames();
/** {@link ValueSpecification}s for all rows in the grid in row index order. */
private final List<ValueSpecification> _valueSpecifications;
/** Function names for all rows in the grid in row index order. */
private final List<String> _fnNames;
/** For looking up calculation targets using their specification. */
private final ComputationTargetResolver _computationTargetResolver;
/** The root node of the tree structure representing the rows. */
private final AnalyticsNode _root;
/** The calculation configuration name. */
private final String _calcConfigName;
/** The columns in the grid. */
private final GridColumnGroups _columnGroups;
/** The fixed column structure. */
private final GridColumnGroup _fixedColumnGroup;
/** The non fixed column structure. */
private final GridColumnGroups _nonFixedColumnGroups;
/* package */ DependencyGraphGridStructure(AnalyticsNode root,
String calcConfigName,
List<ValueSpecification> valueSpecifications,
List<String> fnNames,
ComputationTargetResolver targetResolver) {
ArgumentChecker.notNull(valueSpecifications, "valueSpecifications");
ArgumentChecker.notNull(fnNames, "fnNames");
ArgumentChecker.notNull(targetResolver, "targetResolver");
_root = root;
_calcConfigName = calcConfigName;
_valueSpecifications = Collections.unmodifiableList(valueSpecifications);
_fnNames = Collections.unmodifiableList(fnNames);
_computationTargetResolver = targetResolver;
// fixed column group with one column for the row label
_fixedColumnGroup = new GridColumnGroup("", ImmutableList.<GridColumn>of(column("Target", 0)), false);
// non-fixed columns
GridColumnGroup nonFixedColumnGroup = new GridColumnGroup("", ImmutableList.<GridColumn>of(
column("Type", 1),
column("Value Name", 2),
column("Value", null, 3),
column("Function", 4),
column("Properties", ValueProperties.class, 5)),
false);
_nonFixedColumnGroups = new GridColumnGroups(nonFixedColumnGroup);
_columnGroups = new GridColumnGroups(ImmutableList.of(_fixedColumnGroup, nonFixedColumnGroup));
}
/**
* Returns the value specifications used to calculate the values in the grid.
* @return The value specifications used to calculate the values
*/
/* package */ List<ValueSpecification> getValueSpecifications() {
return _valueSpecifications;
}
/**
* Builds the results for a viewport.
*
* @param viewportDefinition Defines the viewport
* @param cache Cache of results for the grid
* @param previousResults The results before the latest calculation cycle, possibly null
* @return The results for the cells in the viewport and the new viewport state
*/
/* package */ Pair<ViewportResults, Viewport.State> createResults(ViewportDefinition viewportDefinition,
ResultsCache cache,
ViewportResults previousResults) {
List<ResultsCell> results = Lists.newArrayList();
for (GridCell cell : viewportDefinition) {
GridColumn column = _columnGroups.getColumn(cell.getColumn());
results.add(column.buildResults(cell.getRow(), cell.getFormat(), cache));
}
ViewportResults newResults = new ViewportResults(results,
viewportDefinition,
_columnGroups,
cache.getLastCalculationDuration(), cache.getValuationTime());
Viewport.State state;
if (previousResults != null && results.equals(previousResults.getResults())) {
state = Viewport.State.STALE_DATA;
} else {
state = Viewport.State.FRESH_DATA;
}
return Pairs.of(newResults, state);
}
/**
*
* @param header The column header string
* @param colIndex The column index
* @return A column for displaying a string value
*/
private GridColumn column(String header, int colIndex) {
return column(header, String.class, colIndex);
}
/**
*
* @param header The column header string
* @param type The type of value the column contains
* @param colIndex The column index
* @return A column for displaying values of the specified type
*/
private GridColumn column(String header, Class<?> type, int colIndex) {
DependencyGraphCellRenderer renderer = new DependencyGraphCellRenderer(colIndex,
_valueSpecifications,
_fnNames,
_computationTargetResolver,
_calcConfigName);
return new GridColumn(header, header, type, renderer);
}
@Override
public int getRowCount() {
return _valueSpecifications.size();
}
@Override
public int getColumnCount() {
return _columnGroups.getColumnCount();
}
@Override
public GridColumnGroups getColumnStructure() {
return _columnGroups;
}
@Override
public GridColumnGroup getFixedColumns() {
return _fixedColumnGroup;
}
@Override
public GridColumnGroups getNonFixedColumns() {
return _nonFixedColumnGroups;
}
@Override
public Pair<String, ValueRequirement> getValueRequirementForCell(int row, int col) {
// there is no value requirement available here
return null;
}
public Pair<String, ValueSpecification> getValueSpecificationForCell(int row, int col) {
if (_calcConfigName == null || col != VALUE_COL) {
return null;
}
ValueSpecification valueSpec = _valueSpecifications.get(row);
return valueSpec != null ? Pairs.of(_calcConfigName, valueSpec) : null;
}
/**
* @return The root of the node structure representing the dependency graph, possibly null
*/
public AnalyticsNode getRootNode() {
return _root;
}
private static ComputationTargetTypeMap<String> createTargetTypeNames() {
ComputationTargetTypeMap<String> map = new ComputationTargetTypeMap<>();
map.put(ComputationTargetType.PORTFOLIO_NODE, "Agg");
map.put(ComputationTargetType.POSITION, "Pos");
map.put(ComputationTargetType.SECURITY, "Sec");
map.put(ComputationTargetType.ANYTHING, "Prim");
map.put(ComputationTargetType.NULL, "Prim");
map.put(ComputationTargetType.TRADE, "Trade");
return map;
}
/**
* @return The name of the calculation config containing the dependency graph's root value.
*/
public String getCalculationConfigurationName() {
return _calcConfigName;
}
/**
* Renderer for cells in the dependency graph grid.
*/
private static final class DependencyGraphCellRenderer implements GridColumn.CellRenderer {
/** Index of the renderer's column. */
private final int _colIndex;
/** {@link ValueSpecification}s for each row in the grid. */
private final List<ValueSpecification> _valueSpecs;
/** Names of the functions used to calculate each row in the grid. */
private final List<String> _fnNames;
/** For looking up calculation targets using their specification. */
private final ComputationTargetResolver _computationTargetResolver;
/** The calculation configuration name. */
private final String _calcConfigName;
private DependencyGraphCellRenderer(int colIndex,
List<ValueSpecification> valueSpecs,
List<String> fnNames,
ComputationTargetResolver computationTargetResolver,
String calcConfigName) {
ArgumentChecker.notNull(valueSpecs, "valueSpecs");
ArgumentChecker.notNull(fnNames, "fnNames");
ArgumentChecker.notNull(computationTargetResolver, "computationTargetResolver");
ArgumentChecker.notNull(calcConfigName, "calcConfigName");
_calcConfigName = calcConfigName;
_computationTargetResolver = computationTargetResolver;
_colIndex = colIndex;
_valueSpecs = valueSpecs;
_fnNames = fnNames;
}
@Override
public ResultsCell getResults(int rowIndex,
TypeFormatter.Format format,
ResultsCache cache,
Class<?> columnType,
Object inlineKey) {
ValueSpecification valueSpec = _valueSpecs.get(rowIndex);
switch (_colIndex) {
case TARGET_COL:
return ResultsCell.forStaticValue(getTargetName(valueSpec), columnType, format);
case TARGET_TYPE_COL:
return ResultsCell.forStaticValue(TARGET_TYPE_NAMES.get(valueSpec.getTargetSpecification().getType()), columnType, format);
case VALUE_NAME_COL:
return ResultsCell.forStaticValue(valueSpec.getValueName(), columnType, format);
case VALUE_COL:
ResultsCache.Result cacheResult = cache.getResult(_calcConfigName, valueSpec, null);
Collection<Object> history = cacheResult.getHistory();
Object value = cacheResult.getValue();
AggregatedExecutionLog executionLog = cacheResult.getAggregatedExecutionLog();
return ResultsCell.forCalculatedValue(value, valueSpec, history, executionLog, cacheResult.isUpdated(), columnType, format);
case FUNCTION_NAME_COL:
String fnName = _fnNames.get(rowIndex);
return ResultsCell.forStaticValue(fnName, columnType, format);
case PROPERTIES_COL:
return ResultsCell.forStaticValue(valueSpec.getProperties(), columnType, format);
default: // never happen
throw new IllegalArgumentException("Column index " + _colIndex + " is invalid");
}
}
/**
* @param valueSpec Specification of the target for a grid row
* @return The name of the target
*/
private String getTargetName(ValueSpecification valueSpec) {
final ComputationTargetSpecification targetSpec = valueSpec.getTargetSpecification();
// TODO I don't think LATEST will do long term. resolution time available on the result model
if (targetSpec.getType() == ComputationTargetType.NULL) {
return getNullTargetName(valueSpec);
} else {
ComputationTarget target = _computationTargetResolver.resolve(targetSpec, VersionCorrection.LATEST);
if (target != null) { // doubt this branch ever happens - don't think it will be executed for NULL targets.
return target.getName();
} else {
UniqueId uid = targetSpec.getUniqueId();
if (uid != null) {
return uid.toString();
} else {
return getNullTargetName(valueSpec);
}
}
}
}
private String getNullTargetName(ValueSpecification valueSpec) {
String curveName = valueSpec.getProperty(ValuePropertyNames.CURVE);
String surfaceName = valueSpec.getProperty(ValuePropertyNames.SURFACE);
if (curveName != null) {
return valueSpec.getValueName() + " [" + curveName + "]";
} else if (surfaceName != null) {
return valueSpec.getValueName() + " [" + surfaceName + "]";
} else {
return valueSpec.getValueName();
}
}
}
@Override
public String toString() {
return "DependencyGraphGridStructure [" +
", _valueSpecifications=" + _valueSpecifications +
", _fnNames=" + _fnNames +
", _computationTargetResolver=" + _computationTargetResolver +
", _root=" + _root +
", _calcConfigName='" + _calcConfigName + "'" +
", _columnGroups=" + _columnGroups +
", _fixedColumnGroup=" + _fixedColumnGroup +
", _nonFixedColumnGroups=" + _nonFixedColumnGroups +
"]";
}
}