/**
* 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 java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import com.google.common.collect.Maps;
import com.opengamma.DataNotFoundException;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.util.ArgumentChecker;
import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.lang.GroovyObjectSupport;
import groovy.lang.Script;
/**
* Base class for scripts that create {@link Simulation}s and {@link Scenario}s. The methods in this class are available
* in the script and form the basis of a DSL.
*/
public abstract class SimulationScript extends Script {
/** The currently building simulation. */
private Simulation _simulation;
/** The currently building scenario. */
private Scenario _scenario;
public SimulationScript() {
initialize();
}
public SimulationScript(Binding binding) {
super(binding);
initialize();
}
// TODO is there a nicer way to do this?
private void initialize() {
InputStream scriptStream = SimulationScript.class.getResourceAsStream("InitializeScript.groovy");
try {
evaluate(IOUtils.toString(scriptStream));
} catch (IOException e) {
throw new OpenGammaRuntimeException("Failed to initialize DSL script", e);
}
}
/**
* Defines a method in the DSL taking a block to define the script parameters and their types. It checks the
* parameters are available in the script's binding and that they have the expected type.
* <pre>
* parameters {
* foo String
* bar Number
* }
* </pre>
* @param body The block that defines the script's parameters
*/
public void parameters(Closure body) {
ParametersDelegate parametersDelegate = new ParametersDelegate();
body.setDelegate(parametersDelegate);
body.setResolveStrategy(Closure.DELEGATE_FIRST);
body.call();
// check the parameters are all in the binding and have the expected types
Binding binding = getBinding();
Map<String, Class<?>> parameters = parametersDelegate.getParameters();
for (Map.Entry<String, Class<?>> entry : parameters.entrySet()) {
String varName = entry.getKey();
Class<?> varType = entry.getValue();
if (!binding.hasVariable(varName)) {
throw new DataNotFoundException("Parameter named " + varName + " not found");
}
Object varValue = binding.getVariable(varName);
if (!varType.isInstance(varValue)) {
throw new IllegalArgumentException("Parameter " + varName + " has type " + varValue.getClass().getName() + ", " +
"required type is " + varType.getName());
}
}
}
/**
* Delegate for the closure that declares the script parameters and their types.
*/
private static class ParametersDelegate extends GroovyObjectSupport {
/** Map of parameter names to types. */
private final Map<String, Class<?>> _params = Maps.newHashMap();
@Override
public Object invokeMethod(String name, Object args) {
ArgumentChecker.notEmpty(name, "name");
if (!(args instanceof Object[])) {
throw new IllegalArgumentException();
}
Object[] argArray = (Object[]) args;
if (argArray.length != 1 || !argArray[0].getClass().equals(Class.class)) {
throw new IllegalArgumentException("parameter declarations must be of the form 'var Type");
}
if (_params.containsKey(name)) {
throw new IllegalArgumentException("parameter " + name + " can only be declared once");
}
_params.put(name, (Class<?>) argArray[0]);
return null;
}
private Map<String, Class<?>> getParameters() {
return _params;
}
}
/**
* Defines a method in the DSL that takes a closure defining a simulation.
* @param name The simulation name
* @param body The block that defines the simulation
* @return The simulation
*/
public Simulation simulation(String name, Closure body) {
_simulation = new Simulation(name);
body.setDelegate(_simulation);
body.setResolveStrategy(Closure.DELEGATE_FIRST);
body.call();
return _simulation;
}
/**
* Defines a method in the DSL that takes a closure defining a scenario.
* @param name The scenario name, not empty
* @param body The block that defines the scenario
* @return The scenario
*/
public Scenario scenario(String name, Closure body) {
// scenarios can be defined as part of a simulation or stand-alone
if (_simulation != null) {
_scenario = _simulation.scenario(name);
} else {
_scenario = new Scenario(name);
}
body.setDelegate(_scenario);
body.setResolveStrategy(Closure.DELEGATE_FIRST);
body.call();
return _scenario;
}
/**
* Defines a method in the DSL that takes a closure which defines how to select and transform a curve.
* @param body The block that defines the selection and transformation
*/
public void curve(Closure body) {
CurveBuilder selector = new CurveBuilder(_scenario);
body.setDelegate(selector);
body.setResolveStrategy(Closure.DELEGATE_FIRST);
body.call();
}
/**
* Defines a method in the DSL that takes a closure which defines how to select and transform a market data point.
* @param body The block that defines the selection and transformation
*/
public void marketData(Closure body) {
PointBuilder selector = new PointBuilder(_scenario);
body.setDelegate(selector);
body.setResolveStrategy(Closure.DELEGATE_FIRST);
body.call();
}
/**
* Defines a method in the DSL that takes a closure which defines how to select and transform a volatility surface.
* @param body The block that defines the selection and transformation
*/
public void surface(Closure body) {
SurfaceBuilder selector = new SurfaceBuilder(_scenario);
body.setDelegate(selector);
body.setResolveStrategy(Closure.DELEGATE_FIRST);
body.call();
}
/**
* Delegate class for closures that define a surface transformation in the DSL.
*/
private static final class SurfaceBuilder extends VolatilitySurfaceSelector.Builder {
private SurfaceBuilder(Scenario scenario) {
super(scenario);
}
public void apply(Closure body) {
VolatilitySurfaceManipulatorBuilder builder = new VolatilitySurfaceManipulatorBuilder(getScenario(), getSelector());
body.setDelegate(builder);
body.setResolveStrategy(Closure.DELEGATE_FIRST);
body.call();
}
}
/**
* Delegate class for blocks that define a market data point transformation in the DSL.
*/
private static final class PointBuilder extends PointSelector.Builder {
private PointBuilder(Scenario scenario) {
super(scenario);
}
public void apply(Closure body) {
PointManipulatorBuilder builder = new PointManipulatorBuilder(getScenario(), getSelector());
body.setDelegate(builder);
body.setResolveStrategy(Closure.DELEGATE_FIRST);
body.call();
}
}
/**
* Delegate class for closures that define a curve transformation in the DSL.
*/
private static final class CurveBuilder extends YieldCurveSelector.Builder {
private CurveBuilder(Scenario scenario) {
super(scenario);
}
public void apply(Closure body) {
YieldCurveManipulatorBuilder builder = new YieldCurveManipulatorBuilder(getSelector(), getScenario());
body.setDelegate(builder);
body.setResolveStrategy(Closure.DELEGATE_FIRST);
body.call();
}
}
}