[jboss-svn-commits] JBL Code SVN: r15774 - in labs/jbossrules/branches/4.0.x: drools-compiler/src/test/java/org/drools/integrationtests and 2 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Thu Oct 11 14:45:11 EDT 2007


Author: fmeyer
Date: 2007-10-11 14:45:10 -0400 (Thu, 11 Oct 2007)
New Revision: 15774

Modified:
   labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/Message.java
   labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/integrationtests/ExecutionFlowControlTest.java
   labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/integrationtests/MiscTest.java
   labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/HelloWorld.drl
   labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/test_EffectiveDate.drl
   labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/test_ExplicitAnd.drl
   labs/jbossrules/branches/4.0.x/drools-core/src/main/java/org/drools/reteoo/CompositeObjectSinkAdapter.java
Log:
[JBRULES-1264] NPE at BaseObjectClassFieldExtractor.getLongValue with null fields 


Modified: labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/Message.java
===================================================================
--- labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/Message.java	2007-10-11 18:40:45 UTC (rev 15773)
+++ labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/Message.java	2007-10-11 18:45:10 UTC (rev 15774)
@@ -1,67 +1,119 @@
-/**
- * 
- */
-package org.drools;
-
-public class Message {
-
-    private String          message1 = "One";
-    private String          message2 = "Two";
-    private String          message3 = "Three";
-    private String          message4 = "Four";
-
-    public static final int HELLO    = 0;
-    public static final int GOODBYE  = 1;
-
-    private String          message;
-
-    private int             status;
-
-    public String getMessage() {
-        return this.message;
-    }
-
-    public void setMessage(final String message) {
-        this.message = message;
-    }
-
-    public int getStatus() {
-        return this.status;
-    }
-
-    public void setStatus(final int status) {
-        this.status = status;
-    }
-
-    public String getMessage1() {
-        return this.message1;
-    }
-
-    public void setMessage1(final String message1) {
-        this.message1 = message1;
-    }
-
-    public String getMessage2() {
-        return this.message2;
-    }
-
-    public void setMessage2(final String message2) {
-        this.message2 = message2;
-    }
-
-    public String getMessage3() {
-        return this.message3;
-    }
-
-    public void setMessage3(final String message3) {
-        this.message3 = message3;
-    }
-
-    public String getMessage4() {
-        return this.message4;
-    }
-
-    public void setMessage4(final String message4) {
-        this.message4 = message4;
-    }
+/**
+ * 
+ */
+package org.drools;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class Message {
+
+    private String          message1 = "One";
+    private String          message2 = "Two";
+    private String          message3 = "Three";
+    private String          message4 = "Four";
+
+    public static final int HELLO    = 0;
+    public static final int GOODBYE  = 1;
+
+    private String          message;
+
+    private int             status;
+
+    private List    list     = new ArrayList();
+    private int     number   = 0;
+    private Date    birthday = new Date();
+    private boolean fired    = false;    
+    
+    public Message() {
+    }
+    
+    public Message(final String msg) {
+        this.message = msg;
+    }    
+    
+    public String getMessage() {
+        return this.message;
+    }
+
+    public void setMessage(final String message) {
+        this.message = message;
+    }
+
+    public int getStatus() {
+        return this.status;
+    }
+
+    public void setStatus(final int status) {
+        this.status = status;
+    }
+
+    public String getMessage1() {
+        return this.message1;
+    }
+
+    public void setMessage1(final String message1) {
+        this.message1 = message1;
+    }
+
+    public String getMessage2() {
+        return this.message2;
+    }
+
+    public void setMessage2(final String message2) {
+        this.message2 = message2;
+    }
+
+    public String getMessage3() {
+        return this.message3;
+    }
+
+    public void setMessage3(final String message3) {
+        this.message3 = message3;
+    }
+
+    public String getMessage4() {
+        return this.message4;
+    }
+
+    public void setMessage4(final String message4) {
+        this.message4 = message4;
+    }
+    
+    public boolean isFired() {
+        return this.fired;
+    }
+
+    public void setFired(final boolean fired) {
+        this.fired = fired;
+    }
+
+    public Date getBirthday() {
+        return this.birthday;
+    }
+
+    public void setBirthday(final Date birthday) {
+        this.birthday = birthday;
+    }
+
+    public int getNumber() {
+        return this.number;
+    }
+
+    public void setNumber(final int number) {
+        this.number = number;
+    }
+
+    public List getList() {
+        return this.list;
+    }
+
+    public void setList(final List list) {
+        this.list = list;
+    }
+
+    public void addToList(final String s) {
+        this.list.add( s );
+    }    
 }
\ No newline at end of file

Modified: labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/integrationtests/ExecutionFlowControlTest.java
===================================================================
--- labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/integrationtests/ExecutionFlowControlTest.java	2007-10-11 18:40:45 UTC (rev 15773)
+++ labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/integrationtests/ExecutionFlowControlTest.java	2007-10-11 18:45:10 UTC (rev 15774)
@@ -1,780 +1,780 @@
-package org.drools.integrationtests;
-
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.List;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-import org.drools.Cheese;
-import org.drools.FactHandle;
-import org.drools.Person;
-import org.drools.PersonInterface;
-import org.drools.RuleBase;
-import org.drools.RuleBaseConfiguration;
-import org.drools.RuleBaseFactory;
-import org.drools.WorkingMemory;
-import org.drools.common.DefaultAgenda;
-import org.drools.common.InternalWorkingMemoryActions;
-import org.drools.common.RuleFlowGroupImpl;
-import org.drools.compiler.DrlParser;
-import org.drools.compiler.DroolsParserException;
-import org.drools.compiler.PackageBuilder;
-import org.drools.compiler.ProcessBuilder;
-import org.drools.compiler.PackageBuilder.PackageMergeException;
-import org.drools.event.ActivationCancelledEvent;
-import org.drools.event.ActivationCreatedEvent;
-import org.drools.event.AgendaEventListener;
-import org.drools.event.DefaultAgendaEventListener;
-import org.drools.integrationtests.helloworld.Message;
-import org.drools.lang.descr.PackageDescr;
-import org.drools.rule.Package;
-import org.drools.ruleflow.common.instance.ProcessInstance;
-import org.drools.spi.Activation;
-import org.drools.spi.ActivationGroup;
-import org.drools.spi.AgendaGroup;
-
-public class ExecutionFlowControlTest extends TestCase {
-    protected RuleBase getRuleBase() throws Exception {
-
-        return RuleBaseFactory.newRuleBase( RuleBase.RETEOO,
-                                            null );
-    }
-
-    protected RuleBase getRuleBase(final RuleBaseConfiguration config) throws Exception {
-
-        return RuleBaseFactory.newRuleBase( RuleBase.RETEOO,
-                                            config );
-    }
-
-    public void testSalienceInteger() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_salienceIntegerRule.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        final PersonInterface person = new Person( "Edson",
-                                                   "cheese" );
-        workingMemory.insert( person );
-
-        workingMemory.fireAllRules();
-
-        Assert.assertEquals( "Two rules should have been fired",
-                             2,
-                             list.size() );
-        Assert.assertEquals( "Rule 3 should have been fired first",
-                             "Rule 3",
-                             list.get( 0 ) );
-        Assert.assertEquals( "Rule 2 should have been fired second",
-                             "Rule 2",
-                             list.get( 1 ) );
-    }
-     
-    public void testSalienceExpression() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_salienceExpressionRule.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        final PersonInterface person10 = new Person( "bob",
-                                                   "cheese",
-                                                   10);
-        workingMemory.insert( person10 );
-        
-        final PersonInterface person20 = new Person( "mic",
-                                                     "cheese",
-                                                     20);
-          workingMemory.insert( person20 );        
-
-        workingMemory.fireAllRules();
-
-        Assert.assertEquals( "Two rules should have been fired",
-                             2,
-                             list.size() );
-        Assert.assertEquals( "Rule 3 should have been fired first",
-                             "Rule 3",
-                             list.get( 0 ) );
-        Assert.assertEquals( "Rule 2 should have been fired second",
-                             "Rule 2",
-                             list.get( 1 ) );
-    }    
-
-    public void testNoLoop() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "no-loop.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        final Cheese brie = new Cheese( "brie",
-                                        12 );
-        workingMemory.insert( brie );
-
-        workingMemory.fireAllRules();
-
-        Assert.assertEquals( "Should not loop  and thus size should be 1",
-                             1,
-                             list.size() );
-
-    }
-
-    public void testLockOnActive() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_LockOnActive.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        // AgendaGroup "group1" is not active, so should receive activation
-        final Cheese brie12 = new Cheese( "brie",
-                                          12 );
-        workingMemory.insert( brie12 );
-        final AgendaGroup group1 = workingMemory.getAgenda().getAgendaGroup( "group1" );
-        assertEquals( 1,
-                      group1.size() );
-
-        workingMemory.setFocus( "group1" );
-        // AgendaqGroup "group1" is now active, so should not receive activations
-        final Cheese brie10 = new Cheese( "brie",
-                                          10 );
-        workingMemory.insert( brie10 );
-        assertEquals( 1,
-                      group1.size() );
-
-        final Cheese cheddar20 = new Cheese( "cheddar",
-                                             20 );
-        workingMemory.insert( cheddar20 );
-        final AgendaGroup group2 = workingMemory.getAgenda().getAgendaGroup( "group1" );
-        assertEquals( 1,
-                      group2.size() );
-
-        final RuleFlowGroupImpl rfg = (RuleFlowGroupImpl) workingMemory.getAgenda().getRuleFlowGroup( "ruleflow2" );
-        rfg.setActive( true );
-        final Cheese cheddar17 = new Cheese( "cheddar",
-                                             17 );
-        workingMemory.insert( cheddar17 );
-        assertEquals( 1,
-                      group2.size() );
-    }
-
-    public void testLockOnActiveWithModify() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_LockOnActiveWithUpdate.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory wm = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        wm.setGlobal( "list",
-                      list );
-
-        final Cheese brie = new Cheese( "brie",
-                                        13 );
-
-        final Person bob = new Person( "bob" );
-        bob.setCheese( brie );
-
-        final Person mic = new Person( "mic" );
-        mic.setCheese( brie );
-
-        final Person mark = new Person( "mark" );
-        mark.setCheese( brie );
-
-        final FactHandle brieHandle = wm.insert( brie );
-        wm.insert( bob );
-        wm.insert( mic );
-        wm.insert( mark );
-
-        final DefaultAgenda agenda = (DefaultAgenda) wm.getAgenda();
-        final AgendaGroup group1 = agenda.getAgendaGroup( "group1" );
-        agenda.setFocus( group1 );
-        assertEquals( 3,
-                      group1.size() );
-        agenda.fireNextItem( null );
-        assertEquals( 2,
-                      group1.size() );
-        wm.update( brieHandle,
-                         brie );
-        assertEquals( 2,
-                      group1.size() );
-
-        AgendaGroup group2 = agenda.getAgendaGroup( "group2" );
-        agenda.setFocus( group2 );
-
-        RuleFlowGroupImpl rfg = (RuleFlowGroupImpl) wm.getAgenda().getRuleFlowGroup( "ruleflow2" );
-        assertEquals( 3,
-                      rfg.size() );
-
-        agenda.activateRuleFlowGroup( "ruleflow2" );
-        agenda.fireNextItem( null );
-        assertEquals( 2,
-                      rfg.size() );
-        wm.update( brieHandle,
-                         brie );
-        assertEquals( 2,
-                      group2.size() );
-    }
-
-    public void testAgendaGroups() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_AgendaGroups.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        final Cheese brie = new Cheese( "brie",
-                                        12 );
-        workingMemory.insert( brie );
-
-        workingMemory.fireAllRules();
-
-        assertEquals( 7,
-                      list.size() );
-
-        assertEquals( "group3",
-                      list.get( 0 ) );
-        assertEquals( "group4",
-                      list.get( 1 ) );
-        assertEquals( "group3",
-                      list.get( 2 ) );
-        assertEquals( "MAIN",
-                      list.get( 3 ) );
-        assertEquals( "group1",
-                      list.get( 4 ) );
-        assertEquals( "group1",
-                      list.get( 5 ) );
-        assertEquals( "MAIN",
-                      list.get( 6 ) );
-
-        workingMemory.setFocus( "group2" );
-        workingMemory.fireAllRules();
-
-        assertEquals( 8,
-                      list.size() );
-        assertEquals( "group2",
-                      list.get( 7 ) );
-    }
-
-    public void testActivationGroups() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_ActivationGroups.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        final Cheese brie = new Cheese( "brie",
-                                        12 );
-        workingMemory.insert( brie );
-
-        final ActivationGroup activationGroup0 = workingMemory.getAgenda().getActivationGroup( "activation-group-0" );
-        assertEquals( 2,
-                      activationGroup0.size() );
-
-        final ActivationGroup activationGroup3 = workingMemory.getAgenda().getActivationGroup( "activation-group-3" );
-        assertEquals( 1,
-                      activationGroup3.size() );
-
-        final AgendaGroup agendaGroup3 = workingMemory.getAgenda().getAgendaGroup( "agenda-group-3" );
-        assertEquals( 1,
-                      agendaGroup3.size() );
-
-        final AgendaGroup agendaGroupMain = workingMemory.getAgenda().getAgendaGroup( "MAIN" );
-        assertEquals( 3,
-                      agendaGroupMain.size() );
-
-        workingMemory.clearAgendaGroup( "agenda-group-3" );
-        assertEquals( 0,
-                      activationGroup3.size() );
-        assertEquals( 0,
-                      agendaGroup3.size() );
-
-        workingMemory.fireAllRules();
-
-        assertEquals( 0,
-                      activationGroup0.size() );
-
-        assertEquals( 2,
-                      list.size() );
-        assertEquals( "rule0",
-                      list.get( 0 ) );
-        assertEquals( "rule2",
-                      list.get( 1 ) );
-
-    }
-
-    public void testDuration() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Duration.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        final Cheese brie = new Cheese( "brie",
-                                        12 );
-        final FactHandle brieHandle = workingMemory.insert( brie );
-
-        workingMemory.fireAllRules();
-
-        // now check for update
-        assertEquals( 0,
-                      list.size() );
-
-        // sleep for 300ms
-        Thread.sleep( 300 );
-
-        // now check for update
-        assertEquals( 1,
-                      list.size() );
-
-    }
-
-    public void testInsertRetractNoloop() throws Exception {
-        // read in the source
-        final Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_Insert_Retract_Noloop.drl" ) );
-        final RuleBase ruleBase = loadRuleBase( reader );
-
-        final WorkingMemory wm = ruleBase.newStatefulSession();
-        wm.insert( new Cheese( "stilton",
-                                     15 ) );
-
-        wm.fireAllRules();
-    }
-
-    public void testDurationWithNoLoop() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Duration_with_NoLoop.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        final Cheese brie = new Cheese( "brie",
-                                        12 );
-        final FactHandle brieHandle = workingMemory.insert( brie );
-
-        workingMemory.fireAllRules();
-
-        // now check for update
-        assertEquals( 0,
-                      list.size() );
-
-        // sleep for 300ms
-        Thread.sleep( 300 );
-
-        // now check for update
-        assertEquals( 1,
-                      list.size() );
-    }
-
-    public void testFireRuleAfterDuration() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_FireRuleAfterDuration.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        final Cheese brie = new Cheese( "brie",
-                                        12 );
-        final FactHandle brieHandle = workingMemory.insert( brie );
-
-        workingMemory.fireAllRules();
-
-        // now check for update
-        assertEquals( 0,
-                      list.size() );
-
-        // sleep for 300ms
-        Thread.sleep( 300 );
-
-        workingMemory.fireAllRules();
-
-        // now check for update
-        assertEquals( 2,
-                      list.size() );
-
-    }
-
-    public void testUpdateNoLoop() throws Exception {
-        // JBRULES-780, throws a NullPointer or infinite loop if there is an issue
-        final Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_UpdateNoloop.drl" ) );
-        final RuleBase ruleBase = loadRuleBase( reader );
-
-        final WorkingMemory wm = ruleBase.newStatefulSession();
-        wm.insert( new Cheese( "stilton",
-                                     15 ) );
-
-        wm.fireAllRules();
-    }
-
-    public void testUpdateActivationCreationNoLoop() throws Exception {
-        // JBRULES-787, no-loop blocks all dependant tuples for update 
-        final Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_UpdateActivationCreationNoLoop.drl" ) );
-        final RuleBase ruleBase = loadRuleBase( reader );
-
-        final InternalWorkingMemoryActions wm = (InternalWorkingMemoryActions) ruleBase.newStatefulSession();
-        final List created = new ArrayList();
-        final List cancelled = new ArrayList();
-        final AgendaEventListener l = new DefaultAgendaEventListener() {
-            public void activationCreated(ActivationCreatedEvent event,
-                                          WorkingMemory workingMemory) {
-                created.add( event );
-            }
-
-            public void activationCancelled(ActivationCancelledEvent event,
-                                            WorkingMemory workingMemory) {
-                cancelled.add( event );
-            }
-
-        };
-
-        wm.addEventListener( l );
-
-        final Cheese stilton = new Cheese( "stilton",
-                                           15 );
-        final FactHandle stiltonHandle = wm.insert( stilton );
-
-        final Person p1 = new Person( "p1" );
-        p1.setCheese( stilton );
-        wm.insert( p1 );
-
-        final Person p2 = new Person( "p2" );
-        p2.setCheese( stilton );
-        wm.insert( p2 );
-
-        final Person p3 = new Person( "p3" );
-        p3.setCheese( stilton );
-        wm.insert( p3 );
-
-        assertEquals( 3,
-                      created.size() );
-        assertEquals( 0,
-                      cancelled.size() );
-
-        final Activation item = ((ActivationCreatedEvent) created.get( 2 )).getActivation();
-
-        // simulate a modify inside a consequence
-        wm.update( stiltonHandle,
-                         stilton,
-                         item.getRule(),
-                         item );
-
-        // the two of the three tuples should re-activate
-        assertEquals( 5,
-                      created.size() );
-        assertEquals( 3,
-                      cancelled.size() );
-    }
-
-    public void testRuleFlowGroup() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflowgroup.drl" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        workingMemory.insert( "Test" );
-        workingMemory.fireAllRules();
-        assertEquals( 0,
-                      list.size() );
-
-        workingMemory.getAgenda().activateRuleFlowGroup( "Group1" );
-        workingMemory.fireAllRules();
-
-        assertEquals( 1,
-                      list.size() );
-    }
-
-    public void testRuleFlow() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
-        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
-        final Package pkg = builder.getPackage();
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        workingMemory.fireAllRules();
-        assertEquals( 0,
-                      list.size() );
-
-        final ProcessInstance processInstance = workingMemory.startProcess( "0" );
-        assertEquals( ProcessInstance.STATE_ACTIVE,
-                      processInstance.getState() );
-        workingMemory.fireAllRules();
-        assertEquals( 4,
-                      list.size() );
-        assertEquals( "Rule1",
-                      list.get( 0 ) );
-        assertEquals( "Rule3",
-                      list.get( 1 ) );
-        assertEquals( "Rule2",
-                      list.get( 2 ) );
-        assertEquals( "Rule4",
-                      list.get( 3 ) );
-        assertEquals( ProcessInstance.STATE_COMPLETED,
-                      processInstance.getState() );
-    }
-    
-    public void testRuleFlowClear() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_ruleflowClear.drl" ) ) );
-        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "test_ruleflowClear.rfm" ) ) );
-        final Package pkg = builder.getPackage();
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-        
-        final List activations = new ArrayList();
-        AgendaEventListener listener = new DefaultAgendaEventListener() {            
-            public void activationCancelled(ActivationCancelledEvent event,
-                                            WorkingMemory workingMemory) {
-                activations.add( event.getActivation() );
-            }
-        };
-        
-        workingMemory.addEventListener( listener );
-     
-        assertEquals( 0 , workingMemory.getAgenda().getRuleFlowGroup( "flowgroup-1" ).size() );
-        
-        // We need to call fireAllRules here to get the InitialFact into the system, to the eval(true)'s kick in
-        workingMemory.fireAllRules();
-        
-        // Now we have 4 in the RuleFlow, but not yet in the agenda
-        assertEquals( 4 , workingMemory.getAgenda().getRuleFlowGroup( "flowgroup-1" ).size() );
-        
-        // Check they aren't in the Agenda
-        assertEquals( 0, workingMemory.getAgenda().getAgendaGroup( "MAIN" ).size() );
-        
-        // Start the process, which shoudl populate the Agenda
-        final ProcessInstance processInstance = workingMemory.startProcess( "ruleFlowClear" );
-        assertEquals( 4, workingMemory.getAgenda().getAgendaGroup( "MAIN" ).size() );
-        
-        
-        // Check we have 0 activation cancellation events
-        assertEquals( 0, activations.size() );
-        
-        workingMemory.getAgenda().clearRuleFlowGroup( "flowgroup-1" );
-        
-        // Check the AgendaGroup and RuleFlowGroup  are now empty
-        assertEquals( 0, workingMemory.getAgenda().getAgendaGroup( "MAIN" ).size() );
-        assertEquals( 0 , workingMemory.getAgenda().getRuleFlowGroup( "flowgroup-1" ).size() );
-        
-        // Check we have four activation cancellation events
-        assertEquals( 4, activations.size() );              
-    }
-    
-    public void testRuleFlowInPackage() throws Exception {
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
-        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
-        final Package pkg = builder.getPackage();
-
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        workingMemory.fireAllRules();
-        assertEquals( 0,
-                      list.size() );
-
-        final ProcessInstance processInstance = workingMemory.startProcess( "0" );
-        assertEquals( ProcessInstance.STATE_ACTIVE,
-                      processInstance.getState() );
-        workingMemory.fireAllRules();
-        assertEquals( 4,
-                      list.size() );
-        assertEquals( "Rule1",
-                      list.get( 0 ) );
-        assertEquals( "Rule3",
-                      list.get( 1 ) );
-        assertEquals( "Rule2",
-                      list.get( 2 ) );
-        assertEquals( "Rule4",
-                      list.get( 3 ) );
-        assertEquals( ProcessInstance.STATE_COMPLETED,
-                      processInstance.getState() );
-        
-    }
-    
-    public void testLoadingRuleFlowInPackage1() throws Exception {
-    	// adding ruleflow before adding package
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
-        builder.getPackage();
-    }
-
-    public void testLoadingRuleFlowInPackage2() throws Exception {
-    	// only adding ruleflow
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
-        builder.getPackage();
-    }
-
-    public void testLoadingRuleFlowInPackage3() throws Exception {
-    	// only adding ruleflow without any generated rules
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "empty_ruleflow.rfm" ) ) );
-        builder.getPackage();
-    }
-
-    public void testLoadingRuleFlowInPackage4() throws Exception {
-    	// adding ruleflows of different package
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "empty_ruleflow.rfm" ) ) );
-        try {
-        	builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
-        	throw new Exception("An exception should have been thrown.");
-        } catch (PackageMergeException e) {
-        	// do nothing
-        }
-    }
-
-    public void testLoadingRuleFlowInPackage5() throws Exception {
-    	// adding ruleflow of different package than rules
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
-        try {
-        	builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "empty_ruleflow.rfm" ) ) );
-        	throw new Exception("An exception should have been thrown.");
-        } catch (PackageMergeException e) {
-        	// do nothing
-        }
-    }
-
-    public void testLoadingRuleFlowInPackage6() throws Exception {
-    	// adding rules of different package than ruleflow
-        final PackageBuilder builder = new PackageBuilder();
-    	builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "empty_ruleflow.rfm" ) ) );
-        try {
-            builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
-        	throw new Exception("An exception should have been thrown.");
-        } catch (PackageMergeException e) {
-        	// do nothing
-        }
-    }
-
-    public void testLoadingRuleFlowInPackage7() throws Exception {
-    	// loading a ruleflow with errors
-        final PackageBuilder builder = new PackageBuilder();
-    	builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "error_ruleflow.rfm" ) ) );
-    	assertEquals(1, builder.getErrors().getErrors().length);
-    }
-
-    private RuleBase loadRuleBase(final Reader reader) throws IOException,
-                                                      DroolsParserException,
-                                                      Exception {
-        final DrlParser parser = new DrlParser();
-        final PackageDescr packageDescr = parser.parse( reader );
-        if ( parser.hasErrors() ) {
-            Assert.fail( "Error messages in parser, need to sort this our (or else collect error messages)" );
-        }
-        // pre build the package
-        final PackageBuilder builder = new PackageBuilder();
-        builder.addPackage( packageDescr );
-        final Package pkg = builder.getPackage();
-
-        // add the package to a rulebase
-        final RuleBase ruleBase = getRuleBase();
-        ruleBase.addPackage( pkg );
-        // load up the rulebase
-        return ruleBase;
-    }
-
-    public void testDateEffective() throws Exception {
-        // read in the source
-        final Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_EffectiveDate.drl" ) );
-        final RuleBase ruleBase = loadRuleBase( reader );
-
-        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
-
-        final List list = new ArrayList();
-        workingMemory.setGlobal( "list",
-                                 list );
-
-        // go !
-        final Message message = new Message( "hola" );
-        workingMemory.insert( message );
-        workingMemory.fireAllRules();
-        assertFalse( message.isFired() );
-
-    }    
-}
+package org.drools.integrationtests;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.drools.Cheese;
+import org.drools.FactHandle;
+import org.drools.Person;
+import org.drools.PersonInterface;
+import org.drools.RuleBase;
+import org.drools.RuleBaseConfiguration;
+import org.drools.RuleBaseFactory;
+import org.drools.WorkingMemory;
+import org.drools.common.DefaultAgenda;
+import org.drools.common.InternalWorkingMemoryActions;
+import org.drools.common.RuleFlowGroupImpl;
+import org.drools.compiler.DrlParser;
+import org.drools.compiler.DroolsParserException;
+import org.drools.compiler.PackageBuilder;
+import org.drools.compiler.ProcessBuilder;
+import org.drools.compiler.PackageBuilder.PackageMergeException;
+import org.drools.event.ActivationCancelledEvent;
+import org.drools.event.ActivationCreatedEvent;
+import org.drools.event.AgendaEventListener;
+import org.drools.event.DefaultAgendaEventListener;
+import org.drools.Message;
+import org.drools.lang.descr.PackageDescr;
+import org.drools.rule.Package;
+import org.drools.ruleflow.common.instance.ProcessInstance;
+import org.drools.spi.Activation;
+import org.drools.spi.ActivationGroup;
+import org.drools.spi.AgendaGroup;
+
+public class ExecutionFlowControlTest extends TestCase {
+    protected RuleBase getRuleBase() throws Exception {
+
+        return RuleBaseFactory.newRuleBase( RuleBase.RETEOO,
+                                            null );
+    }
+
+    protected RuleBase getRuleBase(final RuleBaseConfiguration config) throws Exception {
+
+        return RuleBaseFactory.newRuleBase( RuleBase.RETEOO,
+                                            config );
+    }
+
+    public void testSalienceInteger() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_salienceIntegerRule.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        final PersonInterface person = new Person( "Edson",
+                                                   "cheese" );
+        workingMemory.insert( person );
+
+        workingMemory.fireAllRules();
+
+        Assert.assertEquals( "Two rules should have been fired",
+                             2,
+                             list.size() );
+        Assert.assertEquals( "Rule 3 should have been fired first",
+                             "Rule 3",
+                             list.get( 0 ) );
+        Assert.assertEquals( "Rule 2 should have been fired second",
+                             "Rule 2",
+                             list.get( 1 ) );
+    }
+     
+    public void testSalienceExpression() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_salienceExpressionRule.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        final PersonInterface person10 = new Person( "bob",
+                                                   "cheese",
+                                                   10);
+        workingMemory.insert( person10 );
+        
+        final PersonInterface person20 = new Person( "mic",
+                                                     "cheese",
+                                                     20);
+          workingMemory.insert( person20 );        
+
+        workingMemory.fireAllRules();
+
+        Assert.assertEquals( "Two rules should have been fired",
+                             2,
+                             list.size() );
+        Assert.assertEquals( "Rule 3 should have been fired first",
+                             "Rule 3",
+                             list.get( 0 ) );
+        Assert.assertEquals( "Rule 2 should have been fired second",
+                             "Rule 2",
+                             list.get( 1 ) );
+    }    
+
+    public void testNoLoop() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "no-loop.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        final Cheese brie = new Cheese( "brie",
+                                        12 );
+        workingMemory.insert( brie );
+
+        workingMemory.fireAllRules();
+
+        Assert.assertEquals( "Should not loop  and thus size should be 1",
+                             1,
+                             list.size() );
+
+    }
+
+    public void testLockOnActive() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_LockOnActive.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        // AgendaGroup "group1" is not active, so should receive activation
+        final Cheese brie12 = new Cheese( "brie",
+                                          12 );
+        workingMemory.insert( brie12 );
+        final AgendaGroup group1 = workingMemory.getAgenda().getAgendaGroup( "group1" );
+        assertEquals( 1,
+                      group1.size() );
+
+        workingMemory.setFocus( "group1" );
+        // AgendaqGroup "group1" is now active, so should not receive activations
+        final Cheese brie10 = new Cheese( "brie",
+                                          10 );
+        workingMemory.insert( brie10 );
+        assertEquals( 1,
+                      group1.size() );
+
+        final Cheese cheddar20 = new Cheese( "cheddar",
+                                             20 );
+        workingMemory.insert( cheddar20 );
+        final AgendaGroup group2 = workingMemory.getAgenda().getAgendaGroup( "group1" );
+        assertEquals( 1,
+                      group2.size() );
+
+        final RuleFlowGroupImpl rfg = (RuleFlowGroupImpl) workingMemory.getAgenda().getRuleFlowGroup( "ruleflow2" );
+        rfg.setActive( true );
+        final Cheese cheddar17 = new Cheese( "cheddar",
+                                             17 );
+        workingMemory.insert( cheddar17 );
+        assertEquals( 1,
+                      group2.size() );
+    }
+
+    public void testLockOnActiveWithModify() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_LockOnActiveWithUpdate.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory wm = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        wm.setGlobal( "list",
+                      list );
+
+        final Cheese brie = new Cheese( "brie",
+                                        13 );
+
+        final Person bob = new Person( "bob" );
+        bob.setCheese( brie );
+
+        final Person mic = new Person( "mic" );
+        mic.setCheese( brie );
+
+        final Person mark = new Person( "mark" );
+        mark.setCheese( brie );
+
+        final FactHandle brieHandle = wm.insert( brie );
+        wm.insert( bob );
+        wm.insert( mic );
+        wm.insert( mark );
+
+        final DefaultAgenda agenda = (DefaultAgenda) wm.getAgenda();
+        final AgendaGroup group1 = agenda.getAgendaGroup( "group1" );
+        agenda.setFocus( group1 );
+        assertEquals( 3,
+                      group1.size() );
+        agenda.fireNextItem( null );
+        assertEquals( 2,
+                      group1.size() );
+        wm.update( brieHandle,
+                         brie );
+        assertEquals( 2,
+                      group1.size() );
+
+        AgendaGroup group2 = agenda.getAgendaGroup( "group2" );
+        agenda.setFocus( group2 );
+
+        RuleFlowGroupImpl rfg = (RuleFlowGroupImpl) wm.getAgenda().getRuleFlowGroup( "ruleflow2" );
+        assertEquals( 3,
+                      rfg.size() );
+
+        agenda.activateRuleFlowGroup( "ruleflow2" );
+        agenda.fireNextItem( null );
+        assertEquals( 2,
+                      rfg.size() );
+        wm.update( brieHandle,
+                         brie );
+        assertEquals( 2,
+                      group2.size() );
+    }
+
+    public void testAgendaGroups() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_AgendaGroups.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        final Cheese brie = new Cheese( "brie",
+                                        12 );
+        workingMemory.insert( brie );
+
+        workingMemory.fireAllRules();
+
+        assertEquals( 7,
+                      list.size() );
+
+        assertEquals( "group3",
+                      list.get( 0 ) );
+        assertEquals( "group4",
+                      list.get( 1 ) );
+        assertEquals( "group3",
+                      list.get( 2 ) );
+        assertEquals( "MAIN",
+                      list.get( 3 ) );
+        assertEquals( "group1",
+                      list.get( 4 ) );
+        assertEquals( "group1",
+                      list.get( 5 ) );
+        assertEquals( "MAIN",
+                      list.get( 6 ) );
+
+        workingMemory.setFocus( "group2" );
+        workingMemory.fireAllRules();
+
+        assertEquals( 8,
+                      list.size() );
+        assertEquals( "group2",
+                      list.get( 7 ) );
+    }
+
+    public void testActivationGroups() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_ActivationGroups.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        final Cheese brie = new Cheese( "brie",
+                                        12 );
+        workingMemory.insert( brie );
+
+        final ActivationGroup activationGroup0 = workingMemory.getAgenda().getActivationGroup( "activation-group-0" );
+        assertEquals( 2,
+                      activationGroup0.size() );
+
+        final ActivationGroup activationGroup3 = workingMemory.getAgenda().getActivationGroup( "activation-group-3" );
+        assertEquals( 1,
+                      activationGroup3.size() );
+
+        final AgendaGroup agendaGroup3 = workingMemory.getAgenda().getAgendaGroup( "agenda-group-3" );
+        assertEquals( 1,
+                      agendaGroup3.size() );
+
+        final AgendaGroup agendaGroupMain = workingMemory.getAgenda().getAgendaGroup( "MAIN" );
+        assertEquals( 3,
+                      agendaGroupMain.size() );
+
+        workingMemory.clearAgendaGroup( "agenda-group-3" );
+        assertEquals( 0,
+                      activationGroup3.size() );
+        assertEquals( 0,
+                      agendaGroup3.size() );
+
+        workingMemory.fireAllRules();
+
+        assertEquals( 0,
+                      activationGroup0.size() );
+
+        assertEquals( 2,
+                      list.size() );
+        assertEquals( "rule0",
+                      list.get( 0 ) );
+        assertEquals( "rule2",
+                      list.get( 1 ) );
+
+    }
+
+    public void testDuration() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Duration.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        final Cheese brie = new Cheese( "brie",
+                                        12 );
+        final FactHandle brieHandle = workingMemory.insert( brie );
+
+        workingMemory.fireAllRules();
+
+        // now check for update
+        assertEquals( 0,
+                      list.size() );
+
+        // sleep for 300ms
+        Thread.sleep( 300 );
+
+        // now check for update
+        assertEquals( 1,
+                      list.size() );
+
+    }
+
+    public void testInsertRetractNoloop() throws Exception {
+        // read in the source
+        final Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_Insert_Retract_Noloop.drl" ) );
+        final RuleBase ruleBase = loadRuleBase( reader );
+
+        final WorkingMemory wm = ruleBase.newStatefulSession();
+        wm.insert( new Cheese( "stilton",
+                                     15 ) );
+
+        wm.fireAllRules();
+    }
+
+    public void testDurationWithNoLoop() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Duration_with_NoLoop.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        final Cheese brie = new Cheese( "brie",
+                                        12 );
+        final FactHandle brieHandle = workingMemory.insert( brie );
+
+        workingMemory.fireAllRules();
+
+        // now check for update
+        assertEquals( 0,
+                      list.size() );
+
+        // sleep for 300ms
+        Thread.sleep( 300 );
+
+        // now check for update
+        assertEquals( 1,
+                      list.size() );
+    }
+
+    public void testFireRuleAfterDuration() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_FireRuleAfterDuration.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        final Cheese brie = new Cheese( "brie",
+                                        12 );
+        final FactHandle brieHandle = workingMemory.insert( brie );
+
+        workingMemory.fireAllRules();
+
+        // now check for update
+        assertEquals( 0,
+                      list.size() );
+
+        // sleep for 300ms
+        Thread.sleep( 300 );
+
+        workingMemory.fireAllRules();
+
+        // now check for update
+        assertEquals( 2,
+                      list.size() );
+
+    }
+
+    public void testUpdateNoLoop() throws Exception {
+        // JBRULES-780, throws a NullPointer or infinite loop if there is an issue
+        final Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_UpdateNoloop.drl" ) );
+        final RuleBase ruleBase = loadRuleBase( reader );
+
+        final WorkingMemory wm = ruleBase.newStatefulSession();
+        wm.insert( new Cheese( "stilton",
+                                     15 ) );
+
+        wm.fireAllRules();
+    }
+
+    public void testUpdateActivationCreationNoLoop() throws Exception {
+        // JBRULES-787, no-loop blocks all dependant tuples for update 
+        final Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_UpdateActivationCreationNoLoop.drl" ) );
+        final RuleBase ruleBase = loadRuleBase( reader );
+
+        final InternalWorkingMemoryActions wm = (InternalWorkingMemoryActions) ruleBase.newStatefulSession();
+        final List created = new ArrayList();
+        final List cancelled = new ArrayList();
+        final AgendaEventListener l = new DefaultAgendaEventListener() {
+            public void activationCreated(ActivationCreatedEvent event,
+                                          WorkingMemory workingMemory) {
+                created.add( event );
+            }
+
+            public void activationCancelled(ActivationCancelledEvent event,
+                                            WorkingMemory workingMemory) {
+                cancelled.add( event );
+            }
+
+        };
+
+        wm.addEventListener( l );
+
+        final Cheese stilton = new Cheese( "stilton",
+                                           15 );
+        final FactHandle stiltonHandle = wm.insert( stilton );
+
+        final Person p1 = new Person( "p1" );
+        p1.setCheese( stilton );
+        wm.insert( p1 );
+
+        final Person p2 = new Person( "p2" );
+        p2.setCheese( stilton );
+        wm.insert( p2 );
+
+        final Person p3 = new Person( "p3" );
+        p3.setCheese( stilton );
+        wm.insert( p3 );
+
+        assertEquals( 3,
+                      created.size() );
+        assertEquals( 0,
+                      cancelled.size() );
+
+        final Activation item = ((ActivationCreatedEvent) created.get( 2 )).getActivation();
+
+        // simulate a modify inside a consequence
+        wm.update( stiltonHandle,
+                         stilton,
+                         item.getRule(),
+                         item );
+
+        // the two of the three tuples should re-activate
+        assertEquals( 5,
+                      created.size() );
+        assertEquals( 3,
+                      cancelled.size() );
+    }
+
+    public void testRuleFlowGroup() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflowgroup.drl" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        workingMemory.insert( "Test" );
+        workingMemory.fireAllRules();
+        assertEquals( 0,
+                      list.size() );
+
+        workingMemory.getAgenda().activateRuleFlowGroup( "Group1" );
+        workingMemory.fireAllRules();
+
+        assertEquals( 1,
+                      list.size() );
+    }
+
+    public void testRuleFlow() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
+        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
+        final Package pkg = builder.getPackage();
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        workingMemory.fireAllRules();
+        assertEquals( 0,
+                      list.size() );
+
+        final ProcessInstance processInstance = workingMemory.startProcess( "0" );
+        assertEquals( ProcessInstance.STATE_ACTIVE,
+                      processInstance.getState() );
+        workingMemory.fireAllRules();
+        assertEquals( 4,
+                      list.size() );
+        assertEquals( "Rule1",
+                      list.get( 0 ) );
+        assertEquals( "Rule3",
+                      list.get( 1 ) );
+        assertEquals( "Rule2",
+                      list.get( 2 ) );
+        assertEquals( "Rule4",
+                      list.get( 3 ) );
+        assertEquals( ProcessInstance.STATE_COMPLETED,
+                      processInstance.getState() );
+    }
+    
+    public void testRuleFlowClear() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_ruleflowClear.drl" ) ) );
+        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "test_ruleflowClear.rfm" ) ) );
+        final Package pkg = builder.getPackage();
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+        
+        final List activations = new ArrayList();
+        AgendaEventListener listener = new DefaultAgendaEventListener() {            
+            public void activationCancelled(ActivationCancelledEvent event,
+                                            WorkingMemory workingMemory) {
+                activations.add( event.getActivation() );
+            }
+        };
+        
+        workingMemory.addEventListener( listener );
+     
+        assertEquals( 0 , workingMemory.getAgenda().getRuleFlowGroup( "flowgroup-1" ).size() );
+        
+        // We need to call fireAllRules here to get the InitialFact into the system, to the eval(true)'s kick in
+        workingMemory.fireAllRules();
+        
+        // Now we have 4 in the RuleFlow, but not yet in the agenda
+        assertEquals( 4 , workingMemory.getAgenda().getRuleFlowGroup( "flowgroup-1" ).size() );
+        
+        // Check they aren't in the Agenda
+        assertEquals( 0, workingMemory.getAgenda().getAgendaGroup( "MAIN" ).size() );
+        
+        // Start the process, which shoudl populate the Agenda
+        final ProcessInstance processInstance = workingMemory.startProcess( "ruleFlowClear" );
+        assertEquals( 4, workingMemory.getAgenda().getAgendaGroup( "MAIN" ).size() );
+        
+        
+        // Check we have 0 activation cancellation events
+        assertEquals( 0, activations.size() );
+        
+        workingMemory.getAgenda().clearRuleFlowGroup( "flowgroup-1" );
+        
+        // Check the AgendaGroup and RuleFlowGroup  are now empty
+        assertEquals( 0, workingMemory.getAgenda().getAgendaGroup( "MAIN" ).size() );
+        assertEquals( 0 , workingMemory.getAgenda().getRuleFlowGroup( "flowgroup-1" ).size() );
+        
+        // Check we have four activation cancellation events
+        assertEquals( 4, activations.size() );              
+    }
+    
+    public void testRuleFlowInPackage() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
+        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
+        final Package pkg = builder.getPackage();
+
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        workingMemory.fireAllRules();
+        assertEquals( 0,
+                      list.size() );
+
+        final ProcessInstance processInstance = workingMemory.startProcess( "0" );
+        assertEquals( ProcessInstance.STATE_ACTIVE,
+                      processInstance.getState() );
+        workingMemory.fireAllRules();
+        assertEquals( 4,
+                      list.size() );
+        assertEquals( "Rule1",
+                      list.get( 0 ) );
+        assertEquals( "Rule3",
+                      list.get( 1 ) );
+        assertEquals( "Rule2",
+                      list.get( 2 ) );
+        assertEquals( "Rule4",
+                      list.get( 3 ) );
+        assertEquals( ProcessInstance.STATE_COMPLETED,
+                      processInstance.getState() );
+        
+    }
+    
+    public void testLoadingRuleFlowInPackage1() throws Exception {
+    	// adding ruleflow before adding package
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
+        builder.getPackage();
+    }
+
+    public void testLoadingRuleFlowInPackage2() throws Exception {
+    	// only adding ruleflow
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
+        builder.getPackage();
+    }
+
+    public void testLoadingRuleFlowInPackage3() throws Exception {
+    	// only adding ruleflow without any generated rules
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "empty_ruleflow.rfm" ) ) );
+        builder.getPackage();
+    }
+
+    public void testLoadingRuleFlowInPackage4() throws Exception {
+    	// adding ruleflows of different package
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "empty_ruleflow.rfm" ) ) );
+        try {
+        	builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.rfm" ) ) );
+        	throw new Exception("An exception should have been thrown.");
+        } catch (PackageMergeException e) {
+        	// do nothing
+        }
+    }
+
+    public void testLoadingRuleFlowInPackage5() throws Exception {
+    	// adding ruleflow of different package than rules
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
+        try {
+        	builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "empty_ruleflow.rfm" ) ) );
+        	throw new Exception("An exception should have been thrown.");
+        } catch (PackageMergeException e) {
+        	// do nothing
+        }
+    }
+
+    public void testLoadingRuleFlowInPackage6() throws Exception {
+    	// adding rules of different package than ruleflow
+        final PackageBuilder builder = new PackageBuilder();
+    	builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "empty_ruleflow.rfm" ) ) );
+        try {
+            builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "ruleflow.drl" ) ) );
+        	throw new Exception("An exception should have been thrown.");
+        } catch (PackageMergeException e) {
+        	// do nothing
+        }
+    }
+
+    public void testLoadingRuleFlowInPackage7() throws Exception {
+    	// loading a ruleflow with errors
+        final PackageBuilder builder = new PackageBuilder();
+    	builder.addRuleFlow( new InputStreamReader( getClass().getResourceAsStream( "error_ruleflow.rfm" ) ) );
+    	assertEquals(1, builder.getErrors().getErrors().length);
+    }
+
+    private RuleBase loadRuleBase(final Reader reader) throws IOException,
+                                                      DroolsParserException,
+                                                      Exception {
+        final DrlParser parser = new DrlParser();
+        final PackageDescr packageDescr = parser.parse( reader );
+        if ( parser.hasErrors() ) {
+            Assert.fail( "Error messages in parser, need to sort this our (or else collect error messages)" );
+        }
+        // pre build the package
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackage( packageDescr );
+        final Package pkg = builder.getPackage();
+
+        // add the package to a rulebase
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        // load up the rulebase
+        return ruleBase;
+    }
+
+    public void testDateEffective() throws Exception {
+        // read in the source
+        final Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_EffectiveDate.drl" ) );
+        final RuleBase ruleBase = loadRuleBase( reader );
+
+        final WorkingMemory workingMemory = ruleBase.newStatefulSession();
+
+        final List list = new ArrayList();
+        workingMemory.setGlobal( "list",
+                                 list );
+
+        // go !
+        final Message message = new Message( "hola" );
+        workingMemory.insert( message );
+        workingMemory.fireAllRules();
+        assertFalse( message.isFired() );
+
+    }    
+}

Modified: labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/integrationtests/MiscTest.java
===================================================================
--- labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/integrationtests/MiscTest.java	2007-10-11 18:40:45 UTC (rev 15773)
+++ labs/jbossrules/branches/4.0.x/drools-compiler/src/test/java/org/drools/integrationtests/MiscTest.java	2007-10-11 18:45:10 UTC (rev 15774)
@@ -39,6 +39,7 @@
 import org.acme.insurance.Driver;
 import org.acme.insurance.Policy;
 import org.drools.Address;
+import org.drools.Attribute;
 import org.drools.Cell;
 import org.drools.Cheese;
 import org.drools.Cheesery;
@@ -96,7 +97,7 @@
 import org.drools.event.WorkingMemoryEventListener;
 import org.drools.facttemplates.Fact;
 import org.drools.facttemplates.FactTemplate;
-import org.drools.integrationtests.helloworld.Message;
+import org.drools.Message;
 import org.drools.lang.DrlDumper;
 import org.drools.lang.descr.AttributeDescr;
 import org.drools.lang.descr.PackageDescr;
@@ -110,6 +111,7 @@
 import org.drools.spi.GlobalResolver;
 import org.drools.xml.XmlDumper;
 
+
 /** Run all the tests with the ReteOO engine implementation */
 public class MiscTest extends TestCase {
 
@@ -397,7 +399,29 @@
                       list.size() );
 
     }
+    
+    public void NullFieldOnCompositeSink() throws Exception {
+        final PackageBuilder builder = new PackageBuilder();
+        builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_NullFieldOnCompositeSink.drl" ) ) );
+        final Package pkg = builder.getPackage();
 
+        // add the package to a rulebase
+        final RuleBase ruleBase = getRuleBase();
+        ruleBase.addPackage( pkg );
+        
+        WorkingMemory workingMemory = ruleBase.newStatefulSession();
+        List list = new ArrayList();
+        workingMemory.setGlobal("list", list);
+        
+        workingMemory.insert(new Attribute());
+        workingMemory.insert(new Message());
+        workingMemory.fireAllRules();
+        
+        assertEquals(1, list.size());
+        assertEquals("X", list.get(0));
+    	
+    }
+
     public void testEmptyPattern() throws Exception {
         // pre build the package
         final PackageBuilder builder = new PackageBuilder();

Modified: labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/HelloWorld.drl
===================================================================
--- labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/HelloWorld.drl	2007-10-11 18:40:45 UTC (rev 15773)
+++ labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/HelloWorld.drl	2007-10-11 18:45:10 UTC (rev 15774)
@@ -7,7 +7,7 @@
 
 rule "Hello World"
 	when
-		$m : org.drools.integrationtests.helloworld.Message(list contains "hello", 
+		$m : org.drools.Message(list contains "hello", 
 					text:message == "hola", 
 					number > 40, 
 					birthday > "10-Jul-1974", 

Modified: labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/test_EffectiveDate.drl
===================================================================
--- labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/test_EffectiveDate.drl	2007-10-11 18:40:45 UTC (rev 15773)
+++ labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/test_EffectiveDate.drl	2007-10-11 18:45:10 UTC (rev 15774)
@@ -9,7 +9,7 @@
 	date-effective "9-Jul-1974"
 	date-expires "10-Jul-1974"
 	when
-		$m : org.drools.integrationtests.helloworld.Message()
+		$m : org.drools.Message()
 	then
 		$m.setFired(true); 
 end

Modified: labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/test_ExplicitAnd.drl
===================================================================
--- labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/test_ExplicitAnd.drl	2007-10-11 18:40:45 UTC (rev 15773)
+++ labs/jbossrules/branches/4.0.x/drools-compiler/src/test/resources/org/drools/integrationtests/test_ExplicitAnd.drl	2007-10-11 18:45:10 UTC (rev 15774)
@@ -1,6 +1,6 @@
 package HelloWorld
  
-import org.drools.integrationtests.helloworld.Message
+import org.drools.Message
 import org.drools.Cheese;
 
 global java.util.List list;

Modified: labs/jbossrules/branches/4.0.x/drools-core/src/main/java/org/drools/reteoo/CompositeObjectSinkAdapter.java
===================================================================
--- labs/jbossrules/branches/4.0.x/drools-core/src/main/java/org/drools/reteoo/CompositeObjectSinkAdapter.java	2007-10-11 18:40:45 UTC (rev 15773)
+++ labs/jbossrules/branches/4.0.x/drools-core/src/main/java/org/drools/reteoo/CompositeObjectSinkAdapter.java	2007-10-11 18:45:10 UTC (rev 15774)
@@ -1,690 +1,734 @@
-package org.drools.reteoo;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.drools.base.ValueType;
-import org.drools.base.evaluators.Operator;
-import org.drools.common.InternalFactHandle;
-import org.drools.common.InternalWorkingMemory;
-import org.drools.rule.LiteralConstraint;
-import org.drools.spi.AlphaNodeFieldConstraint;
-import org.drools.spi.Evaluator;
-import org.drools.spi.Extractor;
-import org.drools.spi.FieldExtractor;
-import org.drools.spi.FieldValue;
-import org.drools.spi.PropagationContext;
-import org.drools.util.Iterator;
-import org.drools.util.LinkedList;
-import org.drools.util.LinkedListNode;
-import org.drools.util.ObjectHashMap;
-import org.drools.util.ObjectHashMap.ObjectEntry;
-
-public class CompositeObjectSinkAdapter
-    implements
-    ObjectSinkPropagator {
-
-    //    /** You can override this property via a system property (eg -Ddrools.hashThreshold=4) */
-    //    public static final String HASH_THRESHOLD_SYSTEM_PROPERTY = "drools.hashThreshold";
-    //
-    //    /** The threshold for when hashing kicks in */
-    //    public static final int    THRESHOLD_TO_HASH              = Integer.parseInt( System.getProperty( HASH_THRESHOLD_SYSTEM_PROPERTY,
-    //                                                                                                      "3" ) );
-
-    private static final long serialVersionUID = 400L;
-    ObjectSinkNodeList        otherSinks;
-    ObjectSinkNodeList        hashableSinks;
-
-    LinkedList                hashedFieldIndexes;
-
-    ObjectHashMap             hashedSinkMap;
-
-    private HashKey           hashKey;
-
-    private final int         alphaNodeHashingThreshold;
-
-    public CompositeObjectSinkAdapter() {
-        this( 3 );
-    }
-
-    public CompositeObjectSinkAdapter(final int alphaNodeHashingThreshold) {
-        this.hashKey = new HashKey();
-        this.alphaNodeHashingThreshold = alphaNodeHashingThreshold;
-    }
-
-    public void addObjectSink(final ObjectSink sink) {
-        if ( sink instanceof AlphaNode ) {
-            final AlphaNode alphaNode = (AlphaNode) sink;
-            final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();
-
-            if ( fieldConstraint instanceof LiteralConstraint ) {
-                final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
-                final Evaluator evaluator = literalConstraint.getEvaluator();
-
-                if ( evaluator.getOperator() == Operator.EQUAL ) {
-                    final int index = literalConstraint.getFieldExtractor().getIndex();
-                    final FieldIndex fieldIndex = registerFieldIndex( index,
-                                                                      literalConstraint.getFieldExtractor() );
-
-                    if ( fieldIndex.getCount() >= this.alphaNodeHashingThreshold ) {
-                        if ( !fieldIndex.isHashed() ) {
-                            hashSinks( fieldIndex );
-                        }
-                        final FieldValue value = literalConstraint.getField();
-                        // no need to check, we know  the sink  does not exist
-                        this.hashedSinkMap.put( new HashKey( index,
-                                                             value ),
-                                                sink,
-                                                false );
-                    } else {
-                        if ( this.hashableSinks == null ) {
-                            this.hashableSinks = new ObjectSinkNodeList();
-                        }
-                        this.hashableSinks.add( (ObjectSinkNode) sink );
-                    }
-                    return;
-                }
-
-            }
-        }
-
-        if ( this.otherSinks == null ) {
-            this.otherSinks = new ObjectSinkNodeList();
-        }
-
-        this.otherSinks.add( (ObjectSinkNode) sink );
-    }
-
-    public void removeObjectSink(final ObjectSink sink) {
-        if ( sink instanceof AlphaNode ) {
-            final AlphaNode alphaNode = (AlphaNode) sink;
-            final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();
-
-            if ( fieldConstraint instanceof LiteralConstraint ) {
-                final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
-                final Evaluator evaluator = literalConstraint.getEvaluator();
-                final FieldValue value = literalConstraint.getField();
-
-                if ( evaluator.getOperator() == Operator.EQUAL ) {
-                    final int index = literalConstraint.getFieldExtractor().getIndex();
-                    final FieldIndex fieldIndex = unregisterFieldIndex( index );
-
-                    if ( fieldIndex.isHashed() ) {
-                        this.hashKey.setValue( index,
-                                               value );
-                        this.hashedSinkMap.remove( this.hashKey );
-                        if ( fieldIndex.getCount() <= this.alphaNodeHashingThreshold - 1 ) {
-                            // we have less than three so unhash
-                            unHashSinks( fieldIndex );
-                        }
-                    } else {
-                        this.hashableSinks.remove( (ObjectSinkNode) sink );
-                    }
-
-                    if ( this.hashableSinks != null && this.hashableSinks.isEmpty() ) {
-                        this.hashableSinks = null;
-                    }
-
-                    return;
-                }
-            }
-        }
-
-        this.otherSinks.remove( (ObjectSinkNode) sink );
-
-        if ( this.otherSinks.isEmpty() ) {
-            this.otherSinks = null;
-        }
-    }
-
-    public void hashSinks(final FieldIndex fieldIndex) {
-        final int index = fieldIndex.getIndex();
-
-        final List list = new ArrayList();
-
-        if ( this.hashedSinkMap == null ) {
-            this.hashedSinkMap = new ObjectHashMap();
-        }
-
-        for ( ObjectSinkNode sink = this.hashableSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
-            final AlphaNode alphaNode = (AlphaNode) sink;
-            final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();
-            final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
-            final Evaluator evaluator = literalConstraint.getEvaluator();
-            if ( evaluator.getOperator() == Operator.EQUAL && index == literalConstraint.getFieldExtractor().getIndex() ) {
-                final FieldValue value = literalConstraint.getField();
-                list.add( sink );
-                this.hashedSinkMap.put( new HashKey( index,
-                                                     value ),
-                                        sink );
-            }
-        }
-
-        for ( final java.util.Iterator it = list.iterator(); it.hasNext(); ) {
-            final ObjectSinkNode sink = (ObjectSinkNode) it.next();
-            this.hashableSinks.remove( sink );
-        }
-
-        if ( this.hashableSinks.isEmpty() ) {
-            this.hashableSinks = null;
-        }
-
-        fieldIndex.setHashed( true );
-    }
-
-    public void unHashSinks(final FieldIndex fieldIndex) {
-        final int index = fieldIndex.getIndex();
-
-        final List sinks = new ArrayList();
-
-        //iterate twice as custom iterator is immutable
-        final Iterator mapIt = this.hashedSinkMap.newIterator();
-        for ( ObjectHashMap.ObjectEntry e = (ObjectHashMap.ObjectEntry) mapIt.next(); e != null; ) {
-
-            sinks.add( e.getValue() );
-            e = (ObjectHashMap.ObjectEntry) mapIt.next();
-        }
-
-        for ( final java.util.Iterator iter = sinks.iterator(); iter.hasNext(); ) {
-            final AlphaNode sink = (AlphaNode) iter.next();
-            final AlphaNode alphaNode = sink;
-            final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();
-            final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
-            final Evaluator evaluator = literalConstraint.getEvaluator();
-            if ( evaluator.getOperator() == Operator.EQUAL && index == literalConstraint.getFieldExtractor().getIndex() ) {
-                final FieldValue value = literalConstraint.getField();
-                if ( this.hashableSinks == null ) {
-                    this.hashableSinks = new ObjectSinkNodeList();
-                }
-                this.hashableSinks.add( sink );
-                this.hashedSinkMap.remove( new HashKey( index,
-                                                        value ) );
-            };
-        }
-
-        if ( this.hashedSinkMap.isEmpty() ) {
-            this.hashedSinkMap = null;
-        }
-
-        fieldIndex.setHashed( false );
-    }
-
-    /**
-     * Returns a FieldIndex which Keeps a count on how many times a particular field is used with an equality check in the sinks.
-     * @param index
-     * @param fieldExtractor
-     * @return
-     */
-    private FieldIndex registerFieldIndex(final int index,
-                                          final FieldExtractor fieldExtractor) {
-        FieldIndex fieldIndex = null;
-
-        // is linkedlist null, if so create and add
-        if ( this.hashedFieldIndexes == null ) {
-            this.hashedFieldIndexes = new LinkedList();
-            fieldIndex = new FieldIndex( index,
-                                         fieldExtractor );
-            this.hashedFieldIndexes.add( fieldIndex );
-        }
-
-        // still null, so see if it already exists
-        if ( fieldIndex == null ) {
-            fieldIndex = findFieldIndex( index );
-        }
-
-        // doesn't exist so create it
-        if ( fieldIndex == null ) {
-            fieldIndex = new FieldIndex( index,
-                                         fieldExtractor );
-            this.hashedFieldIndexes.add( fieldIndex );
-        }
-
-        fieldIndex.increaseCounter();
-
-        return fieldIndex;
-    }
-
-    private FieldIndex unregisterFieldIndex(final int index) {
-        final FieldIndex fieldIndex = findFieldIndex( index );
-        fieldIndex.decreaseCounter();
-
-        // if the fieldcount is 0 then remove it from the linkedlist
-        if ( fieldIndex.getCount() == 0 ) {
-            this.hashedFieldIndexes.remove( fieldIndex );
-
-            // if the linkedlist is empty then null it
-            if ( this.hashedFieldIndexes.isEmpty() ) {
-                this.hashedFieldIndexes = null;
-            }
-        }
-
-        return fieldIndex;
-    }
-
-    private FieldIndex findFieldIndex(final int index) {
-        for ( FieldIndex node = (FieldIndex) this.hashedFieldIndexes.getFirst(); node != null; node = (FieldIndex) node.getNext() ) {
-            if ( node.getIndex() == index ) {
-                return node;
-            }
-        }
-
-        return null;
-    }
-
-    public void propagateAssertObject(final InternalFactHandle handle,
-                                      final PropagationContext context,
-                                      final InternalWorkingMemory workingMemory) {
-        final Object object = handle.getObject();
-
-        // Iterates t he FieldIndex collection, which tells you if particularly field is hashed or not
-        // if the field is hashed then it builds the hashkey to return the correct sink for the current objects slot's
-        // value, one object may have multiple fields indexed.
-        if ( this.hashedFieldIndexes != null ) {
-            // Iterate the FieldIndexes to see if any are hashed        
-            for ( FieldIndex fieldIndex = (FieldIndex) this.hashedFieldIndexes.getFirst(); fieldIndex != null; fieldIndex = (FieldIndex) fieldIndex.getNext() ) {
-                if ( !fieldIndex.isHashed() ) {
-                    continue;
-                }
-                // this field is hashed so set the existing hashKey and see if there is a sink for it
-                final int index = fieldIndex.getIndex();
-                final FieldExtractor extractor = fieldIndex.getFieldExtactor();
-                this.hashKey.setValue( index,
-                                       object,
-                                       extractor );
-                final ObjectSink sink = (ObjectSink) this.hashedSinkMap.get( this.hashKey );
-                if ( sink != null ) {
-                    // The sink exists so propagate
-                    sink.assertObject( handle,
-                                       context,
-                                       workingMemory );
-                }
-            }
-        }
-
-        // propagate unhashed
-        if ( this.hashableSinks != null ) {
-            for ( ObjectSinkNode sink = this.hashableSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
-                sink.assertObject( handle,
-                                   context,
-                                   workingMemory );
-            }
-        }
-
-        if ( this.otherSinks != null ) {
-            // propagate others
-            for ( ObjectSinkNode sink = this.otherSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
-                sink.assertObject( handle,
-                                   context,
-                                   workingMemory );
-            }
-        }
-
-    }
-
-    public void propagateRetractObject(final InternalFactHandle handle,
-                                       final PropagationContext context,
-                                       final InternalWorkingMemory workingMemory,
-                                       final boolean useHash) {
-        if ( this.hashedFieldIndexes != null ) {
-            if ( useHash && this.hashedSinkMap != null ) {
-                final Object object = handle.getObject();
-                // Iterate the FieldIndexes to see if any are hashed        
-                for ( FieldIndex fieldIndex = (FieldIndex) this.hashedFieldIndexes.getFirst(); fieldIndex != null; fieldIndex = (FieldIndex) fieldIndex.getNext() ) {
-                    // this field is hashed so set the existing hashKey and see if there is a sink for it
-                    if ( !fieldIndex.isHashed() ) {
-                        continue;
-                    }
-
-                    final int index = fieldIndex.getIndex();
-                    final FieldExtractor extractor = fieldIndex.getFieldExtactor();
-                    this.hashKey.setValue( index,
-                                           object,
-                                           extractor );
-                    final ObjectSink sink = (ObjectSink) this.hashedSinkMap.get( this.hashKey );
-                    if ( sink != null ) {
-                        // The sink exists so propagate
-                        sink.retractObject( handle,
-                                            context,
-                                            workingMemory );
-                    }
-                }
-            } else if ( this.hashedSinkMap != null ) {
-                final Iterator it = this.hashedSinkMap.newIterator();
-                for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
-                    final ObjectSink sink = (ObjectSink) entry.getValue();
-                    sink.retractObject( handle,
-                                        context,
-                                        workingMemory );
-                }
-            }
-        }
-
-        if ( this.hashableSinks != null ) {
-            // we can't retrieve hashed sinks, as the field value might have changed, so we have to iterate and propagate to all hashed sinks
-            for ( ObjectSinkNode sink = this.hashableSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
-                sink.retractObject( handle,
-                                    context,
-                                    workingMemory );
-            }
-        }
-
-        if ( this.otherSinks != null ) {
-            // propagate others
-            for ( ObjectSinkNode sink = this.otherSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
-                sink.retractObject( handle,
-                                    context,
-                                    workingMemory );
-            }
-        }
-    }
-
-    public ObjectSink[] getSinks() {
-        final List list = new ArrayList();
-
-        if ( this.otherSinks != null ) {
-            for ( ObjectSinkNode sink = this.otherSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
-                list.add( sink );
-            }
-        }
-
-        if ( this.hashableSinks != null ) {
-            for ( ObjectSinkNode sink = this.hashableSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
-                list.add( sink );
-            }
-        }
-
-        if ( this.hashedSinkMap != null ) {
-            final Iterator it = this.hashedSinkMap.newIterator();
-            for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
-                final ObjectSink sink = (ObjectSink) entry.getValue();
-                list.add( sink );
-            }
-        }
-
-        return (ObjectSink[]) list.toArray( new ObjectSink[list.size()] );
-    }
-
-    public int size() {
-        int size = 0;
-        size += ((this.otherSinks != null) ? this.otherSinks.size() : 0);
-        size += ((this.hashableSinks != null) ? this.hashableSinks.size() : 0);
-        size += ((this.hashedSinkMap != null) ? this.hashedSinkMap.size() : 0);
-        return size;
-    }
-
-    public static class HashKey
-        implements
-        Serializable {
-        private static final long serialVersionUID = 400L;
-
-        private static final byte OBJECT           = 1;
-        private static final byte LONG             = 2;
-        private static final byte DOUBLE           = 3;
-        private static final byte BOOL             = 4;
-
-        private int               index;
-
-        private byte              type;
-        private Object            ovalue;
-        private long              lvalue;
-        private boolean           bvalue;
-        private double            dvalue;
-
-        private int               hashCode;
-
-        public HashKey() {
-        }
-
-        public HashKey(final int index,
-                       final FieldValue value) {
-            this.setValue( index,
-                           value );
-        }
-
-        public HashKey(final int index,
-                       final Object value,
-                       final Extractor extractor) {
-            this.setValue( index,
-                           value,
-                           extractor );
-        }
-
-        public int getIndex() {
-            return this.index;
-        }
-
-        public void setValue(final int index,
-                             final Object value,
-                             final Extractor extractor) {
-            this.index = index;
-            final ValueType vtype = extractor.getValueType();
-            if ( vtype.isBoolean() ) {
-                this.bvalue = extractor.getBooleanValue( null, value );
-                this.type = BOOL;
-                this.setHashCode( this.bvalue ? 1231 : 1237 );
-            } else if ( vtype.isIntegerNumber() ) {
-                this.lvalue = extractor.getLongValue( null, value );
-                this.type = LONG;
-                this.setHashCode( (int) (this.lvalue ^ (this.lvalue >>> 32)) );
-            } else if ( vtype.isFloatNumber() ) {
-                this.dvalue = extractor.getDoubleValue( null, value );
-                this.type = DOUBLE;
-                final long temp = Double.doubleToLongBits( this.dvalue );
-                this.setHashCode( (int) (temp ^ (temp >>> 32)) );
-            } else {
-                this.ovalue = extractor.getValue( null, value );
-                this.type = OBJECT;
-                this.setHashCode( this.ovalue != null ? this.ovalue.hashCode() : 0 );
-            }
-        }
-
-        public void setValue(final int index,
-                             final FieldValue value) {
-            this.index = index;
-            if ( value.isBooleanField() ) {
-                this.bvalue = value.getBooleanValue();
-                this.type = BOOL;
-                this.setHashCode( this.bvalue ? 1231 : 1237 );
-            } else if ( value.isIntegerNumberField() ) {
-                this.lvalue = value.getLongValue();
-                this.type = LONG;
-                this.setHashCode( (int) (this.lvalue ^ (this.lvalue >>> 32)) );
-            } else if ( value.isFloatNumberField() ) {
-                this.dvalue = value.getDoubleValue();
-                this.type = DOUBLE;
-                final long temp = Double.doubleToLongBits( this.dvalue );
-                this.setHashCode( (int) (temp ^ (temp >>> 32)) );
-            } else {
-                this.ovalue = value.getValue();
-                this.type = OBJECT;
-                this.setHashCode( this.ovalue != null ? this.ovalue.hashCode() : 0 );
-            }
-        }
-
-        private void setHashCode(final int hashSeed) {
-            final int PRIME = 31;
-            int result = 1;
-            result = PRIME * result + hashSeed;
-            result = PRIME * result + this.index;
-            this.hashCode = result;
-        }
-
-        public boolean getBooleanValue() {
-            switch ( this.type ) {
-                case BOOL :
-                    return this.bvalue;
-                case OBJECT :
-                    if ( this.ovalue == null ) {
-                        return false;
-                    } else if ( this.ovalue instanceof Boolean ) {
-                        return ((Boolean) this.ovalue).booleanValue();
-                    } else if ( this.ovalue instanceof String ) {
-                        return Boolean.valueOf( (String) this.ovalue ).booleanValue();
-                    } else {
-                        throw new ClassCastException( "Can't convert " + this.ovalue.getClass() + " to a boolean value." );
-                    }
-                case LONG :
-                    throw new ClassCastException( "Can't convert long to a boolean value." );
-                case DOUBLE :
-                    throw new ClassCastException( "Can't convert double to a boolean value." );
-
-            }
-            return false;
-        }
-
-        public long getLongValue() {
-            switch ( this.type ) {
-                case BOOL :
-                    return this.bvalue ? 1 : 0;
-                case OBJECT :
-                    if ( this.ovalue == null ) {
-                        return 0;
-                    } else if ( this.ovalue instanceof Number ) {
-                        return ((Number) this.ovalue).longValue();
-                    } else if ( this.ovalue instanceof String ) {
-                        return Long.parseLong( (String) this.ovalue );
-                    } else {
-                        throw new ClassCastException( "Can't convert " + this.ovalue.getClass() + " to a long value." );
-                    }
-                case LONG :
-                    return this.lvalue;
-                case DOUBLE :
-                    return (long) this.dvalue;
-
-            }
-            return 0;
-        }
-
-        public double getDoubleValue() {
-            switch ( this.type ) {
-                case BOOL :
-                    return this.bvalue ? 1 : 0;
-                case OBJECT :
-                    if ( this.ovalue == null ) {
-                        return 0;
-                    } else if ( this.ovalue instanceof Number ) {
-                        return ((Number) this.ovalue).doubleValue();
-                    } else if ( this.ovalue instanceof String ) {
-                        return Double.parseDouble( (String) this.ovalue );
-                    } else {
-                        throw new ClassCastException( "Can't convert " + this.ovalue.getClass() + " to a double value." );
-                    }
-                case LONG :
-                    return this.lvalue;
-                case DOUBLE :
-                    return this.dvalue;
-            }
-            return 0;
-        }
-
-        public Object getObjectValue() {
-            switch ( this.type ) {
-                case BOOL :
-                    return this.bvalue ? Boolean.TRUE : Boolean.FALSE;
-                case OBJECT :
-                    return this.ovalue;
-                case LONG :
-                    return new Long( this.lvalue );
-                case DOUBLE :
-                    return new Double( this.dvalue );
-            }
-            return null;
-        }
-
-        public int hashCode() {
-            return this.hashCode;
-        }
-
-        public boolean equals(final Object object) {
-            final HashKey other = (HashKey) object;
-
-            switch ( this.type ) {
-                case BOOL :
-                    return (this.index == other.index) && (this.bvalue == other.getBooleanValue());
-                case LONG :
-                    return (this.index == other.index) && (this.lvalue == other.getLongValue());
-                case DOUBLE :
-                    return (this.index == other.index) && (this.dvalue == other.getDoubleValue());
-                case OBJECT :
-                    final Object otherValue = other.getObjectValue();
-                    if ( (this.ovalue != null) && (this.ovalue instanceof Number) && (otherValue instanceof Number) ) {
-                        return (this.index == other.index) && (((Number) this.ovalue).doubleValue() == ((Number) otherValue).doubleValue());
-                    }
-                    return (this.index == other.index) && ( this.ovalue == null ? otherValue == null : this.ovalue.equals( otherValue ));
-            }
-            return false;
-        }
-
-    }
-
-    public static class FieldIndex
-        implements
-        LinkedListNode {
-        private static final long serialVersionUID = 400L;
-        private final int         index;
-        private FieldExtractor    fieldExtactor;
-
-        private int               count;
-
-        private boolean           hashed;
-
-        private LinkedListNode    previous;
-        private LinkedListNode    next;
-
-        public FieldIndex(final int index,
-                          final FieldExtractor fieldExtractor) {
-            this.index = index;
-            this.fieldExtactor = fieldExtractor;
-        }
-
-        public FieldExtractor getFieldExtractor() {
-            return this.fieldExtactor;
-        }
-
-        public int getIndex() {
-            return this.index;
-        }
-
-        public int getCount() {
-            return this.count;
-        }
-
-        public FieldExtractor getFieldExtactor() {
-            return this.fieldExtactor;
-        }
-
-        public boolean isHashed() {
-            return this.hashed;
-        }
-
-        public void setHashed(final boolean hashed) {
-            this.hashed = hashed;
-        }
-
-        public void increaseCounter() {
-            this.count++;
-        }
-
-        public void decreaseCounter() {
-            this.count--;
-        }
-
-        public LinkedListNode getNext() {
-            return this.next;
-        }
-
-        public LinkedListNode getPrevious() {
-            return this.previous;
-        }
-
-        public void setNext(final LinkedListNode next) {
-            this.next = next;
-
-        }
-
-        public void setPrevious(final LinkedListNode previous) {
-            this.previous = previous;
-        }
-    }
-}
+package org.drools.reteoo;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.drools.base.ValueType;
+import org.drools.base.evaluators.Operator;
+import org.drools.common.InternalFactHandle;
+import org.drools.common.InternalWorkingMemory;
+import org.drools.rule.LiteralConstraint;
+import org.drools.spi.AlphaNodeFieldConstraint;
+import org.drools.spi.Evaluator;
+import org.drools.spi.Extractor;
+import org.drools.spi.FieldExtractor;
+import org.drools.spi.FieldValue;
+import org.drools.spi.PropagationContext;
+import org.drools.util.Iterator;
+import org.drools.util.LinkedList;
+import org.drools.util.LinkedListNode;
+import org.drools.util.ObjectHashMap;
+import org.drools.util.ObjectHashMap.ObjectEntry;
+
+public class CompositeObjectSinkAdapter
+    implements
+    ObjectSinkPropagator {
+
+    //    /** You can override this property via a system property (eg -Ddrools.hashThreshold=4) */
+    //    public static final String HASH_THRESHOLD_SYSTEM_PROPERTY = "drools.hashThreshold";
+    //
+    //    /** The threshold for when hashing kicks in */
+    //    public static final int    THRESHOLD_TO_HASH              = Integer.parseInt( System.getProperty( HASH_THRESHOLD_SYSTEM_PROPERTY,
+    //                                                                                                      "3" ) );
+
+    private static final long serialVersionUID = 400L;
+    ObjectSinkNodeList        otherSinks;
+    ObjectSinkNodeList        hashableSinks;
+
+    LinkedList                hashedFieldIndexes;
+
+    ObjectHashMap             hashedSinkMap;
+
+    private HashKey           hashKey;
+
+    private final int         alphaNodeHashingThreshold;
+
+    public CompositeObjectSinkAdapter() {
+        this( 3 );
+    }
+
+    public CompositeObjectSinkAdapter(final int alphaNodeHashingThreshold) {
+        this.hashKey = new HashKey();
+        this.alphaNodeHashingThreshold = alphaNodeHashingThreshold;
+    }
+
+    public void addObjectSink(final ObjectSink sink) {
+        if ( sink instanceof AlphaNode ) {
+            final AlphaNode alphaNode = (AlphaNode) sink;
+            final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();
+
+            if ( fieldConstraint instanceof LiteralConstraint ) {
+                final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
+                final Evaluator evaluator = literalConstraint.getEvaluator();
+
+                if ( evaluator.getOperator() == Operator.EQUAL ) {
+                    final int index = literalConstraint.getFieldExtractor().getIndex();
+                    final FieldIndex fieldIndex = registerFieldIndex( index,
+                                                                      literalConstraint.getFieldExtractor() );
+
+                    if ( fieldIndex.getCount() >= this.alphaNodeHashingThreshold && this.alphaNodeHashingThreshold != 0) {
+                        if ( !fieldIndex.isHashed() ) {
+                            hashSinks( fieldIndex );
+                        }
+                        final FieldValue value = literalConstraint.getField();
+                        // no need to check, we know  the sink  does not exist
+                        this.hashedSinkMap.put( new HashKey( index,
+                                                             value ),
+                                                sink,
+                                                false );
+                    } else {
+                        if ( this.hashableSinks == null ) {
+                            this.hashableSinks = new ObjectSinkNodeList();
+                        }
+                        this.hashableSinks.add( (ObjectSinkNode) sink );
+                    }
+                    return;
+                }
+
+            }
+        }
+
+        if ( this.otherSinks == null ) {
+            this.otherSinks = new ObjectSinkNodeList();
+        }
+
+        this.otherSinks.add( (ObjectSinkNode) sink );
+    }
+
+    public void removeObjectSink(final ObjectSink sink) {
+        if ( sink instanceof AlphaNode ) {
+            final AlphaNode alphaNode = (AlphaNode) sink;
+            final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();
+
+            if ( fieldConstraint instanceof LiteralConstraint ) {
+                final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
+                final Evaluator evaluator = literalConstraint.getEvaluator();
+                final FieldValue value = literalConstraint.getField();
+
+                if ( evaluator.getOperator() == Operator.EQUAL ) {
+                    final int index = literalConstraint.getFieldExtractor().getIndex();
+                    final FieldIndex fieldIndex = unregisterFieldIndex( index );
+
+                    if ( fieldIndex.isHashed() ) {
+                        this.hashKey.setValue( index,
+                                               value );
+                        this.hashedSinkMap.remove( this.hashKey );
+                        if ( fieldIndex.getCount() <= this.alphaNodeHashingThreshold - 1 ) {
+                            // we have less than three so unhash
+                            unHashSinks( fieldIndex );
+                        }
+                    } else {
+                        this.hashableSinks.remove( (ObjectSinkNode) sink );
+                    }
+
+                    if ( this.hashableSinks != null && this.hashableSinks.isEmpty() ) {
+                        this.hashableSinks = null;
+                    }
+
+                    return;
+                }
+            }
+        }
+
+        this.otherSinks.remove( (ObjectSinkNode) sink );
+
+        if ( this.otherSinks.isEmpty() ) {
+            this.otherSinks = null;
+        }
+    }
+
+    public void hashSinks(final FieldIndex fieldIndex) {
+        final int index = fieldIndex.getIndex();
+
+        final List list = new ArrayList();
+
+        if ( this.hashedSinkMap == null ) {
+            this.hashedSinkMap = new ObjectHashMap();
+        }
+
+        for ( ObjectSinkNode sink = this.hashableSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
+            final AlphaNode alphaNode = (AlphaNode) sink;
+            final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();
+            final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
+            final Evaluator evaluator = literalConstraint.getEvaluator();
+            if ( evaluator.getOperator() == Operator.EQUAL && index == literalConstraint.getFieldExtractor().getIndex() ) {
+                final FieldValue value = literalConstraint.getField();
+                list.add( sink );
+                this.hashedSinkMap.put( new HashKey( index,
+                                                     value ),
+                                        sink );
+            }
+        }
+
+        for ( final java.util.Iterator it = list.iterator(); it.hasNext(); ) {
+            final ObjectSinkNode sink = (ObjectSinkNode) it.next();
+            this.hashableSinks.remove( sink );
+        }
+
+        if ( this.hashableSinks.isEmpty() ) {
+            this.hashableSinks = null;
+        }
+
+        fieldIndex.setHashed( true );
+    }
+
+    public void unHashSinks(final FieldIndex fieldIndex) {
+        final int index = fieldIndex.getIndex();
+
+        final List sinks = new ArrayList();
+
+        //iterate twice as custom iterator is immutable
+        final Iterator mapIt = this.hashedSinkMap.newIterator();
+        for ( ObjectHashMap.ObjectEntry e = (ObjectHashMap.ObjectEntry) mapIt.next(); e != null; ) {
+
+            sinks.add( e.getValue() );
+            e = (ObjectHashMap.ObjectEntry) mapIt.next();
+        }
+
+        for ( final java.util.Iterator iter = sinks.iterator(); iter.hasNext(); ) {
+            final AlphaNode sink = (AlphaNode) iter.next();
+            final AlphaNode alphaNode = sink;
+            final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();
+            final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
+            final Evaluator evaluator = literalConstraint.getEvaluator();
+            if ( evaluator.getOperator() == Operator.EQUAL && index == literalConstraint.getFieldExtractor().getIndex() ) {
+                final FieldValue value = literalConstraint.getField();
+                if ( this.hashableSinks == null ) {
+                    this.hashableSinks = new ObjectSinkNodeList();
+                }
+                this.hashableSinks.add( sink );
+                this.hashedSinkMap.remove( new HashKey( index,
+                                                        value ) );
+            };
+        }
+
+        if ( this.hashedSinkMap.isEmpty() ) {
+            this.hashedSinkMap = null;
+        }
+
+        fieldIndex.setHashed( false );
+    }
+
+    /**
+     * Returns a FieldIndex which Keeps a count on how many times a particular field is used with an equality check in the sinks.
+     * @param index
+     * @param fieldExtractor
+     * @return
+     */
+    private FieldIndex registerFieldIndex(final int index,
+                                          final FieldExtractor fieldExtractor) {
+        FieldIndex fieldIndex = null;
+
+        // is linkedlist null, if so create and add
+        if ( this.hashedFieldIndexes == null ) {
+            this.hashedFieldIndexes = new LinkedList();
+            fieldIndex = new FieldIndex( index,
+                                         fieldExtractor );
+            this.hashedFieldIndexes.add( fieldIndex );
+        }
+
+        // still null, so see if it already exists
+        if ( fieldIndex == null ) {
+            fieldIndex = findFieldIndex( index );
+        }
+
+        // doesn't exist so create it
+        if ( fieldIndex == null ) {
+            fieldIndex = new FieldIndex( index,
+                                         fieldExtractor );
+            this.hashedFieldIndexes.add( fieldIndex );
+        }
+
+        fieldIndex.increaseCounter();
+
+        return fieldIndex;
+    }
+
+    private FieldIndex unregisterFieldIndex(final int index) {
+        final FieldIndex fieldIndex = findFieldIndex( index );
+        fieldIndex.decreaseCounter();
+
+        // if the fieldcount is 0 then remove it from the linkedlist
+        if ( fieldIndex.getCount() == 0 ) {
+            this.hashedFieldIndexes.remove( fieldIndex );
+
+            // if the linkedlist is empty then null it
+            if ( this.hashedFieldIndexes.isEmpty() ) {
+                this.hashedFieldIndexes = null;
+            }
+        }
+
+        return fieldIndex;
+    }
+
+    private FieldIndex findFieldIndex(final int index) {
+        for ( FieldIndex node = (FieldIndex) this.hashedFieldIndexes.getFirst(); node != null; node = (FieldIndex) node.getNext() ) {
+            if ( node.getIndex() == index ) {
+                return node;
+            }
+        }
+
+        return null;
+    }
+
+    public void propagateAssertObject(final InternalFactHandle handle,
+                                      final PropagationContext context,
+                                      final InternalWorkingMemory workingMemory) {
+        final Object object = handle.getObject();
+
+        // Iterates t he FieldIndex collection, which tells you if particularly field is hashed or not
+        // if the field is hashed then it builds the hashkey to return the correct sink for the current objects slot's
+        // value, one object may have multiple fields indexed.
+        if ( this.hashedFieldIndexes != null ) {
+            // Iterate the FieldIndexes to see if any are hashed        
+            for ( FieldIndex fieldIndex = (FieldIndex) this.hashedFieldIndexes.getFirst(); fieldIndex != null; fieldIndex = (FieldIndex) fieldIndex.getNext() ) {
+                if ( !fieldIndex.isHashed() ) {
+                    continue;
+                }
+                // this field is hashed so set the existing hashKey and see if there is a sink for it
+                final int index = fieldIndex.getIndex();
+                final FieldExtractor extractor = fieldIndex.getFieldExtactor();
+                this.hashKey.setValue( index,
+                                       object,
+                                       extractor );
+                final ObjectSink sink = (ObjectSink) this.hashedSinkMap.get( this.hashKey );
+                if ( sink != null ) {
+                    // The sink exists so propagate
+                    sink.assertObject( handle,
+                                       context,
+                                       workingMemory );
+                }
+            }
+        }
+
+        // propagate unhashed
+        if ( this.hashableSinks != null ) {
+            for ( ObjectSinkNode sink = this.hashableSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
+                sink.assertObject( handle,
+                                   context,
+                                   workingMemory );
+            }
+        }
+
+        if ( this.otherSinks != null ) {
+            // propagate others
+            for ( ObjectSinkNode sink = this.otherSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
+                sink.assertObject( handle,
+                                   context,
+                                   workingMemory );
+            }
+        }
+
+    }
+
+    public void propagateRetractObject(final InternalFactHandle handle,
+                                       final PropagationContext context,
+                                       final InternalWorkingMemory workingMemory,
+                                       final boolean useHash) {
+        if ( this.hashedFieldIndexes != null ) {
+            if ( useHash && this.hashedSinkMap != null ) {
+                final Object object = handle.getObject();
+                // Iterate the FieldIndexes to see if any are hashed        
+                for ( FieldIndex fieldIndex = (FieldIndex) this.hashedFieldIndexes.getFirst(); fieldIndex != null; fieldIndex = (FieldIndex) fieldIndex.getNext() ) {
+                    // this field is hashed so set the existing hashKey and see if there is a sink for it
+                    if ( !fieldIndex.isHashed() ) {
+                        continue;
+                    }
+
+                    final int index = fieldIndex.getIndex();
+                    final FieldExtractor extractor = fieldIndex.getFieldExtactor();
+                    this.hashKey.setValue( index,
+                                           object,
+                                           extractor );
+                    final ObjectSink sink = (ObjectSink) this.hashedSinkMap.get( this.hashKey );
+                    if ( sink != null ) {
+                        // The sink exists so propagate
+                        sink.retractObject( handle,
+                                            context,
+                                            workingMemory );
+                    }
+                }
+            } else if ( this.hashedSinkMap != null ) {
+                final Iterator it = this.hashedSinkMap.newIterator();
+                for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
+                    final ObjectSink sink = (ObjectSink) entry.getValue();
+                    sink.retractObject( handle,
+                                        context,
+                                        workingMemory );
+                }
+            }
+        }
+
+        if ( this.hashableSinks != null ) {
+            // we can't retrieve hashed sinks, as the field value might have changed, so we have to iterate and propagate to all hashed sinks
+            for ( ObjectSinkNode sink = this.hashableSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
+                sink.retractObject( handle,
+                                    context,
+                                    workingMemory );
+            }
+        }
+
+        if ( this.otherSinks != null ) {
+            // propagate others
+            for ( ObjectSinkNode sink = this.otherSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
+                sink.retractObject( handle,
+                                    context,
+                                    workingMemory );
+            }
+        }
+    }
+
+    public ObjectSink[] getSinks() {
+        final List list = new ArrayList();
+
+        if ( this.otherSinks != null ) {
+            for ( ObjectSinkNode sink = this.otherSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
+                list.add( sink );
+            }
+        }
+
+        if ( this.hashableSinks != null ) {
+            for ( ObjectSinkNode sink = this.hashableSinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode() ) {
+                list.add( sink );
+            }
+        }
+
+        if ( this.hashedSinkMap != null ) {
+            final Iterator it = this.hashedSinkMap.newIterator();
+            for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
+                final ObjectSink sink = (ObjectSink) entry.getValue();
+                list.add( sink );
+            }
+        }
+
+        return (ObjectSink[]) list.toArray( new ObjectSink[list.size()] );
+    }
+
+    public int size() {
+        int size = 0;
+        size += ((this.otherSinks != null) ? this.otherSinks.size() : 0);
+        size += ((this.hashableSinks != null) ? this.hashableSinks.size() : 0);
+        size += ((this.hashedSinkMap != null) ? this.hashedSinkMap.size() : 0);
+        return size;
+    }
+
+    public static class HashKey
+        implements
+        Serializable {
+        private static final long serialVersionUID = 400L;
+
+        private static final byte OBJECT           = 1;
+        private static final byte LONG             = 2;
+        private static final byte DOUBLE           = 3;
+        private static final byte BOOL             = 4;
+
+        private int               index;
+
+        private byte              type;
+        private Object            ovalue;
+        private long              lvalue;
+        private boolean           bvalue;
+        private double            dvalue;
+        
+        private boolean           isNull;
+
+        private int               hashCode;
+
+        public HashKey() {
+        }
+
+        public HashKey(final int index,
+                       final FieldValue value) {
+            this.setValue( index,
+                           value );
+        }
+
+        public HashKey(final int index,
+                       final Object value,
+                       final Extractor extractor) {
+            this.setValue( index,
+                           value,
+                           extractor );
+        }
+
+        public int getIndex() {
+            return this.index;
+        }
+
+        public void setValue(final int index,
+                             final Object value,
+                             final Extractor extractor) {
+            this.index = index;
+            final ValueType vtype = extractor.getValueType();
+            
+            isNull = extractor.isNullValue(null, value);
+            
+            if ( vtype.isBoolean() ) {
+                this.type = BOOL;
+            	if ( !isNull ) {
+            		this.bvalue = extractor.getBooleanValue( null, value );
+                    this.setHashCode( this.bvalue ? 1231 : 1237 );
+            	} else {
+                    this.setHashCode( 0 );
+            	}
+            } else if ( vtype.isIntegerNumber() ) {
+                this.type = LONG;
+            	if ( !isNull ) {
+	                this.lvalue = extractor.getLongValue( null, value );
+	                this.setHashCode( (int) (this.lvalue ^ (this.lvalue >>> 32)) );
+            	} else {
+            		this.setHashCode( 0 );
+            	}
+            } else if ( vtype.isFloatNumber() ) {
+                this.type = DOUBLE;
+            	if ( !isNull ) {                
+	                this.dvalue = extractor.getDoubleValue( null, value );
+	                final long temp = Double.doubleToLongBits( this.dvalue );
+	                this.setHashCode( (int) (temp ^ (temp >>> 32)) );
+            	} else {
+            		this.setHashCode( 0 );
+            	}
+            } else {
+                this.type = OBJECT;
+            	if ( !isNull ) {
+            		this.ovalue = extractor.getValue( null, value );
+            		this.setHashCode( this.ovalue != null ? this.ovalue.hashCode() : 0 );
+            	} else {
+            		this.setHashCode( 0 );
+            	}                
+            }
+        }
+
+        public void setValue(final int index,
+                             final FieldValue value) {
+            this.index = index;
+            
+            this.isNull = value.isNull();
+            
+            if ( value.isBooleanField() ) {
+                this.type = BOOL;
+                if ( !isNull ) {      
+	                this.bvalue = value.getBooleanValue();
+	                this.setHashCode( this.bvalue ? 1231 : 1237 );
+                } else {
+             		this.setHashCode( 0 );
+                }
+            } else if ( value.isIntegerNumberField() ) {
+                this.type = LONG;
+                if ( !isNull ) {      
+	                this.lvalue = value.getLongValue();
+	                this.setHashCode( (int) (this.lvalue ^ (this.lvalue >>> 32)) );
+                } else {
+             		this.setHashCode( 0 );
+                }
+            } else if ( value.isFloatNumberField() ) {
+                this.type = DOUBLE;
+                if ( !isNull ) {      
+	                this.dvalue = value.getDoubleValue();
+	                final long temp = Double.doubleToLongBits( this.dvalue );
+	                this.setHashCode( (int) (temp ^ (temp >>> 32)) );
+                } else {
+             		this.setHashCode( 0 );
+                }
+            } else {
+                this.type = OBJECT;
+                if ( !isNull ) {      
+	                this.ovalue = value.getValue();
+	                this.setHashCode( this.ovalue != null ? this.ovalue.hashCode() : 0 );
+                } else {
+             		this.setHashCode( 0 );
+                }
+            }
+        }
+
+        private void setHashCode(final int hashSeed) {
+            final int PRIME = 31;
+            int result = 1;
+            result = PRIME * result + hashSeed;
+            result = PRIME * result + this.index;
+            this.hashCode = result;
+        }
+
+        public boolean getBooleanValue() {
+            switch ( this.type ) {
+                case BOOL :
+                    return this.bvalue;
+                case OBJECT :
+                    if ( this.ovalue == null ) {
+                        return false;
+                    } else if ( this.ovalue instanceof Boolean ) {
+                        return ((Boolean) this.ovalue).booleanValue();
+                    } else if ( this.ovalue instanceof String ) {
+                        return Boolean.valueOf( (String) this.ovalue ).booleanValue();
+                    } else {
+                        throw new ClassCastException( "Can't convert " + this.ovalue.getClass() + " to a boolean value." );
+                    }
+                case LONG :
+                    throw new ClassCastException( "Can't convert long to a boolean value." );
+                case DOUBLE :
+                    throw new ClassCastException( "Can't convert double to a boolean value." );
+
+            }
+            return false;
+        }
+
+        public long getLongValue() {
+            switch ( this.type ) {
+                case BOOL :
+                    return this.bvalue ? 1 : 0;
+                case OBJECT :
+                    if ( this.ovalue == null ) {
+                        return 0;
+                    } else if ( this.ovalue instanceof Number ) {
+                        return ((Number) this.ovalue).longValue();
+                    } else if ( this.ovalue instanceof String ) {
+                        return Long.parseLong( (String) this.ovalue );
+                    } else {
+                        throw new ClassCastException( "Can't convert " + this.ovalue.getClass() + " to a long value." );
+                    }
+                case LONG :
+                    return this.lvalue;
+                case DOUBLE :
+                    return (long) this.dvalue;
+
+            }
+            return 0;
+        }
+
+        public double getDoubleValue() {
+            switch ( this.type ) {
+                case BOOL :
+                    return this.bvalue ? 1 : 0;
+                case OBJECT :
+                    if ( this.ovalue == null ) {
+                        return 0;
+                    } else if ( this.ovalue instanceof Number ) {
+                        return ((Number) this.ovalue).doubleValue();
+                    } else if ( this.ovalue instanceof String ) {
+                        return Double.parseDouble( (String) this.ovalue );
+                    } else {
+                        throw new ClassCastException( "Can't convert " + this.ovalue.getClass() + " to a double value." );
+                    }
+                case LONG :
+                    return this.lvalue;
+                case DOUBLE :
+                    return this.dvalue;
+            }
+            return 0;
+        }
+
+        public Object getObjectValue() {
+            switch ( this.type ) {
+                case BOOL :
+                    return this.bvalue ? Boolean.TRUE : Boolean.FALSE;
+                case OBJECT :
+                    return this.ovalue;
+                case LONG :
+                    return new Long( this.lvalue );
+                case DOUBLE :
+                    return new Double( this.dvalue );
+            }
+            return null;
+        }
+
+        public int hashCode() {
+            return this.hashCode;
+        }
+
+        public boolean equals(final Object object) {
+            final HashKey other = (HashKey) object;
+            
+            if ( this.isNull ) {
+            	return ( other.isNull );
+            }
+
+            switch ( this.type ) {
+                case BOOL :
+                    return (this.index == other.index) && (this.bvalue == other.getBooleanValue());
+                case LONG :
+                    return (this.index == other.index) && (this.lvalue == other.getLongValue());
+                case DOUBLE :
+                    return (this.index == other.index) && (this.dvalue == other.getDoubleValue());
+                case OBJECT :
+                    final Object otherValue = other.getObjectValue();
+                    if ( (this.ovalue != null) && (this.ovalue instanceof Number) && (otherValue instanceof Number) ) {
+                        return (this.index == other.index) && (((Number) this.ovalue).doubleValue() == ((Number) otherValue).doubleValue());
+                    }
+                    return (this.index == other.index) && ( this.ovalue == null ? otherValue == null : this.ovalue.equals( otherValue ));
+            }
+            return false;
+        }
+
+    }
+
+    public static class FieldIndex
+        implements
+        LinkedListNode {
+        private static final long serialVersionUID = 400L;
+        private final int         index;
+        private FieldExtractor    fieldExtactor;
+
+        private int               count;
+
+        private boolean           hashed;
+
+        private LinkedListNode    previous;
+        private LinkedListNode    next;
+
+        public FieldIndex(final int index,
+                          final FieldExtractor fieldExtractor) {
+            this.index = index;
+            this.fieldExtactor = fieldExtractor;
+        }
+
+        public FieldExtractor getFieldExtractor() {
+            return this.fieldExtactor;
+        }
+
+        public int getIndex() {
+            return this.index;
+        }
+
+        public int getCount() {
+            return this.count;
+        }
+
+        public FieldExtractor getFieldExtactor() {
+            return this.fieldExtactor;
+        }
+
+        public boolean isHashed() {
+            return this.hashed;
+        }
+
+        public void setHashed(final boolean hashed) {
+            this.hashed = hashed;
+        }
+
+        public void increaseCounter() {
+            this.count++;
+        }
+
+        public void decreaseCounter() {
+            this.count--;
+        }
+
+        public LinkedListNode getNext() {
+            return this.next;
+        }
+
+        public LinkedListNode getPrevious() {
+            return this.previous;
+        }
+
+        public void setNext(final LinkedListNode next) {
+            this.next = next;
+
+        }
+
+        public void setPrevious(final LinkedListNode previous) {
+            this.previous = previous;
+        }
+    }
+}




More information about the jboss-svn-commits mailing list