From: <jbo...@li...> - 2006-05-06 15:51:25
|
Author: mar...@jb... Date: 2006-05-06 11:51:12 -0400 (Sat, 06 May 2006) New Revision: 4109 Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/XorGroupImpl.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/XorGroupNode.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/XorGroup.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/util/LinkedListObjectWrapper.java Removed: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/util/LinkedListNodeWrapper.java Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/SynchronizedWorkingMemory.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/WorkingMemory.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/DefaultKnowledgeHelper.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/Agenda.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/AgendaItem.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/ScheduledAgendaItem.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/leaps/WorkingMemoryImpl.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/LeftInputAdapterNode.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/TerminalNode.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/TupleSource.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/WorkingMemoryImpl.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/Rule.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/Activation.java labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/KnowledgeHelper.java labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/AgendaTest.java labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/LeftInputAdapterNodeTest.java Log: JBRULES-259 Implement Xor Group -includes unit and integration tests JBRULES-258 Clear Agenda Group Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/SynchronizedWorkingMemory.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/SynchronizedWorkingMemory.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/SynchronizedWorkingMemory.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -68,6 +68,10 @@ this.workingMemory.clearAgenda(); } + public synchronized void clearAgendaGroup(String group) { + this.workingMemory.clearAgendaGroup(group); + } + public synchronized boolean containsObject(FactHandle handle) { return this.workingMemory.containsObject( handle ); } Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/WorkingMemory.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/WorkingMemory.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/WorkingMemory.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -278,6 +278,11 @@ * */ void clearAgenda(); + + /** + * Clear the Agenda Group + */ + public void clearAgendaGroup(String group); /** * Forces the workingMemory to be derefenced from Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/DefaultKnowledgeHelper.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/DefaultKnowledgeHelper.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/DefaultKnowledgeHelper.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -105,6 +105,10 @@ public void clearAgenda() { this.workingMemory.clearAgenda(); } + + public void clearAgendaGroup(String group) { + this.workingMemory.clearAgendaGroup( group ); + } public Object get(Declaration declaration) { return declaration.getValue( this.tuple.get( declaration ).getObject() ); Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/Agenda.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/Agenda.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/Agenda.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -31,7 +31,10 @@ import org.drools.spi.AgendaGroup; import org.drools.spi.ConsequenceException; import org.drools.spi.KnowledgeHelper; +import org.drools.spi.XorGroup; import org.drools.util.LinkedListNode; +import org.drools.util.LinkedListObjectWrapper; +import org.drools.util.Queueable; /** * Rule-firing Agenda. @@ -65,6 +68,8 @@ /** Items time-delayed. */ private final Map agendaGroups; + + private final Map xorGroups; private final LinkedList focusStack; @@ -87,6 +92,7 @@ public Agenda(WorkingMemory workingMemory) { this.workingMemory = workingMemory; this.agendaGroups = new HashMap(); + this.xorGroups = new HashMap(); this.focusStack = new LinkedList(); // MAIN should always be the first AgendaGroup and can never be removed @@ -104,26 +110,6 @@ } /** - * Clears all Activations from the Agenda - * - */ - public void clearAgenda() { - EventSupport eventsupport = (EventSupport) this.workingMemory; - // Cancel all items and fire a Cancelled event for each Activation - for ( Iterator agendaGroupIterator = this.agendaGroups.values().iterator(); agendaGroupIterator.hasNext(); ) { - AgendaGroupImpl group = (AgendaGroupImpl) agendaGroupIterator.next(); - group.clear(); - } - - if ( this.scheduledActivations != null && !this.scheduledActivations.isEmpty() ) { - for ( ScheduledAgendaItem item = (ScheduledAgendaItem) this.scheduledActivations.removeFirst(); item != null; item = (ScheduledAgendaItem) this.scheduledActivations.removeFirst() ) { - item.remove(); - eventsupport.getAgendaEventSupport().fireActivationCancelled( item ); - } - } - } - - /** * Schedule an agenda item for delayed firing. * * @param item @@ -222,6 +208,15 @@ return (AgendaGroup[]) this.focusStack.toArray( new AgendaGroup[this.focusStack.size()] ); } + public XorGroup getXorGroup(String name) { + XorGroupImpl xorGroup = (XorGroupImpl) this.xorGroups.get( name ); + if (xorGroup == null) { + xorGroup = new XorGroupImpl( name ); + this.xorGroups.put( name, xorGroup ); + } + return xorGroup; + } + /** * Iterates all the <code>AgendGroup<code>s in the focus stack returning the total number of <code>Activation</code>s * @return @@ -268,6 +263,102 @@ } /** + * Clears all Activations from the Agenda + * + */ + public void clearAgenda() { + // Cancel all items and fire a Cancelled event for each Activation + for ( Iterator agendaGroupIterator = this.agendaGroups.values().iterator(); agendaGroupIterator.hasNext(); ) { + AgendaGroupImpl group = (AgendaGroupImpl) agendaGroupIterator.next(); + clearAgendaGroup( group ); + } + + EventSupport eventsupport = (EventSupport) this.workingMemory; + if ( this.scheduledActivations != null && !this.scheduledActivations.isEmpty() ) { + for ( ScheduledAgendaItem item = (ScheduledAgendaItem) this.scheduledActivations.removeFirst(); item != null; item = (ScheduledAgendaItem) this.scheduledActivations.removeFirst() ) { + item.remove(); + eventsupport.getAgendaEventSupport().fireActivationCancelled( item ); + } + } + } + + /** + * Clears all Activations from an Agenda Group. Any Activations that are also in an Xor Group are removed the + * the Xor Group. + * + * @param agendaGroup + */ + public void clearAgendaGroup(String name) { + AgendaGroupImpl agendaGroup = (AgendaGroupImpl) this.agendaGroups.get( name ); + if ( agendaGroup != null ) { + clearAgendaGroup( agendaGroup ); + } + } + + /** + * Clears all Activations from an Agenda Group. Any Activations that are also in an Xor Group are removed the + * the Xor Group. + * + * @param agendaGroup + */ + public void clearAgendaGroup(AgendaGroupImpl agendaGroup) { + EventSupport eventsupport = (EventSupport) this.workingMemory; + + Queueable[] queueable = agendaGroup.getQueueable(); + for(int i = 0, length = queueable.length; i < length; i++) { + AgendaItem item = (AgendaItem) queueable[i]; + if ( item == null ) { + continue; + } + + // this must be set false before removal from the XorGroup. Otherwise the XorGroup will also try to cancel the Actvation + item.setActivated( false ); + + if ( item.getXorGroupNode() != null ) { + item.getXorGroupNode().getXorGroup().removeActivation( item ); + } + + eventsupport.getAgendaEventSupport().fireActivationCancelled( item ); + } + agendaGroup.clear(); + } + + + /** + * Clears all Activations from an Xor Group. Any Activations that are also in an Agenda Group are removed + * from the Agenda Group. + * + * @param xorGroup + */ + public void clearXorGroup(String name) { + XorGroup xorGroup = (XorGroup) this.xorGroups.get( name ); + if ( xorGroup != null ) { + clearXorGroup( xorGroup ); + } + } + + /** + * Clears all Activations from an Xor Group. Any Activations that are also in an Agenda Group are removed + * from the Agenda Group. + * + * @param xorGroup + */ + public void clearXorGroup(XorGroup xorGroup) { + EventSupport eventsupport = (EventSupport) this.workingMemory; + for ( Iterator it = xorGroup.iterator(); it.hasNext(); ) { + Activation activation = ( Activation)( (XorGroupNode) it.next() ).getActivation(); + activation.setXorGroupNode( null ); + + if ( activation.isActivated() ) { + activation.setActivated( false ); + activation.remove(); + eventsupport.getAgendaEventSupport().fireActivationCancelled( activation ); + } + } + xorGroup.clear(); + } + + /** * Fire the next scheduled <code>Agenda</code> item. * * @throws ConsequenceException @@ -281,12 +372,12 @@ return false; } - Activation item = group.getNext(); + AgendaItem item = (AgendaItem) group.getNext(); if ( item == null ) { return false; } - if ( filter == null || filter.accept( item ) ) { + if ( filter == null || filter.accept( item ) ) { fireActivation( item ); } @@ -302,10 +393,17 @@ * @throws ConsequenceException * If an error occurs while attempting to fire the consequence. */ - public synchronized void fireActivation(Activation activation) throws ConsequenceException { + public synchronized void fireActivation(Activation activation) throws ConsequenceException { EventSupport eventsupport = (EventSupport) this.workingMemory; - eventsupport.getAgendaEventSupport().fireBeforeActivationFired( activation ); + eventsupport.getAgendaEventSupport().fireBeforeActivationFired( activation ); + + if ( activation.getXorGroupNode() != null ) { + XorGroup xorGroup = activation.getXorGroupNode().getXorGroup(); + xorGroup.removeActivation( activation ); + clearXorGroup(xorGroup); + } + activation.setActivated( false ); try { KnowledgeHelper knowledgeHelper = new org.drools.base.DefaultKnowledgeHelper( activation, @@ -319,5 +417,5 @@ eventsupport.getAgendaEventSupport().fireAfterActivationFired( activation ); } - + } Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/AgendaItem.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/AgendaItem.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/AgendaItem.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -19,11 +19,13 @@ import java.io.Serializable; import org.drools.FactHandle; +import org.drools.WorkingMemory; import org.drools.rule.Rule; import org.drools.spi.Activation; import org.drools.spi.PropagationContext; import org.drools.spi.Tuple; import org.drools.util.LinkedList; +import org.drools.util.LinkedListObjectWrapper; import org.drools.util.Queue; import org.drools.util.Queueable; @@ -63,6 +65,8 @@ private boolean activated; + private XorGroupNode xorGroupNode; + // ------------------------------------------------------------ // Constructors // ------------------------------------------------------------ @@ -199,4 +203,12 @@ public void remove() { dequeue(); } + + public XorGroupNode getXorGroupNode() { + return this.xorGroupNode; + } + + public void setXorGroupNode(XorGroupNode xorNode) { + this.xorGroupNode = xorNode; + } } Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/ScheduledAgendaItem.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/ScheduledAgendaItem.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/ScheduledAgendaItem.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -24,8 +24,10 @@ import org.drools.spi.Activation; import org.drools.spi.PropagationContext; import org.drools.spi.Tuple; +import org.drools.spi.XorGroup; import org.drools.util.LinkedList; import org.drools.util.LinkedListNode; +import org.drools.util.LinkedListObjectWrapper; /** * Item entry in the <code>Agenda</code>. @@ -60,6 +62,8 @@ private LinkedList justified; private boolean activated; + + private XorGroupNode xorGroupNode; // ------------------------------------------------------------ // Constructors @@ -128,7 +132,7 @@ * Handle the firing of an alarm. */ public void run() { - this.agenda.fireActivation( this ); + this.agenda.fireActivation( this ); this.agenda.getWorkingMemory().fireAllRules(); } @@ -179,7 +183,15 @@ public void setActivated(boolean activated) { this.activated = activated; } + + public XorGroupNode getXorGroupNode() { + return this.xorGroupNode; + } + public void setXorGroupNode(XorGroupNode xorGroupNode) { + this.xorGroupNode = xorGroupNode; + } + /* * (non-Javadoc) * Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/XorGroupImpl.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/XorGroupImpl.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/XorGroupImpl.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -0,0 +1,52 @@ +package org.drools.common; + +import java.util.Iterator; + +import org.drools.spi.Activation; +import org.drools.spi.XorGroup; +import org.drools.util.LinkedList; +import org.drools.util.LinkedListObjectWrapper; + +public class XorGroupImpl implements XorGroup { + private String name; + + private final LinkedList list; + + public XorGroupImpl(String name) { + this.name = name; + this.list = new LinkedList(); + } + + public String getName() { + return this.name; + } + + public void addActivation(Activation activation) { + XorGroupNode node = new XorGroupNode(activation, this); + activation.setXorGroupNode( node ); + this.list.add( node ); + } + + public void removeActivation(Activation activation) { + XorGroupNode node = activation.getXorGroupNode( ); + this.list.remove( node ); + activation.setXorGroupNode( null ); + } + + public Iterator iterator() { + return this.list.iterator(); + } + + public boolean isEmpty() { + return this.list.isEmpty(); + } + + public int size() { + return this.list.size(); + } + + public void clear() { + this.list.clear(); + } + +} Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/XorGroupNode.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/XorGroupNode.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/XorGroupNode.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -0,0 +1,30 @@ +package org.drools.common; + +import org.drools.spi.Activation; +import org.drools.spi.XorGroup; +import org.drools.util.AbstractBaseLinkedListNode; + +public class XorGroupNode extends AbstractBaseLinkedListNode { + + private Activation activation; + + private XorGroup xorGroup; + + public XorGroupNode(Activation activation, + XorGroup xorGroup) { + super(); + this.activation = activation; + this.xorGroup = xorGroup; + } + + public Activation getActivation() { + return this.activation; + } + + public XorGroup getXorGroup() { + return this.xorGroup; + } + + + +} Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/leaps/WorkingMemoryImpl.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/leaps/WorkingMemoryImpl.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/leaps/WorkingMemoryImpl.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -119,6 +119,10 @@ public void clearAgenda() { this.agenda.clearAgenda(); } + + public void clearAgendaGroup(String group) { + this.agenda.clearAgendaGroup( group ); + } /** * Returns the fact Object for the given <code>FactHandle</code>. It Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/LeftInputAdapterNode.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/LeftInputAdapterNode.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/LeftInputAdapterNode.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -25,7 +25,7 @@ import org.drools.spi.PropagationContext; import org.drools.util.LinkedList; import org.drools.util.LinkedListNode; -import org.drools.util.LinkedListNodeWrapper; +import org.drools.util.LinkedListObjectWrapper; /** * All asserting Facts must propagated into the right <code>ObjectSink</code> side of a BetaNode, if this is the first Column @@ -148,7 +148,7 @@ ReteTuple tuple = new ReteTuple( handle ); - list.add( new LinkedListNodeWrapper( tuple ) ); + list.add( new LinkedListObjectWrapper( tuple ) ); if ( !getTupleSinks().isEmpty() ) { // We do this one seperately so we avoid another tuple replication @@ -158,7 +158,7 @@ for ( int i = 1; i < size; i++ ) { tuple = new ReteTuple( tuple ); - list.add( new LinkedListNodeWrapper( tuple ) ); + list.add( new LinkedListObjectWrapper( tuple ) ); ((TupleSink) getTupleSinks().get( i )).assertTuple( tuple, context, workingMemory ); @@ -190,7 +190,7 @@ if ( list != null ) { int i = 0; for ( LinkedListNode node = list.removeFirst(); node != null; node = list.removeFirst() ) { - ((TupleSink) getTupleSinks().get( i++ )).retractTuple( (ReteTuple) ((LinkedListNodeWrapper) node).getNode(), + ((TupleSink) getTupleSinks().get( i++ )).retractTuple( (ReteTuple) ((LinkedListObjectWrapper) node).getObject(), context, workingMemory ); } @@ -211,7 +211,7 @@ // already existed, so propagate as a modify int i = 0; for ( LinkedListNode node = list.getFirst(); node != null; node = node.getNext() ) { - ((TupleSink) getTupleSinks().get( i++ )).modifyTuple( (ReteTuple) ((LinkedListNodeWrapper) node).getNode(), + ((TupleSink) getTupleSinks().get( i++ )).modifyTuple( (ReteTuple) ((LinkedListObjectWrapper) node).getObject(), context, workingMemory ); } @@ -228,7 +228,7 @@ if ( list != null ) { int i = 0; for ( LinkedListNode node = list.getFirst(); node != null; node = node.getNext() ) { - ((TupleSink) getTupleSinks().get( i++ )).retractTuple( (ReteTuple) ((LinkedListNodeWrapper) node).getNode(), + ((TupleSink) getTupleSinks().get( i++ )).retractTuple( (ReteTuple) ((LinkedListObjectWrapper) node).getObject(), context, workingMemory ); } @@ -251,7 +251,7 @@ for ( Iterator it = memory.values().iterator(); it.hasNext(); ) { LinkedList list = (LinkedList) it.next(); for ( LinkedListNode node = list.getFirst(); node != null; node = node.getNext() ) { - sink.assertTuple( (ReteTuple) ((LinkedListNodeWrapper) node).getNode(), + sink.assertTuple( (ReteTuple) ((LinkedListObjectWrapper) node).getObject(), context, workingMemory ); } Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/TerminalNode.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/TerminalNode.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/TerminalNode.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -30,6 +30,8 @@ import org.drools.spi.AgendaGroup; import org.drools.spi.Duration; import org.drools.spi.PropagationContext; +import org.drools.spi.XorGroup; +import org.drools.util.LinkedListObjectWrapper; import org.drools.util.Queueable; /** @@ -49,8 +51,9 @@ // ------------------------------------------------------------ /** The rule to invoke upon match. */ - private Rule rule; + private final Rule rule; private final TupleSource tupleSource; + private XorGroup xorGroup; // ------------------------------------------------------------ // Constructors @@ -64,9 +67,9 @@ * @param rule * The rule. */ - TerminalNode(int id, - TupleSource source, - Rule rule) { + TerminalNode(final int id, + final TupleSource source, + final Rule rule) { super( id ); this.rule = rule; this.tupleSource = source; @@ -89,6 +92,16 @@ // org.drools.impl.TupleSink // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + public void assertTuple(final ReteTuple tuple, + final PropagationContext context, + final WorkingMemoryImpl workingMemory) { + assertTuple( tuple, + context, + workingMemory, + true ); + + } + /** * Assert a new <code>Tuple</code>. * @@ -99,24 +112,34 @@ * @throws AssertionException * If an error occurs while asserting. */ - public void assertTuple(ReteTuple tuple, - PropagationContext context, - WorkingMemoryImpl workingMemory) { + public void assertTuple(final ReteTuple tuple, + final PropagationContext context, + final WorkingMemoryImpl workingMemory, + final boolean fireActivationCreated) { // if the current Rule is no-loop and the origin rule is the same then // return - if ( rule.getNoLoop() && rule.equals( context.getRuleOrigin() ) ) { + if ( this.rule.getNoLoop() && this.rule.equals( context.getRuleOrigin() ) ) { return; } - Agenda agenda = workingMemory.getAgenda(); + final Agenda agenda = workingMemory.getAgenda(); - Duration dur = rule.getDuration(); + final Duration dur = this.rule.getDuration(); if ( dur != null && dur.getDuration( tuple ) > 0 ) { - ScheduledAgendaItem item = new ScheduledAgendaItem( context.getPropagationNumber(), + final ScheduledAgendaItem item = new ScheduledAgendaItem( context.getPropagationNumber(), tuple, workingMemory.getAgenda(), context, - rule ); + this.rule ); + + if ( this.rule.getXorGroup() != null ) { + // Lazy cache xorGroup + if ( this.xorGroup == null ) { + this.xorGroup = workingMemory.getAgenda().getXorGroup( this.rule.getXorGroup() ); + } + this.xorGroup.addActivation( item ); + } + agenda.scheduleItem( item ); tuple.setActivation( item ); item.setActivated( true ); @@ -126,23 +149,23 @@ // Lazy instantiation and addition to the Agenda of AgendGroup // implementations // ---------------- - TerminalNodeMemory memory = (TerminalNodeMemory) workingMemory.getNodeMemory( this ); + final TerminalNodeMemory memory = (TerminalNodeMemory) workingMemory.getNodeMemory( this ); AgendaGroupImpl agendaGroup = memory.getAgendaGroup(); if ( agendaGroup == null ) { - if ( rule.getAgendaGroup() == null || rule.getAgendaGroup().equals( "" ) || rule.getAgendaGroup().equals( AgendaGroup.MAIN ) ) { + if ( this.rule.getAgendaGroup() == null || this.rule.getAgendaGroup().equals( "" ) || this.rule.getAgendaGroup().equals( AgendaGroup.MAIN ) ) { // Is the Rule AgendaGroup undefined? If it is use MAIN, // which is added to the Agenda by default agendaGroup = (AgendaGroupImpl) agenda.getAgendaGroup( AgendaGroup.MAIN ); } else { // AgendaGroup is defined, so try and get the AgendaGroup // from the Agenda - agendaGroup = (AgendaGroupImpl) agenda.getAgendaGroup( rule.getAgendaGroup() ); + agendaGroup = (AgendaGroupImpl) agenda.getAgendaGroup( this.rule.getAgendaGroup() ); } if ( agendaGroup == null ) { // The AgendaGroup is defined but not yet added to the // Agenda, so create the AgendaGroup and add to the Agenda. - agendaGroup = new AgendaGroupImpl( rule.getAgendaGroup() ); + agendaGroup = new AgendaGroupImpl( this.rule.getAgendaGroup() ); workingMemory.getAgenda().addAgendaGroup( agendaGroup ); } @@ -150,14 +173,22 @@ } // set the focus if rule autoFocus is true - if ( rule.getAutoFocus() ) { + if ( this.rule.getAutoFocus() ) { agenda.setFocus( agendaGroup ); } - AgendaItem item = new AgendaItem( context.getPropagationNumber(), + final AgendaItem item = new AgendaItem( context.getPropagationNumber(), tuple, context, - rule ); + this.rule ); + + if ( this.rule.getXorGroup() != null ) { + // Lazy cache xorGroup + if ( this.xorGroup == null ) { + this.xorGroup = workingMemory.getAgenda().getXorGroup( this.rule.getXorGroup() ); + } + this.xorGroup.addActivation( item ); + } // Makes sure the Lifo is added to the AgendaGroup priority queue // If the AgendaGroup is already in the priority queue it just @@ -165,14 +196,18 @@ agendaGroup.add( item ); tuple.setActivation( item ); item.setActivated( true ); - workingMemory.getAgendaEventSupport().fireActivationCreated( item ); + + // We only want to fire an event on a truly new Activation and not on an Activation as a result of a modify + if ( fireActivationCreated ) { + workingMemory.getAgendaEventSupport().fireActivationCreated( item ); + } } } - public void retractTuple(ReteTuple tuple, - PropagationContext context, - WorkingMemoryImpl workingMemory) { - Activation activation = tuple.getActivation(); + public void retractTuple(final ReteTuple tuple, + final PropagationContext context, + final WorkingMemoryImpl workingMemory) { + final Activation activation = tuple.getActivation(); if ( activation.isActivated() ) { activation.remove(); workingMemory.getAgendaEventSupport().fireActivationCancelled( activation ); @@ -183,15 +218,17 @@ this.rule ); } - public void modifyTuple(ReteTuple tuple, - PropagationContext context, - WorkingMemoryImpl workingMemory) { + public void modifyTuple(final ReteTuple tuple, + final PropagationContext context, + final WorkingMemoryImpl workingMemory) { + // We have to remove and assert the new tuple as it has modified facts and thus its tuple is newer if ( tuple.getActivation().isActivated() ) { tuple.getActivation().remove(); } assertTuple( tuple, context, - workingMemory ); + workingMemory, + false ); } @@ -205,15 +242,15 @@ } public void attach() { - tupleSource.addTupleSink( this ); + this.tupleSource.addTupleSink( this ); } - public void attach(WorkingMemoryImpl[] workingMemories) { + public void attach(final WorkingMemoryImpl[] workingMemories) { attach(); for ( int i = 0, length = workingMemories.length; i < length; i++ ) { - WorkingMemoryImpl workingMemory = workingMemories[i]; - PropagationContext propagationContext = new PropagationContextImpl( workingMemory.getNextPropagationIdCounter(), + final WorkingMemoryImpl workingMemory = workingMemories[i]; + final PropagationContext propagationContext = new PropagationContextImpl( workingMemory.getNextPropagationIdCounter(), PropagationContext.RULE_ADDITION, null, null ); @@ -222,46 +259,46 @@ } } - public void remove(BaseNode node, - WorkingMemoryImpl[] workingMemories) { + public void remove(final BaseNode node, + final WorkingMemoryImpl[] workingMemories) { for ( int i = 0, length = workingMemories.length; i < length; i++ ) { - WorkingMemoryImpl workingMemory = workingMemories[i]; + final WorkingMemoryImpl workingMemory = workingMemories[i]; - TerminalNodeMemory memory = (TerminalNodeMemory) workingMemory.getNodeMemory( this ); + final TerminalNodeMemory memory = (TerminalNodeMemory) workingMemory.getNodeMemory( this ); - AgendaGroupImpl group = memory.getAgendaGroup(); - Queueable[] elements = group.getQueueable(); - List list = new ArrayList(); + final AgendaGroupImpl group = memory.getAgendaGroup(); + final Queueable[] elements = group.getQueueable(); + final List list = new ArrayList(); //start at 1 as BinaryHeapQueue starts at 1 - for ( int j = 1, size = group.size()+1; j < size; j++ ) { - AgendaItem item = (AgendaItem) elements[j]; + for ( int j = 1, size = group.size() + 1; j < size; j++ ) { + final AgendaItem item = (AgendaItem) elements[j]; if ( item.getRule() == this.rule ) { list.add( item ); } } - for ( Iterator it = list.iterator(); it.hasNext(); ) { - AgendaItem item = ( AgendaItem ) it.next(); + for ( final Iterator it = list.iterator(); it.hasNext(); ) { + final AgendaItem item = (AgendaItem) it.next(); if ( item.isActivated() ) { item.remove(); workingMemory.getAgendaEventSupport().fireActivationCancelled( item ); } - PropagationContext propagationContext = new PropagationContextImpl( workingMemory.getNextPropagationIdCounter(), + final PropagationContext propagationContext = new PropagationContextImpl( workingMemory.getNextPropagationIdCounter(), PropagationContext.RULE_REMOVAL, null, null ); workingMemory.removeLogicalDependencies( item, propagationContext, - this.rule ); + this.rule ); } - } - - tupleSource.remove( this, + } + + this.tupleSource.remove( this, workingMemories ); } - public void updateNewNode(WorkingMemoryImpl workingMemory, - PropagationContext context) { + public void updateNewNode(final WorkingMemoryImpl workingMemory, + final PropagationContext context) { // There are no child nodes to update, do nothing. } @@ -276,7 +313,7 @@ return this.agendaGroup; } - public void setAgendaGroup(AgendaGroupImpl agendaGroup) { + public void setAgendaGroup(final AgendaGroupImpl agendaGroup) { this.agendaGroup = agendaGroup; } } Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/TupleSource.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/TupleSource.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/TupleSource.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -25,7 +25,7 @@ import org.drools.spi.PropagationContext; import org.drools.util.LinkedList; import org.drools.util.LinkedListNode; -import org.drools.util.LinkedListNodeWrapper; +import org.drools.util.LinkedListObjectWrapper; /** * A source of <code>ReteTuple</code> s for a <code>TupleSink</code>. @@ -152,7 +152,7 @@ for ( int i = 0, size = getTupleSinks().size(); i < size; i++ ) { ReteTuple child = new ReteTuple( tuple ); // no TupleMatch so instead add as a linked tuple - tuple.addLinkedTuple( new LinkedListNodeWrapper( child ) ); + tuple.addLinkedTuple( new LinkedListObjectWrapper( child ) ); ((TupleSink) getTupleSinks().get( i )).assertTuple( child, context, workingMemory ); @@ -178,7 +178,7 @@ if ( list != null && !list.isEmpty() ) { int i = 0; for ( LinkedListNode node = list.removeFirst(); node != null; node = list.removeFirst() ) { - ((TupleSink) getTupleSinks().get( i++ )).retractTuple( (ReteTuple) ((LinkedListNodeWrapper) node).getNode(), + ((TupleSink) getTupleSinks().get( i++ )).retractTuple( (ReteTuple) ((LinkedListObjectWrapper) node).getObject(), context, workingMemory ); } @@ -204,7 +204,7 @@ if ( list != null && !list.isEmpty() ) { int i = 0; for ( LinkedListNode node = list.getFirst(); node != null; node = node.getNext() ) { - ((TupleSink) getTupleSinks().get( i++ )).modifyTuple( (ReteTuple) ((LinkedListNodeWrapper) node).getNode(), + ((TupleSink) getTupleSinks().get( i++ )).modifyTuple( (ReteTuple) ((LinkedListObjectWrapper) node).getObject(), context, workingMemory ); } Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/WorkingMemoryImpl.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/WorkingMemoryImpl.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/WorkingMemoryImpl.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -241,6 +241,13 @@ public void clearAgenda() { this.agenda.clearAgenda(); } + + /** + * Clear the Agenda Group + */ + public void clearAgendaGroup(String group) { + this.agenda.clearAgendaGroup(group); + } /** * @see WorkingMemory Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/Rule.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/Rule.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/Rule.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -77,6 +77,8 @@ /** makes the rule's much the current focus */ private boolean autoFocus; + + private String xorGroup; /** indicates that the rule is semantically correct. */ private boolean semanticallyValid = true; @@ -241,8 +243,16 @@ public void setAutoFocus(boolean autoFocus) { this.autoFocus = autoFocus; + } + + public String getXorGroup() { + return this.xorGroup; } + public void setXorGroup(String xorGroup) { + this.xorGroup = xorGroup; + } + /** * Retrieve a parameter <code>Declaration</code> by identifier. * @@ -437,5 +447,4 @@ public boolean isSemanticallyValid() { return semanticallyValid; } - } Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/Activation.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/Activation.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/Activation.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -20,8 +20,10 @@ import org.drools.common.LogicalDependency; +import org.drools.common.XorGroupNode; import org.drools.rule.Rule; import org.drools.util.LinkedList; +import org.drools.util.LinkedListObjectWrapper; /** * When a <code>Tuple</code> fully matches a rule it is added to the <code>Agenda</code> @@ -73,4 +75,8 @@ public boolean isActivated(); public void setActivated(boolean activated); + + public XorGroupNode getXorGroupNode(); + + public void setXorGroupNode(XorGroupNode xorGroupNode); } \ No newline at end of file Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/KnowledgeHelper.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/KnowledgeHelper.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/KnowledgeHelper.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -111,6 +111,8 @@ * ActivationCancelled events. <br> */ void clearAgenda(); + + void clearAgendaGroup(String group); public AgendaGroup getFocus(); Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/XorGroup.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/XorGroup.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/XorGroup.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -0,0 +1,19 @@ +package org.drools.spi; + +import java.util.Iterator; + +public interface XorGroup { + public String getName(); + + public void addActivation(Activation activation); + + public void removeActivation(Activation activation); + + public Iterator iterator(); + + public boolean isEmpty(); + + public int size(); + + public void clear(); +} Deleted: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/util/LinkedListNodeWrapper.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/util/LinkedListNodeWrapper.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/util/LinkedListNodeWrapper.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -1,34 +0,0 @@ -package org.drools.util; - -/* - * Copyright 2005 JBoss Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * 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. - */ - -/** - * The idea behind <code>LinkedListNodeWrapper</code> is to be able to add - * the same <code>LinkedListNode</code> to multiple <code>LinkedList</code>s - * where the node can have different previous and next nodes in each list. - */ -public class LinkedListNodeWrapper extends AbstractBaseLinkedListNode { - private LinkedListNode node; - - public LinkedListNodeWrapper(LinkedListNode node) { - this.node = node; - } - - public LinkedListNode getNode() { - return this.node; - } -} Copied: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/util/LinkedListObjectWrapper.java (from rev 4078, labs/jbossrules/trunk/drools-core/src/main/java/org/drools/util/LinkedListNodeWrapper.java) =================================================================== --- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/util/LinkedListNodeWrapper.java 2006-05-04 01:57:37 UTC (rev 4078) +++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/util/LinkedListObjectWrapper.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -0,0 +1,34 @@ +package org.drools.util; + +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ + +/** + * The idea behind <code>LinkedListNodeWrapper</code> is to be able to add + * the same <code>LinkedListNode</code> to multiple <code>LinkedList</code>s + * where the node can have different previous and next nodes in each list. + */ +public class LinkedListObjectWrapper extends AbstractBaseLinkedListNode { + private Object object; + + public LinkedListObjectWrapper(Object object) { + this.object = object; + } + + public Object getObject() { + return this.object; + } +} Modified: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/AgendaTest.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/AgendaTest.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/AgendaTest.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -16,7 +16,9 @@ * limitations under the License. */ +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.drools.DroolsTestCase; @@ -33,6 +35,7 @@ import org.drools.spi.ConsequenceException; import org.drools.spi.KnowledgeHelper; import org.drools.spi.PropagationContext; +import org.drools.spi.XorGroup; /** * @author mproctor @@ -457,5 +460,132 @@ // assertEquals( 0, // queue.size() ); } + + public void testXorGroup() { + RuleBase ruleBase = new RuleBaseImpl(); + WorkingMemoryImpl workingMemory = (WorkingMemoryImpl) ruleBase.newWorkingMemory(); + + final Agenda agenda = workingMemory.getAgenda(); + + final List list = new ArrayList(); + + // create the consequence + Consequence consequence = new Consequence() { + public void evaluate(KnowledgeHelper knowledgeHelper, + WorkingMemory workingMemory) { + list.add( knowledgeHelper.getRule() ); + } + }; + + ReteTuple tuple = new ReteTuple( new FactHandleImpl( 1 ) ); + + // create a rule for each agendaGroup + Rule rule0 = new Rule( "test-rule0" ); + rule0.setXorGroup( "xor-group-0" ); + TerminalNode node0 = new TerminalNode( 3, + new MockTupleSource( 2 ), + rule0 ); + rule0.setConsequence( consequence ); + PropagationContext context0 = new PropagationContextImpl( 0, + PropagationContext.ASSERTION, + rule0, + null ); + + Rule rule1 = new Rule( "test-rule1" ); + rule1.setXorGroup( "xor-group-0" ); + TerminalNode node1 = new TerminalNode( 5, + new MockTupleSource( 4 ), + rule1 ); + rule1.setConsequence( consequence ); + PropagationContext context1 = new PropagationContextImpl( 0, + PropagationContext.ASSERTION, + rule1, + null ); + + Rule rule2 = new Rule( "test-rule2" ); + TerminalNode node2 = new TerminalNode( 7, + new MockTupleSource( 6 ), + rule2 ); + rule2.setConsequence( consequence ); + PropagationContext context2 = new PropagationContextImpl( 0, + PropagationContext.ASSERTION, + rule2, + null ); + + Rule rule3 = new Rule( "test-rule3", + "agendaGroup3" ); + rule3.setXorGroup( "xor-group-3" ); + TerminalNode node3 = new TerminalNode( 9, + new MockTupleSource( 8 ), + rule3 ); + rule3.setConsequence( consequence ); + PropagationContext context3 = new PropagationContextImpl( 0, + PropagationContext.ASSERTION, + rule3, + null ); + + // Assert the tuple and check it was added to xor-group-0 + node0.assertTuple( tuple, context0, workingMemory ); + XorGroup xorGroup0 = agenda.getXorGroup( "xor-group-0" ); + assertEquals( 1, xorGroup0.size() ); + + // Assert another tuple and check it was added to xor-group-0 + node1.assertTuple( tuple, context1, workingMemory ); + assertEquals( 2, xorGroup0.size() ); + + // There should now be two potential activations to fire + assertEquals( 2, agenda.focusStackSize() ); + + // The first tuple should fire, adding itself to the List and clearing and cancelling the other Activations in the xor-group-0 + agenda.fireNextItem( null ); + + // Make sure the xor-group-0 is clear + assertEquals( 0, xorGroup0.size() ); + + // Make sure the Agenda is empty + assertEquals( 0, agenda.focusStackSize() ); + + // List should only have a single item, "rule0" + assertEquals( 1, list.size() ); + assertSame( rule0, list.get( 0 ) ); + + list.clear(); + + //------------------- + // Now try a more complex scenario involving two Xor Groups and one rule not in a Group + node0.assertTuple( tuple, context0, workingMemory ); + node1.assertTuple( tuple, context1, workingMemory ); + node2.assertTuple( tuple, context2, workingMemory ); + node3.assertTuple( tuple, context3, workingMemory ); + + // xor-group-0 should be populated again + assertEquals( 2, xorGroup0.size() ); + + // make sure the xor-group-3 is cleared when we can clear the Agenda Group for the activation that is in both + XorGroup xorGroup3 = agenda.getXorGroup( "xor-group-3" ); + + assertEquals( 4, agenda.agendaSize() ); + assertEquals( 1, xorGroup3.size() ); + + agenda.clearAgendaGroup( "agendaGroup3" ); + assertEquals( 3, agenda.agendaSize() ); + assertEquals( 0, xorGroup3.size() ); + + // Activation for xor-group-0 should be next - the activation in no xor/agenda group should remain on the agenda + agenda.fireNextItem( null ); + assertEquals( 1, agenda.agendaSize() ); + assertEquals( 0, xorGroup0.size() ); + + + // Fire the last activation and make sure the Agenda Empties + agenda.fireNextItem( null ); + assertEquals( 0, agenda.agendaSize() ); + + assertEquals( 2, list.size() ); + assertEquals( rule0, list.get(0)); + assertEquals( rule2, list.get(1)); + + } + } Modified: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/LeftInputAdapterNodeTest.java =================================================================== --- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/LeftInputAdapterNodeTest.java 2006-05-06 15:51:09 UTC (rev 4108) +++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/LeftInputAdapterNodeTest.java 2006-05-06 15:51:12 UTC (rev 4109) @@ -24,7 +24,7 @@ import org.drools.common.PropagationContextImpl; import org.drools.spi.PropagationContext; import org.drools.util.LinkedList; -import org.drools.util.LinkedListNodeWrapper; +import org.drools.util.LinkedListObjectWrapper; public class LeftInputAdapterNodeTest extends DroolsTestCase { @@ -106,7 +106,7 @@ assertEquals( 1, list0.size() ); assertSame( tuple0, - ((LinkedListNodeWrapper) list0.getFirst()).getNode() ); + ((LinkedListObjectWrapper) list0.getFirst()).getObject() ); // check memory stacks correctly FactHandleImpl f1 = (FactHandleImpl) workingMemory.assertObject( "test1" ); @@ -122,7 +122,7 @@ assertEquals( 1, list1.size() ); assertSame( tuple1, - ((LinkedListNodeWrapper) list1.getFirst()).getNode() ); + ((LinkedListObjectWrapper) list1.getFirst()).getObject() ); assertNotSame( tuple0, tuple1 ); |