/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame.graph;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.opengamma.sesame.OutputName;
import com.opengamma.sesame.config.FunctionModelConfig;
import com.opengamma.sesame.config.NonPortfolioOutput;
import com.opengamma.sesame.config.ViewColumn;
import com.opengamma.sesame.config.ViewConfig;
import com.opengamma.sesame.function.AvailableImplementations;
import com.opengamma.sesame.function.AvailableOutputs;
import com.opengamma.sesame.function.FunctionMetadata;
import com.opengamma.sesame.function.NoOutputFunction;
import com.opengamma.sesame.graph.convert.ArgumentConverter;
import com.opengamma.sesame.graph.convert.DefaultArgumentConverter;
import com.opengamma.util.ArgumentChecker;
/**
* Builder for a graph.
*/
public final class GraphBuilder {
/** Logger. */
private static final Logger s_logger = LoggerFactory.getLogger(GraphBuilder.class);
private final AvailableOutputs _availableOutputs;
private final FunctionModelConfig _defaultConfig;
private final NodeDecorator _nodeDecorator;
private final FunctionModelConfig _defaultImplementations;
private final Set<Class<?>> _availableComponents;
private final ArgumentConverter _argumentConverter = new DefaultArgumentConverter(); // should this be an argument?
public GraphBuilder(AvailableOutputs availableOutputs,
AvailableImplementations availableImplementations,
Set<Class<?>> availableComponents,
FunctionModelConfig defaultConfig,
NodeDecorator nodeDecorator) {
_availableOutputs = ArgumentChecker.notNull(availableOutputs, "functionRepo");
_availableComponents = ArgumentChecker.notNull(availableComponents, "availableComponents");
_defaultConfig = ArgumentChecker.notNull(defaultConfig, "defaultConfig");
_nodeDecorator = ArgumentChecker.notNull(nodeDecorator, "nodeDecorator");
// TODO should this be an argument?
_defaultImplementations = new FunctionModelConfig(availableImplementations.getDefaultImplementations());
}
//-------------------------------------------------------------------------
/**
* Builds a model of the functions needed to calculate view outputs for a set of input types.
*
* @param viewConfig the configuration to use, not null
* @param inputTypes the types of the inputs to the calculations, e.g. trades, positions, securities
* @return the model, not null
*/
public GraphModel build(ViewConfig viewConfig, Set<Class<?>> inputTypes) {
ArgumentChecker.notNull(viewConfig, "viewConfig");
ArgumentChecker.notNull(inputTypes, "inputTypes");
ImmutableMap.Builder<String, Map<Class<?>, FunctionModel>> builder = ImmutableMap.builder();
FunctionModelConfig modelConfig = viewConfig.getDefaultConfig();
FunctionModelConfig defaultConfig = modelConfig.mergedWith(_defaultConfig, _defaultImplementations);
for (ViewColumn column : viewConfig.getColumns()) {
Map<Class<?>, FunctionModel> functions = Maps.newHashMap();
for (Class<?> inputType : inputTypes) {
// if we need to support stateful functions this is the place to do it.
// the FunctionModel could flag if its tree contains any functions annotated as @Stateful and
// it wouldn't be eligible for sharing with other inputs
// would need to key on input ID instead of type. would need to assign ID for in-memory trades
FunctionModel existingFunction = functions.get(inputType);
OutputName outputName = column.getOutputName(inputType);
FunctionMetadata function =
outputName != null ?
_availableOutputs.getOutputFunction(outputName, inputType) :
null;
if (existingFunction == null && function != null) {
FunctionModelConfig columnConfig = column.getFunctionConfig(inputType);
FunctionModelConfig config = columnConfig.mergedWith(defaultConfig);
FunctionModel functionModel =
FunctionModel.forFunction(function, config, _availableComponents, _nodeDecorator, _argumentConverter);
functions.put(inputType, functionModel);
if (s_logger.isDebugEnabled()) {
s_logger.debug("created function for {}/{}\n{}",
column.getName(), inputType.getSimpleName(), functionModel.prettyPrint());
}
} else {
if (outputName != null) {
s_logger.warn("No function registered that can produce output '{}' for input type {}, column '{}'",
outputName.getName(), inputType.getSimpleName(), column.getName());
} else {
s_logger.warn("No function registered that can produce output for input type {}, column '{}'",
inputType.getSimpleName(), column.getName());
}
functions.put(inputType, FunctionModel.forFunction(NoOutputFunction.METADATA));
}
}
builder.put(column.getName(), Collections.unmodifiableMap(functions));
}
// build the function models for non-portfolio outputs
ImmutableMap.Builder<String, FunctionModel> nonPortfolioFunctionModels = ImmutableMap.builder();
for (NonPortfolioOutput output : viewConfig.getNonPortfolioOutputs()) {
OutputName outputName = output.getOutput().getOutputName();
FunctionMetadata function = _availableOutputs.getOutputFunction(outputName);
if (function != null) {
FunctionModelConfig functionModelConfig = output.getOutput().getFunctionModelConfig();
FunctionModelConfig config = functionModelConfig.mergedWith(defaultConfig);
FunctionModel functionModel =
FunctionModel.forFunction(function, config, _availableComponents, _nodeDecorator, _argumentConverter);
nonPortfolioFunctionModels.put(output.getName(), functionModel);
if (s_logger.isDebugEnabled()) {
s_logger.debug("created function for {}/{}\n{}", output.getName(), functionModel.prettyPrint());
}
} else {
nonPortfolioFunctionModels.put(output.getName(), FunctionModel.forFunction(NoOutputFunction.METADATA));
s_logger.warn("Failed to find function to provide output named {}", outputName);
}
}
return new GraphModel(builder.build(), nonPortfolioFunctionModels.build());
}
}