/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.aggregation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.opengamma.core.position.PortfolioNode;
import com.opengamma.core.position.Position;
import com.opengamma.engine.ComputationTarget;
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.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;
/**
* Function for producing a sorted list of {@link ComputedValue} outputs for all positions underneath a node. The values are
* sorted in ascending {@link Comparable#compareTo} order. The values must implement the comparable interface for the result to
* be calculated.
* <p>
* This is intended as a "back-end" to functions which produce a specific ordering and filtering (e.g. top N or bottom N) but
* require sorted input in the first place so that the sort operation can be shared among a number. Otherwise splitting a portfolio
* into 100 percentiles might mean sorting the results 100 times.
*/
public class SortedPositionValues extends AbstractSortedPositionValues {
// TODO: instead of flattening the positions, could request the sorted values from any nodes -- it's then a merge operation
// to combine the lists. This may be quicker if calculating Top-N or something for sub-nodes in the tree as well which require
// the intermediate sorted results. This requires PLAT-1049 as the same function will be applied multiple times (on different
// targets) in graph walk.
/**
* Value name on the result produced.
*/
public static final String VALUE_NAME = "SortedPositionValues";
private static final Comparator<ComputedValue> s_comparator = new Comparator<ComputedValue>() {
@SuppressWarnings("unchecked")
@Override
public int compare(final ComputedValue o1, final ComputedValue o2) {
return ((Comparable<Object>) o1.getValue()).compareTo(o2.getValue());
}
};
private static void getPositionRequirements(final PortfolioNode node, final String valueName, final ValueProperties requirementConstraints, final Set<ValueRequirement> requirements) {
for (Position position : node.getPositions()) {
requirements.add(new ValueRequirement(valueName, ComputationTargetType.POSITION, position.getUniqueId(), requirementConstraints));
}
for (PortfolioNode childNode : node.getChildNodes()) {
getPositionRequirements(childNode, valueName, requirementConstraints, requirements);
}
}
// AbstractSortedPositionValues
@Override
protected String getValueName() {
return VALUE_NAME;
}
@Override
protected Set<ValueRequirement> getRequirements(final String valueName, final ComputationTarget target, final ValueProperties constraints) {
final ValueProperties.Builder requirementConstraints = ValueProperties.builder();
final String valueNameDot = valueName + ".";
for (String constraint : constraints.getProperties()) {
if (constraint.startsWith(valueNameDot)) {
final Set<String> values = constraints.getValues(constraint);
if (values.isEmpty()) {
requirementConstraints.withAny(constraint.substring(valueNameDot.length()));
} else {
requirementConstraints.with(constraint.substring(valueNameDot.length()), values);
}
}
}
final Set<ValueRequirement> requirements = new HashSet<ValueRequirement>();
getPositionRequirements(target.getPortfolioNode(), valueName, requirementConstraints.get(), requirements);
return requirements;
}
@Override
protected void composeValueProperties(final ValueProperties.Builder builder, final ValueSpecification inputValue) {
builder.with(VALUE_NAME_PROPERTY, inputValue.getValueName());
for (String inputProperty : inputValue.getProperties().getProperties()) {
final Set<String> inputPropertyValues = inputValue.getProperties().getValues(inputProperty);
if (inputPropertyValues.isEmpty()) {
builder.withAny(inputValue.getValueName() + "." + inputProperty);
} else {
builder.with(inputValue.getValueName() + "." + inputProperty, inputPropertyValues);
}
}
}
// AbstractFunction
@Override
public boolean canHandleMissingRequirements() {
return true;
}
@Override
public boolean canHandleMissingInputs() {
return true;
}
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
final List<ComputedValue> values = new ArrayList<ComputedValue>(inputs.getAllValues());
final ValueProperties.Builder properties = createValueProperties();
for (ComputedValue value : values) {
composeValueProperties(properties, value.getSpecification());
}
Collections.sort(values, s_comparator);
return Collections.singleton(new ComputedValue(ValueSpecification.of(getValueName(), ComputationTargetType.PORTFOLIO_NODE, target.getUniqueId(), properties.get()), values));
}
}