/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.graph; import static com.opengamma.sesame.config.ConfigBuilder.argument; import static com.opengamma.sesame.config.ConfigBuilder.arguments; import static com.opengamma.sesame.config.ConfigBuilder.config; import static com.opengamma.sesame.config.ConfigBuilder.function; import static com.opengamma.sesame.config.ConfigBuilder.implementations; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import com.opengamma.sesame.config.FunctionModelConfig; import com.opengamma.util.test.TestGroup; /** * Tests the ability to create multiple instances of the same class in a function graph with different * configuration. Or multiple different implementations of an interface at different points in the graph. * In Google Guice (which heavily influenced the design of this code) this is known as * the "robot legs problem". This refers to the difficultly of building a robot using Guice and injecting * two different foot classes into two instances of the same leg class. * <p> * The solution is to specify an instance of {@link FunctionModelConfig} as an argument for a parameter * that isn't of type {@code FunctionModelConfig} and where the engine would normally create and inject the * argument. The engine still creates and injects the argument, but the config is used to override the default * config. * <p> * This is intended to be a workaround for unusual situations, not a common approach to function building. * It should normally be possible to design functions to avoid the need to do this. */ @Test(groups = TestGroup.UNIT) public class RobotLegsTest { /** * Build a robot with two {@link FootImpl} instances, one with side=left and the other with side=right. * Tests overriding of function arguments. */ @Test public void overrideDefaultArgument() { FunctionModelConfig rightConfig = config(arguments(function(FootImpl.class, argument("side", "right")))); FunctionModelConfig config = config(implementations(Robot.class, RobotImpl.class, Leg.class, LegImpl.class, Foot.class, FootImpl.class), arguments( function(FootImpl.class, argument("side", "left")), function(RobotImpl.class, argument("rightLeg", rightConfig), argument("name", "Robbie")))); Robot robot = FunctionModel.build(Robot.class, config); assertEquals("left", robot.getLeftLeg().getFoot().getSide()); assertEquals("right", robot.getRightLeg().getFoot().getSide()); assertTrue(robot.getLeftLeg().getFoot() instanceof FootImpl); assertTrue(robot.getRightLeg().getFoot() instanceof FootImpl); } /** * Build a robot using {@link LegImpl} instances containing a {@link LeftFoot} and a {@link RightFoot}. * Tests overriding of function implementations. */ @Test public void overrideDefaultImplementation() { FunctionModelConfig rightConfig = config(implementations(Foot.class, RightFoot.class)); FunctionModelConfig config = config(implementations(Robot.class, RobotImpl.class, Leg.class, LegImpl.class, Foot.class, LeftFoot.class), arguments( function(RobotImpl.class, argument("rightLeg", rightConfig), argument("name", "Robbie")))); Robot robot = FunctionModel.build(Robot.class, config); assertTrue(robot.getLeftLeg().getFoot() instanceof LeftFoot); assertTrue(robot.getRightLeg().getFoot() instanceof RightFoot); } /** * Builds multiple robots. the robots are both instances of {@link RobotImpl} but with different names. One of them * has a {@link LeftFoot} and a {@link RightFoot}. The other has two instances of {@link FootImpl} called * 'left' and right'. * <p> * Do not try this at home. Any real code that needs to do this should almost certainly be refactored * so it doesn't need to any more. */ @Test public void nestedOverrides() { // config for robotA's right leg FunctionModelConfig robotARightConfig = config(arguments(function(FootImpl.class, argument("side", "right")))); // config for robotB's right leg FunctionModelConfig robotBRightConfig = config(implementations(Foot.class, RightFoot.class)); // config for robotB. uses LeftFoot as the default foot implementation. RightFoot is specified as an override FunctionModelConfig robotBConfig = config(arguments(function(RobotImpl.class, argument("name", "RobotB"), argument("rightLeg", robotBRightConfig))), implementations(Foot.class, LeftFoot.class)); // default config. name=RobotA, foot=FootImpl[side=left] FunctionModelConfig config = config(implementations(Robot.class, RobotImpl.class, Leg.class, LegImpl.class, Foot.class, FootImpl.class), arguments( function(FootImpl.class, argument("side", "left")), function(RobotImpl.class, // override the config for the subtree below robotA's right leg argument("rightLeg", robotARightConfig), argument("name", "RobotA")), function(Robots.class, // override the config for the subtree below robot2 argument("robot2", robotBConfig)))); Robots robots = FunctionModel.build(Robots.class, config); Robot robot1 = robots.getRobot1(); assertEquals("RobotA", robot1.getName()); assertEquals("left", robot1.getLeftLeg().getFoot().getSide()); assertEquals("right", robot1.getRightLeg().getFoot().getSide()); assertTrue(robot1.getLeftLeg().getFoot() instanceof FootImpl); assertTrue(robot1.getRightLeg().getFoot() instanceof FootImpl); Robot robot2 = robots.getRobot2(); assertEquals("RobotB", robot2.getName()); assertTrue(robot2.getRightLeg().getFoot() instanceof RightFoot); assertTrue(robot2.getLeftLeg().getFoot() instanceof LeftFoot); } public interface Robot { String getName(); Leg getLeftLeg(); Leg getRightLeg(); } public interface Leg { Foot getFoot(); } public interface Foot { String getSide(); } public static final class RobotImpl implements Robot { private final Leg _leftLeg; private final Leg _rightLeg; private final String _name; public RobotImpl(String name, Leg leftLeg, Leg rightLeg) { _leftLeg = leftLeg; _rightLeg = rightLeg; _name = name; } @Override public String getName() { return _name; } @Override public Leg getLeftLeg() { return _leftLeg; } @Override public Leg getRightLeg() { return _rightLeg; } } public static final class LegImpl implements Leg { private final Foot _foot; public LegImpl(Foot foot) { _foot = foot; } public Foot getFoot() { return _foot; } } /** * A foot that can be used on either side. */ public static final class FootImpl implements Foot { private final String _side; public FootImpl(String side) { _side = side; } @Override public String getSide() { return _side; } } public static final class LeftFoot implements Foot { @Override public String getSide() { return "left"; } } public static final class RightFoot implements Foot { @Override public String getSide() { return "right"; } } public static final class Robots { private final Robot _robot1; private final Robot _robot2; public Robots(Robot robot1, Robot robot2) { _robot1 = robot1; _robot2 = robot2; } public Robot getRobot1() { return _robot1; } public Robot getRobot2() { return _robot2; } } }