com.opengamma.strata.calc.runner.DerivedCalculationFunctionWrapper.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.strata.calc.runner.DerivedCalculationFunctionWrapper.java

Source

/**
 * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.calc.runner;

import static java.util.stream.Collectors.toList;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.opengamma.strata.basics.CalculationTarget;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.calc.Measure;
import com.opengamma.strata.collect.MapStream;
import com.opengamma.strata.collect.result.FailureReason;
import com.opengamma.strata.collect.result.Result;
import com.opengamma.strata.data.scenario.ScenarioMarketData;

/**
 * A {@link CalculationFunction} implementation which wraps a {@link DerivedCalculationFunction}.
 * <p>
 * A derived calculation function calculates a measure using the measures calculated by another function.
 * This functions takes care of calling the delegate function and passing the results to the derived function.
 * <p>
 * Most of the logic is concerned with bookkeeping - packing and unpacking maps of measures and results before
 * passing them on or returning them.
 */
class DerivedCalculationFunctionWrapper<T extends CalculationTarget, R> implements CalculationFunction<T> {

    /**
     * The derived calculation function which calculates one measure.
     * <p>
     * The inputs to the measure can include measures calculated by the delegate calculation function.
     */
    private final DerivedCalculationFunction<T, R> derivedFunction;

    /**
     * A calculation function whose results can be used by the derived calculation function.
     */
    private final CalculationFunction<T> delegate;

    /**
     * The measures supported by this function; the union of the measures supported by the delegate function and
     * the derived function.
     */
    private final Set<Measure> supportedMeasures;

    /**
     * True if the delegate function supports all measures required by the calculation function.
     * If this is true the calculation function can be invoked.
     * If it is false the only measures which can be calculated are the measures supported by the delegate.
     */
    private final boolean requiredMeasuresSupported;

    /**
     * Creates a new function which invokes the delegate function, passes the result to the derived function
     * and returns the combined results.
     *
     * @param derivedFunction  a function which calculates one measure using the measure values calculated by the other function
     * @param delegate  a function which calculates multiple measures
     */
    DerivedCalculationFunctionWrapper(DerivedCalculationFunction<T, R> derivedFunction,
            CalculationFunction<T> delegate) {

        this.derivedFunction = derivedFunction;
        this.delegate = delegate;

        Set<Measure> delegateMeasures = delegate.supportedMeasures();
        this.requiredMeasuresSupported = delegateMeasures.containsAll(derivedFunction.requiredMeasures());
        this.supportedMeasures = requiredMeasuresSupported
                ? ImmutableSet.<Measure>builder().addAll(delegateMeasures).add(derivedFunction.measure()).build()
                : delegateMeasures;
    }

    @Override
    public Class<T> targetType() {
        return derivedFunction.targetType();
    }

    @Override
    public Set<Measure> supportedMeasures() {
        return supportedMeasures;
    }

    @Override
    public Optional<String> identifier(T target) {
        return delegate.identifier(target);
    }

    @Override
    public Currency naturalCurrency(T target, ReferenceData refData) {
        return delegate.naturalCurrency(target, refData);
    }

    @Override
    public FunctionRequirements requirements(T target, Set<Measure> measures, CalculationParameters parameters,
            ReferenceData refData) {

        FunctionRequirements delegateRequirements = delegate.requirements(target, measures, parameters, refData);
        FunctionRequirements functionRequirements = derivedFunction.requirements(target, parameters, refData);
        return delegateRequirements.combinedWith(functionRequirements);
    }

    @Override
    public Map<Measure, Result<?>> calculate(T target, Set<Measure> measures, CalculationParameters parameters,
            ScenarioMarketData marketData, ReferenceData refData) {

        // The caller didn't ask for the derived measure so just return the measures calculated by the delegate
        Measure derivedMeasure = derivedFunction.measure();
        if (!measures.contains(derivedMeasure)) {
            return delegate.calculate(target, measures, parameters, marketData, refData);
        }
        // Add the measures required to calculate the derived measure to the measures requested by the caller
        Set<Measure> allRequiredMeasures = Sets.union(measures, derivedFunction.requiredMeasures());
        Set<Measure> requiredMeasures = Sets.difference(allRequiredMeasures, ImmutableSet.of(derivedMeasure));
        Map<Measure, Result<?>> delegateResults = delegate.calculate(target, requiredMeasures, parameters,
                marketData, refData);

        // Calculate the derived measure
        Result<?> result = calculateMeasure(target, delegateResults, parameters, marketData, refData);

        // The results containing only the requested measures and not including extra measures that were inserted above
        // Also filter out any results for calculationFunction.measure(). There will be failures from functions below
        // that don't support that measure.
        Map<Measure, Result<?>> requestedResults = MapStream.of(delegateResults).filterKeys(measures::contains)
                .filterKeys(measure -> !measure.equals(derivedMeasure)).toMap();

        return ImmutableMap.<Measure, Result<?>>builder().put(derivedMeasure, result).putAll(requestedResults)
                .build();
    }

    private Result<?> calculateMeasure(T target, Map<Measure, Result<?>> delegateResults,
            CalculationParameters parameters, ScenarioMarketData marketData, ReferenceData refData) {

        if (!requiredMeasuresSupported) {
            // Can't calculate the measure if the delegate can't calculate its inputs
            return Result.failure(FailureReason.NOT_APPLICABLE,
                    "The delegate function cannot calculate the required measures. Required measures: {}, "
                            + "supported measures: {}, delegate {}",
                    derivedFunction.requiredMeasures(), delegate.supportedMeasures(), delegate);
        }
        if (!delegateResults.keySet().containsAll(derivedFunction.requiredMeasures())) {
            // There's a bug in the delegate function - it claims to support the required measures but didn't return
            // a result for all of them.
            return Result.failure(FailureReason.CALCULATION_FAILED,
                    "Delegate did not return the expected measures. Required {}, actual {}, delegate {}",
                    derivedFunction.requiredMeasures(), delegateResults.keySet(), delegate);
        }
        // Check whether all the required measures were successfully calculated
        List<Result<?>> failures = MapStream.of(delegateResults)
                .filterKeys(derivedFunction.requiredMeasures()::contains).map(entry -> entry.getValue())
                .filter(result -> result.isFailure()).collect(toList());

        if (!failures.isEmpty()) {
            return Result.failure(failures);
        }
        // Unwrap the results before passing them to the function
        Map<Measure, Object> resultValues = MapStream.of(delegateResults)
                .filterKeys(derivedFunction.requiredMeasures()::contains)
                .mapValues(result -> (Object) result.getValue()) // This compiler needs this cast. Which seems odd.
                .toMap();
        return Result.of(() -> derivedFunction.calculate(target, resultValues, parameters, marketData, refData));
    }
}