/**
* 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 java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.opengamma.DataNotFoundException;
import com.opengamma.core.change.ChangeManager;
import com.opengamma.core.change.DummyChangeManager;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetResolver;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.config.FunctionRepositoryFactory;
import com.opengamma.engine.management.ValueMappings;
import com.opengamma.engine.target.ComputationTargetSpecificationResolver;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.target.resolver.ObjectResolver;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.view.compilation.CompiledViewDefinition;
import com.opengamma.engine.view.cycle.ViewCycle;
import com.opengamma.id.VersionCorrection;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.tuple.Pair;
import com.opengamma.web.analytics.formatting.TypeFormatter;
/**
* Grid for displaying analytics data for a portfolio or for calculated values that aren't associated with the portfolio (primitives). This class isn't thread safe.
*/
/* package */abstract class MainAnalyticsGrid<T extends MainGridViewport> extends AnalyticsGrid<T> {
private static final Logger s_logger = LoggerFactory.getLogger(MainAnalyticsGrid.class);
/** Type of data in the grid, portfolio or primitives. */
private final AnalyticsView.GridType _gridType;
/** Dependency graph grids for cells in this grid, keyed by grid ID. */
private final Map<Integer, DependencyGraphGrid> _depGraphs = Maps.newHashMap();
/** For looking up calculation targets using their specifications. */
private final ComputationTargetResolver _targetResolver;
/** For lookup up function metadata */
private final FunctionRepositoryFactory _functions;
/** The calculation cycle used to calculate the most recent set of results. */
private ViewCycle _cycle = EmptyViewCycle.INSTANCE;
/* package */MainAnalyticsGrid(AnalyticsView.GridType gridType, String gridId, ComputationTargetResolver targetResolver, FunctionRepositoryFactory functions,
ViewportListener viewportListener) {
super(viewportListener, gridId);
ArgumentChecker.notNull(gridType, "gridType");
ArgumentChecker.notNull(targetResolver, "targetResolver");
ArgumentChecker.notNull(functions, "functions");
_gridType = gridType;
_targetResolver = targetResolver;
_functions = functions;
}
/* package */MainAnalyticsGrid(AnalyticsView.GridType gridType, MainAnalyticsGrid<T> previousGrid, CompiledViewDefinition compiledViewDef, ValueMappings valueMappings) {
super(previousGrid.getViewportListener(), previousGrid.getCallbackId(), previousGrid.getViewports());
ArgumentChecker.notNull(gridType, "gridType");
_gridType = gridType;
_targetResolver = previousGrid.getTargetResolver();
_functions = previousGrid.getFunctionRepository();
// reopen existing dependency graphs using the value requirements from the depgraph grid structures
for (Map.Entry<Integer, DependencyGraphGrid> entry : previousGrid._depGraphs.entrySet()) {
openDependencyGraph(entry.getKey(), entry.getValue(), compiledViewDef, valueMappings);
}
}
/**
* Updates the data in the viewports of the main grid and all dependency graph grids when new results arrive from the calculation engine.
*
* @param cache Cache of calculation results
* @param cycle Calculation cycle that calculated the latest results
* @return List of IDs specifying the viewports whose data has changed as a result of the new update
*/
/* package */List<String> updateResults(ResultsCache cache, ViewCycle cycle) {
_cycle = cycle;
List<String> updatedIds = Lists.newArrayList();
for (MainGridViewport viewport : getViewports().values()) {
viewport.updateResults(cache);
if (viewport.getState() == Viewport.State.FRESH_DATA) {
updatedIds.add(viewport.getCallbackId());
}
}
for (DependencyGraphGrid grid : _depGraphs.values()) {
updatedIds.addAll(grid.updateResults(cycle, cache));
}
return updatedIds;
}
// -------- dependency graph grids --------
/**
* Opens a dependency graph grid showing the steps used to calculate a cell's value. This variant is intended for clients to use when first opening a dependency graph.
*
* @param graphId Unique ID of the dependency graph
* @param gridId ID passed to listeners when the grid's row and column structure changes, this can be any unique value
* @param row Row index of the cell whose dependency graph is required
* @param col Column index of the cell whose dependency graph is required
* @param compiledViewDef Compiled view definition containing the full dependency graph
* @param viewportListener Receives notification when there are changes to a viewport TODO should include a version ID for the structure to avoid race condition when the structure is updated
*/
/* package */void openDependencyGraph(int graphId, String gridId, int row, int col, CompiledViewDefinition compiledViewDef, ViewportListener viewportListener) {
if (_depGraphs.containsKey(graphId)) {
throw new IllegalArgumentException("Dependency graph ID " + graphId + " is already in use");
}
Pair<String, ValueRequirement> targetForCell = getGridStructure().getValueRequirementForCell(row, col);
if (targetForCell == null) {
throw new DataNotFoundException("No dependency graph is available for row " + row + ", col " + col);
}
String calcConfigName = targetForCell.getFirst();
ValueRequirement valueRequirement = targetForCell.getSecond();
DependencyGraphGrid grid = DependencyGraphGrid.create(compiledViewDef, valueRequirement, calcConfigName, _cycle, gridId, _targetResolver, getFunctionRepository(), viewportListener,
getGridStructure().getValueMappings());
_depGraphs.put(graphId, grid);
}
/**
* Opens a dependency graph grid showing the steps used to calculate a cell's value. This variant is intended for clients to use when reconnecting after a server restart.
*
* @param graphId Unique ID of the dependency graph
* @param gridId ID passed to listeners when the grid's row and column structure changes, this can be any unique value
* @param calcConfigName Name of the calculation configuration containing the value
* @param valueRequirement value requirement of target cell
* @param compiledViewDef Compiled view definition containing the full dependency graph
* @param viewportListener Receives notification when there are changes to a viewport
*/
/* package */void openDependencyGraph(int graphId, String gridId, String calcConfigName, ValueRequirement valueRequirement, CompiledViewDefinition compiledViewDef,
ViewportListener viewportListener) {
if (_depGraphs.containsKey(graphId)) {
throw new IllegalArgumentException("Dependency graph ID " + graphId + " is already in use");
}
DependencyGraphGrid grid = DependencyGraphGrid.create(compiledViewDef, valueRequirement, calcConfigName, _cycle, gridId, _targetResolver, getFunctionRepository(), viewportListener,
getGridStructure().getValueMappings());
_depGraphs.put(graphId, grid);
}
/**
* TODO specify what this is intended for Opens a dependency graph grid showing the steps used to calculate a cell's value.
*
* @param graphId Unique ID of the dependency graph
* @param previousGrid Previous version of the same grid, created with the previous version of the view definition
* @param compiledViewDef Compiled view definition containing the full dependency graph
*/
private void openDependencyGraph(int graphId, DependencyGraphGrid previousGrid, CompiledViewDefinition compiledViewDef, ValueMappings valueMappings) {
s_logger.debug("Creating new version of dependency graph grid {}", previousGrid.getCallbackId());
DependencyGraphGridStructure structure = previousGrid.getGridStructure();
String calcConfigName = structure.getCalculationConfigurationName();
DependencyGraphGrid grid = DependencyGraphGrid.create(compiledViewDef, previousGrid.getTargetValueRequirement(), calcConfigName, _cycle, previousGrid.getCallbackId(), _targetResolver,
getFunctionRepository(), previousGrid.getViewportListener(), valueMappings);
// empty invalid viewport which can never be used to create data
// the client will update it before it produces data
ViewportDefinition viewportDefinition = new RectangularViewportDefinition(-1, Collections.<Integer>emptyList(), Collections.<Integer>emptyList(), TypeFormatter.Format.CELL, false);
// the cache can be empty because we can guarantee the viewport is always empty
ResultsCache emptyCache = new ResultsCache();
for (Map.Entry<Integer, DependencyGraphViewport> entry : previousGrid.getViewports().entrySet()) {
Integer id = entry.getKey();
DependencyGraphViewport viewport = entry.getValue();
grid.createViewport(id, viewport.getCallbackId(), viewport.getStructureCallbackId(), viewportDefinition, emptyCache);
}
_depGraphs.put(graphId, grid);
}
/**
* Returns an existing dependency graph grid.
*
* @param graphId ID of the dependency graph
* @return The dependency graph grid
* @throws DataNotFoundException If no dependency graph exists with the specified ID
*/
private DependencyGraphGrid getDependencyGraph(int graphId) {
DependencyGraphGrid grid = _depGraphs.get(graphId);
if (grid == null) {
throw new DataNotFoundException("No dependency graph found with ID " + graphId + " for " + _gridType + " grid");
}
return grid;
}
/* package */Map<Integer, DependencyGraphGrid> getDependencyGraphs() {
return _depGraphs;
}
/**
* Closes an existing dependency graph grid.
*
* @param graphId ID of the dependency graph
* @throws DataNotFoundException If no dependency graph exists with the specified ID
*/
/* package */void closeDependencyGraph(int graphId) {
AnalyticsGrid<?> grid = _depGraphs.remove(graphId);
if (grid == null) {
throw new DataNotFoundException("No dependency graph found with ID " + graphId + " for " + _gridType + " grid");
}
}
/**
* Returns the grid structure for a dependency graph.
*
* @param graphId ID of the dependency graph
* @return The grid structure of the specified dependency graph
* @throws DataNotFoundException If no dependency graph exists with the specified ID
*/
/* package */DependencyGraphGridStructure getGridStructure(int graphId) {
return getDependencyGraph(graphId).getGridStructure();
}
/**
* Returns the grid structure for a dependency graph.
*
* @param graphId ID of the dependency graph
* @param viewportId ID of the dependency graph
* @return The grid structure of the specified dependency graph
* @throws DataNotFoundException If no dependency graph exists with the specified ID
*/
/* package */DependencyGraphGridStructure getGridStructure(int graphId, int viewportId) {
return (DependencyGraphGridStructure) getDependencyGraph(graphId).getViewport(viewportId).getGridStructure();
}
/**
* Creates a viewport on a dependency graph grid.
*
* @param graphId the ID of the dependency graph
* @param viewportId the ID of the viewport, can be any unique value
* @param callbackId the ID passed to listeners when the viewport's data changes, can be any unique value
* @param structureCallbackId the ID passed to listeners when the viewport's structure changes, can be any unique value
* @param viewportDefinition the definition of the viewport
* @param cache the cache
* @return true if there is data available for the new viewport
*/
/* package */boolean createViewport(int graphId, int viewportId, String callbackId, String structureCallbackId, ViewportDefinition viewportDefinition, ResultsCache cache) {
return getDependencyGraph(graphId).createViewport(viewportId, callbackId, structureCallbackId, viewportDefinition, cache);
}
@Override
abstract T createViewport(ViewportDefinition viewportDefinition, String callbackId, String structureCallbackId, ResultsCache cache);
/**
* Updates an existing viewport on a dependency graph grid
*
* @param graphId the ID of the dependency graph
* @param viewportId the ID of the viewport, can be any unique value
* @param viewportDefinition the definition of the viewport
* @param cache the cache
* @return the viewport's callback ID if it has data available, null if not
* @throws DataNotFoundException If no dependency graph exists with the specified ID
*/
/* package */String updateViewport(int graphId, int viewportId, ViewportDefinition viewportDefinition, ResultsCache cache) {
return getDependencyGraph(graphId).updateViewport(viewportId, viewportDefinition, cache);
}
/**
* Deletes an existing viewport on a dependency graph grid.
*
* @param graphId ID of the dependency graph
* @param viewportId ID of the viewport, can be any unique value
* @throws DataNotFoundException If no dependency graph exists with the specified ID
*/
/* package */void deleteViewport(int graphId, int viewportId) {
getDependencyGraph(graphId).deleteViewport(viewportId);
}
/**
* Returns the data for a viewport on a dependency graph grid.
*
* @param graphId ID of the dependency graph
* @param viewportId ID of the viewport, can be any unique value
* @return The current data for the viewport
* @throws DataNotFoundException If no dependency graph exists with the specified ID
*/
/* package */ViewportResults getData(int graphId, int viewportId) {
return getDependencyGraph(graphId).getData(viewportId);
}
/**
* @return The IDs for all dependency graph grids that are sent to listeners when the grid structure changes
*/
/* package */List<String> getDependencyGraphCallbackIds() {
List<String> gridIds = Lists.newArrayList();
for (AnalyticsGrid<?> grid : _depGraphs.values()) {
gridIds.add(grid.getCallbackId());
}
return gridIds;
}
/**
* @return The row and column structure of the main grid
*/
@Override
abstract MainGridStructure getGridStructure();
@Override
protected ViewCycle getViewCycle() {
return _cycle;
}
/** For looking up calculation targets using their specifications. */
ComputationTargetResolver getTargetResolver() {
return _targetResolver;
}
/** For lookup up function metadata based on the function identifier */
/* package */FunctionRepositoryFactory getFunctionRepository() {
return _functions;
}
/**
* Resolver that doesn't resolve anything, used for grids that will always be empty.
*/
protected static class DummyTargetResolver implements ComputationTargetResolver {
@Override
public ComputationTarget resolve(final ComputationTargetSpecification specification, final VersionCorrection versionCorrection) {
return null;
}
@Override
public ObjectResolver<?> getResolver(final ComputationTargetSpecification specification) {
return null;
}
@Override
public ComputationTargetType simplifyType(final ComputationTargetType type) {
return type;
}
@Override
public SecuritySource getSecuritySource() {
return null;
}
@Override
public ComputationTargetSpecificationResolver getSpecificationResolver() {
throw new UnsupportedOperationException();
}
@Override
public AtVersionCorrection atVersionCorrection(final VersionCorrection versionCorrection) {
throw new UnsupportedOperationException();
}
@Override
public ChangeManager changeManager() {
return DummyChangeManager.INSTANCE;
}
}
}