/**
* Copyright 2010 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.reteoo.test;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import junit.framework.AssertionFailedError;
import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.ANTLRReaderStream;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.TokenStream;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.runtime.tree.Tree;
import org.drools.RuleBaseConfiguration;
import org.drools.common.InternalFactHandle;
import org.drools.common.InternalWorkingMemory;
import org.drools.common.PropagationContextImpl;
import org.drools.core.util.Iterator;
import org.drools.core.util.ObjectHashMap;
import org.drools.core.util.ObjectHashMap.ObjectEntry;
import org.drools.reteoo.AccumulateNode;
import org.drools.reteoo.BetaMemory;
import org.drools.reteoo.BetaNode;
import org.drools.reteoo.LeftTuple;
import org.drools.reteoo.LeftTupleMemory;
import org.drools.reteoo.LeftTupleSink;
import org.drools.reteoo.ModifyPreviousTuples;
import org.drools.reteoo.ObjectSink;
import org.drools.reteoo.ObjectTypeNode;
import org.drools.reteoo.ReteooRuleBase;
import org.drools.reteoo.RightInputAdapterNode;
import org.drools.reteoo.RightTuple;
import org.drools.reteoo.RightTupleMemory;
import org.drools.reteoo.RuleTerminalNode;
import org.drools.reteoo.Sink;
import org.drools.reteoo.AccumulateNode.AccumulateMemory;
import org.drools.reteoo.builder.BuildContext;
import org.drools.reteoo.test.dsl.AccumulateNodeStep;
import org.drools.reteoo.test.dsl.BetaNodeStep;
import org.drools.reteoo.test.dsl.BindingStep;
import org.drools.reteoo.test.dsl.DSLMock;
import org.drools.reteoo.test.dsl.DslStep;
import org.drools.reteoo.test.dsl.EvalNodeStep;
import org.drools.reteoo.test.dsl.ExistsNodeStep;
import org.drools.reteoo.test.dsl.FactsStep;
import org.drools.reteoo.test.dsl.JoinNodeStep;
import org.drools.reteoo.test.dsl.LeftInputAdapterNodeStep;
import org.drools.reteoo.test.dsl.LeftTupleSinkStep;
import org.drools.reteoo.test.dsl.MockitoHelper;
import org.drools.reteoo.test.dsl.NodeTestCase;
import org.drools.reteoo.test.dsl.NodeTestCaseResult;
import org.drools.reteoo.test.dsl.NodeTestDef;
import org.drools.reteoo.test.dsl.NotNodeStep;
import org.drools.reteoo.test.dsl.ObjectTypeNodeStep;
import org.drools.reteoo.test.dsl.RIANodeStep;
import org.drools.reteoo.test.dsl.ReteTesterHelper;
import org.drools.reteoo.test.dsl.RuleTerminalNodeStep;
import org.drools.reteoo.test.dsl.Step;
import org.drools.reteoo.test.dsl.WithStep;
import org.drools.reteoo.test.dsl.NodeTestCaseResult.NodeTestResult;
import org.drools.reteoo.test.dsl.NodeTestCaseResult.Result;
import org.drools.reteoo.test.parser.NodeTestDSLLexer;
import org.drools.reteoo.test.parser.NodeTestDSLParser;
import org.drools.reteoo.test.parser.NodeTestDSLTree;
import org.drools.reteoo.test.parser.NodeTestDSLParser.compilation_unit_return;
import org.drools.spi.PropagationContext;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runner.notification.StoppedByUserException;
import org.mvel2.MVEL;
public class ReteDslTestEngine {
private static final String OBJECT_TYPE_NODE = "ObjectTypeNode";
private static final String LEFT_INPUT_ADAPTER_NODE = "LeftInputAdapterNode";
private static final String BINDING = "Binding";
private static final String JOIN_NODE = "JoinNode";
private static final String NOT_NODE = "NotNode";
private static final String EXISTS_NODE = "ExistsNode";
private static final String ACCUMULATE_NODE = "AccumulateNode";
private static final String RULE_TERMINAL_NODE = "RuleTerminalNode";
private static final String EVAL_NODE = "EvalNode";
private static final String WITH = "With";
private static final String FACTS = "Facts";
private static final String RIGHT_INPUT_ADAPTER_NODE = "RightInputAdapterNode";
private static final String LEFT_TUPLE_SINK_STEP = "LeftTupleSink";
private static final String BETA_NODE_STEP = "BetaNodeStep";
private ReteTesterHelper reteTesterHelper;
private Map<String, Object> steps;
public ReteDslTestEngine() {
this.reteTesterHelper = new ReteTesterHelper();
this.steps = new HashMap<String, Object>();
this.steps.put(OBJECT_TYPE_NODE, new ObjectTypeNodeStep(
this.reteTesterHelper));
this.steps.put(LEFT_INPUT_ADAPTER_NODE, new LeftInputAdapterNodeStep(
this.reteTesterHelper));
this.steps.put(BINDING, new BindingStep(this.reteTesterHelper));
this.steps.put(JOIN_NODE, new JoinNodeStep(this.reteTesterHelper));
this.steps.put(NOT_NODE, new NotNodeStep(this.reteTesterHelper));
this.steps.put(EXISTS_NODE, new ExistsNodeStep(this.reteTesterHelper));
this.steps.put(ACCUMULATE_NODE, new AccumulateNodeStep(
this.reteTesterHelper));
this.steps.put(RULE_TERMINAL_NODE, new RuleTerminalNodeStep(
this.reteTesterHelper));
this.steps.put(EVAL_NODE, new EvalNodeStep(this.reteTesterHelper));
this.steps.put(RIGHT_INPUT_ADAPTER_NODE, new RIANodeStep(
this.reteTesterHelper));
this.steps.put(FACTS, new FactsStep(this.reteTesterHelper));
this.steps.put(WITH, new WithStep(this.reteTesterHelper));
this.steps.put(LEFT_TUPLE_SINK_STEP, new LeftTupleSinkStep(
this.reteTesterHelper));
this.steps.put(BETA_NODE_STEP, new BetaNodeStep(this.reteTesterHelper));
}
public NodeTestCaseResult run(NodeTestCase testCase, RunNotifier notifier) {
if (testCase == null || testCase.hasErrors()) {
throw new IllegalArgumentException(
"Impossible to execute test case due to existing errors: "
+ testCase.getErrors());
}
if (notifier == null) {
notifier = EmptyNotifier.INSTANCE;
}
this.reteTesterHelper.addImports(testCase.getImports());
NodeTestCaseResult result = new NodeTestCaseResult(testCase);
for (NodeTestDef test : testCase.getTests()) {
notifier.fireTestStarted(test.getDescription());
NodeTestResult testResult = createTestResult(test, null);
try {
testResult = run(testCase, test);
switch (testResult.result) {
case SUCCESS:
notifier.fireTestFinished(test.getDescription());
break;
case ERROR:
case FAILURE:
notifier.fireTestFailure(new Failure(test.getDescription(),
new AssertionError(testResult.errorMsgs)));
break;
}
} catch (Throwable e) {
notifier.fireTestFailure(new Failure(test.getDescription(), e));
}
result.add(testResult);
}
return result;
}
private NodeTestResult run(NodeTestCase testCase, NodeTestDef test) {
Map<String, Object> context = createContext(testCase);
NodeTestResult result = createTestResult(test, context);
try {
// run setup
run(context, testCase.getSetup(), result);
// run test
run(context, test.getSteps(), result);
// run tearDown
run(context, testCase.getTearDown(), result);
result.result = Result.SUCCESS;
} catch (Throwable e) {
result.result = Result.ERROR;
result.errorMsgs.add(e.toString());
}
return result;
}
private NodeTestResult createTestResult(NodeTestDef test,
Map<String, Object> context) {
NodeTestResult result = new NodeTestResult(test, Result.NOT_EXECUTED,
context, new LinkedList<String>());
return result;
}
private Map<String, Object> createContext(NodeTestCase testCase) {
Map<String, Object> context = new HashMap<String, Object>();
context.put("TestCase", testCase);
RuleBaseConfiguration conf = new RuleBaseConfiguration();
ReteooRuleBase rbase = new ReteooRuleBase("ID", conf);
BuildContext buildContext = new BuildContext(rbase, rbase
.getReteooBuilder().getIdGenerator());
context.put("BuildContext", buildContext);
context
.put("ClassFieldAccessorStore", this.reteTesterHelper
.getStore());
InternalWorkingMemory wm = (InternalWorkingMemory) rbase
.newStatefulSession(true);
context.put("WorkingMemory", wm);
return context;
}
public Map<String, Object> run(Map<String, Object> context,
List<DslStep> steps, NodeTestResult result) {
InternalWorkingMemory wm = (InternalWorkingMemory) context
.get("WorkingMemory");
for (DslStep step : steps) {
String name = step.getName();
Object object = this.steps.get(name);
if (object != null && object instanceof Step) {
Step stepImpl = (Step) object;
try {
stepImpl.execute(context, step.getCommands());
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute step " + step, e);
}
} else if ("assert".equals(name.trim())) {
assertObject(step, context, wm);
} else if ("retract".equals(name.trim())) {
retractObject(step, context, wm);
} else if ("modify".equals(name.trim())) {
modifyObject(step, context, wm);
} else {
Object node = context.get(name.trim());
if (node == null) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": step " + name + " does not exist");
}
if (node instanceof DSLMock) {
// it is a mock
MockitoHelper.process(step, (LeftTupleSink) node, context,
wm);
} else if (node instanceof BetaNode) {
betaNode(step, (BetaNode) node, context, wm);
} else if (node instanceof RightInputAdapterNode) {
riaNode(step, (RightInputAdapterNode) node, context, wm);
} else if (node instanceof RuleTerminalNode) {
ruleTerminalNode(step, (RuleTerminalNode) node, context, wm);
} else {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unknown node " + node);
}
}
}
return context;
}
private void betaNode(DslStep step, BetaNode node,
Map<String, Object> context, InternalWorkingMemory wm) {
try {
List<String[]> cmds = step.getCommands();
List<InternalFactHandle> handles = (List<InternalFactHandle>) context
.get("Handles");
BetaMemory memory = null;
if (node instanceof AccumulateNode) {
AccumulateMemory accmem = (AccumulateMemory) wm
.getNodeMemory(node);
memory = accmem.betaMemory;
} else {
memory = (BetaMemory) wm.getNodeMemory(node);
}
for (String[] cmd : cmds) {
if (cmd[0].equals("leftMemory")) {
String args = cmd[1];
String listString = args.replaceAll("h(\\d+)", "h[$1]");
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("h", handles);
List<?> expectedLeftTuples = (List<?>) MVEL.eval(
listString, vars);
LeftTupleMemory leftMemory = memory.getLeftTupleMemory();
if (expectedLeftTuples.isEmpty() && leftMemory.size() != 0) {
throw new AssertionFailedError("line " + step.getLine()
+ ": left Memory expected [] actually "
+ print(leftMemory));
} else if (expectedLeftTuples.isEmpty()
&& leftMemory.size() == 0) {
return;
}
// we always lookup from the first element, in case it's
// indexed
List<InternalFactHandle> first = (List<InternalFactHandle>) expectedLeftTuples
.get(0);
LeftTuple firstTuple = new LeftTuple(first.get(0), null,
false);
for (int i = 1; i < first.size(); i++) {
firstTuple = new LeftTuple(firstTuple, null, false);
}
List<LeftTuple> leftTuples = new ArrayList<LeftTuple>();
for (LeftTuple leftTuple = memory.getLeftTupleMemory()
.getFirst(firstTuple); leftTuple != null; leftTuple = (LeftTuple) leftTuple
.getNext()) {
leftTuples.add(leftTuple);
}
// lgomes: Need to sort the tuples here, because we might have asserted things
// in the wrong order, because linking a node's side means populating its memory
// from the OTN which stores things in a hash-set, so insertion order is not kept.
Collections.sort(leftTuples, new LeftTupleComparator());
List<List<InternalFactHandle>> actualLeftTuples = getHandlesList(leftTuples);
if (!expectedLeftTuples.equals(actualLeftTuples)) {
throw new AssertionFailedError("line " + step.getLine()
+ ": left Memory expected "
+ print(expectedLeftTuples) + " actually "
+ print(actualLeftTuples));
}
} else if (cmd[0].equals("rightMemory")) {
String args = cmd[1];
String listString = args.replaceAll("h(\\d+)", "h[$1]");
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("h", handles);
List<?> expectedFactHandles = (List<?>) MVEL.eval(
listString, vars);
RightTupleMemory rightMemory = memory.getRightTupleMemory();
if (expectedFactHandles.isEmpty()
&& rightMemory.size() != 0) {
throw new AssertionFailedError("line " + step.getLine()
+ ": right Memory expected [] actually "
+ rightMemory);
} else if (expectedFactHandles.isEmpty()
&& rightMemory.size() == 0) {
return;
}
RightTuple first = new RightTuple(
(InternalFactHandle) expectedFactHandles.get(0));
List<RightTuple> actualRightTuples = new ArrayList<RightTuple>();
for (RightTuple rightTuple = memory.getRightTupleMemory()
.getFirst(first); rightTuple != null; rightTuple = (RightTuple) rightTuple
.getNext()) {
actualRightTuples.add(rightTuple);
}
if (expectedFactHandles.size() != actualRightTuples.size()) {
throw new AssertionFailedError("line " + step.getLine()
+ ": right Memory expected "
+ print(expectedFactHandles) + " actually "
+ print(actualRightTuples));
}
for (int i = 0, length = actualRightTuples.size(); i < length; i++) {
if (expectedFactHandles.get(i) != actualRightTuples
.get(i).getFactHandle()) {
throw new AssertionFailedError("line "
+ step.getLine()
+ ": right Memory expected ["
+ print(expectedFactHandles) + "] actually ["
+ print(actualRightTuples)
+ "]");
}
}
} else {
throw new IllegalArgumentException("line " + step.getLine()
+ ": command does not exist "
+ Arrays.toString(cmd));
}
}
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute step " + step, e);
}
}
private List<List<InternalFactHandle>> getHandlesList(
List<LeftTuple> leftTuples) {
List<List<InternalFactHandle>> actualLeftTuples = new ArrayList<List<InternalFactHandle>>(
leftTuples.size());
for (LeftTuple leftTuple : leftTuples) {
List<InternalFactHandle> tupleHandles = Arrays
.asList(leftTuple.toFactHandles());
actualLeftTuples.add(tupleHandles);
}
return actualLeftTuples;
}
private String print(LeftTupleMemory leftMemory) {
List<LeftTuple> tuples = new ArrayList<LeftTuple>();
Iterator it = leftMemory.iterator();
for ( LeftTuple tuple = (LeftTuple) it.next(); tuple != null; tuple = (LeftTuple) it.next() ) {
tuples.add(tuple);
}
Collections.sort(tuples, new LeftTupleComparator());
return print(getHandlesList(tuples));
}
/** Provides better error messages. */
protected String print(List<?> tuples) {
StringBuilder b = new StringBuilder();
for (java.util.Iterator iterator = tuples.iterator(); iterator.hasNext();) {
Object tuple = (Object) iterator.next();
if (tuple instanceof List<?>) {
b.append("[");
b.append(print((List<?>) tuple));
b.append("]");
} else if (tuple instanceof InternalFactHandle){
InternalFactHandle h = (InternalFactHandle) tuple;
b.append("h").append(h.getId()-1);
} else if (tuple instanceof RightTuple){
InternalFactHandle h = (InternalFactHandle) ((RightTuple) tuple).getFactHandle();
b.append("h").append(h.getId()-1);
}
if(iterator.hasNext())
b.append(", ");
}
if (b.length() == 0)
return "[]";
return b.toString();
}
private void riaNode(DslStep step, RightInputAdapterNode node,
Map<String, Object> context, InternalWorkingMemory wm) {
try {
List<String[]> cmds = step.getCommands();
List<InternalFactHandle> handles = (List<InternalFactHandle>) context
.get("Handles");
final ObjectHashMap memory = (ObjectHashMap) wm.getNodeMemory(node);
for (String[] cmd : cmds) {
if (cmd[0].equals("leftMemory")) {
String args = cmd[1];
String listString = args.replaceAll("h(\\d+)", "h[$1]");
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("h", handles);
List<?> expectedLeftTuples = (List<?>) MVEL.eval(
listString, vars);
if (expectedLeftTuples.isEmpty() && memory.size() != 0) {
throw new AssertionFailedError("line " + step.getLine()
+ ": left Memory expected [] actually "
+ memory);
} else if (expectedLeftTuples.isEmpty()
&& memory.size() == 0) {
return;
}
// create expected tuples
List<LeftTuple> leftTuples = new ArrayList<LeftTuple>();
for (List<InternalFactHandle> tlist : (List<List<InternalFactHandle>>) expectedLeftTuples) {
LeftTuple tuple = new LeftTuple(tlist.get(0), null,
false);
for (int i = 1; i < tlist.size(); i++) {
tuple = new LeftTuple(tuple, new RightTuple(tlist
.get(i)), null, false);
}
leftTuples.add(tuple);
}
// get actual tuples
final List<LeftTuple> actualTuples = new ArrayList<LeftTuple>();
final Iterator it = memory.iterator();
for (ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it
.next()) {
actualTuples.add((LeftTuple) entry.getKey());
}
// iterate over expected tuples and compare with actual
// tuples
for (LeftTuple tuple : leftTuples) {
if (!actualTuples.remove(tuple)) {
throw new AssertionFailedError("line "
+ step.getLine()
+ ": left Memory expected " + tuple
+ " not found in memory.");
}
}
if (!actualTuples.isEmpty()) {
throw new AssertionFailedError(
"line "
+ step.getLine()
+ ": left Memory unexpected tuples in the node memory "
+ actualTuples);
}
} else {
throw new IllegalArgumentException("line " + step.getLine()
+ ": command does not exist "
+ Arrays.toString(cmd));
}
}
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute step " + step, e);
}
}
private void ruleTerminalNode(DslStep step, RuleTerminalNode node,
Map<String, Object> context, InternalWorkingMemory wm) {
try {
List<String[]> cmds = step.getCommands();
// List<InternalFactHandle> handles = (List<InternalFactHandle>)
// context.get( "Handles" );
for (String[] cmd : cmds) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": command does not exist " + Arrays.toString(cmd));
}
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute step " + step, e);
}
}
@SuppressWarnings("unchecked")
private void assertObject(DslStep step, Map<String, Object> context,
InternalWorkingMemory wm) {
try {
List<String[]> cmds = step.getCommands();
List<InternalFactHandle> handles = (List<InternalFactHandle>) context
.get("Handles");
for (String[] cmd : cmds) {
try {
String nodeName = cmd[0];
Sink sink = (Sink) context.get(nodeName);
if (sink == null) {
throw new IllegalArgumentException("line "
+ step.getLine() + ": node " + nodeName
+ " does not exist");
}
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("h", handles);
String args = cmd[1];
String listString = args.replaceAll("h(\\d+)", "h[$1]");
List<?> list = (List<?>) MVEL.eval(listString, vars);
if (list == null) {
throw new IllegalArgumentException(cmd
+ " does not specify an existing fact handle");
}
for (Object element : list) {
if (element == null) {
throw new IllegalArgumentException(
cmd
+ " does not specify an existing fact handle");
}
if (element instanceof InternalFactHandle) {
InternalFactHandle handle = (InternalFactHandle) element;
PropagationContext pContext = new PropagationContextImpl(
wm.getNextPropagationIdCounter(),
PropagationContext.ASSERTION, null, null,
handle);
((ObjectSink) sink).assertObject(handle, pContext,
wm);
} else {
List<InternalFactHandle> tlist = (List<InternalFactHandle>) element;
LeftTuple tuple = createTuple(context, tlist);
PropagationContext pContext = new PropagationContextImpl(
wm.getNextPropagationIdCounter(),
PropagationContext.ASSERTION, null, tuple,
null);
((LeftTupleSink) sink).assertLeftTuple(tuple,
pContext, wm);
}
}
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute command " + cmd, e);
}
}
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute step " + step, e);
}
}
private LeftTuple createTuple(Map<String, Object> context,
List<InternalFactHandle> tlist) {
LeftTuple tuple = null;
String id = getTupleId(tlist);
for (InternalFactHandle handle : tlist) {
if (tuple == null) {
tuple = new LeftTuple(handle, null, false); // do not keep
// generated tuples
// on the handle
// list
} else {
tuple = new LeftTuple(tuple, new RightTuple(handle), null, true);
}
}
context.put(id, tuple);
return tuple;
}
private String getTupleId(List<InternalFactHandle> tlist) {
StringBuilder id = new StringBuilder();
id.append("T.");
for (InternalFactHandle handle : tlist) {
id.append(handle.getId());
id.append(".");
}
return id.toString();
}
private void retractObject(DslStep step, Map<String, Object> context,
InternalWorkingMemory wm) {
try {
List<String[]> cmds = step.getCommands();
List<InternalFactHandle> handles = (List<InternalFactHandle>) context
.get("Handles");
for (String[] cmd : cmds) {
try {
String nodeName = cmd[0];
Sink sink = (Sink) context.get(nodeName);
if (sink == null) {
throw new IllegalArgumentException("line "
+ step.getLine() + ": node " + nodeName
+ " does not exist");
}
String args = cmd[1];
String listString = args.replaceAll("h(\\d+)", "h[$1]");
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("h", handles);
List<?> list = (List<?>) MVEL.eval(listString, vars);
if (list == null) {
throw new IllegalArgumentException(Arrays.toString(cmd)
+ " does not specify an existing fact handle");
}
for (Object element : list) {
if (element == null) {
throw new IllegalArgumentException(
Arrays.toString(cmd)
+ " does not specify an existing fact handle");
}
if (element instanceof InternalFactHandle) {
InternalFactHandle handle = (InternalFactHandle) element;
PropagationContext pContext = new PropagationContextImpl(
wm.getNextPropagationIdCounter(),
PropagationContext.RETRACTION, null, null,
handle);
if (sink instanceof ObjectTypeNode) {
((ObjectTypeNode) sink).retractObject(handle,
pContext, wm);
} else {
for (RightTuple rightTuple = handle
.getFirstRightTuple(); rightTuple != null; rightTuple = (RightTuple) rightTuple
.getHandleNext()) {
rightTuple.getRightTupleSink()
.retractRightTuple(rightTuple,
pContext, wm);
}
handle.setFirstRightTuple(null);
handle.setLastRightTuple(null);
for (LeftTuple leftTuple = handle
.getFirstLeftTuple(); leftTuple != null; leftTuple = (LeftTuple) leftTuple
.getLeftParentNext()) {
leftTuple.getLeftTupleSink()
.retractLeftTuple(leftTuple,
pContext, wm);
}
handle.setFirstLeftTuple(null);
handle.setLastLeftTuple(null);
}
} else {
List<InternalFactHandle> tlist = (List<InternalFactHandle>) element;
String id = getTupleId(tlist);
LeftTuple tuple = (LeftTuple) context.remove(id);
if (tuple == null) {
throw new IllegalArgumentException(
"Tuple not found: " + id + " : "
+ tlist.toString());
}
PropagationContext pContext = new PropagationContextImpl(
wm.getNextPropagationIdCounter(),
PropagationContext.RETRACTION, null, tuple,
null);
((LeftTupleSink) sink).retractLeftTuple(tuple,
pContext, wm);
}
}
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute command "
+ Arrays.toString(cmd), e);
}
}
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute step " + step, e);
}
}
private void modifyObject(DslStep step, Map<String, Object> context,
InternalWorkingMemory wm) {
try {
List<String[]> cmds = step.getCommands();
List<InternalFactHandle> handles = (List<InternalFactHandle>) context
.get("Handles");
for (String[] cmd : cmds) {
try {
String nodeName = cmd[0];
Sink sink = (Sink) context.get(nodeName);
if (sink == null) {
throw new IllegalArgumentException("line "
+ step.getLine() + ": node " + nodeName
+ " does not exist");
}
String args = cmd[1];
String listString = args.replaceAll("h(\\d+)", "h[$1]");
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("h", handles);
List<?> list = (List<?>) MVEL.eval(listString, vars);
if (list == null) {
throw new IllegalArgumentException(Arrays.toString(cmd)
+ " does not specify an existing fact handle");
}
for (Object element : list) {
if (element == null) {
throw new IllegalArgumentException(
Arrays.toString(cmd)
+ " does not specify an existing fact handle");
}
if (element instanceof InternalFactHandle) {
InternalFactHandle handle = (InternalFactHandle) element;
PropagationContext pContext = new PropagationContextImpl(
wm.getNextPropagationIdCounter(),
PropagationContext.MODIFICATION, null,
null, handle);
ModifyPreviousTuples modifyPreviousTuples = new ModifyPreviousTuples(
handle.getFirstLeftTuple(), handle
.getFirstRightTuple());
handle.setFirstLeftTuple(null);
handle.setFirstRightTuple(null);
handle.setLastLeftTuple(null);
handle.setLastRightTuple(null);
((ObjectSink) sink).modifyObject(handle,
modifyPreviousTuples, pContext, wm);
modifyPreviousTuples.retractTuples(pContext, wm);
} else {
List<InternalFactHandle> tlist = (List<InternalFactHandle>) element;
String id = getTupleId(tlist);
LeftTuple tuple = (LeftTuple) context.get(id);
if (tuple == null) {
throw new IllegalArgumentException(
"Tuple not found: " + id + " : "
+ tlist.toString());
}
PropagationContext pContext = new PropagationContextImpl(
wm.getNextPropagationIdCounter(),
PropagationContext.MODIFICATION, null,
tuple, null);
((LeftTupleSink) sink).modifyLeftTuple(tuple,
pContext, wm);
}
}
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute command " + cmd, e);
}
}
} catch (Exception e) {
throw new IllegalArgumentException("line " + step.getLine()
+ ": unable to execute step " + step, e);
}
}
public static NodeTestCase compile(final Reader reader) throws IOException {
NodeTestDSLParser parser = getParser(reader);
return compile(parser);
}
public static NodeTestCase compile(final InputStream is) throws IOException {
NodeTestDSLParser parser = getParser(is);
return compile(parser);
}
public static NodeTestCase compile(final String source) throws IOException {
NodeTestDSLParser parser = getParser(source);
return compile(parser);
}
private static NodeTestCase compile(final NodeTestDSLParser parser) {
try {
compilation_unit_return cur = parser.compilation_unit();
if (parser.hasErrors()) {
NodeTestCase result = new NodeTestCase();
result.setErrors(parser.getErrorMessages());
return result;
}
NodeTestCase testCase = walk(parser.getTokenStream(),
(CommonTree) cur.getTree());
return testCase;
} catch (RecognitionException e) {
NodeTestCase result = new NodeTestCase();
result.setErrors(Collections.singletonList(e.getMessage()));
return result;
}
}
private static NodeTestCase walk(TokenStream tokenStream, Tree resultTree)
throws RecognitionException {
CommonTreeNodeStream nodes = new CommonTreeNodeStream(resultTree);
// AST nodes have payload that point into token stream
nodes.setTokenStream(tokenStream);
// Create a tree walker attached to the nodes stream
NodeTestDSLTree walker = new NodeTestDSLTree(nodes);
walker.compilation_unit();
return walker.getTestCase();
}
private static NodeTestDSLParser getParser(final Reader reader)
throws IOException {
NodeTestDSLLexer lexer = new NodeTestDSLLexer(new ANTLRReaderStream(
reader));
NodeTestDSLParser parser = new NodeTestDSLParser(new CommonTokenStream(
lexer));
return parser;
}
private static NodeTestDSLParser getParser(final InputStream is)
throws IOException {
NodeTestDSLLexer lexer = new NodeTestDSLLexer(new ANTLRInputStream(is));
NodeTestDSLParser parser = new NodeTestDSLParser(new CommonTokenStream(
lexer));
return parser;
}
private static NodeTestDSLParser getParser(final String source)
throws IOException {
NodeTestDSLLexer lexer = new NodeTestDSLLexer(new ANTLRStringStream(
source));
NodeTestDSLParser parser = new NodeTestDSLParser(new CommonTokenStream(
lexer));
return parser;
}
private final class LeftTupleComparator implements Comparator<LeftTuple> {
public int compare(LeftTuple o1, LeftTuple o2) {
InternalFactHandle[] h1 = o1.getFactHandles();
InternalFactHandle[] h2 = o2.getFactHandles();
// Handles have to be compared in the inverse order.
for (int i = (h1.length-1); i >= 0; i--) {
int diff = h1[i].getId() - h2[i].getId();
// Will continue comparing handles until
// a difference is found.
if (diff != 0)
return diff;
}
return 0;
}
}
public static class EmptyNotifier extends RunNotifier {
public static final EmptyNotifier INSTANCE = new EmptyNotifier();
@Override
public void fireTestAssumptionFailed(Failure failure) {
}
@Override
public void fireTestFailure(Failure failure) {
}
@Override
public void fireTestFinished(Description description) {
}
@Override
public void fireTestIgnored(Description description) {
}
@Override
public void fireTestRunFinished(org.junit.runner.Result result) {
}
@Override
public void fireTestRunStarted(Description description) {
}
@Override
public void fireTestStarted(Description description)
throws StoppedByUserException {
}
}
}