/*
* 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.common;
import org.drools.core.WorkingMemoryEntryPoint;
import org.drools.core.impl.InternalKnowledgeBase;
import org.drools.core.reteoo.AccumulateNode.AccumulateContext;
import org.drools.core.reteoo.AccumulateNode.AccumulateMemory;
import org.drools.core.reteoo.BetaMemory;
import org.drools.core.reteoo.BetaNode;
import org.drools.core.reteoo.FromNode.FromMemory;
import org.drools.core.reteoo.LeftInputAdapterNode;
import org.drools.core.reteoo.LeftTuple;
import org.drools.core.reteoo.LeftTupleSink;
import org.drools.core.reteoo.LeftTupleSource;
import org.drools.core.reteoo.NodeTypeEnums;
import org.drools.core.reteoo.ObjectSource;
import org.drools.core.reteoo.ObjectTypeNode;
import org.drools.core.reteoo.ObjectTypeNode.ObjectTypeNodeMemory;
import org.drools.core.reteoo.RightTuple;
import org.drools.core.reteoo.RuleTerminalNode;
import org.drools.core.reteoo.TupleMemory;
import org.drools.core.spi.Tuple;
import org.drools.core.util.FastIterator;
import org.drools.core.util.Iterator;
import org.kie.api.KieBase;
import org.kie.internal.runtime.StatefulKnowledgeSession;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class PhreakActivationIterator
implements
Iterator {
private java.util.Iterator<AgendaItem> agendaItemIter;
List<AgendaItem> agendaItems;
PhreakActivationIterator() {
}
private PhreakActivationIterator(InternalWorkingMemory wm,
KieBase kbase) {
agendaItems = collectAgendaItems((InternalKnowledgeBase) kbase, wm);
agendaItemIter = agendaItems.iterator();
}
public static PhreakActivationIterator iterator(InternalWorkingMemory wm) {
return new PhreakActivationIterator( wm,
wm.getKnowledgeBase() );
}
public static PhreakActivationIterator iterator(StatefulKnowledgeSession ksession) {
return new PhreakActivationIterator( ((WorkingMemoryEntryPoint) ksession).getInternalWorkingMemory(),
ksession.getKieBase() );
}
public Object next() {
if ( agendaItemIter.hasNext() ) {
return agendaItemIter.next();
} else {
return null;
}
}
public static List<RuleTerminalNode> populateRuleTerminalNodes(InternalKnowledgeBase kbase, Set<RuleTerminalNode> nodeSet) {
Collection<BaseNode[]> nodesWithArray = kbase.getReteooBuilder().getTerminalNodes().values();
for (BaseNode[] nodeArray : nodesWithArray) {
for (BaseNode node : nodeArray) {
if (node.getType() == NodeTypeEnums.RuleTerminalNode) {
nodeSet.add((RuleTerminalNode) node);
}
}
}
return Arrays.asList(nodeSet.toArray(new RuleTerminalNode[nodeSet.size()]));
}
public static List<AgendaItem> collectAgendaItems(InternalKnowledgeBase kbase, InternalWorkingMemory wm) {
Set<RuleTerminalNode> nodeSet = new HashSet<RuleTerminalNode>();
List<RuleTerminalNode> nodeList = populateRuleTerminalNodes(kbase, nodeSet);
List<AgendaItem> agendaItems = new ArrayList<AgendaItem>();
for ( RuleTerminalNode rtn : nodeList ) {
if ( !nodeSet.contains(rtn) ) {
// this node has already been processed
continue;
}
processLeftTuples( rtn.getLeftTupleSource(), agendaItems, nodeSet, wm);
}
return agendaItems;
}
public static void processLeftTuples(LeftTupleSource node, List<AgendaItem> agendaItems, Set<RuleTerminalNode> nodeSet, InternalWorkingMemory wm) {
LeftTupleSource node1 = node;
while (NodeTypeEnums.LeftInputAdapterNode != node1.getType()) {
node1 = node1.getLeftTupleSource();
}
int maxShareCount = node1.getAssociationsSize();
while (NodeTypeEnums.LeftInputAdapterNode != node.getType()) {
Memory memory = wm.getNodeMemory((MemoryFactory) node);
if (memory.getSegmentMemory() == null) {
// segment has never been initialized, which means the rule has never been linked.
return;
}
if ( node.getAssociationsSize() == maxShareCount ) {
// the recurse must start from the first split node, otherwise we get partial overlaps in propagations
if (NodeTypeEnums.isBetaNode(node)) {
BetaMemory bm;
if (NodeTypeEnums.AccumulateNode == node.getType()) {
AccumulateMemory am = (AccumulateMemory) memory;
bm = am.getBetaMemory();
FastIterator it = bm.getLeftTupleMemory().fullFastIterator();
Tuple lt = BetaNode.getFirstTuple( bm.getLeftTupleMemory(), it );
for (; lt != null; lt = (LeftTuple) it.next(lt)) {
AccumulateContext accctx = (AccumulateContext) lt.getContextObject();
collectFromPeers(accctx.getResultLeftTuple(), agendaItems, nodeSet, wm);
}
} else if ( NodeTypeEnums.ExistsNode == node.getType() ) {
bm = (BetaMemory) wm.getNodeMemory((MemoryFactory) node);
FastIterator it = bm.getRightTupleMemory().fullFastIterator(); // done off the RightTupleMemory, as exists only have unblocked tuples on the left side
RightTuple rt = (RightTuple) BetaNode.getFirstTuple( bm.getRightTupleMemory(), it );
for (; rt != null; rt = (RightTuple) it.next(rt)) {
for ( LeftTuple lt = rt.getBlocked(); lt != null; lt = lt.getBlockedNext() ) {
if ( lt.getFirstChild() != null ) {
collectFromPeers(lt.getFirstChild(), agendaItems, nodeSet, wm);
}
}
}
} else {
bm = (BetaMemory) wm.getNodeMemory((MemoryFactory) node);
FastIterator it = bm.getLeftTupleMemory().fullFastIterator();
Tuple lt = BetaNode.getFirstTuple( bm.getLeftTupleMemory(), it );
for (; lt != null; lt = (LeftTuple) it.next(lt)) {
if ( lt.getFirstChild() != null ) {
collectFromLeftInput(lt.getFirstChild(), agendaItems, nodeSet, wm);
}
}
}
return;
} else if (NodeTypeEnums.FromNode == node.getType()) {
FromMemory fm = (FromMemory) wm.getNodeMemory((MemoryFactory) node);
TupleMemory ltm = fm.getBetaMemory().getLeftTupleMemory();
FastIterator it = ltm.fullFastIterator();
for (LeftTuple lt = (LeftTuple) ltm.getFirst(null); lt != null; lt = (LeftTuple) it.next(lt)) {
if ( lt.getFirstChild() != null ) {
collectFromLeftInput(lt.getFirstChild(), agendaItems, nodeSet, wm);
}
}
return;
}
}
node = node.getLeftTupleSource();
}
// No beta or from nodes, so must retrieve LeftTuples from the LiaNode.
// This is done by scanning all the LeftTuples referenced from the FactHandles in the ObjectTypeNode
LeftInputAdapterNode lian = (LeftInputAdapterNode) node;
Memory memory = wm.getNodeMemory((MemoryFactory) node);
if (memory.getSegmentMemory() == null) {
// segment has never been initialized, which means the rule has never been linked.
return;
}
ObjectSource os = lian.getObjectSource();
while (os.getType() != NodeTypeEnums.ObjectTypeNode) {
os = os.getParentObjectSource();
}
ObjectTypeNode otn = (ObjectTypeNode) os;
final ObjectTypeNodeMemory omem = wm.getNodeMemory(otn);
LeftTupleSink firstLiaSink = lian.getSinkPropagator().getFirstLeftTupleSink();
java.util.Iterator<InternalFactHandle> it = omem.iterator();
while (it.hasNext()) {
InternalFactHandle fh = it.next();
fh.forEachLeftTuple( lt -> {
if ( lt.getTupleSink() == firstLiaSink ) {
collectFromLeftInput(lt, agendaItems, nodeSet, wm);
}
});
}
}
private static void collectFromLeftInput(LeftTuple lt, List<AgendaItem> agendaItems, Set<RuleTerminalNode> nodeSet, InternalWorkingMemory wm) {
for (; lt != null; lt = lt.getHandleNext()) {
collectFromPeers(lt, agendaItems, nodeSet, wm);
}
}
private static void collectFromPeers(LeftTuple peer, List<AgendaItem> agendaItems, Set<RuleTerminalNode> nodeSet, InternalWorkingMemory wm) {
while (peer != null) {
if ( peer.getTupleSink().getType() == NodeTypeEnums.AccumulateNode ) {
AccumulateContext accctx = (AccumulateContext) peer.getContextObject();
if (accctx != null) {
// the accumulate context can be null if the lefttuple hasn't been evaluated yet
collectFromLeftInput(accctx.getResultLeftTuple(), agendaItems, nodeSet, wm);
}
} else if ( peer.getFirstChild() != null ) {
for (LeftTuple childLt = peer.getFirstChild(); childLt != null; childLt = childLt.getHandleNext()) {
collectFromLeftInput(childLt, agendaItems, nodeSet, wm);
}
} else if ( peer.getTupleSink().getType() == NodeTypeEnums.RuleTerminalNode ) {
agendaItems.add((AgendaItem) peer);
nodeSet.remove(peer.getTupleSink()); // remove this RuleTerminalNode, as we know we've visited it already
}
peer = peer.getPeer();
}
}
}