/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* 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.core.phreak;
import org.drools.core.common.InternalAgenda;
import org.drools.core.common.TupleSets;
import org.drools.core.reteoo.ConditionalBranchEvaluator;
import org.drools.core.reteoo.ConditionalBranchEvaluator.ConditionalExecution;
import org.drools.core.reteoo.ConditionalBranchNode;
import org.drools.core.reteoo.ConditionalBranchNode.ConditionalBranchMemory;
import org.drools.core.reteoo.LeftTuple;
import org.drools.core.reteoo.LeftTupleSink;
import org.drools.core.reteoo.RuleTerminalNode;
import org.drools.core.spi.Salience;
import static org.drools.core.phreak.RuleNetworkEvaluator.normalizeStagedTuples;
public class PhreakBranchNode {
public void doNode(ConditionalBranchNode branchNode,
ConditionalBranchMemory cbm,
LeftTupleSink sink,
InternalAgenda agenda,
TupleSets<LeftTuple> srcLeftTuples,
TupleSets<LeftTuple> trgLeftTuples,
TupleSets<LeftTuple> stagedLeftTuples,
RuleExecutor executor) {
if (srcLeftTuples.getDeleteFirst() != null) {
doLeftDeletes(sink, agenda, srcLeftTuples, trgLeftTuples, stagedLeftTuples, executor);
}
if (srcLeftTuples.getUpdateFirst() != null) {
doLeftUpdates(branchNode, cbm, sink, agenda, srcLeftTuples, trgLeftTuples, stagedLeftTuples, executor);
}
if (srcLeftTuples.getInsertFirst() != null) {
doLeftInserts(branchNode, cbm, sink, agenda, srcLeftTuples, trgLeftTuples, executor);
}
srcLeftTuples.resetAll();
}
public void doLeftInserts(ConditionalBranchNode branchNode,
ConditionalBranchMemory cbm,
LeftTupleSink sink,
InternalAgenda agenda,
TupleSets<LeftTuple> srcLeftTuples,
TupleSets<LeftTuple> trgLeftTuples,
RuleExecutor executor) {
ConditionalBranchEvaluator branchEvaluator = branchNode.getBranchEvaluator();
RuleAgendaItem ruleAgendaItem = executor.getRuleAgendaItem();
int salienceInt = 0;
Salience salience = ruleAgendaItem.getRule().getSalience();
if ( !salience.isDynamic() ) {
salienceInt = ruleAgendaItem.getRule().getSalience().getValue();
salience = null;
}
for (LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
LeftTuple next = leftTuple.getStagedNext();
boolean breaking = false;
ConditionalExecution conditionalExecution = branchEvaluator.evaluate(leftTuple, agenda.getWorkingMemory(), cbm.context);
boolean useLeftMemory = RuleNetworkEvaluator.useLeftMemory(branchNode, leftTuple);
if (conditionalExecution != null) {
RuleTerminalNode rtn = (RuleTerminalNode) conditionalExecution.getSink().getFirstLeftTupleSink();
LeftTuple branchedLeftTuple = rtn.createLeftTuple(leftTuple,
rtn,
leftTuple.getPropagationContext(), useLeftMemory);
PhreakRuleTerminalNode.doLeftTupleInsert( rtn, executor, agenda,
executor.getRuleAgendaItem(), salienceInt, salience, branchedLeftTuple) ;
breaking = conditionalExecution.isBreaking();
}
if (!breaking) {
trgLeftTuples.addInsert(sink.createLeftTuple(leftTuple,
sink,
leftTuple.getPropagationContext(), useLeftMemory));
}
leftTuple.clearStaged();
leftTuple = next;
}
}
public void doLeftUpdates(ConditionalBranchNode branchNode,
ConditionalBranchMemory cbm,
LeftTupleSink sink,
InternalAgenda agenda,
TupleSets<LeftTuple> srcLeftTuples,
TupleSets<LeftTuple> trgLeftTuples,
TupleSets<LeftTuple> stagedLeftTuples,
RuleExecutor executor) {
ConditionalBranchEvaluator branchEvaluator = branchNode.getBranchEvaluator();
RuleAgendaItem ruleAgendaItem = executor.getRuleAgendaItem();
int salienceInt = 0;
Salience salience = ruleAgendaItem.getRule().getSalience();
if ( !salience.isDynamic() ) {
salienceInt = ruleAgendaItem.getRule().getSalience().getValue();
salience = null;
}
for (LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
LeftTuple next = leftTuple.getStagedNext();
BranchTuples branchTuples = getBranchTuples(sink, leftTuple);
RuleTerminalNode oldRtn = null;
if (branchTuples.rtnLeftTuple != null) {
oldRtn = branchTuples.rtnLeftTuple.getTupleSink();
}
ConditionalExecution conditionalExecution = branchEvaluator.evaluate(leftTuple, agenda.getWorkingMemory(), cbm.context);
RuleTerminalNode newRtn = null;
boolean breaking = false;
if (conditionalExecution != null) {
newRtn = (RuleTerminalNode) conditionalExecution.getSink().getFirstLeftTupleSink();
breaking = conditionalExecution.isBreaking();
}
// Handle conditional branches
if (oldRtn != null) {
if (newRtn == null) {
// old exits, new does not, so delete
if ( branchTuples.rtnLeftTuple.getMemory() != null ) {
executor.removeLeftTuple(branchTuples.rtnLeftTuple);
}
PhreakRuleTerminalNode.doLeftDelete(agenda, executor, branchTuples.rtnLeftTuple);
} else if (newRtn == oldRtn) {
// old and new on same branch, so update
PhreakRuleTerminalNode.doLeftTupleUpdate(newRtn, executor, agenda, salienceInt, salience, branchTuples.rtnLeftTuple) ;
} else {
// old and new on different branches, delete one and insert the other
if ( branchTuples.rtnLeftTuple.getMemory() != null ) {
executor.removeLeftTuple(branchTuples.rtnLeftTuple);
}
PhreakRuleTerminalNode.doLeftDelete(agenda, executor, branchTuples.rtnLeftTuple);
branchTuples.rtnLeftTuple = newRtn.createLeftTuple(leftTuple,
newRtn,
leftTuple.getPropagationContext(), true);
PhreakRuleTerminalNode.doLeftTupleInsert( newRtn, executor, agenda,
executor.getRuleAgendaItem(), salienceInt, salience, branchTuples.rtnLeftTuple) ;
}
} else if (newRtn != null) {
// old does not exist, new exists, so insert
branchTuples.rtnLeftTuple = newRtn.createLeftTuple(leftTuple, newRtn,
leftTuple.getPropagationContext(), true);
PhreakRuleTerminalNode.doLeftTupleInsert( newRtn, executor, agenda,
executor.getRuleAgendaItem(), salienceInt, salience, branchTuples.rtnLeftTuple) ;
}
// Handle main branch
if (branchTuples.mainLeftTuple != null) {
normalizeStagedTuples( stagedLeftTuples, branchTuples.mainLeftTuple );
if (!breaking) {
// child exist, new one does, so update
trgLeftTuples.addUpdate(branchTuples.mainLeftTuple);
} else {
// child exist, new one does not, so delete
trgLeftTuples.addDelete(branchTuples.mainLeftTuple);
}
} else if (!breaking) {
// child didn't exist, new one does, so insert
trgLeftTuples.addInsert(sink.createLeftTuple(leftTuple,
sink,
leftTuple.getPropagationContext(), true));
}
leftTuple.clearStaged();
leftTuple = next;
}
}
public void doLeftDeletes(LeftTupleSink sink,
InternalAgenda agenda,
TupleSets<LeftTuple> srcLeftTuples,
TupleSets<LeftTuple> trgLeftTuples,
TupleSets<LeftTuple> stagedLeftTuples,
RuleExecutor executor) {
for (LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
LeftTuple next = leftTuple.getStagedNext();
BranchTuples branchTuples = getBranchTuples(sink, leftTuple);
if (branchTuples.rtnLeftTuple != null) {
if ( branchTuples.rtnLeftTuple.getMemory() != null ) {
executor.removeLeftTuple(branchTuples.rtnLeftTuple);
}
PhreakRuleTerminalNode.doLeftDelete(agenda, executor, branchTuples.rtnLeftTuple);
}
if (branchTuples.mainLeftTuple != null) {
RuleNetworkEvaluator.deleteChildLeftTuple(branchTuples.mainLeftTuple, trgLeftTuples, stagedLeftTuples);
}
leftTuple.clearStaged();
leftTuple = next;
}
}
/**
* A branch has two potential sinks. rtnSink is for the sink if the contained logic returns true.
* mainSink is for propagations after the branch node, if they are allowed.
* it may have one or the other or both. there is no state that indicates whether one or the other or both
* are present, so all tuple children must be inspected and references coalesced from that.
* when handling updates and deletes it must search the child tuples to colasce the references.
* This is done by checking the tuple sink with the known main or rtn sink.
*/
private BranchTuples getBranchTuples(LeftTupleSink sink, LeftTuple leftTuple) {
BranchTuples branchTuples = new BranchTuples();
LeftTuple child = leftTuple.getFirstChild();
if ( child != null ) {
// assigns the correct main or rtn LeftTuple based on the identified sink
if ( child.getTupleSink() == sink ) {
branchTuples.mainLeftTuple = child;
} else {
branchTuples.rtnLeftTuple = child;
}
child = child.getHandleNext();
if ( child != null ) {
if ( child.getTupleSink() == sink ) {
branchTuples.mainLeftTuple = child;
} else {
branchTuples.rtnLeftTuple = child;
}
}
}
return branchTuples;
}
private static class BranchTuples {
LeftTuple rtnLeftTuple;
LeftTuple mainLeftTuple;
}
}