/// <summary> Here is a description of the compilation algorithm. /// 1. iterate over the conditional elements /// i. generate the alpha nodes /// a. literal constraints generate alpha node /// b. predicate constaints that compare against a literal generate alpha node /// ii. calculate the bindings /// a. each binding has a rowId /// b. NOT and EXIST CE do not increment the rowId /// 2. iterate over the conditional elements /// i. generate the beta nodes /// ii. attach the Left Input adapater nodes /// iii. attach the join nodes to the alpha nodes /// 3. create the terminal node and attach to the last /// join node. /// /// This means the rule compiler takes a 2 pass approach to /// compiling rules. At the start of the method, it sets 3 /// attributes to null: prevCE, prevJoinNode, joinNode. /// Those attributes are used by the compile join methods, /// so it's important to set it to null at the start. If /// we don't the Current rule won't compile correctly. /// </summary> public virtual bool addRule(Rule.IRule rule) { rule.resolveTemplates(engine); if (!validate || (validate && tval.analyze(rule) == Analysis_Fields.VALIDATION_PASSED)) { // we have to set the attributes to null, before we start compiling a rule. // we've set the attributes to null, so we can compile now!! if (rule.Conditions != null && rule.Conditions.Length > 0) { // we check the name of the rule to see if it is for a specific // module. if it is, we have to Add it to that module Module = rule; try { ICondition[] conds = rule.Conditions; // first we create the constraints, before creating the Conditional // elements which include joins // we use a counter and only increment it to make sure the // row index of the bindings are accurate. this makes it simpler // for the rule compiler and compileJoins is cleaner and does // less work. int counter = 0; for (int idx = 0; idx < conds.Length; idx++) { ICondition con = conds[idx]; // compile object conditions //implement in the ObjectConditionCompiler.compile or ExistConditionCompiler.compile con.getCompiler(this).compile(con, counter, rule, rule.RememberMatch); if ((con is ObjectCondition) && (!((ObjectCondition) con).Negated)) { counter++; } } // now we compile the joins compileJoins(rule); BaseNode last = rule.LastNode; TerminalNode tnode = createTerminalNode(rule); attachTerminalNode(last, tnode); // compile the actions compileActions(rule, rule.Actions); // now we pass the bindings to the rule, so that actiosn can // resolve the bindings // now we Add the rule to the module currentMod.addRule(rule); CompileMessageEventArgs ce = new CompileMessageEventArgs(rule, EventType.ADD_RULE_EVENT); ce.Rule = rule; notifyListener(ce); return true; } catch (AssertException e) { CompileMessageEventArgs ce = new CompileMessageEventArgs(rule, EventType.INVALID_RULE); ce.Message = Messages.getString("RuleCompiler.assert.error"); //$NON-NLS-1$ notifyListener(ce); TraceLogger.Instance.Debug(e); return false; } } else if (rule.Conditions.Length == 0) { Module = rule; // the rule has no LHS, this means it only has actions BaseNode last = (BaseNode) inputnodes.Get(engine.initFact); TerminalNode tnode = createTerminalNode(rule); compileActions(rule, rule.Actions); attachTerminalNode(last, tnode); // now we Add the rule to the module currentMod.addRule(rule); CompileMessageEventArgs ce = new CompileMessageEventArgs(rule, EventType.ADD_RULE_EVENT); ce.Rule = rule; notifyListener(ce); return true; } return false; } else { // we need to print out a message saying the rule was not valid ISummary error = tval.Errors; engine.writeMessage("Rule " + rule.Name + " was not added. ", Constants.DEFAULT_OUTPUT); //$NON-NLS-1$ //$NON-NLS-2$ engine.writeMessage(error.Message, Constants.DEFAULT_OUTPUT); ISummary warn = tval.Warnings; engine.writeMessage(warn.Message, Constants.DEFAULT_OUTPUT); return false; } }