/**
* 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 java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.text.StrBuilder;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.MemoryUtils;
import com.opengamma.engine.target.ComputationTargetReference;
import com.opengamma.engine.target.ComputationTargetRequirement;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.UniqueId;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.PublicAPI;
/**
* An immutable requirement to obtain a value needed to perform a calculation.
* <p>
* This is a metadata-based requirement, and specifies only the minimal number of parameters that are necessary to specify the user requirements.
* <p>
* The actual value which is computed is available as a {@link ValueSpecification} that is capable of satisfying this requirement. A specification satisfies a requirement if its properties satisfy the
* requirement constraints, plus the value name and target specifications match.
* <p>
* This class is immutable and thread-safe.
*/
@PublicAPI
public final class ValueRequirement implements Serializable {
/**
* Default serial version ID
*/
private static final long serialVersionUID = 1L;
/**
* The name of the value being requested.
*/
private String _valueName;
/**
* The object that the value refers to. This may be either a {@link ComputationTargetRequirement} or {@link ComputationTargetSpecification}. If the former, then it will be resolved into a stricter
* form of value requirement during graph construction so that the target can be fully identified.
*/
private final ComputationTargetReference _targetReference;
/**
* The constraints or additional parameters about the target. For example, a currency constraint.
*/
private final ValueProperties _constraints;
/**
* The cached hash code.
*/
private transient volatile int _hashCode;
/**
* Creates a requirement with no value constraints.
* <p>
* This builds a {@link ComputationTargetSpecification} from the target type and id.
*
* @param valueName the value to load, not null
* @param targetType the target type, not null
* @param targetId the target identifier, may be null
*/
public ValueRequirement(final String valueName, final ComputationTargetType targetType, final UniqueId targetId) {
this(valueName, new ComputationTargetSpecification(targetType, targetId));
}
/**
* Creates a requirement with value constraints.
* <p>
* This builds a {@link ComputationTargetSpecification} from the target type and id.
*
* @param valueName the name of the value to load, not null
* @param targetType the target type, not null
* @param targetId the unique identifier of the target, not null
* @param constraints the value constraints that must be satisfied
*/
public ValueRequirement(final String valueName, final ComputationTargetType targetType, final UniqueId targetId, final ValueProperties constraints) {
this(valueName, new ComputationTargetSpecification(targetType, targetId), constraints);
}
/**
* Creates a requirement with no value constraints.
* <p>
* This builds a {@link ComputationTargetRequirement} from the target type and id.
*
* @param valueName the name of the value to load, not null
* @param targetType the target type, not null
* @param targetId the external identifier of the target, not null
*/
public ValueRequirement(final String valueName, final ComputationTargetType targetType, final ExternalId targetId) {
this(valueName, new ComputationTargetRequirement(targetType, targetId));
}
/**
* Creates a requirement with value constraints.
* <p>
* This builds a {@link ComputationTargetRequirement} from the target type and id.
*
* @param valueName the name of the value to load, not null
* @param targetType the target type, not null
* @param targetId the external identifier of the target, not null
* @param constraints the value constraints that must be satisfied
*/
public ValueRequirement(final String valueName, final ComputationTargetType targetType, final ExternalId targetId, final ValueProperties constraints) {
this(valueName, new ComputationTargetRequirement(targetType, targetId), constraints);
}
/**
* Creates a requirement with no value constraints.
* <p>
* This builds a {@link ComputationTargetRequirement} from the target type and id bundle.
*
* @param valueName the name of the value to load, not null
* @param targetType the target type, not null
* @param targetIds the external identifiers of the target, not null
*/
public ValueRequirement(final String valueName, final ComputationTargetType targetType, final ExternalIdBundle targetIds) {
this(valueName, new ComputationTargetRequirement(targetType, targetIds));
}
/**
* Creates a requirement with value constraints.
* <p>
* This builds a {@link ComputationTargetRequirement} from the target type and id bundle.
*
* @param valueName the name of the value to load, not null
* @param targetType the target type, not null
* @param targetIds the external identifiers of the target, not null
* @param constraints the value constraints that must be satisfied
*/
public ValueRequirement(final String valueName, final ComputationTargetType targetType, final ExternalIdBundle targetIds, final ValueProperties constraints) {
this(valueName, new ComputationTargetRequirement(targetType, targetIds), constraints);
}
/**
* Creates a requirement from a target specification with no value constraints.
*
* @param valueName the value to load, not null
* @param targetReference the target reference, not null
*/
public ValueRequirement(final String valueName, final ComputationTargetReference targetReference) {
this(valueName, targetReference, ValueProperties.none());
}
/**
* Creates a requirement from a target specification with value constraints.
*
* @param valueName the name of the value to load, not null
* @param targetReference the target specification, not null
* @param constraints the value constraints that must be satisfied
*/
public ValueRequirement(final String valueName, final ComputationTargetReference targetReference, final ValueProperties constraints) {
ArgumentChecker.notNull(valueName, "Value name");
ArgumentChecker.notNull(targetReference, "Computation target specification");
ArgumentChecker.notNull(constraints, "constraints");
_valueName = getInterned(valueName);
_targetReference = targetReference;
_constraints = constraints;
}
private static final ConcurrentHashMap<String, String> s_interned = new ConcurrentHashMap<String, String>();
public static String getInterned(final String valueName) {
//This has been observed to be faster if a large proportion of valueNames are already interned and we have a large number of cores
String interned = s_interned.get(valueName);
if (interned != null) {
return interned;
}
interned = valueName.intern();
s_interned.putIfAbsent(interned, interned); //NOTE: use interned for keys too
return interned;
}
//-------------------------------------------------------------------------
/**
* Gets the name of the value to load.
*
* @return the valueName, not null
*/
public String getValueName() {
return _valueName;
}
/**
* Gets the reference of the target that is to be loaded.
*
* @return the target reference, not null
*/
public ComputationTargetReference getTargetReference() {
return _targetReference;
}
/**
* Gets the constraints that must be satisfied.
*
* @return the constraints, not null
*/
public ValueProperties getConstraints() {
return _constraints;
}
//-------------------------------------------------------------------------
/**
* Gets a specific constraint that must be specified.
* <p>
* If the constraint allows multiple specific values an arbitrary one is returned.
*
* @param constraintName the constraint to query
* @return the constraint value, null if it is not defined
* @throws IllegalArgumentException if the constraint is a wild-card definition
*/
public String getConstraint(final String constraintName) {
final String value = _constraints.getSingleValue(constraintName);
if (value == null) {
if (_constraints.isDefined(constraintName)) {
throw new IllegalArgumentException("constraint " + constraintName + " contains only wild-card values");
}
}
return value;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ValueRequirement) {
final ValueRequirement other = (ValueRequirement) obj;
return _valueName == other._valueName && // values are interned
_targetReference.equals(other._targetReference) &&
_constraints.equals(other._constraints);
}
return false;
}
@Override
public int hashCode() {
if (_hashCode == 0) {
final int prime = 31;
int result = 1;
result = prime * result + _valueName.hashCode();
result = prime * result + _targetReference.hashCode();
result = prime * result + _constraints.hashCode();
_hashCode = result;
}
return _hashCode;
}
@Override
public String toString() {
return new StrBuilder().append("ValueReq[").append(getValueName()).append(", ").append(getTargetReference()).append(", ").append(getConstraints()).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);
}
}