/* * 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.base.DroolsQuery; import org.drools.core.common.InternalAgenda; import org.drools.core.common.TupleSets; import org.drools.core.reteoo.LeftTuple; import org.drools.core.reteoo.LeftTupleSink; import org.drools.core.reteoo.PathMemory; import org.drools.core.reteoo.QueryElementNode.QueryElementNodeMemory; import org.drools.core.reteoo.QueryTerminalNode; import org.drools.core.reteoo.RuleTerminalNode; import org.drools.core.spi.PropagationContext; import org.drools.core.spi.Tuple; import org.drools.core.util.LinkedList; import java.util.List; /** * Created with IntelliJ IDEA. * User: mdproctor * Date: 03/05/2013 * Time: 15:42 * To change this template use File | Settings | File Templates. */ public class PhreakQueryTerminalNode { public void doNode(QueryTerminalNode qtnNode, InternalAgenda agenda, TupleSets<LeftTuple> srcLeftTuples, LinkedList<StackEntry> stack) { if (srcLeftTuples.getDeleteFirst() != null) { doLeftDeletes(qtnNode, agenda, srcLeftTuples, stack); } if (srcLeftTuples.getUpdateFirst() != null) { doLeftUpdates(qtnNode, agenda, srcLeftTuples, stack); } if (srcLeftTuples.getInsertFirst() != null) { doLeftInserts(qtnNode, agenda, srcLeftTuples, stack); } srcLeftTuples.resetAll(); } public void doLeftInserts(QueryTerminalNode qtnNode, InternalAgenda agenda, TupleSets<LeftTuple> srcLeftTuples, LinkedList<StackEntry> stack) { for (LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) { LeftTuple next = leftTuple.getStagedNext(); //qtnNode.assertLeftTuple( leftTuple, leftTuple.getPropagationContext(), wm ); PropagationContext pCtx = RuleTerminalNode.findMostRecentPropagationContext(leftTuple, leftTuple.getPropagationContext()); // find the DroolsQuery object Tuple rootEntry = leftTuple.getRootTuple(); DroolsQuery dquery = (DroolsQuery) rootEntry.getFactHandle().getObject(); dquery.setQuery(qtnNode.getQuery()); if (dquery.getStackEntry() != null) { checkAndTriggerQueryReevaluation(agenda, stack, rootEntry, dquery); } // Add results to the adapter dquery.getQueryResultCollector().rowAdded(qtnNode.getQuery(), leftTuple, pCtx, agenda.getWorkingMemory()); leftTuple.clearStaged(); leftTuple = next; } } public void doLeftUpdates(QueryTerminalNode qtnNode, InternalAgenda agenda, TupleSets<LeftTuple> srcLeftTuples, LinkedList<StackEntry> stack) { for (LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) { LeftTuple next = leftTuple.getStagedNext(); PropagationContext pCtx = RuleTerminalNode.findMostRecentPropagationContext(leftTuple, leftTuple.getPropagationContext()); // qtnNode.modifyLeftTuple( leftTuple, leftTuple.getPropagationContext(), wm ); LeftTuple rootEntry = leftTuple; // find the DroolsQuery object while (rootEntry.getParent() != null) { rootEntry = rootEntry.getParent(); } DroolsQuery dquery = (DroolsQuery) rootEntry.getFactHandle().getObject(); dquery.setQuery(qtnNode.getQuery()); if (dquery.getStackEntry() != null) { checkAndTriggerQueryReevaluation(agenda, stack, rootEntry, dquery); } // Add results to the adapter dquery.getQueryResultCollector().rowUpdated(qtnNode.getQuery(), leftTuple, pCtx, agenda.getWorkingMemory()); leftTuple.clearStaged(); leftTuple = next; } } public void doLeftDeletes(QueryTerminalNode qtnNode, InternalAgenda agenda, TupleSets<LeftTuple> srcLeftTuples, LinkedList<StackEntry> stack) { for (LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) { LeftTuple next = leftTuple.getStagedNext(); //qtnNode.retractLeftTuple( leftTuple, leftTuple.getPropagationContext(), wm ); PropagationContext pCtx = RuleTerminalNode.findMostRecentPropagationContext(leftTuple, leftTuple.getPropagationContext()); LeftTuple rootEntry = leftTuple; // find the DroolsQuery object while (rootEntry.getParent() != null) { rootEntry = rootEntry.getParent(); } DroolsQuery dquery = (DroolsQuery) rootEntry.getFactHandle().getObject(); dquery.setQuery(qtnNode.getQuery()); if (dquery.getStackEntry() != null) { checkAndTriggerQueryReevaluation(agenda, stack, rootEntry, dquery); } // Add results to the adapter dquery.getQueryResultCollector().rowRemoved(qtnNode.getQuery(), leftTuple, pCtx, agenda.getWorkingMemory()); leftTuple.clearStaged(); leftTuple = next; } } public static void checkAndTriggerQueryReevaluation(InternalAgenda agenda, LinkedList<StackEntry> stack, Tuple rootEntry, DroolsQuery dquery) { StackEntry stackEntry = dquery.getStackEntry(); if (!isAdded(stack, stackEntry)) { // Ignore unless stackEntry is not added to stack // node must be marked as dirty ((QueryElementNodeMemory)stackEntry.getNodeMem()).setNodeDirtyWithoutNotify(); if (stackEntry.getRmem().getPathEndNode().getPathNodes()[0] == ((LeftTupleSink)rootEntry.getTupleSink()).getLeftTupleSource()) { // query is recursive, so just re-add the stack entry to the current stack. This happens for reactive queries, triggered by a beta node right input stack.add(stackEntry); } else { // parents is anther rule/query need to notify for agenda to schedule. query is reactive, triggered by right input, List<PathMemory> pmems = dquery.getRuleMemories(); if (pmems != null) { // StackEntry is null, when query is called directly from java // reactivity comes form within the query, so need to notify parent rules to evaluate the results for (int i = 0, length = pmems.size(); i < length; i++) { PathMemory pmem = pmems.get(i); pmem.doLinkRule(agenda); // method already ignores is rule is activated and on agenda } } } } } public static boolean isAdded(LinkedList<StackEntry> stack, StackEntry stackEntry) { if (stackEntry == null || stackEntry.getPrevious() != null || stackEntry.getNext() != null || stack.getFirst() == stackEntry) { return true; } return false; } }