/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.view;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Sets;
import com.opengamma.engine.function.FunctionParameters;
import com.opengamma.engine.function.resolver.ComputationTargetFilter;
import com.opengamma.engine.function.resolver.IdentityResolutionRuleTransform;
import com.opengamma.engine.function.resolver.ResolutionRuleTransform;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.id.UniqueId;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.PublicAPI;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Pairs;
/**
* The configuration for one set of calculations on a particular view.
*/
@PublicAPI
public class ViewCalculationConfiguration implements Serializable {
private static final Logger s_logger = LoggerFactory.getLogger(ViewCalculationConfiguration.class);
/**
* Dummy "security type" constant to request a value at the aggregate level only.
*/
public static final String SECURITY_TYPE_AGGREGATE_ONLY = "AGGREGATE_ONLY";
// Andrew 2011-09-02 -- Is this a good idea? Should there be a set of requirements for aggregate nodes only?
/** Serialization version. */
private static final long serialVersionUID = 1L;
private final ViewDefinition _viewDefinition;
private final String _name;
/**
* Contains the required portfolio outputs for each security type. These are the outputs produced at the position
* and aggregate position level, with respect to the reference portfolio. Accepting portfolio outputs as a set of
* strings is really just for user convenience; ValueRequirements are eventually still needed for each of these for
* every position and aggregate position in the reference portfolio.
*/
private final Map<String, Set<Pair<String, ValueProperties>>> _portfolioRequirementsBySecurityType = new TreeMap<>();
/**
* Contains any specific outputs required, where each entry really corresponds to a single output at computation
* time.
*/
private final Set<ValueRequirement> _specificRequirements = new LinkedHashSet<>();
/**
* Start with an empty delta definition which will perform simple equality comparisons. This should be customized as
* required for the view configuration.
*/
private DeltaDefinition _deltaDefinition = new DeltaDefinition();
/**
* The scenarioId to be used for this configuration
* */
private UniqueId _scenarioId;
/**
* The scenarioParametersId to be used for this configuration
* */
private UniqueId _scenarioParametersId;
/**
* A set of default properties for functions to configure themselves from. Note that these are intended to represent generic
* concepts that would typically be expressed through constraints, for example a default currency or default curve, that might
* apply to a number of functions and will affect graph construction. Information specific to a particular function to
* override its default execution behavior only (i.e. it will not affect the choice to use that function in the graph, or any
* other aspect of graph building) should be set using the {@link FunctionParameters} for that function (for example a Monte
* Carlo iteration count) - see {@link #_resolutionRuleTransform} - and not constraints or default properties.
*/
private ValueProperties _defaultProperties = ValueProperties.none();
/**
* A transformation to apply to the default resolution rules created by the view processor. Altering the resolution rules can
* affect dependency graph construction by allowing functions to be suppressed and/or priorities changed. The default parameters
* for functions can also be adjusted, either globally or using a {@link ComputationTargetFilter} to affect only a specific
* subset of the graph.
*/
private ResolutionRuleTransform _resolutionRuleTransform = IdentityResolutionRuleTransform.INSTANCE;
/**
* Defines merged outputs. These are sets of portfolio requirements which should be published under a single output
* name with common aggregates.
*/
private final List<MergedOutput> _mergedOutputs = new ArrayList<>();
/**
* Defines the labels and order of the columns used for displaying the data in the UI. This doesn't have to contain
* columns for every value name / properties combination in the configuration. Any columns that aren't defined
* will use the default label and will appear at the end in the order their requirements are defined.
*/
private List<Column> _columns = Collections.emptyList();
/**
* Constructs an instance.
*
* @param definition the parent view definition, not null
* @param name the calculation configuration name, not null
*/
public ViewCalculationConfiguration(final ViewDefinition definition, final String name) {
ArgumentChecker.notNull(definition, "Parent view definition");
ArgumentChecker.notNull(name, "Calculation configuration name");
_viewDefinition = definition;
_name = name;
}
//-------------------------------------------------------------------------
/**
* Copies this view calculation configuration to a new parent view definition, adding the copy to the new owner.
*
* @param newOwner the new parent view definition, not null
* @return
*/
public void copyTo(final ViewDefinition newOwner) {
final ViewCalculationConfiguration copy = new ViewCalculationConfiguration(newOwner, getName());
newOwner.addViewCalculationConfiguration(copy);
copy.setDefaultProperties(getDefaultProperties());
copy.addSpecificRequirements(getSpecificRequirements());
copy.setScenarioId(getScenarioId());
copy.setScenarioParametersId(getScenarioParametersId());
for (final Map.Entry<String, Set<Pair<String, ValueProperties>>> requirementEntry : getPortfolioRequirementsBySecurityType().entrySet()) {
copy.addPortfolioRequirements(requirementEntry.getKey(), requirementEntry.getValue());
}
for (final MergedOutput mergedOutput : getMergedOutputs()) {
copy.addMergedOutput(mergedOutput);
}
// REVIEW jonathan 2011-11-13 -- should really do deep copies of these to avoid references to same objects
copy.getDeltaDefinition().setNumberComparer(getDeltaDefinition().getNumberComparer());
copy.setResolutionRuleTransform(getResolutionRuleTransform());
}
/**
* @return the parent view definition, not null
*/
public ViewDefinition getViewDefinition() {
return _viewDefinition;
}
/**
* @return the name, not null
*/
public String getName() {
return _name;
}
/**
* @return the delta definition, not null
*/
public DeltaDefinition getDeltaDefinition() {
return _deltaDefinition;
}
public void setDeltaDefinition(final DeltaDefinition deltaDefinition) {
ArgumentChecker.notNull(deltaDefinition, "deltaDefinition");
_deltaDefinition = deltaDefinition;
}
/**
* Returns the default value properties for the view. Functions that expect a property constraint on values
* they are asked to produce should refer to the defaults if the constraint is absent, or use the default
* to construct the input requirements.
*
* @return the default property set
*/
public ValueProperties getDefaultProperties() {
return _defaultProperties;
}
/**
* Returns the scenarioId to be used for this configuration - if set, this will refer to a scenario defined in
* the config master.
*
* @return the scenarioId for this configuration, may be null
*/
public UniqueId getScenarioId() {
return _scenarioId;
}
/**
* Sets the scenarioId to be used for this configuration - if set, this will refer to a scenario defined in
* the config master.
*
* @param scenarioId the scenarioId for this configuration, may be null
*/
public void setScenarioId(final UniqueId scenarioId) {
_scenarioId = scenarioId;
}
/**
/**
* Returns the scenarioParametersId to be used for this configuration - if set, this will refer to a scenario
* parameters defined in the config master.
*
* @return the scenarioParametersId for this configuration, may be null
*/
public UniqueId getScenarioParametersId() {
return _scenarioParametersId;
}
/**
* Sets the scenarioParametersId to be used for this configuration - if set, this will refer to a scenario parameters
* defined in the config master.
*
* @param scenarioParametersId the scenarioParametersId for this configuration, may be null
*/
public void setScenarioParametersId(final UniqueId scenarioParametersId) {
_scenarioParametersId = scenarioParametersId;
}
/**
* Sets the default value properties for the view. Functions that expect a property constraint on values
* they are asked to produce should refer to the defaults if the constraint is absent, or use the default
* to construct the input requirements.
*
* @param defaultProperties the default properties
*/
public void setDefaultProperties(final ValueProperties defaultProperties) {
ArgumentChecker.notNull(defaultProperties, "defaultProperties");
_defaultProperties = defaultProperties;
}
/**
* Sets the transformation to use on resolution rules when compiling a view for execution under this
* configuration.
*
* @return the resolution rule transformation
*/
public ResolutionRuleTransform getResolutionRuleTransform() {
return _resolutionRuleTransform;
}
public void setResolutionRuleTransform(final ResolutionRuleTransform resolutionRuleTransform) {
ArgumentChecker.notNull(resolutionRuleTransform, "resolutionRuleTransform");
_resolutionRuleTransform = resolutionRuleTransform;
}
/**
* Gets the required portfolio outputs by security type. These are the outputs produced at the position and
* aggregate position level, with respect to the reference portfolio.
*
* @return a map of security type to the names of the required outputs for that type, not null
*/
public Map<String, Set<Pair<String, ValueProperties>>> getPortfolioRequirementsBySecurityType() {
return Collections.unmodifiableMap(_portfolioRequirementsBySecurityType);
}
/**
* Gets a set containing every portfolio output that is required, regardless of the security type(s) on which the
* output is required. These are outputs produced at the position and aggregate position level, with respect to the
* reference portfolio.
*
* @return a set of every required portfolio output, not null
*/
public Set<Pair<String, ValueProperties>> getAllPortfolioRequirements() {
final Set<Pair<String, ValueProperties>> requirements = Sets.newLinkedHashSet();
for (final Set<Pair<String, ValueProperties>> secTypeDefinitions : _portfolioRequirementsBySecurityType.values()) {
requirements.addAll(secTypeDefinitions);
}
return requirements;
}
/**
* Adds a set of required portfolio outputs for the given security type. These are outputs produced at the position
* and aggregate position level, with respect to the reference portfolio.
*
* @param securityType the type of security for which the outputs should be produced, not null
* @param requiredOutputs a set of output names and value constraints, not null
*/
public void addPortfolioRequirements(final String securityType, final Set<Pair<String, ValueProperties>> requiredOutputs) {
ArgumentChecker.notNull(securityType, "securityType");
ArgumentChecker.notNull(requiredOutputs, "requiredOutputs");
Set<Pair<String, ValueProperties>> secTypeRequirements = _portfolioRequirementsBySecurityType.get(securityType);
if (secTypeRequirements == null) {
secTypeRequirements = Sets.newLinkedHashSet();
_portfolioRequirementsBySecurityType.put(securityType, secTypeRequirements);
}
secTypeRequirements.addAll(requiredOutputs);
}
/**
* Adds a set of required portfolio outputs for the given security type with no value constraints. This is
* equivilant to calling {@link #addPortfolioRequirements (String, Set)} with
* {@code ValueProperties.none ()} against each output name.
*
* @param securityType the type of security for which the outputs should be produced, not null
* @param requiredOutputs a set of output names, not null
*/
public void addPortfolioRequirementNames(final String securityType, final Set<String> requiredOutputs) {
ArgumentChecker.notNull(securityType, "securityType");
ArgumentChecker.notNull(requiredOutputs, "requiredOutput");
for (final String requiredOutput : requiredOutputs) {
addPortfolioRequirementName(securityType, requiredOutput);
}
}
/**
* Adds a required portfolio output for the given security type. This is an output produced at the position and
* aggregate position level, with respect to the reference portfolio.
*
* @param securityType the type of security for which the output should be produced, not null
* @param requiredOutput an output name, not null
* @param constraints constraints on the requirement, not null
*/
public void addPortfolioRequirement(final String securityType, final String requiredOutput, final ValueProperties constraints) {
ArgumentChecker.notNull(securityType, "securityType");
ArgumentChecker.notNull(requiredOutput, "requiredOutput");
ArgumentChecker.notNull(constraints, "constraints");
addPortfolioRequirements(securityType, Collections.singleton(Pairs.of(requiredOutput, constraints)));
}
/**
* Adds a required portfolio output for the given security type with no value constraints. This is equivilant
* to calling {@link #addPortfolioRequirement (String, String, ValueProperties)} with {@code ValueProperties.none ()}.
*
* @param securityType the type of security for which the output should be produced, not null
* @param requiredOutput an output name, not null
*/
public void addPortfolioRequirementName(final String securityType, final String requiredOutput) {
addPortfolioRequirement(securityType, requiredOutput, ValueProperties.none());
}
/**
* Gets a set containing every specific requirement.
*
* @return a set containing every specific requirement, not null
*/
public Set<ValueRequirement> getSpecificRequirements() {
return Collections.unmodifiableSet(_specificRequirements);
}
/**
* Adds a set of required outputs. These outputs are specific in the sense that they have already been resolved into
* {@link ValueRequirement}s which reference the target for which the value is required. Such outputs would usually
* be related to the portfolio outputs in some way, for example to obtain some underlying market data that was input
* to the portfolio calculations. However, no relationship to the portfolio is required, particularly because
* the view might not reference a portfolio, and these outputs could be used to request arbitrary values.
*
* @param requirements the requirements, not null
*/
public void addSpecificRequirements(final Set<ValueRequirement> requirements) {
ArgumentChecker.notNull(requirements, "requirements");
_specificRequirements.addAll(requirements);
}
/**
* Adds a required output. This output is specific in the sense that it has already been resolved into a
* {@link ValueRequirement} which references the target for which the value is required. Such an output would usually
* be related to the portfolio outputs in some way, for example to obtain some underlying market data that was an
* input to the portfolio calculations. However, no relationship to the portfolio is required, particularly because
* the view might not reference a portfolio, and this output could be used to request an arbitrary value.
*
* @param requirement the output, not null
*/
public void addSpecificRequirement(final ValueRequirement requirement) {
ArgumentChecker.notNull(requirement, "requirement");
addSpecificRequirements(Collections.singleton(requirement));
}
/**
* Gets the list of merged outputs.
*
* @return the merged outputs, not null
*/
public List<MergedOutput> getMergedOutputs() {
return _mergedOutputs;
}
/**
* Gets the merged output with a given name.
*
* @param mergedOutputName the merged output name, not null
* @return the merged output, null if not found
*/
public MergedOutput getMergedOutput(final String mergedOutputName) {
ArgumentChecker.notNull(mergedOutputName, "mergedOutputName");
for (final MergedOutput mergedOutput : getMergedOutputs()) {
if (mergedOutput.getMergedOutputName().equals(mergedOutputName)) {
return mergedOutput;
}
}
return null;
}
/**
* Adds a new merged output.
*
* @param mergedOutput the merged output, not null
*/
public void addMergedOutput(final MergedOutput mergedOutput) {
ArgumentChecker.notNull(mergedOutput, "mergedOutput");
_mergedOutputs.add(mergedOutput);
}
/**
* Gets the list of column definitions.
*
* @return the column definitions, not null
*/
public List<Column> getColumns() {
return _columns;
}
/**
* Sets the list of column definitions.
*
* @param columns the column definitions, not null
*/
public void setColumns(final List<Column> columns) {
ArgumentChecker.notNull(columns, "columns");
_columns = columns;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.hashCode(getName());
result = prime * result + ObjectUtils.hashCode(getAllPortfolioRequirements());
result = prime * result + ObjectUtils.hashCode(getSpecificRequirements());
result = prime * result + ObjectUtils.hashCode(getDefaultProperties());
result = prime * result + ObjectUtils.hashCode(getScenarioId());
result = prime * result + ObjectUtils.hashCode(getScenarioParametersId());
result = prime * result + ObjectUtils.hashCode(getResolutionRuleTransform());
result = prime * result + ObjectUtils.hashCode(getColumns());
result = prime * result + ObjectUtils.hashCode(getMergedOutputs());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ViewCalculationConfiguration)) {
return false;
}
final ViewCalculationConfiguration other = (ViewCalculationConfiguration) obj;
if (!(ObjectUtils.equals(getName(), other.getName()) && ObjectUtils.equals(getDeltaDefinition(), other.getDeltaDefinition()) && ObjectUtils.equals(getSpecificRequirements(), other
.getSpecificRequirements()))
&& ObjectUtils.equals(_portfolioRequirementsBySecurityType.keySet(), other._portfolioRequirementsBySecurityType.keySet())) {
return false;
}
final Map<String, Set<Pair<String, ValueProperties>>> otherPortfolioRequirementsBySecurityType = other.getPortfolioRequirementsBySecurityType();
for (final Map.Entry<String, Set<Pair<String, ValueProperties>>> securityTypeRequirements : getPortfolioRequirementsBySecurityType().entrySet()) {
final Set<Pair<String, ValueProperties>> otherRequirements = otherPortfolioRequirementsBySecurityType.get(securityTypeRequirements.getKey());
if (!ObjectUtils.equals(securityTypeRequirements.getValue(), otherRequirements)) {
return false;
}
}
if (!ObjectUtils.equals(getDefaultProperties(), other.getDefaultProperties())) {
return false;
}
if (!ObjectUtils.equals(getResolutionRuleTransform(), other.getResolutionRuleTransform())) {
return false;
}
if (!ObjectUtils.equals(getScenarioId(), other.getScenarioId())) {
return false;
}
if (!ObjectUtils.equals(getScenarioParametersId(), other.getScenarioParametersId())) {
return false;
}
if (!ObjectUtils.equals(getColumns(), other.getColumns())) {
return false;
}
if (!ObjectUtils.equals(getMergedOutputs(), other.getMergedOutputs())) {
return false;
}
return true;
}
@Override
public String toString() {
if (s_logger.isDebugEnabled()) {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE, false);
} else {
return "ViewCalculationConfiguration[" + getName() + "]";
}
}
/**
* Associates a header with a portfolio requirement.
*/
public static final class Column {
private final String _header;
private final String _valueName;
private final ValueProperties _properties;
public Column(final String header, final String valueName, final ValueProperties properties) {
ArgumentChecker.notNull(header, "header");
ArgumentChecker.notNull(valueName, "valueName");
ArgumentChecker.notNull(properties, "properties");
_header = header;
_valueName = valueName;
_properties = properties;
}
public String getHeader() {
return _header;
}
public String getValueName() {
return _valueName;
}
public ValueProperties getProperties() {
return _properties;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + _header.hashCode();
result = prime * result + _properties.hashCode();
result = prime * result + _valueName.hashCode();
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Column)) {
return false;
}
final Column other = (Column) obj;
return ObjectUtils.equals(_header, other._header)
&& ObjectUtils.equals(_valueName, other._valueName)
&& ObjectUtils.equals(_valueName, other._valueName);
}
}
/**
* Represents a set of portfolio requirements to be published under a single output name with common aggregates.
*/
public static final class MergedOutput {
private final String _mergedOutputName;
private final Set<Pair<String, ValueProperties>> _portfolioRequirements = new HashSet<>();
private MergedOutputAggregationType _aggregationType;
/**
* Creates an instance.
*
* @param mergedOutputName the name under which to display the merged output, not null
* @param aggregationType the aggregation to apply to the merged output, not null
*/
public MergedOutput(final String mergedOutputName, final MergedOutputAggregationType aggregationType) {
ArgumentChecker.notNull(mergedOutputName, "mergedOutputName");
ArgumentChecker.notNull(aggregationType, "aggregationType");
_mergedOutputName = mergedOutputName;
_aggregationType = aggregationType;
}
public String getMergedOutputName() {
return _mergedOutputName;
}
public Set<Pair<String, ValueProperties>> getPortfolioRequirements() {
return _portfolioRequirements;
}
public MergedOutputAggregationType getAggregationType() {
return _aggregationType;
}
public void setAggregationType(final MergedOutputAggregationType aggregationType) {
_aggregationType = aggregationType;
}
/**
* Adds a requirement to this merged output.
*
* @param valueName the original value name, not null
* @param properties the original value properties, not null
*/
public void addMergedRequirement(final String valueName, final ValueProperties properties) {
ArgumentChecker.notNull(valueName, "valueName");
ArgumentChecker.notNull(properties, "properties");
_portfolioRequirements.add(Pairs.of(valueName, properties));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + _aggregationType.hashCode();
result = prime * result + _mergedOutputName.hashCode();
result = prime * result + _portfolioRequirements.hashCode();
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ViewCalculationConfiguration)) {
return false;
}
final MergedOutput other = (MergedOutput) obj;
return ObjectUtils.equals(_aggregationType, other._aggregationType)
&& ObjectUtils.equals(_mergedOutputName, other._mergedOutputName)
&& ObjectUtils.equals(_portfolioRequirements, other._portfolioRequirements);
}
}
/**
* Enumerates the ways that aggregates can be calculated for merged outputs.
*/
public static enum MergedOutputAggregationType {
/**
* Specifies that simple, linear aggregation should be used
*/
LINEAR
}
}