com.opengamma.financial.analytics.PropertyPreservingFunction.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.financial.analytics.PropertyPreservingFunction.java

Source

/**
 * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.financial.analytics;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;

import com.google.common.collect.Sets;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValueProperties.Builder;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;

/**
 * A base class for functions which need to preserve a set of properties from their inputs to their outputs, and also therefore need to request the appropriate inputs.
 *
 * @deprecated This is not a good way to translate constraints from the desired result to the requirements and properties from the satisfied requirements to the final results. Refer to
 *             {@link PositionOrTradeScalingFunction} or {@link SummingFunction} for a simpler method
 */
@Deprecated
public abstract class PropertyPreservingFunction extends AbstractFunction.NonCompiledInvoker {

    /**
     * Gets the properties which the function must preserve. If a property in this category occurs on any input then all
     * inputs must declare the same property value, and this will be propagated to the outputs, or the function will
     * fail. If no inputs declare a property then it will not appear on the outputs.
     *
     * @return the properties which the function is required to preserve, not null
     */
    protected abstract Collection<String> getPreservedProperties();

    /**
     * Gets the properties which the function will attempt to preserve. A property in this category will appear on the
     * function outputs if the property is present and has the same value across every input, otherwise it will be dropped.
     *
     * @return the properties which the function will attempt to preserve, not null
     */
    protected abstract Collection<String> getOptionalPreservedProperties();

    private ValueProperties createInputConstraints(final Collection<String> preserve) {
        final ValueProperties.Builder builder = ValueProperties.builder();
        for (final String value : preserve) {
            builder.withOptional(value);
        }
        return builder.get();
    }

    private ValueProperties createResultProperties(final Collection<String> preserve) {
        final ValueProperties.Builder builder = ValueProperties.builder();
        for (final String value : preserve) {
            builder.withAny(value);
        }
        applyAdditionalResultProperties(builder);
        return builder.get();
    }

    /**
     * Add additional properties to the results. The default here adds the function identifier; override
     * this to add further information, but call the superclass method if the function identifier is not
     * added.
     *
     * @param builder to add properties to
     */
    protected void applyAdditionalResultProperties(final ValueProperties.Builder builder) {
        builder.with(ValuePropertyNames.FUNCTION, getUniqueId());
    }

    private ValueProperties _inputConstraints;
    private ValueProperties _resultProperties;
    private ValueProperties _requiredProperties;

    @Override
    public void setUniqueId(final String identifier) {
        super.setUniqueId(identifier);
        final Collection<String> optionalProperties = getOptionalPreservedProperties();
        final Collection<String> requiredProperties = getPreservedProperties();
        final Collection<String> preservationCandidates = new ArrayList<String>(
                optionalProperties.size() + requiredProperties.size());
        preservationCandidates.addAll(optionalProperties);
        preservationCandidates.addAll(requiredProperties);
        _resultProperties = createResultProperties(preservationCandidates);
        _requiredProperties = createInputConstraints(requiredProperties);
        _inputConstraints = createInputConstraints(preservationCandidates);
    }

    protected ValueProperties getInputConstraint(final ValueRequirement desiredValue) {
        return getInputConstraints().compose(desiredValue.getConstraints());
    }

    protected ValueProperties getInputConstraints() {
        return _inputConstraints;
    }

    protected ValueProperties getResultProperties() {
        return _resultProperties;
    }

    private ValueProperties getResultProperties(final ValueProperties properties) {
        final ValueProperties.Builder builder = properties.copy().withoutAny(ValuePropertyNames.FUNCTION);
        applyAdditionalResultProperties(builder);
        return builder.get();
    }

    /**
     * Produces the properties of the input value composed against the input constraints.
     *
     * @param inputSpec an input value specification
     * @return the composed properties
     */
    protected ValueProperties getResultProperties(final ValueSpecification inputSpec) {
        return getResultProperties(inputSpec.getProperties().compose(getInputConstraints()));
    }

    /**
     * Produces the input constraints composed against the properties of the inputs, ensuring that any required preserved
     * properties are identical if present.
     *
     * @param inputs a set of input value specifications
     * @return the composed properties
     */
    protected ValueProperties getResultProperties(final Collection<ValueSpecification> inputs) {
        //NOTE: this function must be invariant under inputs ordering
        if (inputs.isEmpty()) {
            return null;
        }
        ValueProperties compositeProperties = null;
        ValueProperties referenceRequiredProperties = null;
        for (final ValueSpecification input : inputs) {
            if (compositeProperties == null) {
                compositeProperties = composeStrict(getInputConstraints(), input.getProperties());
                referenceRequiredProperties = _requiredProperties.compose(input.getProperties());
            } else {
                final ValueProperties requiredPropertyComposition = _requiredProperties
                        .compose(input.getProperties());
                if (!requiredPropertyComposition.equals(referenceRequiredProperties)) {
                    // Required property composition 'requiredPropertyComposition' produced from input 'input' differs from current required
                    // property composition 'referenceRequiredProperties' implying incompatible property values among the inputs
                    return null;
                }
                // Know that the required properties are preserved correctly, so now compose everything
                compositeProperties = composeStrict(compositeProperties, input.getProperties());
            }
        }
        return getResultProperties(compositeProperties);
    }

    /**
     * Creates the intersection of two valueProperties.
     * Note: this behaves as ValueProperties.compose except for the clause
     *  "Any properties defined in this set but not in the other remain untouched."
     * @param a some properties
     * @param b some properties
     * @return the intersection of a and b
     */
    private ValueProperties composeStrict(final ValueProperties a, final ValueProperties b) {
        final ValueProperties none = ValueProperties.none();
        if (none.equals(a) || none.equals(b)) {
            //NOTE: none has null properties
            return none;
        }
        //NOTE: infinite properties behave as empty

        final ValueProperties compose = a.compose(b);
        final Set<String> mismatchedProperties = Sets.symmetricDifference(a.getProperties(), b.getProperties());
        return without(compose, mismatchedProperties);
    }

    private ValueProperties without(final ValueProperties compose, final Collection<String> symmetricDiff) {
        if (symmetricDiff.isEmpty()) {
            return compose;
        }
        final Builder filtered = compose.copy();
        for (final String propertyName : symmetricDiff) {
            filtered.withoutAny(propertyName);
        }
        return filtered.get();
    }

    protected ValueProperties getResultPropertiesFromInputs(final Collection<ComputedValue> inputs) {
        final Collection<ValueSpecification> specs = new ArrayList<ValueSpecification>(inputs.size());
        for (final ComputedValue input : inputs) {
            specs.add(input.getSpecification());
        }
        return getResultProperties(specs);
    }

}