/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.value;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.util.ArgumentChecker;
/**
* A generic value renaming function. A single instance can be used for changing the name of multiple, mutually exclusive (for a given target) values.
*/
public class ValueRenamingFunction extends AbstractFunction.NonCompiledInvoker {
private final Set<String> _valueNamesToChange;
private final String _newValueName;
private final ComputationTargetType _targetType;
private final ValueProperties _additionalConstraints;
/**
* Constructs an instance.
*
* @param valueNamesToChange the set of mutually exclusive value names (for a given target) which the function will change, not null or empty
* @param newValueName the new name for any matching value, not null
* @param targetType the computation target type for which the function will apply, not null
*/
public ValueRenamingFunction(final Set<String> valueNamesToChange, final String newValueName, final ComputationTargetType targetType) {
this(valueNamesToChange, newValueName, targetType, null);
}
/**
* Constructs an instance.
*
* @param valueNamesToChange the set of mutually exclusive value names (for a given target) which the function will change, not null or empty
* @param newValueName the new name for any matching value, not null
* @param targetType the computation target type for which the function will apply, not null
* @param additionalConstraints additional constraints to set on the origin requirement, null for none
*/
public ValueRenamingFunction(final Set<String> valueNamesToChange, final String newValueName, final ComputationTargetType targetType, final ValueProperties additionalConstraints) {
ArgumentChecker.notNull(valueNamesToChange, "valueNamesToChange");
ArgumentChecker.notEmpty(valueNamesToChange, "valueNamesToChange");
ArgumentChecker.notNull(newValueName, "newValueName");
ArgumentChecker.notNull(targetType, "targetType");
_valueNamesToChange = valueNamesToChange;
_newValueName = newValueName;
_targetType = targetType;
if ((additionalConstraints == null) || ValueProperties.none().equals(additionalConstraints)) {
_additionalConstraints = null;
} else {
_additionalConstraints = additionalConstraints;
}
}
@Override
public Set<ComputedValue> execute(FunctionExecutionContext executionContext, FunctionInputs inputs, ComputationTarget target, Set<ValueRequirement> desiredValues) {
Set<ComputedValue> result = new HashSet<>();
Object prevValue = null;
for (ComputedValue inputValue : inputs.getAllValues()) {
Object value = inputValue.getValue();
if (prevValue == null) {
prevValue = value;
} else if (!value.equals(prevValue)) {
throw new OpenGammaRuntimeException("Attempted to rename two unequal values with the same name: " + _newValueName);
}
ValueSpecification outputSpec = getOutputSpec(inputValue.getSpecification());
result.add(new ComputedValue(outputSpec, value));
}
return result;
}
@Override
public ComputationTargetType getTargetType() {
return _targetType;
}
@Override
public boolean canApplyTo(FunctionCompilationContext context, ComputationTarget target) {
return true;
}
@Override
public Set<ValueSpecification> getResults(FunctionCompilationContext context, ComputationTarget target) {
return ImmutableSet.of(new ValueSpecification(_newValueName, target.toSpecification(), ValueProperties.all()));
}
@Override
public Set<ValueRequirement> getRequirements(FunctionCompilationContext context, ComputationTarget target, ValueRequirement desiredValue) {
final Set<ValueRequirement> result = new HashSet<ValueRequirement>();
final ValueProperties constraints;
if (_additionalConstraints == null) {
constraints = desiredValue.getConstraints();
} else {
final ValueProperties.Builder constraintsBuilder = desiredValue.getConstraints().copy();
for (String constraint : _additionalConstraints.getProperties()) {
Set<String> values = _additionalConstraints.getValues(constraint);
if (values.isEmpty()) {
if (_additionalConstraints.isOptional(constraint)) {
constraintsBuilder.withOptional(constraint);
} else {
constraintsBuilder.withAny(constraint);
}
} else {
constraintsBuilder.with(constraint, values);
if (_additionalConstraints.isOptional(constraint)) {
constraintsBuilder.withOptional(constraint);
}
}
}
constraints = constraintsBuilder.get();
}
for (String possibleInputValueName : _valueNamesToChange) {
result.add(new ValueRequirement(possibleInputValueName, desiredValue.getTargetReference(), constraints));
}
return result;
}
@Override
public boolean canHandleMissingRequirements() {
return _valueNamesToChange.size() > 1;
}
@Override
public Set<ValueSpecification> getResults(FunctionCompilationContext context, ComputationTarget target, Map<ValueSpecification, ValueRequirement> inputs) {
if (inputs.size() != 1) {
final Set<ValueSpecification> result = new HashSet<>();
for (ValueSpecification spec : inputs.keySet()) {
result.add(getOutputSpec(spec));
}
return result;
}
ValueSpecification inputSpec = Iterables.getOnlyElement(inputs.keySet());
return ImmutableSet.of(getOutputSpec(inputSpec));
}
protected ValueSpecification getOutputSpec(ValueSpecification inputSpec) {
ValueProperties outputProperties = inputSpec.getProperties().copy()
.withoutAny(ValuePropertyNames.FUNCTION)
.with(ValuePropertyNames.FUNCTION, getUniqueId()).get();
return new ValueSpecification(_newValueName, inputSpec.getTargetSpecification(), outputProperties);
}
}