/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.function.config; import java.lang.reflect.Constructor; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.Instant; import com.google.common.collect.ImmutableList; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.core.change.ChangeManager; import com.opengamma.core.change.ChangeProvider; import com.opengamma.core.change.DummyChangeManager; import com.opengamma.engine.function.AbstractFunction; import com.opengamma.engine.function.FunctionDefinition; import com.opengamma.engine.function.FunctionRepository; import com.opengamma.engine.function.InMemoryFunctionRepository; import com.opengamma.engine.function.MarketDataAliasingFunction; import com.opengamma.engine.function.NoOpFunction; import com.opengamma.engine.function.StructureManipulationFunction; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.ReflectionUtils; /** * Constructs and bootstraps an {@link InMemoryFunctionRepository} based on configuration provided in a Fudge-encoded stream. */ public abstract class FunctionRepositoryFactory implements ChangeProvider { private static final Logger s_logger = LoggerFactory.getLogger(FunctionRepositoryFactory.class); /** * The set of functions that are always in a constructed repository regardless of the {@link FunctionConfigurationBundle} document used. */ private static final List<FunctionDefinition> INTRINSIC_FUNCTIONS = ImmutableList.<FunctionDefinition>of(NoOpFunction.INSTANCE, MarketDataAliasingFunction.INSTANCE, StructureManipulationFunction.INSTANCE); /** * The number of functions that are always in a constructed repository regardless of the {@link FunctionConfigurationBundle} document used. For example: * <ul> * <li>The no-op function used for execution suppression ({@link NoOpFunction})</li> * <li>The value aliasing function ({@link MarketDataAliasingFunction})</li> * </ul> */ public static final int INTRINSIC_FUNCTION_COUNT = INTRINSIC_FUNCTIONS.size(); /** * Creates a new repository, with functions from the given version timestamp. * * @param configurationVersion the version timestamp, not null * @return the function repository, not null */ public abstract FunctionRepository constructRepository(Instant configurationVersion); /** * Creates a new factory that always returns the same repository. * * @param staticFunctions the function repository to return, not null * @return the repository factory, not null */ public static FunctionRepositoryFactory constructRepositoryFactory(final FunctionRepository staticFunctions) { ArgumentChecker.notNull(staticFunctions, "staticFunctions"); return new FunctionRepositoryFactory() { @Override public FunctionRepository constructRepository(final Instant configurationVersion) { return staticFunctions; } @Override public ChangeManager changeManager() { return DummyChangeManager.INSTANCE; } }; } /** * Creates a new factory that queries a {@link FunctionConfigurationSource} for function definitions. * <p> * The source is queried with each call, but if it returns the same document then the same repository instance is returned. * * @param dynamicFunctions the configuration source, not null * @return the repository factory, not null */ public static FunctionRepositoryFactory constructRepositoryFactory(final FunctionConfigurationSource dynamicFunctions) { ArgumentChecker.notNull(dynamicFunctions, "dynamicFunctions"); return new FunctionRepositoryFactory() { private FunctionConfigurationBundle _previousConfiguration; private FunctionRepository _previousRepository; @Override public synchronized FunctionRepository constructRepository(final Instant configurationVersion) { final FunctionConfigurationBundle repositoryConfiguration = dynamicFunctions.getFunctionConfiguration(configurationVersion); if ((_previousConfiguration == null) || !_previousConfiguration.equals(repositoryConfiguration)) { _previousConfiguration = repositoryConfiguration; _previousRepository = constructRepository(repositoryConfiguration); } return _previousRepository; } @Override public ChangeManager changeManager() { return dynamicFunctions.changeManager(); } }; } /** * Constructs a repository from the configuration. * * @param configuration the configuration, not null * @return the repository, not null */ public static InMemoryFunctionRepository constructRepository(final FunctionConfigurationBundle configuration) { final InMemoryFunctionRepository repository = constructRepositoryWithIntrinsicFunctions(); if (configuration.getFunctions() != null) { for (final FunctionConfiguration functionConfig : configuration.getFunctions()) { if (functionConfig instanceof ParameterizedFunctionConfiguration) { addParameterizedFunctionConfiguration(repository, (ParameterizedFunctionConfiguration) functionConfig); } else if (functionConfig instanceof StaticFunctionConfiguration) { addStaticFunctionConfiguration(repository, (StaticFunctionConfiguration) functionConfig); } else { s_logger.error("Unhandled function configuration {}, ignoring", functionConfig); } } } return repository; } private static InMemoryFunctionRepository constructRepositoryWithIntrinsicFunctions() { final InMemoryFunctionRepository repository = new InMemoryFunctionRepository(); for (FunctionDefinition intrinsicFunction : INTRINSIC_FUNCTIONS) { repository.addFunction(intrinsicFunction); } return repository; } //------------------------------------------------------------------------- protected static void addParameterizedFunctionConfiguration(final InMemoryFunctionRepository repository, final ParameterizedFunctionConfiguration functionConfig) { try { final Class<?> definitionClass = ReflectionUtils.loadClass(functionConfig.getDefinitionClassName()); final AbstractFunction functionDefinition = createParameterizedFunction(definitionClass, functionConfig.getParameter()); repository.addFunction(functionDefinition); } catch (final RuntimeException ex) { s_logger.error("Unable to add function definition {}, ignoring", functionConfig); s_logger.info("Caught exception", ex); } } protected static AbstractFunction createParameterizedFunction(final Class<?> definitionClass, final List<String> parameterList) { try { constructors: for (final Constructor<?> constructor : definitionClass.getConstructors()) { //CSIGNORE final Class<?>[] parameters = constructor.getParameterTypes(); final Object[] args = new Object[parameters.length]; int used = 0; for (int i = 0; i < parameters.length; i++) { if (parameters[i] == String.class) { if (i < parameterList.size()) { args[i] = parameterList.get(i); used++; } else { continue constructors; } } else { if (i == parameters.length - 1) { used = parameterList.size(); if (parameters[i] == String[].class) { args[i] = parameterList.subList(i, used).toArray(new String[used - i]); } else if (parameters[i].isAssignableFrom(List.class)) { args[i] = parameterList.subList(i, used); } else if (parameters[i].isAssignableFrom(Set.class)) { args[i] = new HashSet<String>(parameterList.subList(i, used)); } else { continue constructors; } } else { continue constructors; } } } if (used != parameterList.size()) { continue; } return (AbstractFunction) constructor.newInstance(args); } throw new NoSuchMethodException("No suitable constructor found: " + definitionClass + ": " + parameterList); } catch (final RuntimeException ex) { throw ex; } catch (final Exception ex) { throw new OpenGammaRuntimeException("Unable to create static function: " + definitionClass + ": " + parameterList, ex); } } //------------------------------------------------------------------------- protected static void addStaticFunctionConfiguration(final InMemoryFunctionRepository repository, final StaticFunctionConfiguration functionConfig) { try { final Class<?> definitionClass = ReflectionUtils.loadClass(functionConfig.getDefinitionClassName()); final AbstractFunction functionDefinition = createStaticFunction(definitionClass); repository.addFunction(functionDefinition); } catch (final RuntimeException ex) { s_logger.error("Unable to add function definition {}, ignoring", functionConfig); s_logger.info("Caught exception", ex); } } protected static AbstractFunction createStaticFunction(final Class<?> definitionClass) { try { return (AbstractFunction) definitionClass.newInstance(); } catch (final RuntimeException ex) { throw ex; } catch (final Exception ex) { throw new OpenGammaRuntimeException("Unable to create static function: " + definitionClass, ex); } } }