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

Java tutorial

Introduction

Here is the source code for com.opengamma.strata.calc.runner.DefaultCalculationFunctions.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.groupingBy;
import static java.util.stream.Collectors.toList;

import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;

import org.joda.beans.BeanDefinition;
import org.joda.beans.ImmutableBean;
import org.joda.beans.ImmutableValidator;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaBean;
import org.joda.beans.Property;
import org.joda.beans.PropertyDefinition;
import org.joda.beans.impl.light.LightMetaBean;

import com.google.common.collect.ImmutableMap;
import com.opengamma.strata.basics.CalculationTarget;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.MapStream;

/**
 * The default calculation functions implementation.
 * <p>
 * This provides the complete set of functions that will be used in a calculation.
 * Each {@link CalculationFunction} handles a specific type of {@link CalculationTarget},
 * thus the functions are keyed in a {@code Map} by the target type {@code Class}.
 */
@BeanDefinition(style = "light")
final class DefaultCalculationFunctions implements CalculationFunctions, ImmutableBean, Serializable {

    /**
     * An empty instance.
     */
    static final DefaultCalculationFunctions EMPTY = new DefaultCalculationFunctions(ImmutableMap.of());

    /**
     * The functions, keyed by target type.
     */
    @PropertyDefinition(validate = "notNull")
    private final ImmutableMap<Class<?>, CalculationFunction<?>> functions;

    //-------------------------------------------------------------------------
    /**
     * Obtains an instance from the specified functions.
     * <p>
     * The map will be validated to ensure the {@code Class} is consistent with
     * {@link CalculationFunction#targetType()}.
     * 
     * @param functions  the functions
     * @return the calculation functions
     */
    static DefaultCalculationFunctions of(Map<Class<?>, ? extends CalculationFunction<?>> functions) {
        return new DefaultCalculationFunctions(ImmutableMap.copyOf(functions));
    }

    @ImmutableValidator
    private void validate() {
        for (Entry<Class<?>, CalculationFunction<?>> entry : functions.entrySet()) {
            ArgChecker.isTrue(entry.getValue().targetType().isAssignableFrom(entry.getKey()),
                    "Invalid map, key and function mismatch: {} and {}", entry.getKey(),
                    entry.getValue().targetType());
        }
    }

    //-------------------------------------------------------------------------
    @Override
    public <T extends CalculationTarget> CalculationFunction<? super T> getFunction(T target) {
        @SuppressWarnings("unchecked")
        CalculationFunction<? super T> function = (CalculationFunction<? super T>) functions.get(target.getClass());
        return function != null ? function : MissingConfigCalculationFunction.INSTANCE;
    }

    @Override
    public <T extends CalculationTarget> Optional<CalculationFunction<? super T>> findFunction(T target) {
        @SuppressWarnings("unchecked")
        CalculationFunction<? super T> function = (CalculationFunction<? super T>) functions.get(target.getClass());
        return Optional.ofNullable(function);
    }

    @Override
    public CalculationFunctions composedWith(DerivedCalculationFunction<?, ?>... derivedFunctions) {
        // Override the default implementation for efficiency.
        // The default implementation uses DerivedCalculationFunctions which creates a function instance for every target.
        // This class can do better and can create a single function instance for each target type.
        Map<Class<?>, List<DerivedCalculationFunction<?, ?>>> functionsByTargetType = Arrays
                .stream(derivedFunctions).collect(groupingBy(fn -> fn.targetType()));

        // The calculation functions wrapped up with the derived functions which use them
        List<CalculationFunction<?>> wrappedFunctions = MapStream.of(functionsByTargetType)
                .map((targetType, fns) -> wrap(targetType, fns)).collect(toList());

        Map<Class<?>, CalculationFunction<?>> allFunctions = new HashMap<>(functions);
        wrappedFunctions.forEach(fn -> allFunctions.put(fn.targetType(), fn));
        return CalculationFunctions.of(allFunctions);
    }

    @SuppressWarnings("unchecked")
    private <T extends CalculationTarget, R> CalculationFunction<?> wrap(Class<?> targetType,
            List<DerivedCalculationFunction<?, ?>> derivedFunctions) {

        CalculationFunction<? super T> function = (CalculationFunction<? super T>) functions.get(targetType);

        if (function == null) {
            function = MissingConfigCalculationFunction.INSTANCE;
        }
        CalculationFunction<? super T> wrappedFn = function;

        for (DerivedCalculationFunction<?, ?> derivedFn : derivedFunctions) {
            // These casts are necessary because the type information is lost when the functions are stored in the map.
            // They are safe because T is the target type which is is the map key and R isn't actually used
            CalculationFunction<T> wrappedFnCast = (CalculationFunction<T>) wrappedFn;
            DerivedCalculationFunction<T, R> derivedFnCast = (DerivedCalculationFunction<T, R>) derivedFn;
            wrappedFn = new DerivedCalculationFunctionWrapper<>(derivedFnCast, wrappedFnCast);
        }
        return wrappedFn;
    }

    //------------------------- AUTOGENERATED START -------------------------
    ///CLOVER:OFF
    /**
     * The meta-bean for {@code DefaultCalculationFunctions}.
     */
    private static MetaBean META_BEAN = LightMetaBean.of(DefaultCalculationFunctions.class);

    /**
     * The meta-bean for {@code DefaultCalculationFunctions}.
     * @return the meta-bean, not null
     */
    public static MetaBean meta() {
        return META_BEAN;
    }

    static {
        JodaBeanUtils.registerMetaBean(META_BEAN);
    }

    /**
     * The serialization version id.
     */
    private static final long serialVersionUID = 1L;

    private DefaultCalculationFunctions(Map<Class<?>, CalculationFunction<?>> functions) {
        JodaBeanUtils.notNull(functions, "functions");
        this.functions = ImmutableMap.copyOf(functions);
        validate();
    }

    @Override
    public MetaBean metaBean() {
        return META_BEAN;
    }

    @Override
    public <R> Property<R> property(String propertyName) {
        return metaBean().<R>metaProperty(propertyName).createProperty(this);
    }

    @Override
    public Set<String> propertyNames() {
        return metaBean().metaPropertyMap().keySet();
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the functions, keyed by target type.
     * @return the value of the property, not null
     */
    public ImmutableMap<Class<?>, CalculationFunction<?>> getFunctions() {
        return functions;
    }

    //-----------------------------------------------------------------------
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj != null && obj.getClass() == this.getClass()) {
            DefaultCalculationFunctions other = (DefaultCalculationFunctions) obj;
            return JodaBeanUtils.equal(functions, other.functions);
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = getClass().hashCode();
        hash = hash * 31 + JodaBeanUtils.hashCode(functions);
        return hash;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder(64);
        buf.append("DefaultCalculationFunctions{");
        buf.append("functions").append('=').append(JodaBeanUtils.toString(functions));
        buf.append('}');
        return buf.toString();
    }

    ///CLOVER:ON
    //-------------------------- AUTOGENERATED END --------------------------
}