package org.drools.integrationtests;

import static org.junit.Assert.*;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.drools.Address;
import org.drools.InitialFact;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.Person;
import org.drools.base.ClassObjectType;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.common.InternalFactHandle;
import org.drools.common.InternalRuleBase;
import org.drools.definition.KnowledgePackage;
import org.drools.definition.process.Process;
import org.drools.definition.rule.Rule;
import org.drools.impl.KnowledgeBaseImpl;
import org.drools.impl.StatefulKnowledgeSessionImpl;
import org.drools.io.ResourceFactory;
import org.drools.io.impl.ByteArrayResource;
import org.drools.reteoo.AccumulateNode;
import org.drools.reteoo.BetaMemory;
import org.drools.reteoo.BetaNode;
import org.drools.reteoo.ExistsNode;
import org.drools.reteoo.FromNode;
import org.drools.reteoo.FromNode.FromMemory;
import org.drools.reteoo.JoinNode;
import org.drools.reteoo.LeftInputAdapterNode;
import org.drools.reteoo.LeftTuple;
import org.drools.reteoo.NotNode;
import org.drools.reteoo.ObjectTypeNode;
import org.drools.reteoo.QueryElementNode;
import org.drools.reteoo.QueryRiaFixerNode;
import org.drools.reteoo.ReteooWorkingMemory;
import org.drools.reteoo.RightInputAdapterNode;
import org.drools.reteoo.AccumulateNode.AccumulateMemory;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.rule.FactHandle;
import org.drools.runtime.rule.LiveQuery;
import org.drools.runtime.rule.QueryResults;
import org.drools.runtime.rule.QueryResultsRow;
import org.drools.runtime.rule.Row;
import org.drools.runtime.rule.Variable;
import org.drools.runtime.rule.ViewChangedEventListener;
import org.drools.runtime.rule.impl.OpenQueryViewChangedEventListenerAdapter;
import org.junit.Ignore;
import org.junit.Test;

import static org.drools.integrationtests.SerializationHelper.getSerialisedStatefulKnowledgeSession;
import static org.drools.runtime.rule.Variable.v;

public class BackwardChainingTest {
    @Test
    public void testQueryPositional() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +
                     "import org.drools.Person \n" +
                     "global java.util.List list\n" +
                     "query peeps( String $name, String $likes, int $age ) \n" +
                     "    Person( $name := name, $likes := likes, $age := age; ) \n" +
                     "end\n";

        str += "rule x1\n" +
               "when\n" +
               "    String( this == \"go1\" )\n" +
               //         output, output ,output
               "    ?peeps($name1, $likes1, $age1; )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        str += "rule x2\n" +
               "when\n" +
               "    String( this == \"go2\" )\n" +
               //         output, input      ,output
               "    ?peeps($name1, \"stilton\", $age1; )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        str += "rule x3\n" +
               "when\n" +
               "    String( this == \"go3\" )\n" +
               "    $name1 : String() from \"darth\";\n " +
               //         input , input      ,output        
               "    ?peeps($name1, \"stilton\", $age1; )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        str += "rule x4\n" +
               "when\n" +
               "    String( this == \"go4\" )\n" +
               "    $name1 : String() from \"darth\"\n " +
               "    $age1 : Integer() from 200;\n " +
               //         input , input      ,input        
               "    ?peeps($name1, \"stilton\", $age1; )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        Person p1 = new Person( "darth",
                                "stilton",
                                100 );
        Person p2 = new Person( "darth",
                                "stilton",
                                200 );
        Person p3 = new Person( "yoda",
                                "stilton",
                                300 );
        Person p4 = new Person( "luke",
                                "brie",
                                300 );
        Person p5 = new Person( "bobba",
                                "cheddar",
                                300 );

        ksession.insert( p1 );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.insert( p2 );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.insert( p3 );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.insert( p4 );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.insert( p5 );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );

        ksession.insert( "go1" );

        // Make sure we can serialise query state
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );

        ksession.fireAllRules();
        assertEquals( 5,
                      list.size() );
        assertTrue( list.contains( "darth : 100" ) );
        assertTrue( list.contains( "darth : 200" ) );
        assertTrue( list.contains( "yoda : 300" ) );
        assertTrue( list.contains( "luke : 300" ) );
        assertTrue( list.contains( "bobba : 300" ) );

        list.clear();
        ksession.insert( "go2" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 3,
                      list.size() );
        assertTrue( list.contains( "darth : 100" ) );
        assertTrue( list.contains( "darth : 200" ) );
        assertTrue( list.contains( "yoda : 300" ) );

        list.clear();
        ksession.insert( "go3" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 2,
                      list.size() );
        assertTrue( list.contains( "darth : 100" ) );
        assertTrue( list.contains( "darth : 200" ) );

        list.clear();
        ksession.insert( "go4" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 1,
                      list.size() );
        assertTrue( list.contains( "darth : 200" ) );
    }

    @Test
    public void testQueryNamed() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +
                     "import org.drools.Person \n" +
                     "global java.util.List list\n" +
                     "query peeps( String $name, String $likes, int $age ) \n" +
                     "    Person( $name := name, $likes := likes, $age := age ) \n" +
                     "end\n";

        str += "rule x1\n" +
               "when\n" +
               "    String( this == \"go1\" )\n" +
               //         output        ,output          ,output
               "    ?peeps($name1 : $name, $likes1 : $likes, $age1 : $age )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        str += "rule x2\n" +
               "when\n" +
               "    String( this == \"go2\" )\n" +
               //         output        ,output                ,output
               "    ?peeps($name1 : $name, $likes : \"stilton\", $age1 : $age )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        str += "rule x3\n" +
               "when\n" +
               "    String( this == \"go3\" )\n" +
               "    $name1 : String() from \"darth\";\n " +
               //         input         ,input                ,output
               "    ?peeps($name1 : $name, $likes : \"stilton\", $age1 : $age )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        str += "rule x4\n" +
               "when\n" +
               "    String( this == \"go4\" )\n" +
               "    $name1 : String() from \"darth\";\n " +
               "    $age1 : Integer() from 200;\n " +
               //         input         ,input                ,input
               "    ?peeps($name1 : $name, $likes : \"stilton\", $age1 : $age )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        Person p1 = new Person( "darth",
                                "stilton",
                                100 );
        Person p2 = new Person( "darth",
                                "stilton",
                                200 );
        Person p3 = new Person( "yoda",
                                "stilton",
                                300 );
        Person p4 = new Person( "luke",
                                "brie",
                                300 );
        Person p5 = new Person( "bobba",
                                "cheddar",
                                300 );

        ksession.insert( p1 );
        ksession.insert( p2 );
        ksession.insert( p3 );
        ksession.insert( p4 );
        ksession.insert( p5 );

        ksession.insert( "go1" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 5,
                      list.size() );
        assertTrue( list.contains( "darth : 100" ) );
        assertTrue( list.contains( "darth : 200" ) );
        assertTrue( list.contains( "yoda : 300" ) );
        assertTrue( list.contains( "luke : 300" ) );
        assertTrue( list.contains( "bobba : 300" ) );

        list.clear();
        ksession.insert( "go2" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 3,
                      list.size() );
        assertTrue( list.contains( "darth : 100" ) );
        assertTrue( list.contains( "darth : 200" ) );
        assertTrue( list.contains( "yoda : 300" ) );

        list.clear();
        ksession.insert( "go3" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 2,
                      list.size() );
        assertTrue( list.contains( "darth : 100" ) );
        assertTrue( list.contains( "darth : 200" ) );

        list.clear();
        ksession.insert( "go4" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 1,
                      list.size() );
        assertTrue( list.contains( "darth : 200" ) );
    }

    @Test
    public void testQueryMixed() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +
                     "import org.drools.Person \n" +
                     "global java.util.List list\n" +
                     "query peeps( String $name, String $likes, int $age ) \n" +
                     "    Person( $name := name, $likes := likes, $age := age; ) \n" +
                     "end\n";

        str += "rule x1\n" +
               "when\n" +
               "    String( this == \"go1\" )\n" +
               //         output        ,output          ,output
               "    ?peeps($name1; $likes1 : $likes, $age1 : $age )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        str += "rule x2\n" +
               "when\n" +
               "    String( this == \"go2\" )\n" +
               //         output        ,output                ,output
               "    ?peeps($name1, \"stilton\"; $age1 : $age )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        str += "rule x3\n" +
               "when\n" +
               "    String( this == \"go3\" )\n" +
               "    $name1 : String() from \"darth\";\n " +
               //         input         ,input                ,output
               "    ?peeps($name1, \"stilton\"; $age1 : $age )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        str += "rule x4\n" +
               "when\n" +
               "    String( this == \"go4\" )\n" +
               "    $name1 : String() from \"darth\"\n " +
               "    $age1 : Integer() from 200;\n " +
               //         input         ,input                ,input
               "    ?peeps($name1; $likes : \"stilton\", $age1 : $age )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        Person p1 = new Person( "darth",
                                "stilton",
                                100 );
        Person p2 = new Person( "darth",
                                "stilton",
                                200 );
        Person p3 = new Person( "yoda",
                                "stilton",
                                300 );
        Person p4 = new Person( "luke",
                                "brie",
                                300 );
        Person p5 = new Person( "bobba",
                                "cheddar",
                                300 );

        ksession.insert( p1 );
        ksession.insert( p2 );
        ksession.insert( p3 );
        ksession.insert( p4 );
        ksession.insert( p5 );

        ksession.insert( "go1" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 5,
                      list.size() );
        assertTrue( list.contains( "darth : 100" ) );
        assertTrue( list.contains( "darth : 200" ) );
        assertTrue( list.contains( "yoda : 300" ) );
        assertTrue( list.contains( "luke : 300" ) );
        assertTrue( list.contains( "bobba : 300" ) );

        list.clear();
        ksession.insert( "go2" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 3,
                      list.size() );
        assertTrue( list.contains( "darth : 100" ) );
        assertTrue( list.contains( "darth : 200" ) );
        assertTrue( list.contains( "yoda : 300" ) );

        list.clear();
        ksession.insert( "go3" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 2,
                      list.size() );
        assertTrue( list.contains( "darth : 100" ) );
        assertTrue( list.contains( "darth : 200" ) );

        list.clear();
        ksession.insert( "go4" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 1,
                      list.size() );
        assertTrue( list.contains( "darth : 200" ) );
    }

    @Test
    public void testQueryPatternBindingAsResult() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +
                     "import org.drools.Person \n" +
                     "global java.util.List list\n" +
                     "query peeps( Person $p, String $name, String $likes, int $age ) \n" +
                     "    $p := Person( $name := name, $likes := likes, $age := age; ) \n" +
                     "end\n";

        str += "rule x1\n" +
               "when\n" +
               "    String( this == \"go1\" )\n" +
               //     output, output, output          ,output
               "    ?peeps($p, $name1; $likes1 : $likes, $age1 : $age )\n" +
               "then\n" +
               "   list.add( $p );\n" +
               "   list.add( $name1 + \" : \" + $age1 );\n" +
               "end \n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        Person p1 = new Person( "darth",
                                "stilton",
                                100 );
        Person p2 = new Person( "darth",
                                "stilton",
                                200 );
        Person p3 = new Person( "yoda",
                                "stilton",
                                300 );
        Person p4 = new Person( "luke",
                                "brie",
                                300 );
        Person p5 = new Person( "bobba",
                                "cheddar",
                                300 );

        ksession.insert( p1 );
        ksession.insert( p2 );
        ksession.insert( p3 );
        ksession.insert( p4 );
        ksession.insert( p5 );

        ksession.insert( "go1" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 10,
                      list.size() );
        assertEquals( p1,
                      list.get( list.indexOf( "darth : 100" ) - 1 ) );
        assertTrue( list.contains( "darth : 100" ) );
        assertEquals( p2,
                      list.get( list.indexOf( "darth : 200" ) - 1 ) );
        assertTrue( list.contains( "darth : 200" ) );
        assertEquals( p3,
                      list.get( list.indexOf( "yoda : 300" ) - 1 ) );
        assertTrue( list.contains( "yoda : 300" ) );
        assertEquals( p4,
                      list.get( list.indexOf( "luke : 300" ) - 1 ) );
        assertTrue( list.contains( "luke : 300" ) );
        assertEquals( p5,
                      list.get( list.indexOf( "bobba : 300" ) - 1 ) );
        assertTrue( list.contains( "bobba : 300" ) );

    }

    @Test
    public void testQueriesWithNestedAcecssorsAllOutputs() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +
                     "import org.drools.Person \n" +
                     "global java.util.List list\n" +
                     "query peeps( String $name, String $likes, String $street ) \n" +
                     "   Person( $name := name, $likes := likes, $street := address.street ) \n" +
                     "end\n";

        str += "rule x1\n" +
               "when\n" +
               "    String( this == \"go1\" )\n" +
               //         output, output,         ,output
               "    ?peeps($name1; $likes1 : $likes, $street1 : $street )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $likes1 + \" : \" +  $street1 );\n" +
               "end \n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        Person p1 = new Person( "darth",
                                "stilton",
                                100 );
        p1.setAddress( new Address( "s1" ) );

        Person p2 = new Person( "yoda",
                                "stilton",
                                300 );
        p2.setAddress( new Address( "s2" ) );

        ksession.insert( p1 );
        ksession.insert( p2 );

        ksession.insert( "go1" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 2,
                      list.size() );
        assertTrue( list.contains( "darth : stilton : s1" ) );
        assertTrue( list.contains( "yoda : stilton : s2" ) );
    }

    @Test
    public void testQueriesWithNestedAcecssorsMixedArgs() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +
                     "import org.drools.Person \n" +
                     "global java.util.List list\n" +
                     "query peeps( String $name, String $likes, String $street ) \n" +
                     "   Person( $name := name, $likes := likes, $street := address.street ) \n" +
                     "end\n";

        str += "rule x1\n" +
               "when\n" +
               "    $s : String()\n" +
               //         output, output,         ,input
               "    ?peeps($name1; $likes1 : $likes, $street : $s )\n" +
               "then\n" +
               "   list.add( $name1 + \" : \" + $likes1 + \" : \" +  $s );\n" +
               "end \n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        Person p1 = new Person( "darth",
                                "stilton",
                                100 );
        p1.setAddress( new Address( "s1" ) );

        Person p2 = new Person( "yoda",
                                "stilton",
                                300 );
        p2.setAddress( new Address( "s2" ) );

        ksession.insert( p1 );
        ksession.insert( p2 );

        ksession.insert( "s1" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 1,
                      list.size() );
        assertTrue( list.contains( "darth : stilton : s1" ) );

        list.clear();
        ksession.insert( "s2" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 1,
                      list.size() );
        assertTrue( list.contains( "yoda : stilton : s2" ) );
    }

    @Test
    public void testQueryWithDynamicData() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +
                     "import org.drools.Person \n" +
                     "global java.util.List list\n" +
                     "query peeps( Person $p, String $name, String $likes, int $age ) \n" +
                     "    $p := Person( ) from new Person( $name, $likes, $age ) \n" +
                     "end\n";

        str += "rule x1\n" +
               "when\n" +
               "    $n1 : String( )\n" +
               //     output, input     ,input                 ,input
               "    ?peeps($p; $name : $n1, $likes : \"stilton\", $age : 100 )\n" +
               "then\n" +
               "   list.add( $p );\n" +
               "end \n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        Person p1 = new Person( "darth",
                                "stilton",
                                100 );

        Person p2 = new Person( "yoda",
                                "stilton",
                                100 );

        ksession.insert( "darth" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 1,
                      list.size() );
        assertEquals( p1,
                      list.get( 0 ) );

        list.clear();
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.insert( "yoda" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 1,
                      list.size() );
        assertEquals( p2,
                      list.get( 0 ) );
    }

    @Test
    public void testQueryWithDyanmicInsert() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +
                     "import org.drools.Person \n" +
                     "global java.util.List list\n" +
                     "query peeps( Person $p, String $name, String $likes, int $age ) \n" +
                     "    $p := Person( ) from new Person( $name, $likes, $age ) \n" +
                     "end\n";

        str += "rule x1\n" +
               "when\n" +
               "    $n1 : String( )\n" +
               "    not Person( name == 'darth' )\n " +
               //     output, input     ,input                 ,input
               "    ?peeps($p; $name : $n1, $likes : \"stilton\", $age : 100 )\n" +
               "then\n" +
               "   insert( $p );\n" +
               "end \n";

        str += "rule x2\n" +
               "when\n" +
               "    $p : Person( )\n" +
               "then\n" +
               "   list.add( $p );\n" +
               "end \n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        Person p1 = new Person( "darth",
                                "stilton",
                                100 );

        ksession.insert( "darth" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        ksession.insert( "yoda" ); // darth exists, so yoda won't get created 
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();
        assertEquals( 1,
                      list.size() );
        assertEquals( p1,
                      list.get( 0 ) );
    }

    @Test
    public void testQueryWithOr() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +

                     "import java.util.List\n" +
                     "import java.util.ArrayList\n" +

                     "global List list\n" +

                     "dialect \"mvel\"\n" +
                     "\n" +

                     "import org.drools.integrationtests.BackwardChainingTest.Q\n" +
                     "import org.drools.integrationtests.BackwardChainingTest.R\n" +
                     "import org.drools.integrationtests.BackwardChainingTest.S\n" +

                     "\n" +
                     "query q(int x)\n" +
                     "    Q( x := value )\n" +
                     "end\n" +
                     "\n" +
                     "query r(int x)\n" +
                     "    R( x := value )\n" +
                     "end\n" +
                     "\n" +
                     "query s(int x)\n" +
                     "    S( x := value )    \n" +
                     "end\n" +
                     "\n" +

                     "query p(int x)\n" +
                     "    (?q(x;) and ?r(x;) ) \n" +
                     "    or\n" +
                     "    ?s(x;)\n" +
                     "end\n" +

                     "rule init when\n" +
                     "then\n" +
                     " insert( new Q(1) );\n " +
                     " insert( new Q(5) );\n " +
                     " insert( new Q(6) );\n " +
                     " insert( new R(1) );\n " +
                     " insert( new R(4) );\n " +
                     " insert( new R(6) );\n " +
                     " insert( new R(2) );\n " +
                     " insert( new S(2) );\n " +
                     " insert( new S(3) );\n " +
                     " insert( new S(6) );\n " +
                     "end\n" +
                     "";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<Integer> list = new ArrayList<Integer>();
        ksession.setGlobal( "list",
                            list );

        ksession.fireAllRules();

        QueryResults results = null;

        list.clear();
        results = ksession.getQueryResults( "p",
                                            new Integer[]{0} );
        for ( QueryResultsRow result : results ) {
            list.add( (Integer) result.get( "x" ) );
        }
        assertEquals( 0,
                      list.size() );

        list.clear();
        results = ksession.getQueryResults( "p",
                                            new Integer[]{1} );
        for ( QueryResultsRow result : results ) {
            list.add( (Integer) result.get( "x" ) );
        }

        assertEquals( 1,
                      list.size() );
        assertEquals( 1,
                      list.get( 0 ).intValue() );

        System.out.println();

        list.clear();
        results = ksession.getQueryResults( "p",
                                            new Integer[]{2} );
        for ( QueryResultsRow result : results ) {
            list.add( (Integer) result.get( "x" ) );
        }
        assertEquals( 1,
                      list.size() );
        assertEquals( 2,
                      list.get( 0 ).intValue() );

        list.clear();
        results = ksession.getQueryResults( "p",
                                            new Integer[]{3} );
        for ( QueryResultsRow result : results ) {
            list.add( (Integer) result.get( "x" ) );
        }
        assertEquals( 1,
                      list.size() );
        assertEquals( 3,
                      list.get( 0 ).intValue() );

        list.clear();
        results = ksession.getQueryResults( "p",
                                            new Integer[]{4} );
        for ( QueryResultsRow result : results ) {
            list.add( (Integer) result.get( "x" ) );
        }
        assertEquals( 0,
                      list.size() );

        list.clear();
        results = ksession.getQueryResults( "p",
                                            new Integer[]{5} );
        for ( QueryResultsRow result : results ) {
            list.add( (Integer) result.get( "x" ) );
        }
        assertEquals( 0,
                      list.size() );

        list.clear();
        results = ksession.getQueryResults( "p",
                                            new Integer[]{6} );
        for ( QueryResultsRow result : results ) {
            list.add( (Integer) result.get( "x" ) );
        }
        assertEquals( 2,
                      list.size() );
        assertEquals( 6,
                      list.get( 0 ).intValue() );
        assertEquals( 6,
                      list.get( 1 ).intValue() );
    }

    @Test
    public void testGeneology() throws Exception {
        // from http://kti.mff.cuni.cz/~bartak/prolog/genealogy.html

        String str = "" +
                     "package org.drools.test2  \n" +
                     "global java.util.List list\n" +
                     "dialect \"mvel\"\n" +

                     "query man( String name ) \n" +
                     "   org.drools.integrationtests.BackwardChainingTest.Man( name := name ) \n" +
                     "end\n" +

                     "query woman( String name ) \n" +
                     "   org.drools.integrationtests.BackwardChainingTest.Woman( name := name ) \n" +
                     "end\n" +

                     "query parent( String parent, String child ) \n" +
                     "   org.drools.integrationtests.BackwardChainingTest.Parent( parent := parent, child := child ) \n" +
                     "end\n" +

                     "query father( String father, String child ) \n" +
                     "   ?man( father; ) \n" +
                     "   ?parent( father, child; ) \n" +
                     "end\n" +

                     "query mother( String mother, String child ) \n" +
                     "   ?woman( mother; ) \n" +
                     "   ?parent( mother, child; ) \n" +
                     "end\n" +

                     "query son( String son, String parent ) \n" +
                     "   ?man( son; ) \n" +
                     "   ?parent( parent, son; ) \n" +
                     "end\n" +

                     "query daughter( String daughter, String parent ) \n" +
                     "   ?woman( daughter; ) \n" +
                     "   ?parent( parent, daughter; ) \n" +
                     "end\n" +

                     "query siblings( String c1, String c2 ) \n" +
                     "   ?parent( $p, c1; ) \n" +
                     "   ?parent( $p, c2; ) \n" +
                     "   eval( !c1.equals( c2 ) )\n" +
                     "end\n" +

                     "query fullSiblings( String c1, String c2 )\n" +
                     "   ?parent( $p1, c1; ) ?parent( $p1, c2; )\n" +
                     "   ?parent( $p2, c1; ) ?parent( $p2, c2; )\n" +
                     "   eval( !c1.equals( c2 ) && !$p1.equals( $p2 )  )\n" +
                     "end\n" +

                     "query fullSiblings2( String c1, String c2 )\n" +
                     "   ?father( $p1, c1; ) ?father( $p1, c2; )\n" +
                     "   ?mother( $p2, c1; ) ?mother( $p2, c2; )\n" +
                     "   eval( !c1.equals( c2 ) )\n" +
                     "end\n" +

                     "query uncle( String uncle, String n )\n" +
                     "   ?man( uncle; ) ?siblings( uncle, parent; )\n" +
                     "   ?parent( parent, n; )\n " +
                     "end\n" +

                     "query aunt( String aunt, String n )\n" +
                     "   ?woman( aunt; ) ?siblings( aunt, parent; )\n" +
                     "   ?parent( parent, n; )\n " +
                     "end\n" +

                     "query grantParents( String gp, String gc )\n" +
                     "   ?parent( gp, p; ) ?parent( p, gc; )\n" +
                     "end\n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        // grand parents
        ksession.insert( new Man( "john" ) );
        ksession.insert( new Woman( "janet" ) );

        // parent
        ksession.insert( new Man( "adam" ) );
        ksession.insert( new Parent( "john",
                                     "adam" ) );
        ksession.insert( new Parent( "janet",
                                     "adam" ) );

        ksession.insert( new Man( "stan" ) );
        ksession.insert( new Parent( "john",
                                     "stan" ) );
        ksession.insert( new Parent( "janet",
                                     "stan" ) );

        //        // grand parents
        ksession.insert( new Man( "carl" ) );
        ksession.insert( new Woman( "tina" ) );
        // 
        //        // parent         
        ksession.insert( new Woman( "eve" ) );
        ksession.insert( new Parent( "carl",
                                     "eve" ) );
        ksession.insert( new Parent( "tina",
                                     "eve" ) );
        //
        //        // parent         
        ksession.insert( new Woman( "mary" ) );
        ksession.insert( new Parent( "carl",
                                     "mary" ) );
        ksession.insert( new Parent( "tina",
                                     "mary" ) );

        ksession.insert( new Man( "peter" ) );
        ksession.insert( new Parent( "adam",
                                     "peter" ) );
        ksession.insert( new Parent( "eve",
                                     "peter" ) );

        ksession.insert( new Man( "paul" ) );
        ksession.insert( new Parent( "adam",
                                     "paul" ) );
        ksession.insert( new Parent( "mary",
                                     "paul" ) );

        ksession.insert( new Woman( "jill" ) );
        ksession.insert( new Parent( "adam",
                                     "jill" ) );
        ksession.insert( new Parent( "eve",
                                     "jill" ) );

        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );

        QueryResults results = null;

        //System.out.println("woman");         
        list.clear();
        results = ksession.getQueryResults( "woman",
                                            new Object[]{v} );
        for ( QueryResultsRow result : results ) {
            list.add( (String) result.get( "name" ) );
        }
        assertEquals( 5,
                      list.size() );
        assertContains( new String[]{"janet", "mary", "tina", "eve", "jill"},
                        list );

        list.clear();
        //System.out.println("\nman");        
        results = ksession.getQueryResults( "man",
                                            new Object[]{v} );
        for ( QueryResultsRow result : results ) {
            list.add( (String) result.get( "name" ) );
        }
        assertEquals( 6,
                      list.size() );
        assertContains( new String[]{"stan", "john", "peter", "carl", "adam", "paul"},
                        list );

        list.clear();
        //System.out.println("\nfather");
        results = ksession.getQueryResults( "father",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "father" ) + ", " + result.get( "child" ) );
        }
        assertEquals( 7,
                      list.size() );
        assertContains( new String[]{"john, adam", "john, stan",
                                "carl, eve", "carl, mary",
                                "adam, peter", "adam, paul",
                                "adam, jill"},
                        list );

        list.clear();
        //System.out.println("\nmother");
        results = ksession.getQueryResults( "mother",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "mother" ) + ", " + result.get( "child" ) );
        }
        assertEquals( 7,
                      list.size() );
        assertContains( new String[]{"janet, adam", "janet, stan",
                                "mary, paul", "tina, eve",
                                "tina, mary", "eve, peter",
                                "eve, jill"},
                        list );

        list.clear();
        //System.out.println("\nson");
        results = ksession.getQueryResults( "son",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "son" ) + ", " + result.get( "parent" ) );
        }
        assertEquals( 8,
                      list.size() );
        assertContains( new String[]{"stan, john", "stan, janet",
                                "peter, adam", "peter, eve",
                                "adam, john", "adam, janet",
                                "paul, mary", "paul, adam"},
                        list );

        list.clear();
        //System.out.println("\ndaughter");
        results = ksession.getQueryResults( "daughter",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "daughter" ) + ", " + result.get( "parent" ) );
        }
        assertEquals( 6,
                      list.size() );
        assertContains( new String[]{"mary, carl", "mary, tina",
                                "eve, carl", "eve, tina",
                                "jill, adam", "jill, eve"},
                        list );

        list.clear();
        //System.out.println("\nsiblings");
        results = ksession.getQueryResults( "siblings",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "c1" ) + ", " + result.get( "c2" ) );
        }
        assertEquals( 16,
                      list.size() );
        assertContains( new String[]{"eve, mary", "mary, eve",
                                "adam, stan", "stan, adam",
                                "adam, stan", "stan, adam",
                                "peter, paul", "peter, jill",
                                "paul, peter", "paul, jill",
                                "jill, peter", "jill, paul",
                                "peter, jill", "jill, peter",
                                "eve, mary", "mary, eve"},
                        list );

        list.clear();
        //System.out.println("\nfullSiblings");
        results = ksession.getQueryResults( "fullSiblings",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "c1" ) + ", " + result.get( "c2" ) );
        }
        assertEquals( 12,
                      list.size() );
        assertContains( new String[]{"eve, mary", "mary, eve",
                                "adam, stan", "stan, adam",
                                "adam, stan", "stan, adam",
                                "peter, jill", "jill, peter",
                                "peter, jill", "jill, peter",
                                "eve, mary", "mary, eve"},
                        list );

        list.clear();
        //System.out.println("\nfullSiblings2");
        results = ksession.getQueryResults( "fullSiblings",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "c1" ) + ", " + result.get( "c2" ) );
        }
        assertEquals( 12,
                      list.size() );
        assertContains( new String[]{"eve, mary", "mary, eve",
                                "adam, stan", "stan, adam",
                                "adam, stan", "stan, adam",
                                "peter, jill", "jill, peter",
                                "peter, jill", "jill, peter",
                                "eve, mary", "mary, eve"},
                        list );

        list.clear();
        //System.out.println("\nuncle");
        results = ksession.getQueryResults( "uncle",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "uncle" ) + ", " + result.get( "n" ) );
        }
        assertEquals( 6,
                      list.size() );
        assertContains( new String[]{"stan, peter",
                                "stan, paul",
                                "stan, jill",
                                "stan, peter",
                                "stan, paul",
                                "stan, jill"},
                        list );

        list.clear();
        //System.out.println("\naunt");
        results = ksession.getQueryResults( "aunt",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "aunt" ) + ", " + result.get( "n" ) );
        }
        assertEquals( 6,
                      list.size() );
        assertContains( new String[]{"mary, peter",
                                "mary, jill",
                                "mary, peter",
                                "mary, jill",
                                "eve, paul",
                                "eve, paul"},
                        list );

        list.clear();
        //System.out.println("\ngrantParents");
        results = ksession.getQueryResults( "grantParents",
                                            new Object[]{v, v} );
        for ( QueryResultsRow result : results ) {
            list.add( result.get( "gp" ) + ", " + result.get( "gc" ) );
        }
        assertEquals( 12,
                      list.size() );
        assertContains( new String[]{"carl, peter",
                                "carl, jill",
                                "carl, paul",
                                "john, peter",
                                "john, paul",
                                "john, jill",
                                "janet, peter",
                                "janet, paul",
                                "janet, jill",
                                "tina, peter",
                                "tina, jill",
                                "tina, paul",},
                        list );
    }

    @Test
    public void testNaniSearchs() throws Exception {
        // http://www.amzi.com/AdventureInProlog/advtop.php

        String str = "" +
                     "package org.drools.test  \n" +

                     "import java.util.List\n" +
                     "import java.util.ArrayList\n" +

                     "import java.util.Map\n" +
                     "import java.util.HashMap\n" +

                     "global List list\n" +

                     "dialect \"mvel\"\n" +

                     "declare Room" +
                     "    name : String\n" +
                     "end\n" +
                     "\n" +
                     "declare Location\n" +
                     "    thing : String \n" +
                     "    location : String \n" +
                     "end" +
                     "\n" +
                     "declare Door\n" +
                     "   fromLocation : String\n" +
                     "   toLocation : String\n" +
                     "end" +
                     "\n" +
                     "declare Edible\n" +
                     "   thing : String\n" +
                     "end" +
                     "\n" +
                     "declare TastesYucky\n" +
                     "   thing : String\n" +
                     "end" +
                     "\n" +
                     "declare TastesYucky\n" +
                     "   thing : String\n" +
                     "end\n" +
                     "\n" +
                     "declare Here\n" +
                     "   place : String \n" +
                     "end\n" +
                     "\n" +

                     "query whereFood( String x, String y ) \n" +
                     "    ( Location(x, y;) and\n" +
                     "      Edible(x;) ) " +
                     "     or \n " +
                     "    ( Location(z, y;) and ?whereFood(x, z;) )\n" +
                     "end\n" +

                     "query connect( String x, String y ) \n" +
                     "    Door(x, y;)\n" +
                     "    or \n" +
                     "    Door(y, x;)\n" +
                     "end\n" +
                     "\n" +
                     "query isContainedIn( String x, String y ) \n" +
                     "    Location(x, y;)\n" +
                     "    or \n" +
                     "    ( Location(z, y;) and ?isContainedIn(x, z;) )\n" +
                     "end\n" +
                     "\n" +
                     "query look(String place, List things, List food, List exits ) \n" +
                     "    Here(place;)\n" +
                     "    things := List() from accumulate( Location(thing, place;),\n" +
                     "                                      collectList( thing ) )\n" +
                     "    food := List() from accumulate( ?whereFood(thing, place;) ," +
                     "                                    collectList( thing ) )\n" +
                     "    exits := List() from accumulate( ?connect(place, exit;),\n" +
                     "                                    collectList( exit ) )\n" +
                     "end\n" +
                     "\n" +
                     "rule reactiveLook when\n" +
                     "    Here( place : place) \n" +
                     "    ?look(place, things, food, exits;)\n" +
                     "then\n" +
                     "    Map map = new HashMap();" +
                     "    list.add(map);" +
                     "    map.put( 'place', place); " +
                     "    map.put( 'things', things); " +
                     "    map.put( 'food', food); " +
                     "    map.put( 'exits', exits); " +
                     "    System.out.println( \"You are in the \" + place);\n" +
                     "    System.out.println( \"  You can see \" + things );\n" +
                     "    System.out.println( \"  You can eat \" + food );\n" +
                     "    System.out.println( \"  You can go to \" + exits );\n" +
                     "end\n" +
                     "\n" +
                     "rule init when\n" +
                     "then\n" +
                     "        insert( new Room(\"kitchen\") );\n" +
                     "        insert( new Room(\"office\") );\n" +
                     "        insert( new Room(\"hall\") );\n" +
                     "        insert( new Room(\"dining room\") );\n" +
                     "        insert( new Room(\"cellar\") );\n" +
                     "        \n" +
                     "        insert( new Location(\"apple\", \"kitchen\") );\n" +

                     "        insert( new Location(\"desk\", \"office\") );\n" +
                     "        insert( new Location(\"apple\", \"desk\") );\n" +
                     "        insert( new Location(\"flashlight\", \"desk\") );\n" +
                     "        insert( new Location(\"envelope\", \"desk\") );\n" +
                     "        insert( new Location(\"key\", \"envelope\") );\n" +

                     "        insert( new Location(\"washing machine\", \"cellar\") );\n" +
                     "        insert( new Location(\"nani\", \"washing machine\") );\n" +
                     "        insert( new Location(\"broccoli\", \"kitchen\") );\n" +
                     "        insert( new Location(\"crackers\", \"kitchen\") );\n" +
                     "        insert( new Location(\"computer\", \"office\") );\n" +
                     "        \n" +
                     "        insert( new Door(\"office\", \"hall\") );\n" +
                     "        insert( new Door(\"kitchen\", \"office\") );\n" +
                     "        insert( new Door(\"hall\", \"dining room\") );\n" +
                     "        insert( new Door(\"kitchen\", \"cellar\") );\n" +
                     "        insert( new Door(\"dining room\", \"kitchen\") );\n" +
                     "        \n" +
                     "        insert( new Edible(\"apple\") );\n" +
                     "        insert( new Edible(\"crackers\") );\n" +
                     "        \n" +
                     "        insert( new TastesYucky(\"broccoli\") );  " +
                     "end\n" +
                     "" +
                     "rule go1 when\n" +
                     "   String( this == 'go1' )\n" +
                     "then\n" +
                     "   insert( new Here(\"kitchen\") );\n" +
                     "end\n" +
                     "\n" +
                     "rule go2 when\n" +
                     "   String( this == 'go2' )\n" +
                     "   $h : Here( place == \"kitchen\")" +
                     "then\n" +
                     "   modify( $h ) { place = \"office\" };\n" +
                     "end\n" +
                     "";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        ksession.setGlobal( "list",
                            list );

        QueryResults results = null;
        ksession.fireAllRules();

        ksession.insert( "go1" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();

        Map<String, Object> map = (Map) list.get( 0 );
        assertEquals( "kitchen",
                      map.get( "place" ) );
        List<String> items = (List<String>) map.get( "things" );
        assertEquals( 3,
                      items.size() );
        assertContains( new String[]{"apple", "broccoli", "crackers"},
                        items );

        items = (List<String>) map.get( "food" );
        assertEquals( 2,
                      items.size() );
        assertContains( new String[]{"apple", "crackers"},
                        items );

        items = (List<String>) map.get( "exits" );
        assertEquals( 3,
                      items.size() );
        assertContains( new String[]{"office", "cellar", "dining room"},
                        items );

        ksession.insert( "go2" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();

        map = (Map) list.get( 1 );
        assertEquals( "office",
                      map.get( "place" ) );
        items = (List<String>) map.get( "things" );
        assertEquals( 2,
                      items.size() );
        assertContains( new String[]{"computer", "desk",},
                        items );

        items = (List<String>) map.get( "food" );
        assertEquals( 1,
                      items.size() );
        assertContains( new String[]{"apple"},
                        items ); // notice the apple is on the desk in the office        

        items = (List<String>) map.get( "exits" );
        assertEquals( 2,
                      items.size() );
        assertContains( new String[]{"hall", "kitchen"},
                        items );

        results = ksession.getQueryResults( "isContainedIn",
                                            new Object[]{"key", "office"} );
        assertEquals( 1,
                      results.size() );
        QueryResultsRow result = results.iterator().next();
        assertEquals( "key",
                      result.get( "x" ) );
        assertEquals( "office",
                      result.get( "y" ) );

        results = ksession.getQueryResults( "isContainedIn",
                                            new Object[]{"key", Variable.v} );
        List<List<String>> l = new ArrayList<List<String>>();
        for ( QueryResultsRow r : results ) {
            l.add( Arrays.asList( new String[]{(String) r.get( "x" ), (String) r.get( "y" )} ) );
        }
        assertEquals( 3,
                      results.size() );
        assertContains( Arrays.asList( new String[]{"key", "desk"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"key", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"key", "envelope"} ),
                        l );

        results = ksession.getQueryResults( "isContainedIn",
                                            new Object[]{Variable.v, "office"} );
        l = new ArrayList<List<String>>();
        for ( QueryResultsRow r : results ) {
            l.add( Arrays.asList( new String[]{(String) r.get( "x" ), (String) r.get( "y" )} ) );
        }

        assertEquals( 6,
                      results.size() );
        assertContains( Arrays.asList( new String[]{"desk", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"computer", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"apple", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"envelope", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"flashlight", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"key", "office"} ),
                        l );

        results = ksession.getQueryResults( "isContainedIn",
                                            new Object[]{Variable.v, Variable.v} );
        l = new ArrayList<List<String>>();
        for ( QueryResultsRow r : results ) {
            l.add( Arrays.asList( new String[]{(String) r.get( "x" ), (String) r.get( "y" )} ) );
        }
        assertEquals( 17,
                      results.size() );
        assertContains( Arrays.asList( new String[]{"apple", "kitchen"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"apple", "desk"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"envelope", "desk"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"desk", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"computer", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"washing machine", "cellar"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"key", "envelope"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"broccoli", "kitchen"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"nani", "washing machine"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"crackers", "kitchen"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"flashlight", "desk"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"nani", "cellar"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"apple", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"envelope", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"flashlight", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"key", "office"} ),
                        l );
        assertContains( Arrays.asList( new String[]{"key", "desk"} ),
                        l );
    }

    @Test
    public void testSubNetworksAndQueries() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +

                     "import java.util.List\n" +
                     "import java.util.ArrayList\n" +

                     "import java.util.Map\n" +
                     "import java.util.HashMap\n" +

                     "global List list\n" +

                     "dialect \"mvel\"\n" +
                     "\n" +
                     "declare Location\n" +
                     "    thing : String \n" +
                     "    location : String \n" +
                     "end" +
                     "\n" +
                     "declare Edible\n" +
                     "   thing : String\n" +
                     "end" +
                     "\n" +
                     "query whereFood( String x, String y ) \n" +
                     "    Location(x, y;) Edible(x;) \n" +
                     "end\n" +
                     "\n" +
                     "query look(String place, List food ) \n" +
                     "    $s : String() // just here to give a OTN lookup point\n" +
                     "    food := List() from accumulate( whereFood(thing, place;) ," +
                     "                                    collectList( thing ) )\n" +
                     "    exists( whereFood(thing, place;) )\n" +
                     "    not( whereFood(thing, place;) and\n " +
                     "         String( this == $s ) from thing )\n" +
                     "end\n" +
                     "\n" +
                     "rule init when\n" +
                     "then\n" +
                     "        \n" +
                     "        insert( new Location(\"apple\", \"kitchen\") );\n" +
                     "        insert( new Location(\"crackers\", \"kitchen\") );\n" +
                     "        insert( new Location(\"broccoli\", \"kitchen\") );\n" +
                     "        insert( new Location(\"computer\", \"office\") );\n" +

                     "        insert( new Edible(\"apple\") );\n" +
                     "        insert( new Edible(\"crackers\") );\n" +
                     "end\n" +
                     "";

        System.out.println( str );

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        // Get the accumulate node, so we can test it's memory later
        // now check beta memory was correctly cleared
        List<ObjectTypeNode> nodes = ((InternalRuleBase) ((KnowledgeBaseImpl) kbase).ruleBase).getRete().getObjectTypeNodes();
        ObjectTypeNode node = null;
        for ( ObjectTypeNode n : nodes ) {
            if ( ((ClassObjectType) n.getObjectType()).getClassType() == String.class ) {
                node = n;
                break;
            }
        }

        BetaNode stringBetaNode = (BetaNode) node.getSinkPropagator().getSinks()[0];
        QueryElementNode queryElementNode1 = (QueryElementNode) stringBetaNode.getSinkPropagator().getSinks()[0];
        RightInputAdapterNode riaNode1 = (RightInputAdapterNode) queryElementNode1.getSinkPropagator().getSinks()[0];
        AccumulateNode accNode = (AccumulateNode) riaNode1.getSinkPropagator().getSinks()[0];

        QueryElementNode queryElementNode2 = (QueryElementNode) accNode.getSinkPropagator().getSinks()[0];
        RightInputAdapterNode riaNode2 = (RightInputAdapterNode) queryElementNode2.getSinkPropagator().getSinks()[0];
        ExistsNode existsNode = (ExistsNode) riaNode2.getSinkPropagator().getSinks()[0];

        QueryElementNode queryElementNode3 = (QueryElementNode) existsNode.getSinkPropagator().getSinks()[0];
        FromNode fromNode = (FromNode) queryElementNode3.getSinkPropagator().getSinks()[0];
        RightInputAdapterNode riaNode3 = (RightInputAdapterNode) fromNode.getSinkPropagator().getSinks()[0];
        NotNode notNode = (NotNode) riaNode3.getSinkPropagator().getSinks()[0];

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        ReteooWorkingMemory wm = ((StatefulKnowledgeSessionImpl) ksession).session;
        AccumulateMemory accMemory = (AccumulateMemory) wm.getNodeMemory( accNode );
        BetaMemory existsMemory = (BetaMemory) wm.getNodeMemory( existsNode );
        FromMemory fromMemory = (FromMemory) wm.getNodeMemory( fromNode );
        BetaMemory notMemory = (BetaMemory) wm.getNodeMemory( notNode );

        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        ksession.setGlobal( "list",
                            list );
        FactHandle fh = ksession.insert( "bread" );

        ksession.fireAllRules();

        final List food = new ArrayList();

        QueryResults results = null;

        // Execute normal query and check no subnetwork tuples are left behind
        results = ksession.getQueryResults( "look",
                                            new Object[]{"kitchen", Variable.v} );
        assertEquals( 1,
                      results.size() );

        for ( org.drools.runtime.rule.QueryResultsRow row : results ) {
            food.addAll( (Collection) row.get( "food" ) );
            System.out.println( row.get( "food" ) );
        }
        assertEquals( 2,
                      food.size() );
        assertContains( new String[]{"crackers", "apple"},
                        food );

        assertEquals( 0,
                      accMemory.betaMemory.getRightTupleMemory().size() );
        assertEquals( 0,
                      existsMemory.getRightTupleMemory().size() );
        assertEquals( 0,
                      fromMemory.betaMemory.getLeftTupleMemory().size() );
        assertEquals( 0,
                      notMemory.getRightTupleMemory().size() );

        // Now execute an open query and ensure the memory is left populated
        food.clear();
        final List foodUpdated = new ArrayList();
        LiveQuery query = ksession.openLiveQuery( "look",
                                                  new Object[]{"kitchen", Variable.v},
                                                  new ViewChangedEventListener() {

                                                      public void rowUpdated(Row row) {
                                                          foodUpdated.addAll( (Collection) row.get( "food" ) );
                                                      }

                                                      public void rowRemoved(Row row) {
                                                      }

                                                      public void rowAdded(Row row) {
                                                          food.addAll( (Collection) row.get( "food" ) );
                                                      }
                                                  } );
        assertEquals( 2,
                      food.size() );
        assertContains( new String[]{"crackers", "apple"},
                        food );

        assertEquals( 2,
                      accMemory.betaMemory.getRightTupleMemory().size() );
        assertEquals( 2,
                      existsMemory.getRightTupleMemory().size() );
        assertEquals( 2,
                      fromMemory.betaMemory.getLeftTupleMemory().size() );
        assertEquals( 0,
                      notMemory.getRightTupleMemory().size() );

        food.clear();
        // Now try again, make sure it only delete's it's own tuples
        results = ksession.getQueryResults( "look",
                                            new Object[]{"kitchen", Variable.v} );
        assertEquals( 1,
                      results.size() );

        for ( org.drools.runtime.rule.QueryResultsRow row : results ) {
            food.addAll( (Collection) row.get( "food" ) );
            System.out.println( row.get( "food" ) );
        }
        assertEquals( 2,
                      food.size() );
        assertContains( new String[]{"crackers", "apple"},
                        food );

        assertEquals( 2,
                      accMemory.betaMemory.getRightTupleMemory().size() );
        assertEquals( 2,
                      existsMemory.getRightTupleMemory().size() );
        assertEquals( 2,
                      fromMemory.betaMemory.getLeftTupleMemory().size() );
        assertEquals( 0,
                      notMemory.getRightTupleMemory().size() );

        // do an update and check it's  still memory size 2
        ksession.update( fh,
                         "crackers" );
        assertEquals( 2,
                      accMemory.betaMemory.getRightTupleMemory().size() );
        assertEquals( 2,
                      existsMemory.getRightTupleMemory().size() );
        assertEquals( 2,
                      fromMemory.betaMemory.getLeftTupleMemory().size() );
        assertEquals( 1,
                      notMemory.getRightTupleMemory().size() );

        assertContains( new String[]{"crackers", "apple"},
                        foodUpdated );

        // Close the open 
        query.close();
        assertEquals( 0,
                      accMemory.betaMemory.getRightTupleMemory().size() );
        assertEquals( 0,
                      existsMemory.getRightTupleMemory().size() );
        assertEquals( 0,
                      fromMemory.betaMemory.getLeftTupleMemory().size() );
        assertEquals( 0,
                      notMemory.getRightTupleMemory().size() );
    }

    @Test
    public void testDynamicRulesWithSharing() throws IOException,
                                             ClassNotFoundException {
        String str = "" +
                     "package org.drools.test1  \n" +
                     "\n" +
                     "declare Location\n" +
                     "    thing : String \n" +
                     "    location : String \n" +
                     "end" +
                     "\n" +
                     "declare Edible\n" +
                     "   thing : String\n" +
                     "end" +
                     "\n" +
                     "query whereFood( String x, String y ) \n" +
                     "    Location(x, y;) Edible(x;) \n" +
                     "end\n" +
                     "\n" +         
                     "rule init when\n" +
                     "then\n" +
                     "        \n" +
                     "        insert( new Location(\"apple\", \"kitchen\") );\n" +
                     "        insert( new Location(\"crackers\", \"kitchen\") );\n" +
                     "        insert( new Location(\"broccoli\", \"kitchen\") );\n" +
                     "        insert( new Location(\"computer\", \"office\") );\n" +

                     "        insert( new Edible(\"apple\") );\n" +
                     "        insert( new Edible(\"crackers\") );\n" +
                     "end\n" +
                     "";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );
        
        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }           

        str = "" +
                "package org.drools.test2  \n" +
                
                "import org.drools.test1.*\n" +
                "import java.util.List\n" +
                "import java.util.ArrayList\n" +

                "import java.util.Map\n" +
                "import java.util.HashMap\n" +

                "global List list\n" +
                "\n" +
                "rule look2 when\n" +
                "     $place : String() // just here to give a OTN lookup point\n" +
                "     whereFood(thing, $place;)\n" +
                "then\n" +
                "      list.add( \"2:\" + thing );\n" +
                "end\n";  
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );     
        
        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }        
        
        str = "" +
                "package org.drools.test3  \n" +

                "import org.drools.test1.*\n" +
                "import java.util.List\n" +
                "import java.util.ArrayList\n" +

                "import java.util.Map\n" +
                "import java.util.HashMap\n" +

                "global List list\n" +
                "\n" +
                "rule look3 when\n" +
                "     $place : String() // just here to give a OTN lookup point\n" +
                "     whereFood(thing, $place;)\n" +
                "then\n" +
                "      list.add( \"3:\" + thing );\n" +
                "end\n";
        
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );        
        
        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }
        
        str = "" +
                "package org.drools.test4  \n" +

                "import org.drools.test1.*\n" +
                "import java.util.List\n" +
                "import java.util.ArrayList\n" +

                "import java.util.Map\n" +
                "import java.util.HashMap\n" +

                "global List list\n" +
                "\n" +
                "rule look4 when\n" +
                "     $place : String() // just here to give a OTN lookup point\n" +
                "     whereFood(thing, $place;)\n" +
                "then\n" +
                "      list.add( \"4:\" + thing );\n" +
                "end\n";
        
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );        
        
        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }        

        Map<String, KnowledgePackage> pkgs = new HashMap<String, KnowledgePackage>();
        for ( KnowledgePackage pkg : kbuilder.getKnowledgePackages() ) {
            pkgs.put(  pkg.getName(), pkg );
        }
        
        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages(  Arrays.asList( new KnowledgePackage[] { pkgs.get( "org.drools.test1" ), pkgs.get( "org.drools.test2" ) } ) );

        kbase = SerializationHelper.serializeObject( kbase );
        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();

        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        ksession.setGlobal( "list",
                            list );
        FactHandle fh = ksession.insert( "kitchen" );

        ksession.fireAllRules();
        
        assertEquals( 2, list.size() );
        assertContains( new String[]{"2:crackers", "2:apple"},
                        list ); 
        
        list.clear();
        kbase.addKnowledgePackages(  Arrays.asList( new KnowledgePackage[] { pkgs.get( "org.drools.test3" ) } ) );
        
        ksession.fireAllRules();
        assertEquals( 2, list.size() );
        assertContains( new String[]{"3:crackers", "3:apple"},
                        list );

        list.clear();
        kbase.addKnowledgePackages(  Arrays.asList( new KnowledgePackage[] { pkgs.get( "org.drools.test4" ) } ) );
        
        ksession.fireAllRules();
        assertEquals( 2, list.size() );
        assertContains( new String[]{"4:crackers", "4:apple"},
                        list );        
    }

    @Test
    public void testOpenBackwardChain() throws Exception {
        // http://www.amzi.com/AdventureInProlog/advtop.php

        String str = "" +
                     "package org.drools.test  \n" +

                     "import java.util.List\n" +
                     "import java.util.ArrayList\n" +
                     "import org.drools.Person\n" +

                     "global List list\n" +

                     "dialect \"mvel\"\n" +

                     "declare Location\n" +
                     "    thing : String \n" +
                     "    location : String \n" +
                     "end" +
                     "\n" +
                     "query isContainedIn( String x, String y ) \n" +
                     "    Location(x, y;)\n" +
                     "    or \n" +
                     "    ( Location(z, y;) and isContainedIn(x, z;) )\n" +
                     "end\n" +
                     "\n" +
                     "rule look when \n" +
                     "    Person( $l : likes ) \n" +
                     "    isContainedIn( $l, 'office'; )\n" +
                     "then\n" +
                     "   insertLogical( 'blah' );" +
                     "end\n" +
                     "rule existsBlah when \n" +
                     "    exists String( this == 'blah') \n" +
                     "then\n" +
                     "   list.add( 'exists blah' );" +
                     "end\n" +
                     "\n" +
                     "rule notBlah when \n" +
                     "    not String( this == 'blah') \n" +
                     "then\n" +
                     "   list.add( 'not blah' );" +
                     "end\n" +
                     "\n" +
                     "rule init when\n" +
                     "then\n" +
                     "        insert( new Location(\"apple\", \"kitchen\") );\n" +
                     "        insert( new Location(\"desk\", \"office\") );\n" +
                     "        insert( new Location(\"flashlight\", \"desk\") );\n" +
                     "        insert( new Location(\"envelope\", \"desk\") );\n" +
                     "        insert( new Location(\"key\", \"envelope\") );\n" +
                     "        insert( new Location(\"washing machine\", \"cellar\") );\n" +
                     "        insert( new Location(\"nani\", \"washing machine\") );\n" +
                     "        insert( new Location(\"broccoli\", \"kitchen\") );\n" +
                     "        insert( new Location(\"crackers\", \"kitchen\") );\n" +
                     "        insert( new Location(\"computer\", \"office\") );\n" +
                     "end\n" +
                     "\n" +
                     "rule go1 when \n" +
                     "    String( this == 'go1') \n" +
                     "then\n" +
                     "        list.add( rule.getName() ); \n" +
                     "        insert( new Location('lamp', 'desk') );\n" +
                     "end\n" +
                     "\n" +
                     "rule go2 when \n" +
                     "    String( this == 'go2') \n" +
                     "    $l : Location('lamp', 'desk'; )\n" +
                     "then\n" +
                     "    list.add( rule.getName() ); \n" +
                     "    retract( $l );\n" +
                     "end\n" +
                     "\n" +
                     "rule go3 when \n" +
                     "    String( this == 'go3') \n" +
                     "then\n" +
                     "        list.add( rule.getName() ); \n" +
                     "        insert( new Location('lamp', 'desk') );\n" +
                     "end\n" +
                     "\n" +
                     "rule go4 when \n" +
                     "    String( this == 'go4') \n" +
                     "    $l : Location('lamp', 'desk'; )\n" +
                     "then\n" +
                     "        list.add( rule.getName() ); \n" +
                     "    modify( $l ) { thing = 'book' };\n" +
                     "end\n" +
                     "\n" +
                     "rule go5 when \n" +
                     "    String( this == 'go5') \n" +
                     "    $l : Location('book', 'desk'; )\n" +
                     "then\n" +
                     "    list.add( rule.getName() ); \n" +
                     "    modify( $l ) { thing = 'lamp' };\n" +
                     "end\n" +
                     "\n" +
                     "rule go6 when \n" +
                     "    String( this == 'go6') \n" +
                     "    $l : Location( 'lamp', 'desk'; )\n" +
                     "then\n" +
                     "    list.add( rule.getName() ); \n" +
                     "    modify( $l ) { thing = 'book' };\n" +
                     "end\n" +
                     "\n" +
                     "rule go7 when \n" +
                     "    String( this == 'go7') \n" +
                     "    $p : Person( likes == 'lamp' ) \n" +
                     "then\n" +
                     "    list.add( rule.getName() ); \n" +
                     "    modify( $p ) { likes = 'key' };\n" +
                     "end\n" +
                     "\n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<String> list = new ArrayList<String>();
        ksession.setGlobal( "list",
                            list );

        QueryResults results = null;

        Person p = new Person();
        p.setLikes( "lamp" );
        FactHandle handle = ksession.insert( p );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();

        list.clear();

        InternalFactHandle fh = (InternalFactHandle) ksession.insert( "go1" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();

        fh = getFactHandle( fh,
                            ksession );
        ksession.retract( fh );
        assertEquals( "go1",
                      list.get( 0 ) );
        assertEquals( "exists blah",
                      list.get( 1 ) );

        fh = (InternalFactHandle) ksession.insert( "go2" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();

        fh = getFactHandle( fh,
                            ksession );
        ksession.retract( fh );
        assertEquals( "go2",
                      list.get( 2 ) );
        assertEquals( "not blah",
                      list.get( 3 ) );

        fh = (InternalFactHandle) ksession.insert( "go3" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();

        fh = getFactHandle( fh,
                            ksession );
        ksession.retract( fh );
        assertEquals( "go3",
                      list.get( 4 ) );
        assertEquals( "exists blah",
                      list.get( 5 ) );

        fh = (InternalFactHandle) ksession.insert( "go4" );
        ksession.fireAllRules();

        fh = getFactHandle( fh,
                            ksession );
        ksession.retract( fh );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        assertEquals( "go4",
                      list.get( 6 ) );
        assertEquals( "not blah",
                      list.get( 7 ) );

        fh = (InternalFactHandle) ksession.insert( "go5" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();

        fh = getFactHandle( fh,
                            ksession );
        ksession.retract( fh );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        assertEquals( "go5",
                      list.get( 8 ) );
        assertEquals( "exists blah",
                      list.get( 9 ) );

        // This simulates a modify of the root DroolsQuery object, but first we break it
        fh = (InternalFactHandle) ksession.insert( "go6" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();

        fh = getFactHandle( fh,
                            ksession );
        ksession.retract( fh );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        assertEquals( "go6",
                      list.get( 10 ) );
        assertEquals( "not blah",
                      list.get( 11 ) );

        // now fix it
        fh = (InternalFactHandle) ksession.insert( "go7" );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        ksession.fireAllRules();

        fh = getFactHandle( fh,
                            ksession );
        ksession.retract( fh );
        ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                          true );
        assertEquals( "go7",
                      list.get( 12 ) );
        assertEquals( "exists blah",
                      list.get( 13 ) );
    }

    @Test
    public void testCompile() {
        String drl = "";

        drl = "declare Location\n"
              + "thing : String\n"
              + "location : String\n"
              + "end\n\n";

        drl = drl + "query isContainedIn( String x, String y )\n"
              + "Location( x := thing, y := location)\n"
              + "or \n"
              + "( Location(z := thing, y := location) and ?isContainedIn( x := x, z := y ) )\n"
              + "end\n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( new ByteArrayResource( drl.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }
    }

    @Test
    public void testInsertionOrderTwo() throws Exception {
        String str = "" +
                     "package org.test \n" +
                     "import java.util.List \n" +
                     "global List list \n" +
                     "declare Thing \n" +
                     "    thing : String @key \n" +
                     "end \n" +
                     "declare Edible extends Thing \n" +
                     "end \n" +
                     "declare Location extends Thing \n" +
                     "    location : String  @key \n" +
                     "end \n" +
                     "declare Here \n" +
                     "    place : String \n" +
                     "end \n" +
                     "rule kickOff \n" +
                     "when \n" +
                     "    Integer( $i: intValue ) \n" +
                     "then \n" +
                     "    switch( $i ){ \n";

        String[] facts = new String[]{"new Edible( 'peach' )", "new Location( 'peach', 'table' )", "new Here( 'table' )"};
        int f = 0;
        for ( int i = 0; i < facts.length; i++ ) {
            for ( int j = 0; j < facts.length; j++ ) {
                for ( int k = 0; k < facts.length; k++ ) {
                    // use a Set to make sure we only include 3 unique values
                    Set<String> set = new HashSet<String>();
                    set.add( facts[i] );
                    set.add( facts[j] );
                    set.add( facts[k] );
                    if ( set.size() == 3 ) {
                        str +=
                                "    case " + f++ + ": \n" +
                                        //"        System.out.println( \"s) \"+" + (f-1) + ");\n" +                          
                                        "        insert( " + facts[i] + " ); \n" +
                                        "        insert( " + facts[j] + " ); \n" +
                                        "        insert( " + facts[k] + " ); \n" +
                                        "        break; \n";
                    }
                }
            }
        }

        facts = new String[]{"new Edible( 'peach' )", "new Location( 'table', 'office' )", "new Location( 'peach', 'table' )", "new Here( 'office' )"};
        int h = f;
        for ( int i = 0; i < facts.length; i++ ) {
            for ( int j = 0; j < facts.length; j++ ) {
                for ( int k = 0; k < facts.length; k++ ) {
                    for ( int l = 0; l < facts.length; l++ ) {
                        // use a Set to make sure we only include 3 unique values
                        Set<String> set = new HashSet<String>();
                        set.add( facts[i] );
                        set.add( facts[j] );
                        set.add( facts[k] );
                        set.add( facts[l] );
                        if ( set.size() == 4 ) {
                            str +=
                                    "    case " + h++ + ": \n" +
                                            //"        System.out.println( \"s) \"+" + (h-1) + ");\n" +                              
                                            "        insert( " + facts[i] + " ); \n" +
                                            "        insert( " + facts[j] + " ); \n" +
                                            "        insert( " + facts[k] + " ); \n" +
                                            "        insert( " + facts[l] + " ); \n" +
                                            "        break; \n";
                        }
                    }
                }
            }
        }

        str +=
                "    } \n" +
                        "end \n" +
                        "\n" +
                        "query whereFood( String x, String y ) \n" +
                        "    ( Location(x, y;) and \n" +
                        "    Edible(x;) ) \n " +
                        "    or  \n" +
                        "    ( Location(z, y;) and whereFood(x, z;) ) \n" +
                        "end " +
                        "query look(String place, List things, List food)  \n" +
                        "    Here(place;) \n" +
                        "    things := List() from accumulate( Location(thing, place;), \n" +
                        "                                      collectList( thing ) ) \n" +
                        "    food := List() from accumulate( whereFood(thing, place;), \n" +
                        "                                    collectList( thing ) ) \n" +
                        "end \n" +
                        "rule reactiveLook \n" +
                        "when \n" +
                        "    Here( $place : place)  \n" +
                        "    look($place, $things; $food := food) \n" +
                        "then \n" +
                        "    list.addAll( $things ); \n" +
                        "    list.addAll( $food   ); \n" +
                        //"    System.out.println( $things + \":\" + $food ); \n" +
                        "end \n" +
                        "";

        // System.out.println( str );        

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        for ( int i = 0; i < f; i++ ) {
            StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
            List<String> list = new ArrayList<String>();
            ksession.setGlobal( "list",
                                list );
            ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                              true );
            ksession.fireAllRules();
            list.clear();
            InternalFactHandle fh = (InternalFactHandle) ksession.insert( Integer.valueOf( i ) );
            ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                              true );
            ksession.fireAllRules();

            ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                              true );

            assertEquals( 2,
                          list.size() );
            assertEquals( "peach",
                          list.get( 0 ) );
            assertEquals( "peach",
                          list.get( 1 ) );
            list.clear();

            InternalFactHandle[] handles = ksession.getFactHandles().toArray( new InternalFactHandle[0] );
            for ( int j = 0; j < handles.length; j++ ) {
                if ( handles[j].getObject() instanceof InitialFact || handles[j].getObject() instanceof Integer ) {
                    continue;
                }

                //                if ( !handles[j].getObject().getClass().getSimpleName().equals( "Here" )) {
                //                    continue;
                //                }              

                ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                                  true );

                handles[j] = getFactHandle( handles[j],
                                            ksession );
                Object o = handles[j].getObject();

                // first retract + assert                
                ksession.retract( handles[j] );
                ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                                  true );

                handles[j] = (InternalFactHandle) ksession.insert( o );
                ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                                  true );

                ksession.fireAllRules();
                assertEquals( 2,
                              list.size() );
                assertEquals( "peach",
                              list.get( 0 ) );
                assertEquals( "peach",
                              list.get( 1 ) );
                list.clear();

                // now try update
                // session was serialised so need to get factHandle
                handles[j] = getFactHandle( handles[j],
                                            ksession );
                ksession.update( handles[j],
                                 handles[j].getObject() );
                ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                                  true );

                ksession.fireAllRules();
                assertEquals( 2,
                              list.size() );
                assertEquals( "peach",
                              list.get( 0 ) );
                assertEquals( "peach",
                              list.get( 1 ) );
                list.clear();
            }

            fh = getFactHandle( fh,
                                ksession );
            ksession.retract( fh );
            ksession.dispose();
        }

        for ( int i = f; i < h; i++ ) { //h; i++) {
            StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
            List<String> list = new ArrayList<String>();
            ksession.setGlobal( "list",
                                list );
            ksession.fireAllRules();
            list.clear();

            ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                              true );

            InternalFactHandle fh = (InternalFactHandle) ksession.insert( Integer.valueOf( i ) );
            ksession.fireAllRules();
            assertEquals( 2,
                          list.size() );
            assertEquals( "table",
                          list.get( 0 ) );
            assertEquals( "peach",
                          list.get( 1 ) );
            list.clear();

            ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                              true );

            InternalFactHandle[] handles = ksession.getFactHandles().toArray( new InternalFactHandle[0] );
            for ( int j = 0; j < handles.length; j++ ) {
                if ( handles[j].getObject() instanceof InitialFact || handles[j].getObject() instanceof Integer ) {
                    continue;
                }

                //                if ( !handles[j].getObject().toString().equals( "Location( thing=peach, location=table )" )) {
                //                    continue;
                //                }     

                ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                                  true );

                handles[j] = getFactHandle( handles[j],
                                            ksession );
                Object o = handles[j].getObject();

                // first retract + assert
                ksession.retract( handles[j] );
                ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                                  true );

                handles[j] = (InternalFactHandle) ksession.insert( o );

                ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                                  true );

                ksession.fireAllRules();
                assertEquals( 2,
                              list.size() );
                assertEquals( "table",
                              list.get( 0 ) );
                assertEquals( "peach",
                              list.get( 1 ) );
                list.clear();

                ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                                  true );

                // now try update
                handles[j] = getFactHandle( handles[j],
                                            ksession );
                ksession.update( handles[j],
                                 handles[j].getObject() );

                ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                                  true );

                ksession.fireAllRules();
                assertEquals( 2,
                              list.size() );
                assertEquals( "table",
                              list.get( 0 ) );
                assertEquals( "peach",
                              list.get( 1 ) );
                list.clear();
            }
            ksession = getSerialisedStatefulKnowledgeSession( ksession,
                                                              true );

            fh = getFactHandle( fh,
                                ksession );
            ksession.retract( fh );
            ksession.dispose();
        }
    }

    @Test
    public void testInsertionOrder() throws Exception {
        String str = "" +
                     "package org.test  \n" +

                     "import java.util.List\n" +
                     "import java.util.ArrayList\n" +

                     "global List list\n" +

                     "dialect \"mvel\"\n" +

                     "declare Person\n" +
                     "   name : String\n" +
                     "   likes : String\n" +
                     "end\n" +
                     "\n" +
                     "declare Location\n" +
                     "    thing : String \n" +
                     "    location : String \n" +
                     "end\n" +
                     "\n" +
                     "declare Edible\n" +
                     "   thing : String\n" +
                     "end\n" +
                     "\n" +
                     "\n" +
                     "query hasFood( String x, String y ) \n" +
                     "    Location(x, y;) " +
                     "     or \n " +
                     "    ( Location(z, y;) and hasFood(x, z;) )\n" +
                     "end\n" +
                     "\n" +
                     "rule look when \n" +
                     "    Person( $l : likes ) \n" +
                     "    hasFood( $l, 'kitchen'; )\n" +
                     "then\n" +
                     "   list.add( 'kitchen has ' + $l );" +
                     "end\n" +
                     "rule go1 when\n" +
                     "    String( this == 'go1') \n" +
                     "then\n" +
                     "        insert( new Person('zool', 'peach') );\n" +
                     "        insert( new Location(\"table\", \"kitchen\") );\n" +
                     "        insert( new Location(\"peach\", \"table\") );\n" +
                     "end\n" +
                     "rule go2 when\n" +
                     "    String( this == 'go2') \n" +
                     "then\n" +
                     "        insert( new Person('zool', 'peach') );\n" +
                     "        insert( new Location(\"peach\", \"table\") );\n" +
                     "        insert( new Location(\"table\", \"kitchen\") );\n" +
                     "end\n" +
                     "\n" +
                     "rule go3 when\n" +
                     "    String( this == 'go3') \n" +
                     "then\n" +
                     "        insert( new Location(\"table\", \"kitchen\") );\n" +
                     "        insert( new Location(\"peach\", \"table\") );\n" +
                     "        insert( new Person('zool', 'peach') );\n" +
                     "end\n" +
                     "\n" +
                     "rule go4 when\n" +
                     "    String( this == 'go4') \n" +
                     "then\n" +
                     "        insert( new Location(\"peach\", \"table\") );\n" +
                     "        insert( new Location(\"table\", \"kitchen\") );\n" +
                     "        insert( new Person('zool', 'peach') );\n" +
                     "end\n" +
                     "rule go5 when\n" +
                     "    String( this == 'go5') \n" +
                     "then\n" +
                     "        insert( new Location(\"peach\", \"table\") );\n" +
                     "        insert( new Person('zool', 'peach') );\n" +
                     "        insert( new Location(\"table\", \"kitchen\") );\n" +
                     "end\n" +
                     "rule go6 when\n" +
                     "    String( this == 'go6') \n" +
                     "then\n" +
                     "        insert( new Location(\"table\", \"kitchen\") );\n" +
                     "        insert( new Person('zool', 'peach') );\n" +
                     "        insert( new Location(\"peach\", \"table\") );\n" +
                     "end\n" +
                     "\n" +
                     "\n";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        for ( int i = 1; i <= 6; i++ ) {
            StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
            List<String> list = new ArrayList<String>();
            ksession.setGlobal( "list",
                                list );
            ksession.fireAllRules();
            list.clear();
            FactHandle fh = ksession.insert( "go" + i );
            ksession.fireAllRules();
            ksession.retract( fh );
            assertEquals( 1,
                          list.size() );
            assertEquals( "kitchen has peach",
                          list.get( 0 ) );
            ksession.dispose();
        }
    }

    @Test
    public void testQueryFindAll() throws Exception {
        Object[] objects = new Object[]{Integer.valueOf( 42 ), "a String", Integer.valueOf( 100 )};
        int oCount = objects.length + 1; // +1 for InitialFact

        List<Object> queryList = new ArrayList<Object>();
        List<Object> ruleList = new ArrayList<Object>();
        // expect all inserted objects + InitialFact
        runTestQueryFindAll( 0,
                             queryList,
                             ruleList,
                             objects );

        System.out.println( queryList.size() + " - " + ruleList.size() );

        assertEquals( oCount,
                      queryList.size() );
        assertContains( objects,
                        queryList );

        //        // expect inserted objects + InitialFact
        //        queryList.clear();
        //        ruleList.clear();
        //        runTestQueryFindAll( 1, queryList, ruleList, objects );
        //        assertEquals( oCount*oCount, queryList.size() );
        //
        //        queryList.clear();
        //        ruleList.clear();
        //        runTestQueryFindAll( 2, queryList, ruleList, objects );
        //        assertEquals( oCount*oCount, queryList.size() );
    }

    private void runTestQueryFindAll(int iCase,
                                     List<Object> queryList,
                                     List<Object> ruleList,
                                     Object[] objects) throws Exception,
                                                      Exception {
        String str = "" +
                     "package org.test \n" +
                     "global java.util.List queryList \n" +
                     "global java.util.List ruleList \n" +
                     "query object( Object o ) \n" +
                     "    o := Object( ) \n" +
                     "end \n" +
                     "rule findObjectByQuery \n" +
                     "when \n";
        switch ( iCase ) {
            case 0 :
                // omit Object()
                str += "    object( $a ; ) \n";
                break;
            case 1 :
                str += "    Object() ";
                str += "    object( $a ; ) \n";
                break;
            case 2 :
                str += "    object( $a ; ) \n";
                str += "    Object() ";
                break;
        }
        str +=
                "then \n" +
                        "#   System.out.println( \"Object by query: \" + $a );\n" +
                        "    queryList.add( $a ); \n" +
                        "end \n" +
                        "rule findObject \n" +
                        "salience 10 \n" +
                        "when \n" +
                        "    $o: Object() \n" +
                        "then " +
                        "#   System.out.println( \"Object: \" + $o );\n" +
                        "    ruleList.add( $o ); \n" +
                        "end \n" +
                        "";

        System.out.println( str );

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );
        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        ksession.setGlobal( "queryList",
                            queryList );
        ksession.setGlobal( "ruleList",
                            ruleList );
        for ( Object o : objects ) {
            ksession.insert( o );
        }
        ksession.fireAllRules();
        ksession.dispose();
    }

    @Test
    public void testQueryWithObject() throws Exception {
        String str = "" +
                     "package org.drools.test  \n" +

                     "import java.util.List\n" +
                     "import java.util.ArrayList\n" +

                     "global List list\n" +

                     "dialect \"mvel\"\n" +
                     "\n" +

                     "import org.drools.integrationtests.BackwardChainingTest.Q\n" +
                     "import org.drools.integrationtests.BackwardChainingTest.R\n" +
                     "import org.drools.integrationtests.BackwardChainingTest.S\n" +

                     "query object(Object o)\n" +
                     "    o := Object() \n" +
                     "end\n" +

                     "rule collectObjects when\n" +
                     "   String( this == 'go1' )\n" +
                     "   object( o; )\n" +
                     "then\n" +
                     "   list.add( o );\n" +
                     "end\n" +

                     "rule init when\n" +
                     "   String( this == 'init' )\n" +
                     "then\n" +
                     " insert( new Q(1) );\n " +
                     " insert( new Q(5) );\n " +
                     " insert( new Q(6) );\n " +
                     " insert( new R(1) );\n " +
                     " insert( new R(4) );\n " +
                     " insert( new R(6) );\n " +
                     " insert( new R(2) );\n " +
                     " insert( new S(2) );\n " +
                     " insert( new S(3) );\n " +
                     " insert( new S(6) );\n " +
                     "end\n" +
                     "";

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                      ResourceType.DRL );

        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        kbase = SerializationHelper.serializeObject( kbase );

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        List<Integer> list = new ArrayList<Integer>();
        ksession.setGlobal( "list",
                            list );

        ksession.insert( "init" );
        ksession.fireAllRules();

        ksession.insert( "go1" );
        ksession.fireAllRules();

        assertEquals( 13,
                      list.size() );
        assertContains( new Object[]{"go1", "init",
                                new Q( 6 ), new R( 6 ), new S( 3 ), new R( 2 ), new R( 1 ), new R( 4 ), new S( 2 ), new S( 6 ), new Q( 1 ), new Q( 5 )},
                        list );

        // now reverse the go1 and init order
        ksession = kbase.newStatefulKnowledgeSession();
        list = new ArrayList<Integer>();
        ksession.setGlobal( "list",
                            list );

        ksession.insert( "go1" );
        ksession.fireAllRules();

        ksession.insert( "init" );
        ksession.fireAllRules();

        assertEquals( 13,
                      list.size() );
        assertContains( new Object[]{"go1", "init",
                                new Q( 6 ), new R( 6 ), new S( 3 ), new R( 2 ), new R( 1 ), new R( 4 ), new S( 2 ), new S( 6 ), new Q( 1 ), new Q( 5 )},
                        list );
    }

    public void assertContains(Object[] objects,
                               List list) {
        for ( Object object : objects ) {
            if ( !list.contains( object ) ) {
                fail( "does not contain:" + object );
            }
        }
    }

    public void assertContains(List objects,
                               List list) {
        if ( !list.contains( objects ) ) {
            fail( "does not contain:" + objects );
        }
    }

    public InternalFactHandle getFactHandle(FactHandle factHandle,
                                            StatefulKnowledgeSession ksession) {
        Map<Integer, FactHandle> handles = new HashMap<Integer, FactHandle>();
        for ( FactHandle fh : ksession.getFactHandles() ) {
            handles.put( ((InternalFactHandle) fh).getId(),
                         fh );
        }
        return (InternalFactHandle) handles.get( ((InternalFactHandle) factHandle).getId() );
    }

    public static class Man
        implements
        Serializable {
        private String name;

        public Man(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public static class Woman
        implements
        Serializable {
        private String name;

        public Woman(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public static class Parent
        implements
        Serializable {
        private String parent;
        private String child;

        public Parent(String parent,
                      String child) {
            this.parent = parent;
            this.child = child;
        }

        public String getParent() {
            return parent;
        }

        public void setParent(String parent) {
            this.parent = parent;
        }

        public String getChild() {
            return child;
        }

        public void setChild(String child) {
            this.child = child;
        }
    }

    //    public static class Room {
    //        private String name;
    //
    //        public Room(String name) {
    //            this.name = name;
    //        }
    //
    //        public String getName() {
    //            return name;
    //        }
    //
    //        public void setName(String name) {
    //            this.name = name;
    //        }
    //        
    //    }
    //    
    //    public static class Location {
    //        private String thing;
    //        private String location;
    //        
    //        public Location(String thing,
    //                        String location) {
    //            this.thing = thing;
    //            this.location = location;
    //        }
    //
    //        public String getThing() {
    //            return thing;
    //        }
    //
    //        public void setThing(String thing) {
    //            this.thing = thing;
    //        }
    //
    //        public String getLocation() {
    //            return location;
    //        }
    //
    //        public void setLocation(String location) {
    //            this.location = location;
    //        }
    //    }
    //    
    //    public static class Door {
    //        private String fromLocation;
    //        private String toLocation;
    //        
    //        public Door(String fromLocation,
    //                    String toLocation) {
    //            this.fromLocation = fromLocation;
    //            this.toLocation = toLocation;
    //        }
    //        public String getFromLocation() {
    //            return fromLocation;
    //        }
    //        public void setFromLocation(String fromLocation) {
    //            this.fromLocation = fromLocation;
    //        }
    //        public String getToLocation() {
    //            return toLocation;
    //        }
    //        public void setToLocation(String toLocation) {
    //            this.toLocation = toLocation;
    //        }   
    //    }
    //    
    //    public static class Edible {
    //        private String thing;
    //
    //        public Edible(String thing) {
    //            this.thing = thing;
    //        }
    //
    //        public String getThing() {
    //            return thing;
    //        }
    //
    //        public void setThing(String thing) {
    //            this.thing = thing;
    //        }        
    //    }
    //    
    //    public static class TastesYucky {
    //        private String thing;
    //
    //        public TastesYucky(String thing) {
    //            this.thing = thing;
    //        }
    //
    //        public String getThing() {
    //            return thing;
    //        }
    //
    //        public void setThing(String thing) {
    //            this.thing = thing;
    //        }        
    //    }    
    //    
    //    public static class TurnedOff {
    //        private String thing;
    //
    //        public TurnedOff(String thing) {
    //            this.thing = thing;
    //        }
    //
    //        public String getThing() {
    //            return thing;
    //        }
    //
    //        public void setThing(String thing) {
    //            this.thing = thing;
    //        }
    //    }
    //    
    //    public static class Here {
    //        private String location;
    //
    //        public Here(String location) {
    //            this.location = location;
    //        }
    //
    //        public String getLocation() {
    //            return location;
    //        }
    //
    //        public void setLocation(String location) {
    //            this.location = location;
    //        }
    //    }

    public static class Q {
        int value;

        public Q(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public String toString() {
            return "Q" + value;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + value;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if ( this == obj ) return true;
            if ( obj == null ) return false;
            if ( getClass() != obj.getClass() ) return false;
            Q other = (Q) obj;
            if ( value != other.value ) return false;
            return true;
        }

    }

    public static class R {
        int value;

        public R(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public String toString() {
            return "R" + value;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + value;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if ( this == obj ) return true;
            if ( obj == null ) return false;
            if ( getClass() != obj.getClass() ) return false;
            R other = (R) obj;
            if ( value != other.value ) return false;
            return true;
        }
    }

    public static class S {
        int value;

        public S(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public String toString() {
            return "S" + value;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + value;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if ( this == obj ) return true;
            if ( obj == null ) return false;
            if ( getClass() != obj.getClass() ) return false;
            S other = (S) obj;
            if ( value != other.value ) return false;
            return true;
        }

    }

}
