/**
* 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.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
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.opengamma.core.config.Config;
import com.opengamma.core.config.ConfigGroups;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.id.MutableUniqueIdentifiable;
import com.opengamma.id.UniqueId;
import com.opengamma.id.UniqueIdentifiable;
import com.opengamma.livedata.UserPrincipal;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.PublicAPI;
import com.opengamma.util.money.Currency;
import com.opengamma.util.tuple.Pair;
/**
* The encapsulated logic that controls how precisely a view is to be constructed
* and computed.
*/
@PublicAPI
@Config(description = "View definition", group = ConfigGroups.VIEWS)
public class ViewDefinition implements Serializable, UniqueIdentifiable, MutableUniqueIdentifiable {
private static final Logger s_logger = LoggerFactory.getLogger(ViewDefinition.class);
private static final long serialVersionUID = 1L;
private UniqueId _uniqueIdentifier;
private final String _name;
private final UniqueId _portfolioId;
private final UserPrincipal _marketDataUser;
private final ResultModelDefinition _resultModelDefinition;
private Long _minDeltaCalculationPeriod;
private Long _maxDeltaCalculationPeriod;
private Long _minFullCalculationPeriod;
private Long _maxFullCalculationPeriod;
private boolean _persistent;
private Currency _defaultCurrency;
private final Map<String, ViewCalculationConfiguration> _calculationConfigurationsByName = new TreeMap<String, ViewCalculationConfiguration>();
/**
* If true, when a single computation cycle completes, the outputs are written
* to a temporary file on the disk. This is not useful in a real production
* deployment, but can be useful in tests.
*/
private boolean _dumpComputationCacheToDisk;
/**
* Constructs an instance, including a reference portfolio.
*
* @param name the name of the view definition, not null
* @param portfolioId the unique identifier of the portfolio referenced by this view definition, null if
* no portfolio reference is required
* @param marketDataUser the name of the user who owns the view definition, not null
*/
public ViewDefinition(final String name, final UniqueId portfolioId, final String marketDataUser) {
this(null, name, portfolioId, marketDataUser);
}
/**
* Constructs an instance, without a reference portfolio.
*
* @param name the name of the view definition, not null
* @param marketDataUser the name of the user who owns the view definition, not null
*/
public ViewDefinition(final String name, final String marketDataUser) {
this(null, name, marketDataUser);
}
/**
* Constructs an instance, without a reference portfolio.
*
* @param name the name of the view definition, not null
* @param marketDataUser the user who owns the view definition, not null
*/
public ViewDefinition(final String name, final UserPrincipal marketDataUser) {
this(null, name, marketDataUser);
}
/**
* Constructs an instance, without a reference portfolio.
*
* @param name the name of the view definition, not null
* @param marketDataUser the user who owns the view definition, not null
* @param resultModelDefinition configuration of the results from the view
*/
public ViewDefinition(final String name, final UserPrincipal marketDataUser, final ResultModelDefinition resultModelDefinition) {
this(null, name, marketDataUser, resultModelDefinition);
}
/**
* Constructs an instance
*
* @param name the name of the view definition, not null
* @param portfolioId the unique identifier of the portfolio referenced by this view definition, null if
* no portfolio reference is required
* @param marketDataUser the user who owns the view definition, not null
*/
public ViewDefinition(final String name, final UniqueId portfolioId, final UserPrincipal marketDataUser) {
this(null, name, portfolioId, marketDataUser);
}
/**
* Constructs an instance
*
* @param name the name of the view definition, not null
* @param portfolioId the unique identifier of the portfolio referenced by this view definition, null if
* no portfolio reference is required
* @param marketDataUser the user who owns the view definition, not null
* @param resultModelDefinition configuration of the results from the view, not null
*/
public ViewDefinition(final String name, final UniqueId portfolioId, final UserPrincipal marketDataUser, final ResultModelDefinition resultModelDefinition) {
this(null, name, portfolioId, marketDataUser, resultModelDefinition);
}
//------------------------------------------------------------------------
/**
* Constructs an instance, including a reference portfolio.
*
* @param uniqueId the unique id of the view definition
* @param name the name of the view definition, not null
* @param portfolioId the unique identifier of the portfolio referenced by this view definition, null if
* no portfolio reference is required
* @param marketDataUser the name of the user who owns the view definition, not null
*/
public ViewDefinition(final UniqueId uniqueId, final String name, final UniqueId portfolioId, final String marketDataUser) {
this(uniqueId, name, portfolioId, UserPrincipal.getLocalUser(marketDataUser), new ResultModelDefinition());
}
/**
* Constructs an instance, without a reference portfolio.
*
* @param uniqueId the unique id of the view definition
* @param name the name of the view definition, not null
* @param marketDataUser the name of the user who owns the view definition, not null
*/
public ViewDefinition(final UniqueId uniqueId, final String name, final String marketDataUser) {
this(uniqueId, name, UserPrincipal.getLocalUser(marketDataUser));
}
/**
* Constructs an instance, without a reference portfolio.
*
* @param uniqueId the unique id of the view definition
* @param name the name of the view definition, not null
* @param marketDataUser the user who owns the view definition, not null
*/
public ViewDefinition(final UniqueId uniqueId, final String name, final UserPrincipal marketDataUser) {
this(uniqueId, name, null, marketDataUser);
}
/**
* Constructs an instance, without a reference portfolio.
*
* @param uniqueId the unique id of the view definition
* @param name the name of the view definition, not null
* @param marketDataUser the user who owns the view definition, not null
* @param resultModelDefinition configuration of the results from the view
*/
public ViewDefinition(final UniqueId uniqueId, final String name, final UserPrincipal marketDataUser, final ResultModelDefinition resultModelDefinition) {
this(uniqueId, name, null, marketDataUser, resultModelDefinition);
}
/**
* Constructs an instance
*
* @param uniqueId the unique id of the view definition
* @param name the name of the view definition, not null
* @param portfolioId the unique identifier of the portfolio referenced by this view definition, null if
* no portfolio reference is required
* @param marketDataUser the user who owns the view definition, not null
*/
public ViewDefinition(final UniqueId uniqueId, final String name, final UniqueId portfolioId, final UserPrincipal marketDataUser) {
this(uniqueId, name, portfolioId, marketDataUser, new ResultModelDefinition());
}
/**
* Constructs an instance
*
* @param uniqueId the unique id of the view definition
* @param name the name of the view definition, not null
* @param portfolioId the unique identifier of the portfolio referenced by this view definition, null if
* no portfolio reference is required
* @param marketDataUser the user who owns the view definition, not null
* @param resultModelDefinition configuration of the results from the view, not null
*/
public ViewDefinition(final UniqueId uniqueId, final String name, final UniqueId portfolioId, final UserPrincipal marketDataUser, final ResultModelDefinition resultModelDefinition) {
ArgumentChecker.notNull(name, "View name");
ArgumentChecker.notNull(marketDataUser, "User name");
ArgumentChecker.notNull(resultModelDefinition, "Result model definition");
_name = name;
_portfolioId = portfolioId;
_marketDataUser = marketDataUser;
_resultModelDefinition = resultModelDefinition;
_uniqueIdentifier = uniqueId;
}
//-------------------------------------------------------------------------
/**
* Performs a deep copy of the given view definition, with the opportunity to change its immutable fields.
*
* @param name the name of the new view definition, not null
* @param portfolioId the unique identifier of the portfolio referenced by the new view definition, null if no
* portfolio reference is required
* @param marketDataUser the user who owns the new view definition, not null
* @return a copy of the base view definition with its immutable fields set to the new values, not null
*/
public ViewDefinition copyWith(final String name, final UniqueId portfolioId, final UserPrincipal marketDataUser) {
final ViewDefinition result = new ViewDefinition(name, portfolioId, marketDataUser, getResultModelDefinition());
result.setDefaultCurrency(getDefaultCurrency());
result.setDumpComputationCacheToDisk(isDumpComputationCacheToDisk());
result.setMinDeltaCalculationPeriod(getMinDeltaCalculationPeriod());
result.setMaxDeltaCalculationPeriod(getMaxDeltaCalculationPeriod());
result.setMinFullCalculationPeriod(getMinFullCalculationPeriod());
result.setMaxFullCalculationPeriod(getMaxFullCalculationPeriod());
result.setPersistent(isPersistent());
for (final ViewCalculationConfiguration baseCalcConfig : getAllCalculationConfigurations()) {
baseCalcConfig.copyTo(result);
}
return result;
}
//-------------------------------------------------------------------------
/**
* Gets a set containing every portfolio output that is required, across all calculation configurations, 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 across all calculation configurations, not null
*/
public Set<Pair<String, ValueProperties>> getAllPortfolioRequirementNames() {
final Set<Pair<String, ValueProperties>> requirements = new TreeSet<Pair<String, ValueProperties>>();
for (final ViewCalculationConfiguration calcConfig : _calculationConfigurationsByName.values()) {
requirements.addAll(calcConfig.getAllPortfolioRequirements());
}
return requirements;
}
/**
* Returns the name of the view.
*
* @return the view name
*/
public String getName() {
return _name;
}
/**
* Gets the unique identifier of the portfolio referenced by this view definition. This is the portfolio on which
* position-level calculations should be performed.
*
* @return the unique identifier of the portfolio referenced by this view definition, null if no portfolio is
* referenced
*/
public UniqueId getPortfolioId() {
return _portfolioId;
}
/**
* Gets the user to be associated with any market data subscriptions made for the view.
*
* @return the user to be associated with market data subscriptions
*/
public UserPrincipal getMarketDataUser() {
return _marketDataUser;
}
/**
* Gets the default currency defined for this view
*
* @return the currency
*/
public Currency getDefaultCurrency() {
return _defaultCurrency;
}
/**
* Sets the default currency to use
*
* @param currency The default currency
*/
public void setDefaultCurrency(final Currency currency) {
_defaultCurrency = currency;
}
/**
* Returns the calculation configurations.
*
* @return the configurations
*/
public Collection<ViewCalculationConfiguration> getAllCalculationConfigurations() {
return new ArrayList<ViewCalculationConfiguration>(_calculationConfigurationsByName.values());
}
/**
* Returns the set of calculation configuration names. These names can be passed to {@link #getCalculationConfiguration (String)}
* to retrieve the configuration information.
*
* @return the configuration names
*/
public Set<String> getAllCalculationConfigurationNames() {
return Collections.unmodifiableSet(_calculationConfigurationsByName.keySet());
}
/**
* Returns a map of calculation configuration names to configurations.
*
* @return the calculation configurations
*/
public Map<String, ViewCalculationConfiguration> getAllCalculationConfigurationsByName() {
return Collections.unmodifiableMap(_calculationConfigurationsByName);
}
/**
* Returns the named calculation configuration.
*
* @param configurationName the name of the calculation configuration, not null
* @return the calculation configuration, or null if no calculation configuration exists with that name.
*/
public ViewCalculationConfiguration getCalculationConfiguration(final String configurationName) {
ArgumentChecker.notNull(configurationName, "configurationName");
return _calculationConfigurationsByName.get(configurationName);
}
/**
* Adds a new calculation configuration to the view definition. If there is already a configuration with that name it will
* be replaced.
*
* @param calcConfig the new configuration, not null
*/
public void addViewCalculationConfiguration(final ViewCalculationConfiguration calcConfig) {
ArgumentChecker.notNull(calcConfig, "calculation configuration");
ArgumentChecker.notNull(calcConfig.getName(), "Configuration name");
_calculationConfigurationsByName.put(calcConfig.getName(), calcConfig);
}
/**
* Add an output requirement to the view definition. This will become a terminal output when constructing dependency graphs for the view.
*
* @param calculationConfigurationName the configuration to add this as a requirement to, not null
* @param securityType the type of security for which an output should be produced, not null
* @param requirementName the value name to be produced, not null
* @param constraints additional constraints on the value produced, not null. For example this could be used to specify a currency
* rather than use the view or portfolio default.
*/
public void addPortfolioRequirement(final String calculationConfigurationName, final String securityType, final String requirementName, final ValueProperties constraints) {
ViewCalculationConfiguration calcConfig = _calculationConfigurationsByName.get(calculationConfigurationName);
if (calcConfig == null) {
calcConfig = new ViewCalculationConfiguration(this, calculationConfigurationName);
_calculationConfigurationsByName.put(calculationConfigurationName, calcConfig);
}
calcConfig.addPortfolioRequirement(securityType, requirementName, constraints);
}
/**
* Add an output requirement to the view definition. This will become a terminal output when constructing dependency graphs for the view.
* The value is added without any constraints.
*
* @param calculationConfigurationName the configuration to add this as a requirement to, not null
* @param securityType the type of security for which an output should be produced, not null
* @param requirementName the value name to be produced, not null
*/
public void addPortfolioRequirementName(final String calculationConfigurationName, final String securityType, final String requirementName) {
addPortfolioRequirement(calculationConfigurationName, securityType, requirementName, ValueProperties.none());
}
//-------------------------------------------------------------------------
/**
* Gets the minimum period, in milliseconds, which must have elapsed since the start of the last delta calculation
* before another cycle may be triggered. Delta calculations involve only those nodes in the dependency graph whose
* inputs have changed since the previous calculation.
*
* @return the minimum period between the start of two delta calculations, in milliseconds, or <code>null</code> to
* indicate that no minimum period is required to elapse.
*/
public Long getMinDeltaCalculationPeriod() {
return _minDeltaCalculationPeriod;
}
/**
* Sets the minimum period, in milliseconds, which must have elapsed since the start of the last delta calculation
* before another cycle may be triggered. Delta calculations involve only those nodes in the dependency graph whose
* inputs have changed since the previous calculation.
*
* @param minDeltaCalculationPeriod the minimum period between the start of two delta calculations, in milliseconds,
* or <code>null</code> to indicate that no minimum period is required to elapse.
*/
public void setMinDeltaCalculationPeriod(final Long minDeltaCalculationPeriod) {
_minDeltaCalculationPeriod = minDeltaCalculationPeriod;
}
/**
* Gets the maximum period, in milliseconds, which can elapse since the start of the last full or delta calculation
* before a delta recalculation may be forced. In between the minimum and maximum period, any relevant market data
* changes may immediately trigger a recalculation. The maximum calculation period is therefore a fall-back which can
* be used to ensure that the view has always been calculated recently, even when no market data changes have
* occurred.
*
* @return the maximum period allowed since the start of the last full or delta calculation, in milliseconds, or
* <code>null</code> if no maximum period is required.
*/
public Long getMaxDeltaCalculationPeriod() {
return _maxDeltaCalculationPeriod;
}
/**
* Sets the maximum period, in milliseconds, which can elapse since the start of the last full or delta calculation
* before a delta recalculation may be forced. In between the minimum and maximum period, any relevant market data
* changes may immediately trigger a recalculation. The maximum calculation period is therefore a fall-back which can
* be used to ensure that the view has always been calculated recently, even when no market data changes have
* occurred.
*
* @param maxDeltaCalculationPeriod the maximum period allowed since the start of the last full or delta
* calculation, in milliseconds, or <code>null</code> if no maximum period is
* required.
*/
public void setMaxDeltaCalculationPeriod(final Long maxDeltaCalculationPeriod) {
_maxDeltaCalculationPeriod = maxDeltaCalculationPeriod;
}
/**
* Gets the minimum period, in milliseconds, which must have elapsed since the start of the last full calculation
* before another cycle may be triggered. Full calculations involve recalculating every node in the dependency graph,
* regardless of whether their inputs have changed.
*
* @return the minimum period between the start of two full calculations, in milliseconds, or <code>null</code> to
* indicate that no minimum period is required to elapse.
*/
public Long getMinFullCalculationPeriod() {
return _minFullCalculationPeriod;
}
/**
* Sets the minimum period, in milliseconds, which must have elapsed since the start of the last full calculation
* before another cycle may be triggered. Full calculations involve recalculating every node in the dependency graph,
* regardless of whether their inputs have changed.
*
* @param minFullCalculationPeriod the minimum period between the start of two full calculations, in milliseconds,
* or <code>null</code> to indicate that no minimum period is required to elapse.
*/
public void setMinFullCalculationPeriod(final Long minFullCalculationPeriod) {
_minFullCalculationPeriod = minFullCalculationPeriod;
}
/**
* Gets the maximum period, in milliseconds, which can elapse since the start of the last full calculation before a
* full recalculation is forced. In between the minimum and maximum period, any relevant market data changes may
* immediately trigger a recalculation. The maximum calculation period is therefore a fall-back which can be used to
* ensure that the view has always been calculated recently, even when no market data changes have occurred.
*
* @return the maximum period allowed since the start of the last full calculation, in milliseconds, or
* <code>null</code> if no maximum period is required.
*/
public Long getMaxFullCalculationPeriod() {
return _maxFullCalculationPeriod;
}
/**
* Sets the maximum period, in milliseconds, which can elapse since the start of the last full calculation before a
* full recalculation is forced. In between the minimum and maximum period, any relevant market data changes may
* immediately trigger a recalculation. The maximum calculation period is therefore a fall-back which can be used to
* ensure that the view has always been calculated recently, even when no market data changes have occurred.
*
* @param maxFullCalculationPeriod the maximum period allowed since the start of the last full calculation, in
* milliseconds, or <code>null</code> if no maximum period is required.
*/
public void setMaxFullCalculationPeriod(final Long maxFullCalculationPeriod) {
_maxFullCalculationPeriod = maxFullCalculationPeriod;
}
//-------------------------------------------------------------------------
/**
* Returns the result model definition, describing how the results should be constructed and returned after execution
* of the view.
*
* @return the {@link ResultModelDefinition} instance.
*/
public ResultModelDefinition getResultModelDefinition() {
return _resultModelDefinition;
}
//-------------------------------------------------------------------------
/**
* Gets whether this is a persistent view definition.
* <p>
* A shared view process for a persistent view definition remains alive with its dependency graphs compiled even when
* no clients are connected. This can be useful if compilation is slow.
*
* @return true if this is a persistent view definition, false otherwise
*/
public boolean isPersistent() {
return _persistent;
}
/**
* Sets whether this is a persistent view definition.
* <p>
* A shared view process for a persistent view definition remains alive with its dependency graphs compiled even when
* no clients are connected. This can be useful if compilation is slow.
*
* @param persistent true to make this a persistent view definition, false otherwise
*/
public void setPersistent(boolean persistent) {
_persistent = persistent;
}
//-------------------------------------------------------------------------
/**
* Tests whether to dump the computation cache to disk after execution of the view. This is intended for debugging and
* testing only. There are more efficient ways to interact with the computation cache to obtain terminal and intermediate
* values following view execution.
*
* @return true if the cache should be written to disk after view execution
*/
public boolean isDumpComputationCacheToDisk() {
return _dumpComputationCacheToDisk;
}
/**
* Sets whether to dump the computation cache to disk after execution of the view. This is intended for debugging and
* testing only. There are more efficient ways to interact with the computation cache to obtain terminal and intermediate
* values following view execution.
* <p>
* A view executor should write to a file in the system temporary directory with a filename based on the executing view's name.
*
* @param dumpComputationCacheToDisk true to write the contents of the cache to disk after view execution
*/
public void setDumpComputationCacheToDisk(final boolean dumpComputationCacheToDisk) {
_dumpComputationCacheToDisk = dumpComputationCacheToDisk;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.hashCode(getName());
result = prime * result + ObjectUtils.hashCode(getPortfolioId());
result = prime * result + ObjectUtils.hashCode(getMarketDataUser());
result = prime * result + _calculationConfigurationsByName.hashCode();
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ViewDefinition)) {
return false;
}
final ViewDefinition other = (ViewDefinition) obj;
final boolean basicPropertiesEqual = ObjectUtils.equals(getName(), other.getName()) && ObjectUtils.equals(getPortfolioId(), other.getPortfolioId())
&& ObjectUtils.equals(getResultModelDefinition(), other.getResultModelDefinition()) && ObjectUtils.equals(getMarketDataUser(), other.getMarketDataUser())
&& ObjectUtils.equals(_minDeltaCalculationPeriod, other._minDeltaCalculationPeriod) && ObjectUtils.equals(_maxDeltaCalculationPeriod, other._maxDeltaCalculationPeriod)
&& ObjectUtils.equals(_minFullCalculationPeriod, other._minFullCalculationPeriod) && ObjectUtils.equals(_maxFullCalculationPeriod, other._maxFullCalculationPeriod)
&& ObjectUtils.equals(_dumpComputationCacheToDisk, other._dumpComputationCacheToDisk) && ObjectUtils.equals(getAllCalculationConfigurationNames(), other.getAllCalculationConfigurationNames())
&& ObjectUtils.equals(_defaultCurrency, other._defaultCurrency);
if (!basicPropertiesEqual) {
return false;
}
final Set<ViewCalculationConfiguration> localConfigs = new HashSet<ViewCalculationConfiguration>(_calculationConfigurationsByName.values());
final Set<ViewCalculationConfiguration> otherConfigs = new HashSet<ViewCalculationConfiguration>(other.getAllCalculationConfigurations());
return localConfigs.equals(otherConfigs);
}
@Override
public void setUniqueId(final UniqueId uniqueIdentifier) {
_uniqueIdentifier = uniqueIdentifier;
}
@Override
public UniqueId getUniqueId() {
return _uniqueIdentifier;
}
@Override
public String toString() {
if (s_logger.isDebugEnabled()) {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE, false);
} else {
return "ViewDefinition[" + getName() + "]";
}
}
}