/** * @inheritDoc As the accumulate node will always generate a resulting tuple, we must always * destroy it */ public void retractLeftTuple( final LeftTuple leftTuple, final PropagationContext context, final InternalWorkingMemory workingMemory) { final AccumulateMemory memory = (AccumulateMemory) workingMemory.getNodeMemory(this); if (leftTuple.getMemory().isStagingMemory()) { leftTuple.getMemory().remove(leftTuple); } else { ((BetaMemory) memory.getBetaMemory()).getLeftTupleMemory().remove(leftTuple); } leftTuple.setMemory(null); final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject(); if (accctx.getAction() != null) { // there is a scheduled activation, we must cancel it context.removeInsertAction(accctx.getAction()); } leftTuple.setObject(null); removePreviousMatchesForLeftTuple(leftTuple, workingMemory, memory, accctx); if (accctx.propagated) { // if tuple was previously propagated, retract it and destroy result fact handle this.sink.propagateRetractLeftTupleDestroyRightTuple(leftTuple, context, workingMemory); } else { // if not propagated, just destroy the result fact handle // workingMemory.getFactHandleFactory().destroyFactHandle( accctx.result.getFactHandle() ); } }
/** * @inheritDoc When a new object is asserted into an AccumulateNode, do this: * <p>1. Select all matching tuples from left memory 2. For each matching tuple, call a modify * tuple */ public void assertRightTuple( final RightTuple rightTuple, final PropagationContext context, final InternalWorkingMemory workingMemory) { final AccumulateMemory memory = (AccumulateMemory) workingMemory.getNodeMemory(this); memory.betaMemory.getRightTupleMemory().add(rightTuple); if (memory.betaMemory.getLeftTupleMemory() == null || memory.betaMemory.getLeftTupleMemory().size() == 0) { // do nothing here, as we know there are no left tuples at this stage in sequential mode or // for a query. // unless it's an "Open Query" and thus that will have left memory, so continue as normal return; } this.constraints.updateFromFactHandle( memory.betaMemory.getContext(), workingMemory, rightTuple.getFactHandle()); LeftTupleMemory leftMemory = memory.betaMemory.getLeftTupleMemory(); FastIterator leftIt = getLeftIterator(leftMemory); for (LeftTuple leftTuple = getFirstLeftTuple(rightTuple, leftMemory, context, leftIt); leftTuple != null; leftTuple = (LeftTuple) leftIt.next(leftTuple)) { if (this.constraints.isAllowedCachedRight(memory.betaMemory.getContext(), leftTuple)) { final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject(); addMatch(leftTuple, rightTuple, null, null, workingMemory, memory, accctx, true); if (accctx.getAction() == null) { // schedule a test to evaluate the constraints, this is an optimisation for sub networks // We set Source to LEFT, even though this is a right propagation, because it might end up // doing multiple right propagations anyway EvaluateResultConstraints action = new EvaluateResultConstraints( ActivitySource.LEFT, leftTuple, context, workingMemory, memory, accctx, true, this); accctx.setAction(action); context.addInsertAction(action); } } } this.constraints.resetFactHandle(memory.betaMemory.getContext()); }
/** * @inheritDoc When a new tuple is asserted into an AccumulateNode, do this: * <p>1. Select all matching objects from right memory 2. Execute the initialization code * using the tuple + matching objects 3. Execute the accumulation code for each combination of * tuple+object 4. Execute the return code 5. Create a new CalculatedObjectHandle for the * resulting object and add it to the tuple 6. Propagate the tuple * <p>The initialization, accumulation and return codes, in JBRules, are assembled into a * generated method code and called once for the whole match, as you can see below: * <p>Object result = this.accumulator.accumulate( ... ); */ public void assertLeftTuple( final LeftTuple leftTuple, final PropagationContext context, final InternalWorkingMemory workingMemory) { final AccumulateMemory memory = (AccumulateMemory) workingMemory.getNodeMemory(this); AccumulateContext accresult = new AccumulateContext(); boolean useLeftMemory = true; if (!this.tupleMemoryEnabled) { // This is a hack, to not add closed DroolsQuery objects Object object = ((InternalFactHandle) leftTuple.get(0)).getObject(); if (!(object instanceof DroolsQuery) || !((DroolsQuery) object).isOpen()) { useLeftMemory = false; } } if (useLeftMemory) { memory.betaMemory.getLeftTupleMemory().add(leftTuple); leftTuple.setObject(accresult); } accresult.context = this.accumulate.createContext(); this.accumulate.init(memory.workingMemoryContext, accresult.context, leftTuple, workingMemory); this.constraints.updateFromTuple(memory.betaMemory.getContext(), workingMemory, leftTuple); RightTupleMemory rightMemory = memory.betaMemory.getRightTupleMemory(); FastIterator rightIt = getRightIterator(rightMemory); for (RightTuple rightTuple = getFirstRightTuple(leftTuple, rightMemory, context, rightIt); rightTuple != null; rightTuple = (RightTuple) rightIt.next(rightTuple)) { InternalFactHandle handle = rightTuple.getFactHandle(); if (this.constraints.isAllowedCachedLeft(memory.betaMemory.getContext(), handle)) { // add a match addMatch( leftTuple, rightTuple, null, null, workingMemory, memory, accresult, useLeftMemory); } } this.constraints.resetTuple(memory.betaMemory.getContext()); if (accresult.getAction() == null) { evaluateResultConstraints( ActivitySource.LEFT, leftTuple, context, workingMemory, memory, accresult, useLeftMemory); } // else evaluation is already scheduled, so do nothing }
private void removePreviousMatchesForRightTuple( final RightTuple rightTuple, final PropagationContext context, final InternalWorkingMemory workingMemory, final AccumulateMemory memory, final LeftTuple firstChild) { for (LeftTuple match = firstChild; match != null; ) { final LeftTuple tmp = match.getRightParentNext(); final LeftTuple parent = match.getLeftParent(); final AccumulateContext accctx = (AccumulateContext) parent.getObject(); removeMatch(rightTuple, match, workingMemory, memory, accctx, true); if (accctx.getAction() == null) { // schedule a test to evaluate the constraints, this is an optimisation for sub networks // We set Source to LEFT, even though this is a right propagation, because it might end up // doing multiple right propagations anyway EvaluateResultConstraints action = new EvaluateResultConstraints( ActivitySource.LEFT, parent, context, workingMemory, memory, accctx, true, this); accctx.setAction(action); context.addInsertAction(action); } match = tmp; } }
/** * Evaluate result constraints and propagate assert in case they are true * * @param leftTuple * @param context * @param workingMemory * @param memory * @param accresult * @param handle */ public void evaluateResultConstraints( final ActivitySource source, final LeftTuple leftTuple, final PropagationContext context, final InternalWorkingMemory workingMemory, final AccumulateMemory memory, final AccumulateContext accctx, final boolean useLeftMemory) { // get the actual result final Object[] resultArray = this.accumulate.getResult( memory.workingMemoryContext, accctx.context, leftTuple, workingMemory); Object result = this.accumulate.isMultiFunction() ? resultArray : resultArray[0]; if (result == null) { return; } if (accctx.result == null) { final InternalFactHandle handle = createResultFactHandle(context, workingMemory, leftTuple, result); accctx.result = createRightTuple(handle, this, context); } else { accctx.result.getFactHandle().setObject(result); } // First alpha node filters boolean isAllowed = result != null; for (int i = 0, length = this.resultConstraints.length; isAllowed && i < length; i++) { if (!this.resultConstraints[i].isAllowed( accctx.result.getFactHandle(), workingMemory, memory.alphaContexts[i])) { isAllowed = false; } } if (isAllowed) { this.resultBinder.updateFromTuple(memory.resultsContext, workingMemory, leftTuple); if (!this.resultBinder.isAllowedCachedLeft( memory.resultsContext, accctx.result.getFactHandle())) { isAllowed = false; } this.resultBinder.resetTuple(memory.resultsContext); } if (accctx.propagated == true) { // temporarily break the linked list to avoid wrong interactions LeftTuple[] matchings = splitList(leftTuple, accctx, false); if (isAllowed) { // modify if (ActivitySource.LEFT.equals(source)) { this.sink.propagateModifyChildLeftTuple( leftTuple.getFirstChild(), leftTuple, context, workingMemory, useLeftMemory); } else { this.sink.propagateModifyChildLeftTuple( leftTuple.getFirstChild(), accctx.result, context, workingMemory, useLeftMemory); } } else { // retract this.sink.propagateRetractLeftTuple(leftTuple, context, workingMemory); accctx.propagated = false; } // restore the matchings list restoreList(leftTuple, matchings); } else if (isAllowed) { // temporarily break the linked list to avoid wrong interactions LeftTuple[] matchings = splitList(leftTuple, accctx, false); // assert this.sink.propagateAssertLeftTuple( leftTuple, accctx.result, null, null, context, workingMemory, useLeftMemory); accctx.propagated = true; // restore the matchings list restoreList(leftTuple, matchings); } }
public void modifyRightTuple( RightTuple rightTuple, PropagationContext context, InternalWorkingMemory workingMemory) { final AccumulateMemory memory = (AccumulateMemory) workingMemory.getNodeMemory(this); BetaMemory bm = memory.betaMemory; // Add and remove to make sure we are in the right bucket and at the end // this is needed to fix for indexing and deterministic iteration bm.getRightTupleMemory().removeAdd(rightTuple); if (bm.getLeftTupleMemory() == null || bm.getLeftTupleMemory().size() == 0) { // do nothing here, as we know there are no left tuples at this stage in sequential mode. return; } LeftTuple childLeftTuple = rightTuple.firstChild; LeftTupleMemory leftMemory = bm.getLeftTupleMemory(); FastIterator leftIt = getLeftIterator(leftMemory); LeftTuple leftTuple = getFirstLeftTuple(rightTuple, leftMemory, context, leftIt); this.constraints.updateFromFactHandle( bm.getContext(), workingMemory, rightTuple.getFactHandle()); // first check our index (for indexed nodes only) hasn't changed and we are returning the same // bucket // We assume a bucket change if leftTuple == null if (childLeftTuple != null && leftMemory.isIndexed() && !leftIt.isFullIterator() && (leftTuple == null || (leftTuple.getMemory() != childLeftTuple.getLeftParent().getMemory()))) { // our index has changed, so delete all the previous matches removePreviousMatchesForRightTuple( rightTuple, context, workingMemory, memory, childLeftTuple); childLeftTuple = null; // null so the next check will attempt matches for new bucket } // if LeftTupleMemory is empty, there are no matches to modify if (leftTuple != null) { if (childLeftTuple == null) { // either we are indexed and changed buckets or // we had no children before, but there is a bucket to potentially match, so try as normal // assert for (; leftTuple != null; leftTuple = (LeftTuple) leftIt.next(leftTuple)) { if (this.constraints.isAllowedCachedRight(bm.getContext(), leftTuple)) { final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject(); // add a new match addMatch(leftTuple, rightTuple, null, null, workingMemory, memory, accctx, true); if (accctx.getAction() == null) { // schedule a test to evaluate the constraints, this is an optimisation for sub // networks // We set Source to LEFT, even though this is a right propagation, because it might // end up // doing multiple right propagations anyway EvaluateResultConstraints action = new EvaluateResultConstraints( ActivitySource.LEFT, leftTuple, context, workingMemory, memory, accctx, true, this); accctx.setAction(action); context.addInsertAction(action); } } } } else { // in the same bucket, so iterate and compare for (; leftTuple != null; leftTuple = (LeftTuple) leftIt.next(leftTuple)) { if (this.constraints.isAllowedCachedRight(bm.getContext(), leftTuple)) { final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject(); LeftTuple temp = null; if (childLeftTuple != null && childLeftTuple.getLeftParent() == leftTuple) { temp = childLeftTuple.getRightParentNext(); // we must re-add this to ensure deterministic iteration childLeftTuple.reAddLeft(); removeMatch(rightTuple, childLeftTuple, workingMemory, memory, accctx, true); childLeftTuple = temp; } // add a new match addMatch( leftTuple, rightTuple, null, childLeftTuple, workingMemory, memory, accctx, true); if (temp != null) { childLeftTuple = temp; } if (accctx.getAction() == null) { // schedule a test to evaluate the constraints, this is an optimisation for sub // networks // We set Source to LEFT, even though this is a right propagation, because it might // end up // doing multiple right propagations anyway EvaluateResultConstraints action = new EvaluateResultConstraints( ActivitySource.LEFT, leftTuple, context, workingMemory, memory, accctx, true, this); accctx.setAction(action); context.addInsertAction(action); } } else if (childLeftTuple != null && childLeftTuple.getLeftParent() == leftTuple) { LeftTuple temp = childLeftTuple.getRightParentNext(); final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject(); // remove the match removeMatch(rightTuple, childLeftTuple, workingMemory, memory, accctx, true); if (accctx.getAction() == null) { // schedule a test to evaluate the constraints, this is an optimisation for sub // networks // We set Source to LEFT, even though this is a right propagation, because it might // end up // doing multiple right propagations anyway EvaluateResultConstraints action = new EvaluateResultConstraints( ActivitySource.LEFT, leftTuple, context, workingMemory, memory, accctx, true, this); accctx.setAction(action); context.addInsertAction(action); } childLeftTuple = temp; } // else do nothing, was false before and false now. } } } this.constraints.resetFactHandle(bm.getContext()); }
public void modifyLeftTuple( LeftTuple leftTuple, PropagationContext context, InternalWorkingMemory workingMemory) { final AccumulateMemory memory = (AccumulateMemory) workingMemory.getNodeMemory(this); final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject(); BetaMemory bm = memory.betaMemory; // Add and remove to make sure we are in the right bucket and at the end // this is needed to fix for indexing and deterministic iteration bm.getLeftTupleMemory().removeAdd(leftTuple); this.constraints.updateFromTuple(bm.getContext(), workingMemory, leftTuple); LeftTuple childLeftTuple = getFirstMatch(leftTuple, accctx, false); RightTupleMemory rightMemory = bm.getRightTupleMemory(); FastIterator rightIt = getRightIterator(rightMemory); RightTuple rightTuple = getFirstRightTuple(leftTuple, rightMemory, context, rightIt); // first check our index (for indexed nodes only) hasn't changed and we are returning the same // bucket // if rightTuple is null, we assume there was a bucket change and that bucket is empty if (childLeftTuple != null && rightMemory.isIndexed() && !rightIt.isFullIterator() && (rightTuple == null || (rightTuple.getMemory() != childLeftTuple.getRightParent().getMemory()))) { // our index has changed, so delete all the previous matchings removePreviousMatchesForLeftTuple(leftTuple, workingMemory, memory, accctx); childLeftTuple = null; // null so the next check will attempt matches for new bucket } // we can't do anything if RightTupleMemory is empty if (rightTuple != null) { if (childLeftTuple == null) { // either we are indexed and changed buckets or // we had no children before, but there is a bucket to potentially match, so try as normal // assert for (; rightTuple != null; rightTuple = (RightTuple) rightIt.next(rightTuple)) { final InternalFactHandle handle = rightTuple.getFactHandle(); if (this.constraints.isAllowedCachedLeft(bm.getContext(), handle)) { // add a new match addMatch(leftTuple, rightTuple, null, null, workingMemory, memory, accctx, true); } } } else { boolean isDirty = false; // in the same bucket, so iterate and compare for (; rightTuple != null; rightTuple = (RightTuple) rightIt.next(rightTuple)) { final InternalFactHandle handle = rightTuple.getFactHandle(); if (this.constraints.isAllowedCachedLeft(bm.getContext(), handle)) { if (childLeftTuple == null || childLeftTuple.getRightParent() != rightTuple) { // add a new match addMatch( leftTuple, rightTuple, childLeftTuple, null, workingMemory, memory, accctx, true); } else { // we must re-add this to ensure deterministic iteration LeftTuple temp = childLeftTuple.getLeftParentNext(); childLeftTuple.reAddRight(); childLeftTuple = temp; } } else if (childLeftTuple != null && childLeftTuple.getRightParent() == rightTuple) { LeftTuple temp = childLeftTuple.getLeftParentNext(); // remove the match removeMatch(rightTuple, childLeftTuple, workingMemory, memory, accctx, false); childLeftTuple = temp; // the next line means that when a match is removed from the current leftTuple // and the accumulate does not support the reverse operation, then the whole // result is dirty (since removeMatch above is not recalculating the total) // and we need to do this later isDirty = !accumulate.supportsReverse(); } // else do nothing, was false before and false now. } if (isDirty) { reaccumulateForLeftTuple(leftTuple, workingMemory, memory, accctx); } } } this.constraints.resetTuple(memory.betaMemory.getContext()); if (accctx.getAction() == null) { evaluateResultConstraints( ActivitySource.LEFT, leftTuple, context, workingMemory, memory, accctx, true); } // else evaluation is already scheduled, so do nothing }