/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.config; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import javax.annotation.Nullable; import javax.inject.Provider; import org.joda.beans.Bean; import org.joda.beans.BeanBuilder; import org.joda.beans.BeanDefinition; import org.joda.beans.ImmutableBean; import org.joda.beans.ImmutableConstructor; import org.joda.beans.JodaBeanUtils; import org.joda.beans.MetaProperty; import org.joda.beans.Property; import org.joda.beans.PropertyDefinition; import org.joda.beans.impl.direct.DirectFieldsBeanBuilder; import org.joda.beans.impl.direct.DirectMetaBean; import org.joda.beans.impl.direct.DirectMetaProperty; import org.joda.beans.impl.direct.DirectMetaPropertyMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import com.opengamma.sesame.function.Parameter; import com.opengamma.sesame.function.scenarios.ScenarioFunction; import com.opengamma.util.ArgumentChecker; /** * Configuration for individual functions in the function model. * <p> * Provides the implementation types for function interfaces and the * arguments for creating function instances. */ @BeanDefinition(builderScope = "private") public class FunctionModelConfig implements ImmutableBean { /** * Singleton instance of an empty configuration. * <p> * Always returns a null implementation class and empty arguments. */ public static final FunctionModelConfig EMPTY = new FunctionModelConfig(ImmutableMap.<Class<?>, Class<?>>of(), ImmutableMap.<Class<?>, FunctionArguments>of()); /** * The function implementation classes keyed by function interface. * This only needs to be populated if the implementation is not the default. */ @PropertyDefinition(validate = "notNull") private final ImmutableMap<Class<?>, Class<?>> _implementations; /** The user-specified function arguments keyed by function implementation. */ @PropertyDefinition(validate = "notNull") private final ImmutableMap<Class<?>, FunctionArguments> _arguments; /** * Decorator types, keyed by the type of the function interface they decorate. * <p> * The first decorator in the set is the first one that is invoked, i.e. the outermost. Each decorator * delegates to the next decorator except the last, which delegates to the underlying function. */ @PropertyDefinition(validate = "notNull", get = "private") private final Map<Class<?>, LinkedHashSet<Class<?>>> _decoratorsByFn; /** * Function implementation classes keyed by the parameter where they are injected. * The parameter is a constructor parameter of a decorator and the class is the type being decorated. * The value class can be a decorator if there are multiple decorators attached to the same function. */ private final ImmutableMap<Parameter, Class<?>> _implementationByParameter; @ImmutableConstructor private FunctionModelConfig(Map<Class<?>, Class<?>> implementations, Map<Class<?>, FunctionArguments> arguments, Map<Class<?>, LinkedHashSet<Class<?>>> decoratorsByFn) { _implementations = ImmutableMap.copyOf(implementations); _arguments = ImmutableMap.copyOf(arguments); _decoratorsByFn = ImmutableMap.copyOf(decoratorsByFn); ImmutableMap.Builder<Parameter, Class<?>> builder = ImmutableMap.builder(); for (Map.Entry<Class<?>, LinkedHashSet<Class<?>>> entry : decoratorsByFn.entrySet()) { Class<?> functionInterface = entry.getKey(); Set<Class<?>> decorators = entry.getValue(); // make a list of decorators because we need random access List<Class<?>> decoratorList = new ArrayList<>(decorators); // use (size - 1) because we want to skip the last element in the list // the last decorator in the list is the one that delegates to the real function for (int i = 0; i < decoratorList.size() - 1; i++) { Class<?> firstDecoratorType = decoratorList.get(i); Class<?> secondDecoratorType = decoratorList.get(i + 1); Constructor<?> constructor = EngineUtils.getConstructor(firstDecoratorType); // the constructor parameter where the delegate is passed to the decorator Parameter delegateParameter = Parameter.ofType(functionInterface, constructor); builder.put(delegateParameter, secondDecoratorType); } Class<?> lastDecoratorType = decoratorList.get(decoratorList.size() - 1); Constructor<?> constructor = EngineUtils.getConstructor(lastDecoratorType); Parameter delegateParameter = Parameter.ofType(functionInterface, constructor); Class<?> functionImpl = _implementations.get(functionInterface); // for the last decorator I want to use the function type to look up in implementations if (functionImpl != null) { builder.put(delegateParameter, functionImpl); } } _implementationByParameter = builder.build(); } /** * Creates new configuration using the specified function implementations and arguments. * * @param implementations the implementation classes to use for functions, keyed by function interface * @param arguments the arguments for the function implementations, keyed by implementation type */ public FunctionModelConfig(Map<Class<?>, Class<?>> implementations, Map<Class<?>, FunctionArguments> arguments) { this(implementations, arguments, Collections.<Class<?>, LinkedHashSet<Class<?>>>emptyMap()); } /** * Creates new configuration using the specified function implementations. * * @param implementations the implementation classes to use for functions, keyed by function interface */ public FunctionModelConfig(Map<Class<?>, Class<?>> implementations) { this(implementations, Collections.<Class<?>, FunctionArguments>emptyMap()); } /** * Gets the implementation that should be used for creating instances of a type for injecting into a constructor. * <p> * The result implementation can be: * <ul> * <li>An implementation of an interface</li> * <li>A {@link Provider} that can provide the implementation</li> * </ul> * * @param parameter the constructor parameter for which an implementation is required * @param functionType the type of the function whose implementation is required, normally an interface * @return the implementation that should be used, null if unknown */ public Class<?> getFunctionImplementation(@Nullable Parameter parameter, Class<?> functionType) { ArgumentChecker.notNull(functionType, "functionType"); if (parameter != null) { Class<?> type = _implementationByParameter.get(parameter); if (type != null) { return type; } } LinkedHashSet<Class<?>> decorators = _decoratorsByFn.get(functionType); if (decorators != null) { return decorators.iterator().next(); } return _implementations.get(functionType); } /** * Gets the arguments for a function. * * @param functionType the type of function, not null * @return the arguments, empty if not found, not null */ public FunctionArguments getFunctionArguments(Class<?> functionType) { FunctionArguments functionArguments = _arguments.get(functionType); return functionArguments == null ? FunctionArguments.EMPTY : functionArguments; } /** * Merges this configuration with another set, this configuration takes priority where there are duplicates. * * @param other configuration to merge with * @return the union of the configuration with settings from this instance taking priority */ public FunctionModelConfig mergedWith(FunctionModelConfig other, FunctionModelConfig... others) { ArgumentChecker.notNull(other, "other"); FunctionModelConfig config = this.merge(other); for (FunctionModelConfig otherConfig : others) { config = config.merge(otherConfig); } return config; } private FunctionModelConfig merge(FunctionModelConfig other) { Map<Class<?>, Class<?>> implementations = new HashMap<>(other._implementations); implementations.putAll(_implementations); Set<Class<?>> functionTypesWithArgs = Sets.union(_arguments.keySet(), other._arguments.keySet()); Map<Class<?>, FunctionArguments> arguments = new HashMap<>(); for (Class<?> fnType : functionTypesWithArgs) { arguments.put(fnType, mergeArguments(_arguments.get(fnType), other._arguments.get(fnType))); } Set<Class<?>> decoratedFunctionTypes = Sets.union(_decoratorsByFn.keySet(), other._decoratorsByFn.keySet()); Map<Class<?>, LinkedHashSet<Class<?>>> decoratorsByFn = new HashMap<>(); for (Class<?> fnType : decoratedFunctionTypes) { decoratorsByFn.put(fnType, mergeDecorators(_decoratorsByFn.get(fnType), other._decoratorsByFn.get(fnType))); } return new FunctionModelConfig(implementations, arguments, decoratorsByFn); } /** * Returns a copy of this configuration decorated with a decorator. * * @param decorator a decorator type * @return a copy of this configuration decorated with the decorator */ public FunctionModelConfig decoratedWith(Class<? extends ScenarioFunction<?, ?>> decorator) { return decoratedWith(decorator, Collections.<Class<?>, FunctionArguments>emptyMap()); } /** * Returns a copy of this configuration decorated with a decorator. * * @param scenarioFunction a decorator type * @param arguments function arguments for building the decorator instance * @return a copy of this configuration decorated with the decorator */ public FunctionModelConfig decoratedWith(Class<? extends ScenarioFunction<?, ?>> scenarioFunction, Map<Class<?>, FunctionArguments> arguments) { ArgumentChecker.notNull(scenarioFunction, "decorator"); Set<Class<?>> functionTypesWithArgs = Sets.union(_arguments.keySet(), arguments.keySet()); Map<Class<?>, FunctionArguments> mergedArguments = new HashMap<>(); for (Class<?> fnType : functionTypesWithArgs) { mergedArguments.put(fnType, mergeArguments(_arguments.get(fnType), arguments.get(fnType))); } // get the set of interfaces implemented by the decorator // only one is supported at the moment apart from ScenarioDecorator which is mandatory Set<Class<?>> interfaces = new HashSet<>(EngineUtils.getInterfaces(scenarioFunction)); interfaces.remove(ScenarioFunction.class); if (interfaces.size() != 1) { throw new IllegalArgumentException("Decorator class " + scenarioFunction.getName() + " must implement " + "ScenarioFunction and one other interface"); } Class<?> interfaceType = interfaces.iterator().next(); LinkedHashSet<Class<?>> decorators = _decoratorsByFn.get(interfaceType); LinkedHashSet<Class<?>> mergedDecorators = new LinkedHashSet<>(); mergedDecorators.add(scenarioFunction); if (decorators != null) { mergedDecorators.addAll(decorators); } Map<Class<?>, LinkedHashSet<Class<?>>> decoratorsByFn = new HashMap<>(_decoratorsByFn); decoratorsByFn.put(interfaceType, mergedDecorators); return new FunctionModelConfig(_implementations, mergedArguments, decoratorsByFn); } /** * Null safe merge of arguments. Both arguments are nullable but only one can be null in any given call. * If there are duplicates the arguments from {@code args1} take priority. * * @param args1 some arguments * @param args2 some arguments * @return the merged arguments */ private static FunctionArguments mergeArguments(@Nullable FunctionArguments args1, @Nullable FunctionArguments args2) { if (args1 == null) { return args2; } else if (args2 == null) { return args1; } else { return args1.mergedWith(args2); } } /** * Null safe merge of decorators. Both arguments are nullable but only one can be null in any given call. * * @param decorators1 the first set of decorator types * @param decorators2 the second set of decorator types * @return the merged set of decorator types */ private static LinkedHashSet<Class<?>> mergeDecorators(@Nullable LinkedHashSet<Class<?>> decorators1, @Nullable LinkedHashSet<Class<?>> decorators2) { if (decorators1 == null) { return decorators2; } else if (decorators2 == null) { return decorators1; } else { LinkedHashSet<Class<?>> merged = new LinkedHashSet<>(); merged.addAll(decorators1); merged.addAll(decorators2); return merged; } } //------------------------- AUTOGENERATED START ------------------------- ///CLOVER:OFF /** * The meta-bean for {@code FunctionModelConfig}. * @return the meta-bean, not null */ public static FunctionModelConfig.Meta meta() { return FunctionModelConfig.Meta.INSTANCE; } static { JodaBeanUtils.registerMetaBean(FunctionModelConfig.Meta.INSTANCE); } @Override public FunctionModelConfig.Meta metaBean() { return FunctionModelConfig.Meta.INSTANCE; } @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 function implementation classes keyed by function interface. * This only needs to be populated if the implementation is not the default. * @return the value of the property, not null */ public ImmutableMap<Class<?>, Class<?>> getImplementations() { return _implementations; } //----------------------------------------------------------------------- /** * Gets the user-specified function arguments keyed by function implementation. * @return the value of the property, not null */ public ImmutableMap<Class<?>, FunctionArguments> getArguments() { return _arguments; } //----------------------------------------------------------------------- /** * Gets decorator types, keyed by the type of the function interface they decorate. * <p> * The first decorator in the set is the first one that is invoked, i.e. the outermost. Each decorator * delegates to the next decorator except the last, which delegates to the underlying function. * @return the value of the property, not null */ private Map<Class<?>, LinkedHashSet<Class<?>>> getDecoratorsByFn() { return _decoratorsByFn; } //----------------------------------------------------------------------- @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj != null && obj.getClass() == this.getClass()) { FunctionModelConfig other = (FunctionModelConfig) obj; return JodaBeanUtils.equal(getImplementations(), other.getImplementations()) && JodaBeanUtils.equal(getArguments(), other.getArguments()) && JodaBeanUtils.equal(getDecoratorsByFn(), other.getDecoratorsByFn()); } return false; } @Override public int hashCode() { int hash = getClass().hashCode(); hash = hash * 31 + JodaBeanUtils.hashCode(getImplementations()); hash = hash * 31 + JodaBeanUtils.hashCode(getArguments()); hash = hash * 31 + JodaBeanUtils.hashCode(getDecoratorsByFn()); return hash; } @Override public String toString() { StringBuilder buf = new StringBuilder(128); buf.append("FunctionModelConfig{"); int len = buf.length(); toString(buf); if (buf.length() > len) { buf.setLength(buf.length() - 2); } buf.append('}'); return buf.toString(); } protected void toString(StringBuilder buf) { buf.append("implementations").append('=').append(JodaBeanUtils.toString(getImplementations())).append(',').append(' '); buf.append("arguments").append('=').append(JodaBeanUtils.toString(getArguments())).append(',').append(' '); buf.append("decoratorsByFn").append('=').append(JodaBeanUtils.toString(getDecoratorsByFn())).append(',').append(' '); } //----------------------------------------------------------------------- /** * The meta-bean for {@code FunctionModelConfig}. */ public static class Meta extends DirectMetaBean { /** * The singleton instance of the meta-bean. */ static final Meta INSTANCE = new Meta(); /** * The meta-property for the {@code implementations} property. */ @SuppressWarnings({"unchecked", "rawtypes" }) private final MetaProperty<ImmutableMap<Class<?>, Class<?>>> _implementations = DirectMetaProperty.ofImmutable( this, "implementations", FunctionModelConfig.class, (Class) ImmutableMap.class); /** * The meta-property for the {@code arguments} property. */ @SuppressWarnings({"unchecked", "rawtypes" }) private final MetaProperty<ImmutableMap<Class<?>, FunctionArguments>> _arguments = DirectMetaProperty.ofImmutable( this, "arguments", FunctionModelConfig.class, (Class) ImmutableMap.class); /** * The meta-property for the {@code decoratorsByFn} property. */ @SuppressWarnings({"unchecked", "rawtypes" }) private final MetaProperty<Map<Class<?>, LinkedHashSet<Class<?>>>> _decoratorsByFn = DirectMetaProperty.ofImmutable( this, "decoratorsByFn", FunctionModelConfig.class, (Class) Map.class); /** * The meta-properties. */ private final Map<String, MetaProperty<?>> _metaPropertyMap$ = new DirectMetaPropertyMap( this, null, "implementations", "arguments", "decoratorsByFn"); /** * Restricted constructor. */ protected Meta() { } @Override protected MetaProperty<?> metaPropertyGet(String propertyName) { switch (propertyName.hashCode()) { case 643812097: // implementations return _implementations; case -2035517098: // arguments return _arguments; case -1632890057: // decoratorsByFn return _decoratorsByFn; } return super.metaPropertyGet(propertyName); } @Override public BeanBuilder<? extends FunctionModelConfig> builder() { return new FunctionModelConfig.Builder(); } @Override public Class<? extends FunctionModelConfig> beanType() { return FunctionModelConfig.class; } @Override public Map<String, MetaProperty<?>> metaPropertyMap() { return _metaPropertyMap$; } //----------------------------------------------------------------------- /** * The meta-property for the {@code implementations} property. * @return the meta-property, not null */ public final MetaProperty<ImmutableMap<Class<?>, Class<?>>> implementations() { return _implementations; } /** * The meta-property for the {@code arguments} property. * @return the meta-property, not null */ public final MetaProperty<ImmutableMap<Class<?>, FunctionArguments>> arguments() { return _arguments; } /** * The meta-property for the {@code decoratorsByFn} property. * @return the meta-property, not null */ public final MetaProperty<Map<Class<?>, LinkedHashSet<Class<?>>>> decoratorsByFn() { return _decoratorsByFn; } //----------------------------------------------------------------------- @Override protected Object propertyGet(Bean bean, String propertyName, boolean quiet) { switch (propertyName.hashCode()) { case 643812097: // implementations return ((FunctionModelConfig) bean).getImplementations(); case -2035517098: // arguments return ((FunctionModelConfig) bean).getArguments(); case -1632890057: // decoratorsByFn return ((FunctionModelConfig) bean).getDecoratorsByFn(); } return super.propertyGet(bean, propertyName, quiet); } @Override protected void propertySet(Bean bean, String propertyName, Object newValue, boolean quiet) { metaProperty(propertyName); if (quiet) { return; } throw new UnsupportedOperationException("Property cannot be written: " + propertyName); } } //----------------------------------------------------------------------- /** * The bean-builder for {@code FunctionModelConfig}. */ private static class Builder extends DirectFieldsBeanBuilder<FunctionModelConfig> { private Map<Class<?>, Class<?>> _implementations = new HashMap<Class<?>, Class<?>>(); private Map<Class<?>, FunctionArguments> _arguments = new HashMap<Class<?>, FunctionArguments>(); private Map<Class<?>, LinkedHashSet<Class<?>>> _decoratorsByFn = new HashMap<Class<?>, LinkedHashSet<Class<?>>>(); /** * Restricted constructor. */ protected Builder() { } //----------------------------------------------------------------------- @Override public Object get(String propertyName) { switch (propertyName.hashCode()) { case 643812097: // implementations return _implementations; case -2035517098: // arguments return _arguments; case -1632890057: // decoratorsByFn return _decoratorsByFn; default: throw new NoSuchElementException("Unknown property: " + propertyName); } } @SuppressWarnings("unchecked") @Override public Builder set(String propertyName, Object newValue) { switch (propertyName.hashCode()) { case 643812097: // implementations this._implementations = (Map<Class<?>, Class<?>>) newValue; break; case -2035517098: // arguments this._arguments = (Map<Class<?>, FunctionArguments>) newValue; break; case -1632890057: // decoratorsByFn this._decoratorsByFn = (Map<Class<?>, LinkedHashSet<Class<?>>>) newValue; break; default: throw new NoSuchElementException("Unknown property: " + propertyName); } return this; } @Override public Builder set(MetaProperty<?> property, Object value) { super.set(property, value); return this; } @Override public Builder setString(String propertyName, String value) { setString(meta().metaProperty(propertyName), value); return this; } @Override public Builder setString(MetaProperty<?> property, String value) { super.setString(property, value); return this; } @Override public Builder setAll(Map<String, ? extends Object> propertyValueMap) { super.setAll(propertyValueMap); return this; } @Override public FunctionModelConfig build() { return new FunctionModelConfig( _implementations, _arguments, _decoratorsByFn); } //----------------------------------------------------------------------- @Override public String toString() { StringBuilder buf = new StringBuilder(128); buf.append("FunctionModelConfig.Builder{"); int len = buf.length(); toString(buf); if (buf.length() > len) { buf.setLength(buf.length() - 2); } buf.append('}'); return buf.toString(); } protected void toString(StringBuilder buf) { buf.append("implementations").append('=').append(JodaBeanUtils.toString(_implementations)).append(',').append(' '); buf.append("arguments").append('=').append(JodaBeanUtils.toString(_arguments)).append(',').append(' '); buf.append("decoratorsByFn").append('=').append(JodaBeanUtils.toString(_decoratorsByFn)).append(',').append(' '); } } ///CLOVER:ON //-------------------------- AUTOGENERATED END -------------------------- }