package jadex.rules.test.rulesystem; import jadex.rules.rulesystem.IAction; import jadex.rules.rulesystem.ICondition; import jadex.rules.rulesystem.IRule; import jadex.rules.rulesystem.IVariableAssignments; import jadex.rules.rulesystem.RuleSystem; import jadex.rules.rulesystem.Rulebase; import jadex.rules.rulesystem.rete.RetePatternMatcherFunctionality; import jadex.rules.rulesystem.rules.BoundConstraint; import jadex.rules.rulesystem.rules.CollectCondition; import jadex.rules.rulesystem.rules.FunctionCall; import jadex.rules.rulesystem.rules.IOperator; import jadex.rules.rulesystem.rules.LiteralConstraint; import jadex.rules.rulesystem.rules.ObjectCondition; import jadex.rules.rulesystem.rules.PredicateConstraint; import jadex.rules.rulesystem.rules.Rule; import jadex.rules.rulesystem.rules.Variable; import jadex.rules.rulesystem.rules.functions.ExtractMulti; import jadex.rules.rulesystem.rules.functions.Length; import jadex.rules.rulesystem.rules.functions.OperatorFunction; import jadex.rules.rulesystem.rules.functions.Sum; import jadex.rules.state.IOAVState; import jadex.rules.state.OAVAttributeType; import jadex.rules.state.OAVJavaType; import jadex.rules.state.OAVObjectType; import jadex.rules.state.OAVTypeModel; import jadex.rules.state.javaimpl.OAVStateFactory; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import junit.framework.TestCase; /** * Test collection pattern matching. */ public class CollectNodeTest extends TestCase { //-------- type model -------- protected static OAVTypeModel tmodel; protected static OAVObjectType store_type; protected static OAVObjectType music_type; protected static OAVAttributeType store_has_domain; protected static OAVAttributeType store_has_cds; protected static OAVAttributeType music_has_store; protected static OAVAttributeType music_has_artist; protected static OAVAttributeType music_has_title; protected static OAVAttributeType music_has_price; static { tmodel = new OAVTypeModel("MusicStore"); tmodel.addTypeModel(OAVJavaType.java_type_model); store_type = tmodel.createType("Store"); music_type = tmodel.createType("Music"); store_has_domain = store_type.createAttributeType("store_has_domain", OAVJavaType.java_string_type); store_has_cds = store_type.createAttributeType("store_has_cds", music_type, OAVAttributeType.LIST); music_has_store = music_type.createAttributeType("music_has_store", store_type); music_has_artist = music_type.createAttributeType("music_has_artist", OAVJavaType.java_string_type); music_has_title = music_type.createAttributeType("music_has_title", OAVJavaType.java_string_type); music_has_price = music_type.createAttributeType("music_has_price", OAVJavaType.java_double_type); } //-------- attributes -------- /** The state. */ protected IOAVState state; /** The rule system. */ protected RuleSystem system; /** The store. */ protected Object store; /** The music 1. */ protected Object music1; /** The music 2. */ protected Object music2; /** The list of triggered stores. */ protected List stores; /** The list of triggered music collections. */ protected List collections; //-------- constructors -------- /** * Test setup. */ protected void setUp() throws Exception { state = OAVStateFactory.createOAVState(tmodel); stores = new ArrayList(); collections = new ArrayList(); // ?s <- (Store (store_has_domain "music")) // $?c <- (collect (Music (music_has_store ?s) (music_has_artist "Miles Davis")) // :equal(length($?c), 3)) // Alternative with contains (first object condition is used for collection): // // ?s <- (Store (store_has_domain "music")) // //?u <- (User (use_has_name "hugo")) // $?c <- (collect // ?m <- (Music (music_has_artist "Miles Davis")) // ?s <- (Store (store_has_cds contains ?m)) // //?u <- (User (user_has_recommendations contains ?m))) // Matches a store in the "music" domain. /* ObjectCondition cstore = new ObjectCondition(store_type); cstore.addConstraint(new AttributeBoundConstraint(null, new Variable("?store", store_type))); cstore.addConstraint(new LiteralConstraint(store_has_domain, "music")); // Matches music from "Miles Davis" in store ?s. ObjectCondition cmusic = new ObjectCondition(music_type); cmusic.addConstraint(new LiteralConstraint(music_has_artist, "Miles Davis")); cmusic.addConstraint(new AttributeBoundConstraint(music_has_store, new Variable("?store", store_type))); // Collect music belonging to the same store and check that length==3. CollectCondition collect = new CollectCondition(cmusic); collect.addConstraint(new AttributeBoundConstraint(null, new Variable("$?collection", music_type, true))); FunctionCall fc_length = new FunctionCall(new Length(), new Object[]{new Variable("$?collection", music_type, true)}); FunctionCall fc_length3 = new FunctionCall(new OperatorFunction(IOperator.EQUAL), new Object[]{fc_length, new Integer(3)}); collect.addConstraint(new PredicateConstraint(fc_length3)); FunctionCall fc_extract = new FunctionCall(new ExtractMulti(music_has_price), new Object[]{new Variable("$?collection", music_type, true)}); FunctionCall fc_sum = new FunctionCall(new Sum(), new Object[]{fc_extract}); FunctionCall fc_sum30 = new FunctionCall(new OperatorFunction(IOperator.LESS), new Object[]{fc_sum, new Integer(30)}); collect.addConstraint(new PredicateConstraint(fc_sum30)); // Add block of triggered condition to list. ICondition cond = new AndCondition(new ICondition[]{cstore, collect}); IRule rule = new Rule("collect_rule", cond, new IAction() { public void execute(IOAVState state, IVariableAssignments assigments) { Object store = assigments.getVariableValue("?store"); Object collection = assigments.getVariableValue("$?collection"); stores.add(store); collections.add(collection); } }); // Create rule system. Rulebase rb = new Rulebase(); system = new RuleSystem(state,rb, new RetePatternMatcherFunctionality(rb)); system.getRulebase().addRule(rule); system.init(); // Add store and musics. store = state.createRootObject(store_type); state.setAttributeValue(store, store_has_domain, "music"); music1 = state.createRootObject(music_type); state.setAttributeValue(music1, music_has_artist, "Miles Davis"); state.setAttributeValue(music1, music_has_title, "Kind of Blue"); state.setAttributeValue(music1, music_has_store, store); state.setAttributeValue(music1, music_has_price, new Integer(10)); music2 = state.createRootObject(music_type); state.setAttributeValue(music2, music_has_artist, "Miles Davis"); state.setAttributeValue(music2, music_has_title, "Bitches Brew"); state.setAttributeValue(music2, music_has_store, store); state.setAttributeValue(music2, music_has_price, new Integer(10)); */ // Matches music from "Miles Davis". ObjectCondition cmusic = new ObjectCondition(music_type); cmusic.addConstraint(new BoundConstraint(null, new Variable("?music", music_type))); cmusic.addConstraint(new LiteralConstraint(music_has_artist, "Miles Davis")); // Matches a store in the "music" domain. ObjectCondition cstore = new ObjectCondition(store_type); cstore.addConstraint(new BoundConstraint(null, new Variable("?store", store_type))); cstore.addConstraint(new LiteralConstraint(store_has_domain, "music")); cstore.addConstraint(new BoundConstraint(store_has_cds, new Variable("?music", music_type), IOperator.CONTAINS)); // Collect music belonging to the same store and check that length==3. CollectCondition collect = new CollectCondition(new ObjectCondition[]{cmusic, cstore}, null); collect.addConstraint(new BoundConstraint(null, new Variable("$?collection", music_type, true, false))); FunctionCall fc_length = new FunctionCall(new Length(), new Object[]{new Variable("$?collection", music_type, true, false)}); FunctionCall fc_length3 = new FunctionCall(new OperatorFunction(IOperator.EQUAL), new Object[]{fc_length, new Integer(3)}); collect.addConstraint(new PredicateConstraint(fc_length3)); FunctionCall fc_extract = new FunctionCall(new ExtractMulti(music_has_price), new Object[]{new Variable("$?collection", music_type, true, false)}); FunctionCall fc_sum = new FunctionCall(new Sum(), new Object[]{fc_extract}); FunctionCall fc_sum30 = new FunctionCall(new OperatorFunction(IOperator.LESS), new Object[]{fc_sum, new Integer(30)}); collect.addConstraint(new PredicateConstraint(fc_sum30)); // Add block of triggered condition to list. ICondition cond = collect;//new AndCondition(new ICondition[]{collect}); IRule rule = new Rule("collect_rule", cond, new IAction() { public void execute(IOAVState state, IVariableAssignments assigments) { Object store = assigments.getVariableValue("?store"); Object collection = assigments.getVariableValue("$?collection"); stores.add(store); collections.add(collection); } }); // Create rule system. Rulebase rb = new Rulebase(); system = new RuleSystem(state,rb, new RetePatternMatcherFunctionality(rb)); system.getRulebase().addRule(rule); system.init(); music1 = state.createRootObject(music_type); state.setAttributeValue(music1, music_has_artist, "Miles Davis"); state.setAttributeValue(music1, music_has_title, "Kind of Blue"); state.setAttributeValue(music1, music_has_store, store); state.setAttributeValue(music1, music_has_price, new Integer(10)); music2 = state.createRootObject(music_type); state.setAttributeValue(music2, music_has_artist, "Miles Davis"); state.setAttributeValue(music2, music_has_title, "Bitches Brew"); state.setAttributeValue(music2, music_has_store, store); state.setAttributeValue(music2, music_has_price, new Integer(10)); // Add store and musics. store = state.createRootObject(store_type); state.setAttributeValue(store, store_has_domain, "music"); state.addAttributeValue(store, store_has_cds, music1); state.addAttributeValue(store, store_has_cds, music2); // state.notifyEventListeners(); // RuleEnginePanel.createRuleEngineFrame(new RuleSystemExecutor(system, false), "Collect Node Test"); // RetePanel.createReteFrame("Collect Node Test", system, new Object()); // synchronized(system){system.wait();} } //-------- test methods -------- /** * Test that no condition triggers initially. */ public void testNoInitialTrigger() { // ReteMemory rm = ((RetePatternMatcher)system.getMatcher()).getReteMemory(); // System.out.println(rm); system.fireAllRules(); List test = Collections.EMPTY_LIST; assertEquals("No condition should trigger initially", test, stores); assertEquals("No condition should trigger initially", test, collections); } /** * Test left addition, which triggers condition. */ public void testLeftAddTrigger() { // RetePanel.createReteFrame("Collect Node Test", system, new Object()); // Create new music -> should trigger condition. Object music3 = state.createRootObject(music_type); state.setAttributeValue(music3, music_has_artist, "Miles Davis"); state.setAttributeValue(music3, music_has_title, "Sketches of Spain"); state.setAttributeValue(music3, music_has_store, store); state.setAttributeValue(music3, music_has_price, new Integer(1)); state.addAttributeValue(store, store_has_cds, music3); List teststores = Collections.singletonList(store); List testcollections = Collections.singletonList(new HashSet(Arrays.asList(new Object[]{music1, music2, music3}))); system.fireAllRules(); assertEquals("Condition should trigger for store.", teststores, stores); assertEquals("Condition should trigger for collection of three musics.", testcollections, collections); } /** * Test left addition, which triggers condition. */ public void testLeftAddNoTrigger() { // RetePanel.createReteFrame("Collect Node Test", system, new Object()); // Create new music -> should trigger condition. Object music3 = state.createRootObject(music_type); state.setAttributeValue(music3, music_has_artist, "Miles Davis"); state.setAttributeValue(music3, music_has_title, "Sketches of Spain"); state.setAttributeValue(music3, music_has_store, store); state.setAttributeValue(music3, music_has_price, new Integer(1)); state.addAttributeValue(store, store_has_cds, music3); // Create another music -> should remove activation. Object music4 = state.createRootObject(music_type); state.setAttributeValue(music4, music_has_artist, "Miles Davis"); state.setAttributeValue(music4, music_has_title, "Angels of Pain"); state.setAttributeValue(music4, music_has_store, store); state.setAttributeValue(music4, music_has_price, new Integer(1)); state.addAttributeValue(store, store_has_cds, music4); List test = Collections.EMPTY_LIST; system.fireAllRules(); assertEquals("No condition should trigger for 4 musics", test, stores); assertEquals("No condition should trigger for 4 musics", test, collections); } /** * Test left removal, which triggers condition. */ public void testLeftRemovalTrigger() { // RetePanel.createReteFrame("Collect Node Test", system, new Object()); // Create new music -> should trigger condition. Object music3 = state.createRootObject(music_type); state.setAttributeValue(music3, music_has_artist, "Miles Davis"); state.setAttributeValue(music3, music_has_title, "Sketches of Spain"); state.setAttributeValue(music3, music_has_store, store); state.setAttributeValue(music3, music_has_price, new Integer(1)); state.addAttributeValue(store, store_has_cds, music3); Object music4 = state.createRootObject(music_type); state.setAttributeValue(music4, music_has_artist, "Miles Davis"); state.setAttributeValue(music4, music_has_title, "Angels of Pain"); state.setAttributeValue(music4, music_has_store, store); state.setAttributeValue(music4, music_has_price, new Integer(1)); state.addAttributeValue(store, store_has_cds, music4); List test = Collections.EMPTY_LIST; system.fireAllRules(); assertEquals("No condition should trigger initially", test, stores); assertEquals("No condition should trigger initially", test, collections); // RetePanel.createReteFrame("Collect Node Test", system, new Object()); state.dropObject(music3); List teststores = Collections.singletonList(store); List testcollections = Collections.singletonList(new HashSet(Arrays.asList(new Object[]{music1, music2, music4}))); system.fireAllRules(); assertEquals("Condition should trigger for store.", teststores, stores); assertEquals("Condition should trigger for collection of three musics.", testcollections, collections); } /** * Test left removal, which triggers no condition. */ public void testLeftRemovalNoTrigger() { // RetePanel.createReteFrame("Collect Node Test", system, new Object()); Object music3 = state.createRootObject(music_type); state.setAttributeValue(music3, music_has_artist, "Miles Davis"); state.setAttributeValue(music3, music_has_title, "Sketches of Spain"); state.setAttributeValue(music3, music_has_store, store); state.setAttributeValue(music3, music_has_price, new Integer(1)); state.addAttributeValue(store, store_has_cds, music3); Object music4 = state.createRootObject(music_type); state.setAttributeValue(music4, music_has_artist, "Miles Davis"); state.setAttributeValue(music4, music_has_title, "Angels of Pain"); state.setAttributeValue(music4, music_has_store, store); state.setAttributeValue(music4, music_has_price, new Integer(1)); state.addAttributeValue(store, store_has_cds, music4); Object music5 = state.createRootObject(music_type); state.setAttributeValue(music5, music_has_artist, "Miles Davis"); state.setAttributeValue(music5, music_has_title, "Mr. Vain"); state.setAttributeValue(music5, music_has_store, store); state.setAttributeValue(music5, music_has_price, new Integer(1)); state.addAttributeValue(store, store_has_cds, music5); state.dropObject(music3); List test = Collections.EMPTY_LIST; system.fireAllRules(); assertEquals("No condition should trigger initially", test, stores); assertEquals("No condition should trigger initially", test, collections); } /** * Test left modify, which triggers condition. */ public void testLeftModifyTrigger() { // RetePanel.createReteFrame("Collect Node Test", system, new Object()); // Should not trigger because price not less than 30 Object music3 = state.createRootObject(music_type); state.setAttributeValue(music3, music_has_artist, "Miles Davis"); state.setAttributeValue(music3, music_has_title, "Sketches of Spain"); state.setAttributeValue(music3, music_has_store, store); state.setAttributeValue(music3, music_has_price, new Integer(10)); state.addAttributeValue(store, store_has_cds, music3); List test = Collections.EMPTY_LIST; system.fireAllRules(); assertEquals("No condition should trigger initially", test, stores); assertEquals("No condition should trigger initially", test, collections); state.setAttributeValue(music3, music_has_price, new Integer(1)); List teststores = Collections.singletonList(store); List testcollections = Collections.singletonList(new HashSet(Arrays.asList(new Object[]{music1, music2, music3}))); system.fireAllRules(); assertEquals("Condition should trigger for store.", teststores, stores); assertEquals("Condition should trigger for collection of three musics.", testcollections, collections); } /** * Test left modify, which not triggers condition. */ public void testLeftModifyNoTrigger() { // RetePanel.createReteFrame("Collect Node Test", system, new Object()); // Should not trigger because price not less than 30 Object music3 = state.createRootObject(music_type); state.setAttributeValue(music3, music_has_artist, "Miles Davis"); state.setAttributeValue(music3, music_has_title, "Sketches of Spain"); state.setAttributeValue(music3, music_has_store, store); state.setAttributeValue(music3, music_has_price, new Integer(10)); state.addAttributeValue(store, store_has_cds, music3); List test = Collections.EMPTY_LIST; system.fireAllRules(); assertEquals("No condition should trigger initially", test, stores); assertEquals("No condition should trigger initially", test, collections); state.setAttributeValue(music3, music_has_price, new Integer(11)); system.fireAllRules(); assertEquals("No condition should trigger initially", test, stores); assertEquals("No condition should trigger initially", test, collections); } /** * * @param args */ public static void main(String[] args) { try { CollectNodeTest test = new CollectNodeTest(); test.setUp(); test.testNoInitialTrigger(); test.testLeftAddNoTrigger(); test.testLeftModifyNoTrigger(); test.testLeftRemovalNoTrigger(); test.testLeftAddTrigger(); test.testLeftModifyTrigger(); test.testLeftRemovalTrigger(); } catch(Exception e) { e.printStackTrace(); } } }