/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.view.helper;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.ObjectUtils;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.util.ArgumentChecker;
/**
* Describes an available output from an input set.
* <p>
* The quality of the output - i.e. how likely it is that the value can be generated from the input set, or how accurate/complete
* the property sets are - depends on the implementation of {@link AvailableOutputs} that produced it.
*/
public final class AvailableOutputImpl implements AvailableOutput {
private final String _valueName;
private final String _anyValue;
private final Map<String, ValueProperties> _positionProperties = new HashMap<String, ValueProperties>();
private ValueProperties _portfolioNodeProperties;
protected AvailableOutputImpl(final String valueName, final String anyValue) {
ArgumentChecker.notNull(valueName, "valueName");
_valueName = valueName;
_anyValue = anyValue;
}
protected static AvailableOutputImpl ofPosition(final AvailableOutputImpl copyFrom, final String securityType) {
final AvailableOutputImpl newInstance = new AvailableOutputImpl(copyFrom.getValueName(), copyFrom.getAnyValue());
newInstance._positionProperties.put(securityType, copyFrom._positionProperties.get(securityType));
return newInstance;
}
protected static AvailableOutputImpl ofPosition(final AvailableOutputImpl copyFrom) {
final AvailableOutputImpl newInstance = new AvailableOutputImpl(copyFrom.getValueName(), copyFrom.getAnyValue());
newInstance._positionProperties.putAll(copyFrom._positionProperties);
return newInstance;
}
protected static AvailableOutputImpl ofPortfolioNode(final AvailableOutputImpl copyFrom) {
final AvailableOutputImpl newInstance = new AvailableOutputImpl(copyFrom.getValueName(), copyFrom.getAnyValue());
newInstance._portfolioNodeProperties = copyFrom._portfolioNodeProperties;
return newInstance;
}
public String getValueName() {
return _valueName;
}
/**
* Returns the indicator used to indicate a property can take any value in addition to the listed ones, null if no
* explicit indicator has been used. When there is no indicator, the set of property values may be incomplete -
* wild-card values may be available.
*
* @return the indicator string, or null if none
*/
public String getAnyValue() {
return _anyValue;
}
@Override
public boolean isAvailableOnPosition() {
return !_positionProperties.isEmpty();
}
@Override
public Set<String> getSecurityTypes() {
return Collections.unmodifiableSet(_positionProperties.keySet());
}
@Override
public boolean isAvailableOn(final String securityType) {
return _positionProperties.containsKey(securityType);
}
@Override
public boolean isAvailableOnPortfolioNode() {
return _portfolioNodeProperties != null;
}
/**
* Merges the available properties (left) with an incoming set (right). The result is the possible
* properties on the output value. If a property is not defined on all merged components it is optional.
* If a property is defined on multiple components with different values, the union of the values is
* taken. The result is not guaranteed to be available but is a reasonable indication of what might be
* available on an output value.
* <p>
* This is a commutative operation; i.e. merge(A, B) gives the same result as merge(B, A).
*
* @param left left component of merge, not null
* @param right right component of merge, not null
* @return the merged properties
*/
private ValueProperties merge(final ValueProperties left, final ValueProperties right) {
if (right.isEmpty() || right.getProperties().isEmpty()) {
// left composed against EMPTY or INFINITE (or near-infinite) is unchanged
return left;
}
ValueProperties.Builder builder = left.copy();
for (String property : left.getProperties()) {
final Set<String> rightValues = right.getValues(property);
if (rightValues == null) {
// Right doesn't define, so make optional
builder.withOptional(property);
}
}
for (String property : right.getProperties()) {
final Set<String> rightValues = right.getValues(property);
final Set<String> leftValues = left.getValues(property);
if (leftValues == null) {
// Wasn't defined on the left, so the merged output is the optional version of the right
if (rightValues.isEmpty()) {
builder.withAny(property);
} else {
builder.with(property, rightValues);
}
builder.withOptional(property);
} else {
if (leftValues.isEmpty()) {
if (!rightValues.isEmpty()) {
// Left is "withAny" but right is restricted so use that
builder.withoutAny(property);
if (getAnyValue() != null) {
builder.with(property, getAnyValue());
}
builder.with(property, rightValues);
}
} else if (rightValues.isEmpty()) {
if (getAnyValue() != null) {
// Include "any" indicator
builder.with(property, getAnyValue());
}
} else {
// Merge known values from the right
builder.with(property, rightValues);
}
// Use greatest optionality
if (right.isOptional(property)) {
builder.withOptional(property);
}
}
}
return builder.get();
}
protected void setPortfolioNodeProperties(final ValueProperties properties) {
if (_portfolioNodeProperties == null) {
_portfolioNodeProperties = properties;
} else {
_portfolioNodeProperties = merge(_portfolioNodeProperties, properties);
}
}
protected void setPositionProperties(final ValueProperties properties, final String securityType) {
final ValueProperties existing = _positionProperties.get(securityType);
if (existing == null) {
_positionProperties.put(securityType, properties);
} else {
_positionProperties.put(securityType, merge(existing, properties));
}
}
@Override
public ValueProperties getProperties() {
ValueProperties result = _portfolioNodeProperties;
for (ValueProperties properties : _positionProperties.values()) {
if (result == null) {
result = properties;
} else {
result = merge(result, properties);
}
}
return result;
}
@Override
public ValueProperties getPositionProperties(final String securityType) {
return _positionProperties.get(securityType);
}
@Override
public ValueProperties getPortfolioNodeProperties() {
return _portfolioNodeProperties;
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + getValueName() + ", node=" + _portfolioNodeProperties + ", position=" + _positionProperties + "]";
}
@Override
public int hashCode() {
int hc = 1;
hc += (hc << 4) + getValueName().hashCode();
hc += (hc << 4) + _positionProperties.hashCode();
hc += (hc << 4) + ObjectUtils.hashCode(_portfolioNodeProperties);
return hc;
}
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof AvailableOutputImpl)) {
return false;
}
final AvailableOutputImpl other = (AvailableOutputImpl) o;
if (!getValueName().equals(other.getValueName())) {
return false;
}
if (!_positionProperties.equals(other._positionProperties)) {
return false;
}
return ObjectUtils.equals(_portfolioNodeProperties, other._portfolioNodeProperties);
}
}