/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.value;
import java.io.ObjectInputStream;
import java.io.Serializable;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.text.StrBuilder;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.MemoryUtils;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.id.UniqueId;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.PublicAPI;
/**
* An immutable representation of the metadata that describes an actual value.
* <p>
* This may be a value a function is capable of producing, or describe a resolved value passed into a function to satisfy a {@link ValueRequirement}.
* <p>
* For example the {@code ValueRequirement} for a currency converting function may state a constraint such as "any currency" on its input values. After the graph has been built, the actual value will
* be specified including the specific currency. Similarly a constraint on a {@link ValueRequirement} might restrict the function to be used (or the default of omission allows any) whereas the
* {@link ValueSpecification} will indicate which function was used to compute that value.
* <p>
* This class is immutable and thread-safe.
*/
@PublicAPI
public class ValueSpecification implements Serializable {
private static final long serialVersionUID = 1L;
/**
* The name of the value being requested. This matches that of a {@link ValueRequirement} satisfied by this specification.
*/
private String _valueName;
/**
* The specification of the object that the value refers to. This matches that of a {@link ValueRequirement} satisfied by this specification.
*/
private final ComputationTargetSpecification _targetSpecification;
/**
* The properties of the value described. This property set will satisfy the constraints of all {@link ValueRequirement}s satisfied by this specification.
*/
private final ValueProperties _properties;
/**
* The cached hash code.
*/
private transient volatile int _hashCode;
/**
* Obtains a {@code ValueSpecification} from a target, building the target specification according to the type of object the target refers to. The properties must include the function identifier.
*
* @param valueName the name of the value created, not null
* @param computationTargetSpecification the ComputationTargetSpecification, not null
* @param properties the value properties, not null and must include the function identifier
* @return the created specification, not null
*/
public static ValueSpecification of(final String valueName, final ComputationTargetSpecification computationTargetSpecification, final ValueProperties properties) {
ArgumentChecker.notNull(computationTargetSpecification, "computationTargetSpecification");
ArgumentChecker.notNull(properties, "uid");
return new ValueSpecification(valueName, computationTargetSpecification, properties);
}
/**
* Obtains a {@code ValueSpecification} from a target, building the target specification according to the type of object the target refers to. The properties must include the function identifier.
*
* @param valueName the name of the value created, not null
* @param targetType the ComputationTargetType, not null
* @param targetId the unique id of the target, not null
* @param properties the value properties, not null and must include the function identifier
* @return the created specification, not null
*/
public static ValueSpecification of(final String valueName, final ComputationTargetType targetType, final UniqueId targetId, final ValueProperties properties) {
ArgumentChecker.notNull(targetType, "targetType");
ArgumentChecker.notNull(properties, "uid");
return new ValueSpecification(valueName, new ComputationTargetSpecification(targetType, targetId), properties);
}
/**
* Obtains a {@code ValueSpecification} from a target, building the target specification according to the type of object the target refers to. The properties must include the function identifier
* unless it's provided separately in which case it will be added to the properties if any others are provided.
*
* @param valueName the name of the value created, not null
* @param targetType the ComputationTargetType, not null
* @param targetId the unique id of the target, not null
* @param functionIdentifier the function identifier, may be null
* @param currencyISO the currency ISO code, may be null
* @param properties the value properties, or can be null if the function identifier provided separately
* @return the created specification, not null
*/
public static ValueSpecification of(
final String valueName, final ComputationTargetType targetType, final UniqueId targetId, final String functionIdentifier,
final String currencyISO, final ValueProperties properties) {
ArgumentChecker.notNull(targetType, "targetType");
ArgumentChecker.notNull(properties, "uid");
ValueProperties props;
if ((functionIdentifier == null) && (currencyISO == null)) {
props = properties;
} else {
ValueProperties.Builder builder;
if (properties == null) {
builder = ValueProperties.builder();
} else {
builder = properties.copy();
}
if (currencyISO != null) {
builder = builder.with(ValuePropertyNames.CURRENCY, currencyISO);
}
if (functionIdentifier != null) {
builder = builder.with(ValuePropertyNames.FUNCTION, functionIdentifier);
}
props = builder.get();
}
return new ValueSpecification(valueName, new ComputationTargetSpecification(targetType, targetId), props);
}
/**
* Creates a new specification from a target specification.
* <p>
* The properties must include the function identifier.
*
* @param valueName the name of the value created, not null
* @param targetSpecification the target specification, not null
* @param properties the value properties, not null and must include the function identifier
*/
public ValueSpecification(final String valueName, final ComputationTargetSpecification targetSpecification, final ValueProperties properties) {
ArgumentChecker.notNull(valueName, "valueName");
ArgumentChecker.notNull(targetSpecification, "targetSpecification");
ArgumentChecker.notNull(properties, "properties");
ArgumentChecker.isTrue(properties.isDefined(ValuePropertyNames.FUNCTION), "properties.FUNCTION");
_valueName = ValueRequirement.getInterned(valueName);
_targetSpecification = targetSpecification;
_properties = properties;
}
//-------------------------------------------------------------------------
/**
* Gets the value name.
*
* @return the value name, not null
*/
public String getValueName() {
return _valueName;
}
/**
* Gets the target specification.
*
* @return the target specification, not null
*/
public ComputationTargetSpecification getTargetSpecification() {
return _targetSpecification;
}
/**
* Gets the value properties.
* <p>
* At the minimum the property set will contain the function identifier.
*
* @return the properties, not null
*/
public ValueProperties getProperties() {
return _properties;
}
//-------------------------------------------------------------------------
/**
* Gets a specific property by name.
* <p>
* If multiple values are set for a property then an arbitrary choice is made.
*
* @param propertyName name of the property to search for, not null
* @return the matched property value, null if not found
* @throws IllegalArgumentException if the property has a wild-card definition
*/
public String getProperty(final String propertyName) {
final String value = _properties.getSingleValue(propertyName);
if (value == null) {
if (_properties.isDefined(propertyName)) {
throw new IllegalArgumentException("property " + propertyName + " contains only wild-card values");
}
}
return value;
}
/**
* Creates a maximal {@link ValueRequirement} that would be satisfied by this value specification.
*
* @return the value requirement, not null
* @deprecated Conversion to a value requirement should not be needed - locations where this is called from should probably be using value requirements in the first place
*/
@Deprecated
public ValueRequirement toRequirementSpecification() {
return new ValueRequirement(_valueName, _targetSpecification, _properties);
}
/**
* Gets the identifier of the function that calculates this value.
*
* @return the function identifier, not null
**/
public String getFunctionUniqueId() {
return _properties.getStrictValue(ValuePropertyNames.FUNCTION);
}
/**
* Respecifies the properties to match a tighter requirement.
*
* @param requirement additional requirement to reduce properties against
* @return the value specification based on this with the additional requirement added, not null
*/
public ValueSpecification compose(final ValueRequirement requirement) {
assert requirement.getValueName() == getValueName();
assert requirement.getConstraints().isSatisfiedBy(getProperties());
final ValueProperties oldProperties = getProperties();
final ValueProperties newProperties = oldProperties.compose(requirement.getConstraints());
if (newProperties == oldProperties) {
return this;
} else {
return new ValueSpecification(getValueName(), getTargetSpecification(), newProperties);
}
}
//-------------------------------------------------------------------------
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ValueSpecification) {
final ValueSpecification other = (ValueSpecification) obj;
// valueName is interned
return (_valueName == other._valueName) &&
ObjectUtils.equals(_targetSpecification, other._targetSpecification) &&
ObjectUtils.equals(_properties, other._properties);
}
return false;
}
@Override
public int hashCode() {
if (_hashCode == 0) {
final int prime = 37;
int result = 1;
result = (result * prime) + _valueName.hashCode();
result = (result * prime) + _targetSpecification.hashCode();
result = (result * prime) + _properties.hashCode();
_hashCode = result;
}
return _hashCode;
}
@Override
public String toString() {
return new StrBuilder()
.append("VSpec[")
.append(getValueName())
.append(", ")
.append(getTargetSpecification())
.append(", ")
.append(getProperties())
.append(']')
.toString();
}
private void readObject(final ObjectInputStream in) throws Exception {
in.defaultReadObject();
// Serialization loses the "intern" nature of the string
_valueName = ValueRequirement.getInterned(_valueName);
}
private Object readResolve() throws Exception {
return MemoryUtils.instance(this);
}
}