package nl.ipo.cds.attributemapping.executer; import static org.junit.Assert.*; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; import nl.ipo.cds.attributemapping.MapperContext; import nl.ipo.cds.attributemapping.MappingDestination; import nl.ipo.cds.attributemapping.MappingSource; import nl.ipo.cds.attributemapping.NullReference; import nl.ipo.cds.attributemapping.operations.InputOperationType; import nl.ipo.cds.attributemapping.operations.Operation; import nl.ipo.cds.attributemapping.operations.OperationInput; import nl.ipo.cds.attributemapping.operations.OperationInputType; import nl.ipo.cds.attributemapping.operations.OperationType; import nl.ipo.cds.attributemapping.operations.OutputOperationType; import nl.ipo.cds.attributemapping.operations.TransformOperationType; import org.junit.Test; public class TestExecuter { private final static NullInputOperationType nullInputOperationType = new NullInputOperationType (); private final static StringInputOperationType stringInputOperationType = new StringInputOperationType (); private final static IntegerInputOperationType integerInputOperationType = new IntegerInputOperationType (); private final static StringOutputOperationType stringOutputOperationType = new StringOutputOperationType (); private final static ConcatenateOperationType concatenateOperationType = new ConcatenateOperationType (); private final static VariableConcatenateOperationType variableConcatenateOperationType = new VariableConcatenateOperationType (); private final static ConcatenateIntegerStringsOperationType concatenateIntegerStringsOperationType = new ConcatenateIntegerStringsOperationType (); private final static Operation concatenateAB = output ( concatenate ( input ("a"), input ("b") ) ); private final static Operation concatenateABCD = output ( concatenate ( concatenate ( input ("a"), input ("b") ), concatenate ( input ("c"), input ("d") ) ) ); private final static Operation concatenateABC = output ( concatenate ( input ("a"), input ("b"), input ("c") ) ); private final static Operation concatenateABCDEFGHI = output ( concatenate ( concatenate ( input ("a"), input ("b"), input ("c") ), concatenate ( input ("d"), input ("e"), input ("f") ), concatenate ( input ("g"), input ("h"), input ("i") ) ) ); @Test public void testCreateExecuter () throws MappingValidationException, OperationExecutionException { try (final Executer executor = new Executer (concatenateAB, new MapperContext ())) { } } @Test public void testExecute () throws Exception { assertEquals ("ab", execute (concatenateAB)); assertEquals ("abcd", execute (concatenateABCD)); } @Test public void testExecuteVarargs () throws Exception { assertEquals ("abc", execute (concatenateABC)); assertEquals ("abcdefghi", execute (concatenateABCDEFGHI)); } @Test (expected = MappingValidationException.class) public void testInvalidType () throws Exception { execute ( concatenate ( input ("a"), input (1) ) ); } @Test (expected = MappingValidationException.class) public void testInvalidType2 () throws Exception { execute ( concatenate ( input (1), input ("a") ) ); } @Test (expected = MappingValidationException.class) public void testInvalidType3 () throws Exception { execute ( concatenate ( input (1), input (1) ) ); } @Test (expected = MappingValidationException.class) public void testInvalidTypeVarargs () throws Exception { execute ( concatenate ( input ("a"), input ("b"), input ("c"), input (1), input ("d") ) ); } @Test (expected = MappingValidationException.class) public void testInvalidArgumentCount () throws Exception { execute ( op ( concatenateOperationType, input ("a"), input ("b"), input ("c") ) ); } @Test (expected = MappingValidationException.class) public void testInvalidArgumentCount2 () throws Exception { execute ( op ( concatenateOperationType, input ("a") ) ); } @Test public void testConcatenateIntegerStrings () throws Exception { final String value = execute ( output ( op ( concatenateIntegerStringsOperationType, input (42), input ("a"), input ("b"), input ("c"), input ("d") ) ) ); assertEquals ("42abcd", value); } @Test (expected = MappingValidationException.class) public void testConcatenateIntegerStringsInvalidType () throws Exception { execute ( output ( op ( concatenateIntegerStringsOperationType, input (42), input ("a"), input ("b"), input ("c"), input (1) ) ) ); } @Test (expected = MappingValidationException.class) public void testConcatenateIntegerStringsInvalidCount () throws Exception { execute ( output ( op ( concatenateIntegerStringsOperationType ) ) ); } @Test public void testMapNullValue () throws Exception { execute ( output ( nullValue () ) ); } @Test public void testBeforeAfter () throws Exception { final AtomicBoolean beforeInvoked = new AtomicBoolean (false); final AtomicBoolean afterInvoked = new AtomicBoolean (false); final InputOperationType ot = new InputOperationType () { @Override public Type getReturnType () { return String.class; } @Override public Class<?> getPropertyBeanClass () { return null; } @Override public String getName () { return "TestOperation"; } @Override public String getLabel (final Locale locale) { return getName (); } @Override public List<OperationInputType> getInputs() { return Collections.emptyList (); } @Override public String getFormatLabel (final Locale locale) { return getName (); } @Override public String getDescription (final Locale locale) { return getName (); } @Override public OperationExecuter createExecuter (final Object operationProperties, final MapperContext context) { return new OperationExecuter () { @Override public Object execute(MappingSource source, MappingDestination destination, List<Object> inputs) throws OperationExecutionException { return "Hello, World!"; } @Override public void before() throws OperationExecutionException { beforeInvoked.set (true); } @Override public void after() throws OperationExecutionException { afterInvoked.set (true); } }; } }; execute ( output ( op (ot) ) ); assertTrue (beforeInvoked.get ()); assertTrue (afterInvoked.get ()); } // ========================================================================= // Convenience methods for creating and executing operations: // ========================================================================= private static String execute (final Operation rootOperation) throws OperationExecutionException, MappingValidationException { try (final Executer executer = new Executer (rootOperation, new MapperContext ())) { final StringValue result = new StringValue (null); System.out.println ("Executing:\n" + executer); executer.execute (new MappingSource() { @Override public boolean hasAttribute(String name) { return false; } @Override public Object getAttributeValue(String name) { return null; } }, new MappingDestination () { @Override public void setValue(Object value) { result.setValue ((String)value); } }); return result.getValue (); } } private static Operation input (final String value) { return new Operation() { @Override public OperationType getOperationType() { return stringInputOperationType; } @Override public Object getOperationProperties() { return new StringValue (value); } @Override public List<OperationInput> getInputs() { return new ArrayList<OperationInput> (); } }; } private static Operation input (final int value) { return new Operation() { @Override public OperationType getOperationType() { return integerInputOperationType; } @Override public Object getOperationProperties() { return new IntegerValue (value); } @Override public List<OperationInput> getInputs() { return new ArrayList<OperationInput> (); } }; } private static Operation output (final Operation a) { return op (stringOutputOperationType, a); } private static Operation nullValue () { return op (nullInputOperationType); } private static Operation concatenate (final Operation a, final Operation b) { return op (concatenateOperationType, a, b); } private static Operation concatenate (final Operation ... operations) { return op (variableConcatenateOperationType, operations); } private static Operation op (final OperationType operationType, final Operation ... operations) { return new Operation () { @Override public OperationType getOperationType () { return operationType; } @Override public Object getOperationProperties () { return null; } @Override public List<OperationInput> getInputs() { final List<OperationInput> inputs = new ArrayList<OperationInput> (); for (final Operation op: operations) { inputs.add (new OperationInput() { @Override public Operation getOperation() { return op; } }); } return inputs; } }; } // ========================================================================= // Operation type classes: // ========================================================================= private static class StringValue { public String value; public StringValue (final String value) { this.value = value; } public String getValue () { return value; } public void setValue (final String value) { this.value = value; } @Override public String toString () { return value; } } private static class IntegerValue { public int value; public IntegerValue (final int value) { this.value = value; } public int getValue () { return value; } @Override public String toString () { return String.valueOf (value); } } private abstract static class AbstractOperationType implements OperationType { @Override public String getName() { return getClass ().getName (); } @Override public String getDescription(Locale locale) { return getName (); } @Override public String getLabel(Locale locale) { return getName (); } @Override public String getFormatLabel (Locale locale) { return getName (); } @Override public abstract Class<?> getReturnType(); @Override public abstract Class<?> getPropertyBeanClass(); @Override public abstract List<OperationInputType> getInputs(); @Override public abstract OperationExecuter createExecuter(Object operationProperties, MapperContext context); } private static class NullInputOperationType extends AbstractOperationType implements InputOperationType { @Override public Class<?> getReturnType () { return NullReference.class; } @Override public List<OperationInputType> getInputs () { return new ArrayList<OperationInputType> (); } @Override public OperationExecuter createExecuter (final Object operationProperties, final MapperContext context) { return new OperationExecuter() { @Override public Object execute(MappingSource source, MappingDestination destination, List<Object> inputs) throws OperationExecutionException { return NullReference.VALUE; } @Override public void before() throws OperationExecutionException { } @Override public void after() throws OperationExecutionException { } }; } @Override public Class<?> getPropertyBeanClass() { return null; } } private static class StringInputOperationType extends AbstractOperationType implements InputOperationType { @Override public Class<?> getReturnType() { return String.class; } @Override public Class<?> getPropertyBeanClass () { return StringValue.class; } @Override public List<OperationInputType> getInputs () { return new ArrayList<OperationInputType> (); } @Override public OperationExecuter createExecuter (final Object operationProperties, final MapperContext context) { return new OperationExecuter() { @Override public Object execute(MappingSource source, MappingDestination destination, List<Object> inputs) throws OperationExecutionException { return ((StringValue)operationProperties).getValue (); } @Override public void before() throws OperationExecutionException { } @Override public void after() throws OperationExecutionException { } }; } } private static class IntegerInputOperationType extends AbstractOperationType implements InputOperationType { @Override public Class<?> getReturnType () { return Integer.TYPE; } @Override public Class<?> getPropertyBeanClass() { return IntegerValue.class; } @Override public List<OperationInputType> getInputs() { return new ArrayList<OperationInputType> (); } @Override public OperationExecuter createExecuter(final Object operationProperties, final MapperContext context) { return new OperationExecuter() { @Override public Object execute(MappingSource source, MappingDestination destination, List<Object> inputs) throws OperationExecutionException { return ((IntegerValue)operationProperties).getValue (); } @Override public void before() throws OperationExecutionException { } @Override public void after() throws OperationExecutionException { } }; } } private static class StringOutputOperationType extends AbstractOperationType implements OutputOperationType { @Override public Class<?> getReturnType () { // TODO Auto-generated method stub return Void.TYPE; } @Override public Class<?> getPropertyBeanClass () { return null; } @Override public List<OperationInputType> getInputs () { final List<OperationInputType> inputs = new ArrayList<OperationInputType> (); inputs.add (new OperationInputType() { @Override public boolean isVariableInputCount() { return false; } @Override public String getName() { return "a"; } @Override public Class<?> getInputType() { return String.class; } @Override public String getDescription(Locale locale) { return "a"; } }); return inputs; } @Override public OperationExecuter createExecuter(Object operationProperties, MapperContext context) { return new OperationExecuter() { @Override public Object execute(MappingSource source, MappingDestination destination, List<Object> inputs) throws OperationExecutionException { destination.setValue (inputs.get (0)); return null; } @Override public void before() throws OperationExecutionException { } @Override public void after() throws OperationExecutionException { } }; } } private static class ConcatenateOperationType extends AbstractOperationType implements TransformOperationType { @Override public Class<?> getReturnType() { return String.class; } @Override public Class<?> getPropertyBeanClass() { return null; } @Override public List<OperationInputType> getInputs() { final List<OperationInputType> inputs = new ArrayList<OperationInputType> (); inputs.add (new OperationInputType() { @Override public boolean isVariableInputCount() { return false; } @Override public String getName() { return "a"; } @Override public Class<?> getInputType() { return String.class; } @Override public String getDescription(Locale locale) { return "a"; } }); inputs.add (new OperationInputType () { @Override public String getName() { return "b"; } @Override public Class<?> getInputType() { return String.class; } @Override public String getDescription(Locale locale) { return "a"; } @Override public boolean isVariableInputCount() { return false; } }); return inputs; } @Override public OperationExecuter createExecuter(Object operationProperties, MapperContext context) { return new OperationExecuter() { @Override public Object execute(MappingSource source, MappingDestination destination, List<Object> inputs) throws OperationExecutionException { return ((String)inputs.get (0)) + ((String)inputs.get (1)); } @Override public void before() throws OperationExecutionException { } @Override public void after() throws OperationExecutionException { } }; } } private static class VariableConcatenateOperationType extends AbstractOperationType implements TransformOperationType { @Override public Class<?> getReturnType() { return String.class; } @Override public Class<?> getPropertyBeanClass() { return null; } @Override public List<OperationInputType> getInputs() { final List<OperationInputType> inputs = new ArrayList<OperationInputType> (); inputs.add (new OperationInputType() { @Override public boolean isVariableInputCount() { return true; } @Override public String getName() { return "values"; } @Override public Class<?> getInputType() { return String.class; } @Override public String getDescription(Locale locale) { return "values"; } }); return inputs; } @Override public OperationExecuter createExecuter(Object operationProperties, MapperContext context) { return new OperationExecuter() { @Override public Object execute(MappingSource source, MappingDestination destination, List<Object> inputs) throws OperationExecutionException { final StringBuilder builder = new StringBuilder (); for (final Object o: inputs) { builder.append (o); } return builder.toString (); } @Override public void before() throws OperationExecutionException { } @Override public void after() throws OperationExecutionException { } }; } } private static class ConcatenateIntegerStringsOperationType extends AbstractOperationType implements TransformOperationType { @Override public Class<?> getReturnType() { return String.class; } @Override public Class<?> getPropertyBeanClass() { return null; } @Override public List<OperationInputType> getInputs() { final List<OperationInputType> inputs = new ArrayList<OperationInputType> (); inputs.add (new OperationInputType () { @Override public String getName () { return "a"; } @Override public Type getInputType () { return Integer.TYPE; } @Override public String getDescription (final Locale locale) { return "a"; } @Override public boolean isVariableInputCount () { return false; } }); inputs.add (new OperationInputType () { @Override public boolean isVariableInputCount() { return true; } @Override public String getName() { return "b"; } @Override public Class<?> getInputType() { return String.class; } @Override public String getDescription(Locale locale) { return "values"; } }); return inputs; } @Override public OperationExecuter createExecuter(Object operationProperties, MapperContext context) { return new OperationExecuter () { @Override public Object execute(MappingSource source, MappingDestination destination, List<Object> inputs) throws OperationExecutionException { final StringBuilder builder = new StringBuilder (); for (final Object o: inputs) { builder.append (o.toString ()); } return builder.toString (); } @Override public void before() throws OperationExecutionException { } @Override public void after() throws OperationExecutionException { } }; } } }