/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.integration.marketdata.manipulator.dsl;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import groovy.transform.TimedInterrupt;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Reader;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Period;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.opengamma.DataNotFoundException;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.config.ConfigSource;
import com.opengamma.core.config.impl.ConfigItem;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.engine.view.ViewDefinition;
import com.opengamma.financial.currency.CurrencyPair;
import com.opengamma.id.UniqueId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.util.RegexUtils;
/**
* Utilities for creating and running {@link Simulation}s and {@link Scenario}s.
*/
public final class SimulationUtils {
private static final Logger s_logger = LoggerFactory.getLogger(SimulationUtils.class);
private static List<GroovyAliasable> s_aliases = Lists.newArrayList();
static {
//new enums to register with aliases can be added here:
registerEnumAliases(BucketedShiftType.class);
registerEnumAliases(ScenarioShiftType.class);
}
private static <T extends Enum<T> & GroovyAliasable> void registerEnumAliases(Class<? extends T> enumClazz) { // CSIGNORE (CS doesn't support funky syntax here)
T[] aliases = enumClazz.getEnumConstants();
Collections.addAll(s_aliases, aliases);
}
private SimulationUtils() {
}
/**
* Returns the ID of the latest version of a view definition.
* @param viewDefName The view definition name
* @param configSource A source for looking up the view definition
* @return The ID of the latest version of the named view definition, not null
* @throws DataNotFoundException If no view definition is found with the specified name
*/
public static UniqueId latestViewDefinitionId(String viewDefName, ConfigSource configSource) {
Collection<ConfigItem<ViewDefinition>> viewDefs =
configSource.get(ViewDefinition.class, viewDefName, VersionCorrection.LATEST);
if (viewDefs.isEmpty()) {
throw new DataNotFoundException("No view definition found with name '" + viewDefName + "'");
}
return viewDefs.iterator().next().getValue().getUniqueId();
}
/**
* Runs a Groovy script that defines a {@link Simulation} using the DSL.
* @param groovyScript the script location in the filesystem
* @param parameters the parameters
* @return The simulation defined by the script
*/
public static Simulation createSimulationFromDsl(String groovyScript, Map<String, Object> parameters) {
try {
return runGroovyDslScript(new BufferedReader(new FileReader(groovyScript)), Simulation.class, parameters);
} catch (FileNotFoundException e) {
throw new OpenGammaRuntimeException("Failed to open script file", e);
}
}
/**
* Runs a Groovy script that defines a {@link Simulation} using the DSL.
* @param groovyScript for reading the DSL script
* @param parameters the parameters
* @return The simulation defined by the script
*/
public static Simulation createSimulationFromDsl(Reader groovyScript, Map<String, Object> parameters) {
return runGroovyDslScript(groovyScript, Simulation.class, parameters);
}
/**
* Runs a Groovy script that defines a {@link Scenario} using the DSL.
* @param groovyScript the script location in the filesystem
* @param parameters the parameters
* @return The scenario defined by the script
*/
public static Scenario createScenarioFromDsl(String groovyScript, Map<String, Object> parameters) {
try {
return runGroovyDslScript(new BufferedReader(new FileReader(groovyScript)), Scenario.class, parameters);
} catch (FileNotFoundException e) {
throw new OpenGammaRuntimeException("Failed to open script file", e);
}
}
/**
* Runs a Groovy script that defines a {@link Scenario} using the DSL.
* @param groovyScript for reading the DSL script
* @param parameters the parameters
* @return The scenario defined by the script
*/
public static Scenario createScenarioFromDsl(Reader groovyScript, Map<String, Object> parameters) {
return runGroovyDslScript(groovyScript, Scenario.class, parameters);
}
/**
* Runs a Groovy DSL script and returns the value returned by the script.
* @param scriptReader For reading the script text
* @param expectedType The expected type of the return value
* @param parameters Parameters used by the script, null or empty if the script doesn't need any
* @param <T> The expected type of the return value
* @return The return value of the script, not null
*/
private static <T> T runGroovyDslScript(Reader scriptReader, Class<T> expectedType, Map<String, Object> parameters) {
Map<String, Object> timeoutArgs = ImmutableMap.<String, Object>of("value", 2);
ASTTransformationCustomizer customizer = new ASTTransformationCustomizer(timeoutArgs, TimedInterrupt.class);
CompilerConfiguration config = new CompilerConfiguration();
config.addCompilationCustomizers(customizer);
config.setScriptBaseClass(SimulationScript.class.getName());
Map<String, Object> bindingMap = parameters == null ? Collections.<String, Object>emptyMap() : parameters;
//copy map to ensure that binding is mutable (for use in registerAliases)
Binding binding = new Binding(Maps.newHashMap(bindingMap));
registerAliases(binding);
GroovyShell shell = new GroovyShell(binding, config);
Script script = shell.parse(scriptReader);
Object scriptOutput = script.run();
if (scriptOutput == null) {
throw new IllegalArgumentException("Script " + scriptReader + " didn't return an object");
}
if (expectedType.isInstance(scriptOutput)) {
return expectedType.cast(scriptOutput);
} else {
throw new IllegalArgumentException("Script '" + scriptReader + "' didn't create an object of the expected type. " +
"expected type: " + expectedType.getName() + ", " +
"actual type: " + scriptOutput.getClass().getName() + ", " +
"actual value: " + scriptOutput);
}
}
/**
* Registers aliases in a script's bindings to allow Java enum values to be referred to without being imported
* and qualified with the type name.
* @param binding The script binding in which to register the aliases
*/
/* package */ static void registerAliases(Binding binding) {
for (GroovyAliasable aliasable : s_aliases) {
String alias = aliasable.getGroovyAlias();
if (binding.hasVariable(alias)) {
s_logger.warn("Unable to register default alias {}. Already set in the context as '{}'", alias, binding.getVariable(alias));
continue;
}
binding.setVariable(aliasable.getGroovyAlias(), aliasable);
}
}
/**
* Creates a regular expression pattern from a simple glob string. The special characters recognized in the glob
* string are ? (match any character), * (match any number of characters) and % (same as *). The other characters
* in the glob string are escaped before the pattern is created so it can safely contain regular expression
* characters. Escaping is not supported in the glob string, i.e. there's no way to match any of the special
* characters themselves.
* @param glob The glob string
* @return A pattern for matching the glob
* @deprecated Use RegexUtils.globToPattern(glob)
*/
@Deprecated
public static Pattern patternForGlob(String glob) {
return RegexUtils.globToPattern(glob);
}
public static YieldCurveBucketedShift bucketedShift(Period start, Period end, double shift) {
return new YieldCurveBucketedShift(start, end, shift);
}
public static YieldCurveDataPointShift pointShift(Period tenor, double shift) {
return new YieldCurveDataPointShift(tenor, shift);
}
public static YieldCurvePointShift pointShift(int pointIndex, double shift) {
return new YieldCurvePointShift(pointIndex, shift);
}
/**
* Helper method for creating {@link VolatilitySurfaceShift} instances in the Java API with less code
* @param x The x location of the point to shift
* @param y The y location of the point to shift
* @param shift The shift amount
* @return A {@link VolatilitySurfaceShift} instance built from the arguments
*/
public static VolatilitySurfaceShift volShift(Object x, Object y, Number shift) {
return new VolatilitySurfaceShift(x, y, shift);
}
/* package */ static CurrencyPair getCurrencyPair(ValueSpecification valueSpec) {
ComputationTargetType targetType = valueSpec.getTargetSpecification().getType();
String idValue = valueSpec.getTargetSpecification().getUniqueId().getValue();
if (targetType.equals(CurrencyPair.TYPE)) {
return CurrencyPair.parse(idValue);
/*} else if (targetType.equals(ComputationTargetType.UNORDERED_CURRENCY_PAIR)) {
String quotedPair = valueSpec.getProperties().getStrictValue(ConventionBasedFXRateFunction.QUOTING_CONVENTION_PROPERTY);
return CurrencyPair.parse(quotedPair);*/
} else {
throw new IllegalArgumentException("Only currency pair target types supported. type=" + targetType);
}
}
}