001: package org.drools.reteoo;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.io.Serializable;
020:
021: import org.drools.common.EventSupport;
022: import org.drools.RuleBaseConfiguration;
023: import org.drools.common.BinaryHeapQueueAgendaGroup;
024: import org.drools.common.AgendaItem;
025: import org.drools.common.BaseNode;
026: import org.drools.common.InternalAgenda;
027: import org.drools.common.InternalAgendaGroup;
028: import org.drools.common.InternalFactHandle;
029: import org.drools.common.InternalRuleFlowGroup;
030: import org.drools.common.InternalWorkingMemory;
031: import org.drools.common.LogicalDependency;
032: import org.drools.common.NodeMemory;
033: import org.drools.common.PropagationContextImpl;
034: import org.drools.common.ScheduledAgendaItem;
035: import org.drools.rule.GroupElement;
036: import org.drools.rule.Rule;
037: import org.drools.spi.Activation;
038: import org.drools.spi.ActivationGroup;
039: import org.drools.spi.AgendaGroup;
040: import org.drools.spi.Duration;
041: import org.drools.spi.PropagationContext;
042: import org.drools.spi.RuleFlowGroup;
043: import org.drools.util.Iterator;
044: import org.drools.util.LinkedList;
045: import org.drools.util.TupleHashTable;
046:
047: /**
048: * Leaf Rete-OO node responsible for enacting <code>Action</code> s on a
049: * matched <code>Rule</code>.
050: *
051: * @see org.drools.rule.Rule
052: *
053: * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter </a>
054: */
055: public final class RuleTerminalNode extends BaseNode implements
056: TupleSinkNode, NodeMemory, TerminalNode {
057: // ------------------------------------------------------------
058: // Instance members
059: // ------------------------------------------------------------
060:
061: private int sequence;
062:
063: /**
064: *
065: */
066: private static final long serialVersionUID = 400L;
067: /** The rule to invoke upon match. */
068: private final Rule rule;
069: /**
070: * the subrule reference is needed to resolve declarations
071: * because declarations may have different offsets in each subrule
072: */
073: private final GroupElement subrule;
074: private final TupleSource tupleSource;
075:
076: private TupleSinkNode previousTupleSinkNode;
077: private TupleSinkNode nextTupleSinkNode;
078:
079: // ------------------------------------------------------------
080: // Constructors
081: // ------------------------------------------------------------
082:
083: /**
084: * Construct.
085: *
086: * @param inputSource
087: * The parent tuple source.
088: * @param rule
089: * The rule.
090: */
091: public RuleTerminalNode(final int id, final TupleSource source,
092: final Rule rule, final GroupElement subrule) {
093: super (id);
094: this .rule = rule;
095: this .tupleSource = source;
096: this .subrule = subrule;
097: }
098:
099: // ------------------------------------------------------------
100: // Instance methods
101: // ------------------------------------------------------------
102:
103: /**
104: * Retrieve the <code>Action</code> associated with this node.
105: *
106: * @return The <code>Action</code> associated with this node.
107: */
108: public Rule getRule() {
109: return this .rule;
110: }
111:
112: public void setSequence(int seq) {
113: this .sequence = seq;
114: }
115:
116: public int getSequence() {
117: return this .sequence;
118: }
119:
120: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
121: // org.drools.impl.TupleSink
122: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
123:
124: public void assertTuple(final ReteTuple tuple,
125: final PropagationContext context,
126: final InternalWorkingMemory workingMemory) {
127: assertTuple(tuple, context, workingMemory, true);
128:
129: }
130:
131: /**
132: * Assert a new <code>Tuple</code>.
133: *
134: * @param tuple
135: * The <code>Tuple</code> being asserted.
136: * @param workingMemory
137: * The working memory seesion.
138: * @throws AssertionException
139: * If an error occurs while asserting.
140: */
141: public void assertTuple(final ReteTuple tuple,
142: final PropagationContext context,
143: final InternalWorkingMemory workingMemory,
144: final boolean fireActivationCreated) {
145:
146: //check if the rule is effective
147: if (!this .rule.isEffective()) {
148: return;
149: }
150:
151: // if the current Rule is no-loop and the origin rule is the same and its the same set of facts (tuple) then return
152: if (context.getType() == PropagationContext.MODIFICATION) {
153: if (this .rule.isNoLoop()
154: && this .rule.equals(context.getRuleOrigin())
155: && context.getActivationOrigin().getTuple().equals(
156: tuple)) {
157: return;
158: }
159: } else if (this .rule.isNoLoop()
160: && this .rule.equals(context.getRuleOrigin())) {
161: return;
162: }
163:
164: //we only have to clone the head fact to make sure the graph is not affected during consequence reads after a modify
165: final ReteTuple cloned = new ReteTuple(tuple);
166:
167: final InternalAgenda agenda = (InternalAgenda) workingMemory
168: .getAgenda();
169:
170: final Duration dur = this .rule.getDuration();
171:
172: if (dur != null && dur.getDuration(tuple) > 0) {
173: final ScheduledAgendaItem item = new ScheduledAgendaItem(
174: context.getPropagationNumber(), cloned, agenda,
175: context, this .rule, this .subrule);
176: final TerminalNodeMemory memory = (TerminalNodeMemory) workingMemory
177: .getNodeMemory(this );
178: if (this .rule.getActivationGroup() != null) {
179: // Lazy cache activationGroup
180: if (memory.getActivationGroup() == null) {
181: memory.setActivationGroup(workingMemory.getAgenda()
182: .getActivationGroup(
183: this .rule.getActivationGroup()));
184: }
185: memory.getActivationGroup().addActivation(item);
186: }
187:
188: agenda.scheduleItem(item);
189: tuple.setActivation(item);
190:
191: if (!workingMemory.isSequential()) {
192: memory.getTupleMemory().add(tuple);
193: }
194:
195: item.setActivated(true);
196: ((EventSupport) workingMemory).getAgendaEventSupport()
197: .fireActivationCreated(item, workingMemory);
198: } else {
199: // -----------------
200: // Lazy instantiation and addition to the Agenda of AgendGroup
201: // implementations
202: // ----------------
203: final TerminalNodeMemory memory = (TerminalNodeMemory) workingMemory
204: .getNodeMemory(this );
205: InternalAgendaGroup agendaGroup = memory.getAgendaGroup();
206: if (agendaGroup == null) {
207: // @todo: this logic really should be encapsulated inside the Agenda
208: if (this .rule.getAgendaGroup() == null
209: || this .rule.getAgendaGroup().equals("")
210: || this .rule.getAgendaGroup().equals(
211: AgendaGroup.MAIN)) {
212: // Is the Rule AgendaGroup undefined? If it is use MAIN,
213: // which is added to the Agenda by default
214: agendaGroup = (InternalAgendaGroup) agenda
215: .getAgendaGroup(AgendaGroup.MAIN);
216: } else {
217: // AgendaGroup is defined, so try and get the AgendaGroup
218: // from the Agenda
219: agendaGroup = (InternalAgendaGroup) agenda
220: .getAgendaGroup(this .rule.getAgendaGroup());
221: }
222:
223: memory.setAgendaGroup(agendaGroup);
224: }
225:
226: // set the focus if rule autoFocus is true
227: if (this .rule.getAutoFocus()) {
228: agenda.setFocus(agendaGroup);
229: }
230:
231: final AgendaItem item = new AgendaItem(context
232: .getPropagationNumber(), cloned, rule.getSalience()
233: .getValue(tuple, workingMemory), context,
234: this .rule, this .subrule);
235:
236: if (workingMemory.isSequential()) {
237: item.setSequenence(this .sequence);
238: }
239:
240: if (this .rule.getActivationGroup() != null) {
241: // Lazy cache activationGroup
242: if (memory.getActivationGroup() == null) {
243: memory.setActivationGroup(workingMemory.getAgenda()
244: .getActivationGroup(
245: this .rule.getActivationGroup()));
246: }
247: memory.getActivationGroup().addActivation(item);
248: }
249:
250: item.setAgendaGroup(agendaGroup);
251: if (this .rule.getRuleFlowGroup() == null) {
252: // No RuleFlowNode so add it directly to the Agenda
253:
254: // do not add the activation if the rule is "lock-on-active" and the AgendaGroup is active
255: // we must check the context to determine if its a new tuple or an exist re-activated tuple as part of the retract
256: if (context.getType() == PropagationContext.MODIFICATION) {
257: if (this .rule.isLockOnActive()
258: && agendaGroup.isActive()) {
259: Activation justifier = context
260: .removeRetractedTuple(this .rule, tuple);
261: if (justifier == null) {
262: // This rule is locked and active, do not allow new tuples to activate
263: return;
264: } else if (this .rule.hasLogicalDependency()) {
265: copyLogicalDependencies(context,
266: workingMemory, item, justifier);
267: }
268: } else if (this .rule.hasLogicalDependency()) {
269: Activation justifier = context
270: .removeRetractedTuple(this .rule, tuple);
271: copyLogicalDependencies(context, workingMemory,
272: item, justifier);
273: }
274: } else if (this .rule.isLockOnActive()
275: && agendaGroup.isActive()) {
276: return;
277: }
278:
279: agendaGroup.add(item);
280: } else {
281: //There is a RuleFlowNode so add it there, instead of the Agenda
282: RuleFlowGroup rfg = memory.getRuleFlowGroup();
283: // Lazy cache ruleFlowGroup
284: if (rfg == null) {
285: rfg = workingMemory.getAgenda().getRuleFlowGroup(
286: this .rule.getRuleFlowGroup());
287: memory.setRuleFlowGroup(rfg);
288: }
289:
290: // do not add the activation if the rule is "lock-on-active" and the RuleFlowGroup is active
291: // we must check the context to determine if its a new tuple or an exist re-activated tuple as part of the retract
292: if (context.getType() == PropagationContext.MODIFICATION) {
293: if (this .rule.isLockOnActive() && rfg.isActive()) {
294: Activation justifier = context
295: .removeRetractedTuple(this .rule, tuple);
296: if (justifier == null) {
297: // This rule is locked and active, do not allow new tuples to activate
298: return;
299: } else if (this .rule.hasLogicalDependency()) {
300: copyLogicalDependencies(context,
301: workingMemory, item, justifier);
302: }
303: } else if (this .rule.hasLogicalDependency()) {
304: Activation justifier = context
305: .removeRetractedTuple(this .rule, tuple);
306: copyLogicalDependencies(context, workingMemory,
307: item, justifier);
308: }
309: } else if (this .rule.isLockOnActive() && rfg.isActive()) {
310: return;
311: }
312:
313: ((InternalRuleFlowGroup) memory.getRuleFlowGroup())
314: .addActivation(item);
315:
316: }
317:
318: tuple.setActivation(item);
319: memory.getTupleMemory().add(tuple);
320:
321: item.setActivated(true);
322:
323: // We only want to fire an event on a truly new Activation and not on an Activation as a result of a modify
324: if (fireActivationCreated) {
325: ((EventSupport) workingMemory).getAgendaEventSupport()
326: .fireActivationCreated(item, workingMemory);
327: }
328: }
329:
330: agenda.increaseActiveActivations();
331: }
332:
333: private void copyLogicalDependencies(
334: final PropagationContext context,
335: final InternalWorkingMemory workingMemory,
336: final AgendaItem item, Activation justifier) {
337: if (justifier != null) {
338: final LinkedList list = justifier.getLogicalDependencies();
339: if (list != null && !list.isEmpty()) {
340: for (LogicalDependency node = (LogicalDependency) list
341: .getFirst(); node != null; node = (LogicalDependency) node
342: .getNext()) {
343: final InternalFactHandle handle = (InternalFactHandle) node
344: .getFactHandle();
345: workingMemory.getTruthMaintenanceSystem()
346: .addLogicalDependency(handle, item,
347: context, this .rule);
348: }
349: }
350: }
351: }
352:
353: public void retractTuple(final ReteTuple leftTuple,
354: final PropagationContext context,
355: final InternalWorkingMemory workingMemory) {
356: final TerminalNodeMemory memory = (TerminalNodeMemory) workingMemory
357: .getNodeMemory(this );
358: final ReteTuple tuple = memory.getTupleMemory().remove(
359: leftTuple);
360: if (tuple == null) {
361: // tuple should only be null if it was asserted and reached a no-loop causing it to exit early
362: // before being added to the node memory and an activation created and attached
363: return;
364: }
365:
366: final Activation activation = tuple.getActivation();
367: if (activation.getLogicalDependencies() != null
368: && !activation.getLogicalDependencies().isEmpty()) {
369: context.addRetractedTuple(this .rule, activation);
370: }
371:
372: if (activation.isActivated()) {
373: if (context.getType() == PropagationContext.MODIFICATION) {
374: // during a modify if we have either isLockOnActive or the activation has logical dependencies
375: // then we need to track retractions, so we know which are exising activations and which are truly new
376: if (this .rule.isLockOnActive()) {
377: context.addRetractedTuple(this .rule, activation);
378: }
379: }
380:
381: activation.remove();
382:
383: if (activation.getActivationGroupNode() != null) {
384: activation.getActivationGroupNode()
385: .getActivationGroup().removeActivation(
386: activation);
387: }
388:
389: if (activation.getRuleFlowGroupNode() != null) {
390: final InternalRuleFlowGroup ruleFlowGroup = activation
391: .getRuleFlowGroupNode().getRuleFlowGroup();
392: ruleFlowGroup.removeActivation(activation);
393: }
394:
395: ((EventSupport) workingMemory).getAgendaEventSupport()
396: .fireActivationCancelled(activation, workingMemory);
397: ((InternalAgenda) workingMemory.getAgenda())
398: .decreaseActiveActivations();
399: } else {
400: ((InternalAgenda) workingMemory.getAgenda())
401: .decreaseDormantActivations();
402: }
403:
404: workingMemory.removeLogicalDependencies(activation, context,
405: this .rule);
406: }
407:
408: public String toString() {
409: return "[RuleTerminalNode: rule=" + this .rule.getName() + "]";
410: }
411:
412: public void ruleAttached() {
413: // TODO Auto-generated method stub
414:
415: }
416:
417: public void attach() {
418: this .tupleSource.addTupleSink(this );
419: }
420:
421: public void attach(final InternalWorkingMemory[] workingMemories) {
422: attach();
423:
424: for (int i = 0, length = workingMemories.length; i < length; i++) {
425: final InternalWorkingMemory workingMemory = workingMemories[i];
426: final PropagationContext propagationContext = new PropagationContextImpl(
427: workingMemory.getNextPropagationIdCounter(),
428: PropagationContext.RULE_ADDITION, null, null);
429: this .tupleSource.updateSink(this , propagationContext,
430: workingMemory);
431: }
432: }
433:
434: public void remove(final BaseNode node,
435: final InternalWorkingMemory[] workingMemories) {
436: for (int i = 0, length = workingMemories.length; i < length; i++) {
437: final InternalWorkingMemory workingMemory = workingMemories[i];
438:
439: final TerminalNodeMemory memory = (TerminalNodeMemory) workingMemory
440: .getNodeMemory(this );
441: final Iterator it = memory.getTupleMemory().iterator();
442: for (ReteTuple tuple = (ReteTuple) it.next(); tuple != null; tuple = (ReteTuple) it
443: .next()) {
444: final Activation activation = tuple.getActivation();
445:
446: if (activation.isActivated()) {
447: activation.remove();
448: ((EventSupport) workingMemory)
449: .getAgendaEventSupport()
450: .fireActivationCancelled(activation,
451: workingMemory);
452: }
453:
454: final PropagationContext propagationContext = new PropagationContextImpl(
455: workingMemory.getNextPropagationIdCounter(),
456: PropagationContext.RULE_REMOVAL, null, null);
457: workingMemory.getTruthMaintenanceSystem()
458: .removeLogicalDependencies(activation,
459: propagationContext, this .rule);
460: }
461:
462: workingMemory.executeQueuedActions();
463: workingMemory.clearNodeMemory(this );
464: }
465:
466: removeShare();
467:
468: this .tupleSource.remove(this , workingMemories);
469: }
470:
471: public Object createMemory(final RuleBaseConfiguration config) {
472: return new TerminalNodeMemory();
473: }
474:
475: /**
476: * Returns the next node
477: * @return
478: * The next TupleSinkNode
479: */
480: public TupleSinkNode getNextTupleSinkNode() {
481: return this .nextTupleSinkNode;
482: }
483:
484: /**
485: * Sets the next node
486: * @param next
487: * The next TupleSinkNode
488: */
489: public void setNextTupleSinkNode(final TupleSinkNode next) {
490: this .nextTupleSinkNode = next;
491: }
492:
493: /**
494: * Returns the previous node
495: * @return
496: * The previous TupleSinkNode
497: */
498: public TupleSinkNode getPreviousTupleSinkNode() {
499: return this .previousTupleSinkNode;
500: }
501:
502: /**
503: * Sets the previous node
504: * @param previous
505: * The previous TupleSinkNode
506: */
507: public void setPreviousTupleSinkNode(final TupleSinkNode previous) {
508: this .previousTupleSinkNode = previous;
509: }
510:
511: public int hashCode() {
512: return this .rule.hashCode();
513: }
514:
515: public boolean equals(final Object object) {
516: if (object == this ) {
517: return true;
518: }
519:
520: if (object == null || !(object instanceof RuleTerminalNode)) {
521: return false;
522: }
523:
524: final RuleTerminalNode other = (RuleTerminalNode) object;
525: return this .rule.equals(other.rule);
526: }
527:
528: class TerminalNodeMemory implements Serializable {
529: private static final long serialVersionUID = 400L;
530:
531: private InternalAgendaGroup agendaGroup;
532:
533: private ActivationGroup activationGroup;
534:
535: private RuleFlowGroup ruleFlowGroup;
536:
537: private TupleHashTable tupleMemory;
538:
539: public TerminalNodeMemory() {
540: this .tupleMemory = new TupleHashTable();
541: }
542:
543: public InternalAgendaGroup getAgendaGroup() {
544: return this .agendaGroup;
545: }
546:
547: public void setAgendaGroup(final InternalAgendaGroup agendaGroup) {
548: this .agendaGroup = agendaGroup;
549: }
550:
551: public ActivationGroup getActivationGroup() {
552: return this .activationGroup;
553: }
554:
555: public void setActivationGroup(
556: final ActivationGroup activationGroup) {
557: this .activationGroup = activationGroup;
558: }
559:
560: public TupleHashTable getTupleMemory() {
561: return this .tupleMemory;
562: }
563:
564: public RuleFlowGroup getRuleFlowGroup() {
565: return this .ruleFlowGroup;
566: }
567:
568: public void setRuleFlowGroup(final RuleFlowGroup ruleFlowGroup) {
569: this.ruleFlowGroup = ruleFlowGroup;
570: }
571: }
572: }
|