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

Java tutorial

Introduction

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

Source

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

import java.util.Collections;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Instant;

import com.google.common.collect.Sets;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.CompiledFunctionDefinition;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionDefinition;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.function.FunctionInvoker;
import com.opengamma.engine.function.FunctionParameters;
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;
import com.opengamma.util.async.AsynchronousExecution;
import com.opengamma.util.async.AsynchronousOperation;
import com.opengamma.util.async.AsynchronousResult;
import com.opengamma.util.async.ResultListener;

/**
 * Wraps another function definition into a form that can work with one or more of its inputs missing.
 */
public class MissingInputsFunction extends AbstractFunction implements CompiledFunctionDefinition, FunctionInvoker {

    private static final Logger s_logger = LoggerFactory.getLogger(MissingInputsFunction.class);

    /**
     * Value of the {@link ValuePropertyNames#AGGREGATION} property when one or more of the inputs may be missing.
     */
    public static final String AGGREGATION_STYLE_MISSING = "MissingInputs";

    /**
     * Value of the {@link ValuePropertyNames#AGGREGATION} property when all of the inputs must be available.
     */
    public static final String AGGREGATION_STYLE_FULL = "Full";

    private final FunctionDefinition _underlyingDefinition;
    private final CompiledFunctionDefinition _underlyingCompiled;
    private final FunctionInvoker _underlyingInvoker;

    public MissingInputsFunction(final FunctionDefinition underlying) {
        ArgumentChecker.notNull(underlying, "underlying");
        _underlyingDefinition = underlying;
        if (underlying instanceof CompiledFunctionDefinition) {
            _underlyingCompiled = (CompiledFunctionDefinition) underlying;
            if (underlying instanceof FunctionInvoker) {
                _underlyingInvoker = (FunctionInvoker) underlying;
            } else {
                _underlyingInvoker = null;
            }
        } else {
            _underlyingCompiled = null;
            _underlyingInvoker = null;
        }
    }

    protected MissingInputsFunction(final CompiledFunctionDefinition underlying) {
        ArgumentChecker.notNull(underlying, "underlying");
        _underlyingDefinition = underlying.getFunctionDefinition();
        _underlyingCompiled = underlying;
        if (underlying instanceof FunctionInvoker) {
            _underlyingInvoker = (FunctionInvoker) underlying;
        } else {
            _underlyingInvoker = null;
        }
    }

    protected MissingInputsFunction(final FunctionInvoker underlying) {
        ArgumentChecker.notNull(underlying, "underlying");
        _underlyingDefinition = null;
        _underlyingCompiled = null;
        _underlyingInvoker = underlying;
    }

    protected MissingInputsFunction create(final CompiledFunctionDefinition underlying) {
        return new MissingInputsFunction(underlying);
    }

    protected MissingInputsFunction create(final FunctionInvoker underlying) {
        return new MissingInputsFunction(underlying);
    }

    protected FunctionDefinition getUnderlyingDefinition() {
        return _underlyingDefinition;
    }

    protected CompiledFunctionDefinition getUnderlyingCompiled() {
        return _underlyingCompiled;
    }

    protected FunctionInvoker getUnderlyingInvoker() {
        return _underlyingInvoker;
    }

    protected String getAggregationStyleMissing() {
        return AGGREGATION_STYLE_MISSING;
    }

    protected String getAggregationStyleFull() {
        return AGGREGATION_STYLE_FULL;
    }

    // AbstractFunction

    @Override
    public void setUniqueId(final String identifier) {
        if (getUnderlyingDefinition() instanceof AbstractFunction) {
            ((AbstractFunction) getUnderlyingDefinition()).setUniqueId(identifier);
        }
        super.setUniqueId(identifier);
    }

    // FunctionDefinition

    @Override
    public void init(final FunctionCompilationContext context) {
        getUnderlyingDefinition().init(context);
    }

    @Override
    public CompiledFunctionDefinition compile(final FunctionCompilationContext context, final Instant atInstant) {
        final CompiledFunctionDefinition underlying = getUnderlyingDefinition().compile(context, atInstant);
        if (underlying == getUnderlyingCompiled()) {
            s_logger.debug("Compiling underlying on {} gives self", this);
            return this;
        } else {
            s_logger.debug("Creating delegate for compiled underlying on {}", this);
            return create(underlying);
        }
    }

    @Override
    public String getShortName() {
        return getUnderlyingDefinition().getShortName();
    }

    @Override
    public FunctionParameters getDefaultParameters() {
        return getUnderlyingDefinition().getDefaultParameters();
    }

    // CompiledFunctionDefinition

    @Override
    public FunctionDefinition getFunctionDefinition() {
        return this;
    }

    @Override
    public ComputationTargetType getTargetType() {
        return getUnderlyingCompiled().getTargetType();
    }

    @Override
    public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
        return getUnderlyingCompiled().canApplyTo(context, target);
    }

    @Override
    public Set<ValueSpecification> getResults(final FunctionCompilationContext context,
            final ComputationTarget target) {
        final Set<ValueSpecification> underlyingResults = getUnderlyingCompiled().getResults(context, target);
        if (underlyingResults == null) {
            s_logger.debug("Underlying returned null for target {}", target);
            return null;
        }
        final Set<ValueSpecification> results = Sets.newHashSetWithExpectedSize(underlyingResults.size());
        for (final ValueSpecification underlyingResult : underlyingResults) {
            final ValueProperties underlyingProperties = underlyingResult.getProperties();
            final ValueProperties.Builder properties = underlyingProperties.copy();
            if (underlyingProperties.getProperties().isEmpty()) {
                // Got the infinite or nearly infinite property set
                properties.withAny(ValuePropertyNames.AGGREGATION);
                results.add(new ValueSpecification(underlyingResult.getValueName(),
                        underlyingResult.getTargetSpecification(), properties.get()));
            } else {
                // Got a finite property set; republish with both aggregation modes
                properties.withoutAny(ValuePropertyNames.AGGREGATION).with(ValuePropertyNames.AGGREGATION,
                        getAggregationStyleFull());
                results.add(new ValueSpecification(underlyingResult.getValueName(),
                        underlyingResult.getTargetSpecification(), properties.get()));
                properties.withoutAny(ValuePropertyNames.AGGREGATION).with(ValuePropertyNames.AGGREGATION,
                        getAggregationStyleMissing());
                results.add(new ValueSpecification(underlyingResult.getValueName(),
                        underlyingResult.getTargetSpecification(), properties.get()));
            }
        }
        s_logger.debug("Returning results {}", results);
        return results;
    }

    @Override
    public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context,
            final ComputationTarget target, ValueRequirement desiredValue) {
        // User must have requested our aggregation style
        final ValueProperties constraints = desiredValue.getConstraints();
        if (constraints.getProperties() == null) {
            // No constraints - assume FULL and make it optional for the inputs
            desiredValue = new ValueRequirement(desiredValue.getValueName(), desiredValue.getTargetReference(),
                    ValueProperties.with(ValuePropertyNames.AGGREGATION, getAggregationStyleFull())
                            .withOptional(ValuePropertyNames.AGGREGATION).get());
        } else if (constraints.getProperties().isEmpty()) {
            // Infinite/near-infinite constraints - make aggregation style optional
            if (!constraints.isOptional(ValuePropertyNames.AGGREGATION)) {
                desiredValue = new ValueRequirement(desiredValue.getValueName(), desiredValue.getTargetReference(),
                        constraints.copy().withOptional(ValuePropertyNames.AGGREGATION).get());
            }
        } else {
            final Set<String> aggregationStyle = constraints.getValues(ValuePropertyNames.AGGREGATION);
            final String full = getAggregationStyleFull();
            if ((aggregationStyle == null) || aggregationStyle.isEmpty()) {
                // No constraint or wild-card - assume FULL and make it optional for the inputs
                desiredValue = new ValueRequirement(desiredValue.getValueName(), desiredValue.getTargetReference(),
                        constraints.copy().withoutAny(ValuePropertyNames.AGGREGATION)
                                .withOptional(ValuePropertyNames.AGGREGATION)
                                .with(ValuePropertyNames.AGGREGATION, full).get());
            } else if (aggregationStyle.contains(full)) {
                // Constraint allows FULL - make it optional for the inputs
                if ((aggregationStyle.size() != 1) || !constraints.isOptional(ValuePropertyNames.AGGREGATION)) {
                    desiredValue = new ValueRequirement(desiredValue.getValueName(),
                            desiredValue.getTargetReference(),
                            constraints.copy().withoutAny(ValuePropertyNames.AGGREGATION)
                                    .withOptional(ValuePropertyNames.AGGREGATION)
                                    .with(ValuePropertyNames.AGGREGATION, full).get());
                }
            } else {
                final String missing = getAggregationStyleMissing();
                if (aggregationStyle.contains(missing)) {
                    // Constraint allows MISSING - make it optional for the inputs
                    if ((aggregationStyle.size() != 1) || !constraints.isOptional(ValuePropertyNames.AGGREGATION)) {
                        desiredValue = new ValueRequirement(desiredValue.getValueName(),
                                desiredValue.getTargetReference(),
                                constraints.copy().withoutAny(ValuePropertyNames.AGGREGATION)
                                        .withOptional(ValuePropertyNames.AGGREGATION)
                                        .with(ValuePropertyNames.AGGREGATION, missing).get());
                    }
                } else {
                    // Unsupported aggregation style
                    return null;
                }
            }
        }
        final Set<ValueRequirement> requirements = getUnderlyingCompiled().getRequirements(context, target,
                desiredValue);
        s_logger.debug("Returning requirements {} for {}", requirements, desiredValue);
        return requirements;
    }

    @Override
    public boolean canHandleMissingRequirements() {
        return getUnderlyingCompiled().canHandleMissingRequirements();
    }

    @Override
    public Set<ValueSpecification> getResults(final FunctionCompilationContext context,
            final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) {
        final Set<ValueSpecification> underlyingResults = getUnderlyingCompiled().getResults(context, target,
                inputs);
        if (underlyingResults == null) {
            s_logger.debug("Underlying returned null inputs {}", inputs);
            return null;
        }
        final String full = getAggregationStyleFull();
        final String missing = getAggregationStyleMissing();
        boolean resultFull = false;
        boolean resultMissing = false;
        for (ValueRequirement input : inputs.values()) {
            final Set<String> inputAgg = input.getConstraints().getValues(ValuePropertyNames.AGGREGATION);
            if (inputAgg != null) {
                if (inputAgg.contains(full)) {
                    resultFull = true;
                }
                if (inputAgg.contains(missing)) {
                    resultMissing = true;
                }
            }
        }
        if (!resultFull && !resultMissing) {
            resultFull = true;
            resultMissing = true;
        }
        final Set<ValueSpecification> results = Sets
                .newHashSetWithExpectedSize(underlyingResults.size() * ((resultFull && resultMissing) ? 2 : 1));
        for (final ValueSpecification underlyingResult : underlyingResults) {
            final ValueProperties properties = underlyingResult.getProperties();
            if ((properties.getProperties() != null) && properties.getProperties().isEmpty()) {
                results.add(underlyingResult);
            } else {
                final ValueProperties.Builder builder = properties.copy();
                if (resultFull) {
                    builder.withoutAny(ValuePropertyNames.AGGREGATION).with(ValuePropertyNames.AGGREGATION,
                            getAggregationStyleFull());
                    results.add(new ValueSpecification(underlyingResult.getValueName(),
                            underlyingResult.getTargetSpecification(), builder.get()));
                }
                if (resultMissing) {
                    builder.withoutAny(ValuePropertyNames.AGGREGATION).with(ValuePropertyNames.AGGREGATION,
                            getAggregationStyleMissing());
                    results.add(new ValueSpecification(underlyingResult.getValueName(),
                            underlyingResult.getTargetSpecification(), builder.get()));
                }
            }
        }
        s_logger.debug("Returning results {} for {}", results, inputs);
        return results;
    }

    @Override
    public Set<ValueRequirement> getAdditionalRequirements(final FunctionCompilationContext context,
            final ComputationTarget target, final Set<ValueSpecification> inputs,
            final Set<ValueSpecification> outputs) {
        final Set<ValueSpecification> underlyingOutputs = Sets.newHashSetWithExpectedSize(outputs.size());
        for (final ValueSpecification output : outputs) {
            final ValueProperties properties = output.getProperties().withoutAny(ValuePropertyNames.AGGREGATION);
            underlyingOutputs.add(
                    new ValueSpecification(output.getValueName(), output.getTargetSpecification(), properties));
        }
        return getUnderlyingCompiled().getAdditionalRequirements(context, target, inputs, underlyingOutputs);
    }

    @Override
    public Instant getEarliestInvocationTime() {
        return getUnderlyingCompiled().getEarliestInvocationTime();
    }

    @Override
    public Instant getLatestInvocationTime() {
        return getUnderlyingCompiled().getLatestInvocationTime();
    }

    @Override
    public FunctionInvoker getFunctionInvoker() {
        final FunctionInvoker underlying = getUnderlyingCompiled().getFunctionInvoker();
        if (underlying == getUnderlyingInvoker()) {
            return this;
        } else {
            return create(underlying);
        }
    }

    // FunctionInvoker

    private Set<ComputedValue> createExecuteResults(final FunctionInputs inputs,
            final Set<ComputedValue> underlyingResults) {
        if (underlyingResults == null) {
            return Collections.emptySet();
        }
        final Set<ComputedValue> results = Sets.newHashSetWithExpectedSize(underlyingResults.size());
        for (final ComputedValue underlyingResult : underlyingResults) {
            final ValueSpecification resultSpec = underlyingResult.getSpecification();
            final ValueProperties.Builder properties = resultSpec.getProperties().copy();
            properties.withoutAny(ValuePropertyNames.AGGREGATION).with(ValuePropertyNames.AGGREGATION,
                    getAggregationStyleMissing());
            results.add(new ComputedValue(new ValueSpecification(resultSpec.getValueName(),
                    resultSpec.getTargetSpecification(), properties.get()), underlyingResult.getValue()));
            if (inputs.getMissingValues().isEmpty()) {
                properties.withoutAny(ValuePropertyNames.AGGREGATION).with(ValuePropertyNames.AGGREGATION,
                        getAggregationStyleFull());
                results.add(new ComputedValue(new ValueSpecification(resultSpec.getValueName(),
                        resultSpec.getTargetSpecification(), properties.get()), underlyingResult.getValue()));
            }
        }
        return results;
    }

    @Override
    public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs,
            final ComputationTarget target, final Set<ValueRequirement> desiredValues)
            throws AsynchronousExecution {
        final Set<ValueRequirement> underlyingDesired = Sets.newHashSetWithExpectedSize(desiredValues.size());
        for (final ValueRequirement desiredValue : desiredValues) {
            final ValueProperties requirementConstraints = desiredValue.getConstraints()
                    .withoutAny(ValuePropertyNames.AGGREGATION);
            underlyingDesired.add(new ValueRequirement(desiredValue.getValueName(),
                    desiredValue.getTargetReference(), requirementConstraints));
        }
        try {
            return createExecuteResults(inputs,
                    getUnderlyingInvoker().execute(executionContext, inputs, target, underlyingDesired));
        } catch (final AsynchronousExecution e) {
            final AsynchronousOperation<Set<ComputedValue>> async = AsynchronousOperation.createSet();
            e.setResultListener(new ResultListener<Set<ComputedValue>>() {
                @Override
                public void operationComplete(final AsynchronousResult<Set<ComputedValue>> result) {
                    try {
                        async.getCallback().setResult(createExecuteResults(inputs, result.getResult()));
                    } catch (final RuntimeException e) {
                        async.getCallback().setException(e);
                    }
                }
            });
            return async.getResult();
        }
    }

    @Override
    public boolean canHandleMissingInputs() {
        return true;
    }

}