View Javadoc

1   /*
2   * The contents of this file are subject to the BT "ZEUS" Open Source 
3   * Licence (L77741), Version 1.0 (the "Licence"); you may not use this file 
4   * except in compliance with the Licence. You may obtain a copy of the Licence
5   * from $ZEUS_INSTALL/licence.html or alternatively from
6   * http://www.labs.bt.com/projects/agents/zeus/licence.htm
7   * 
8   * Except as stated in Clause 7 of the Licence, software distributed under the
9   * Licence is distributed WITHOUT WARRANTY OF ANY KIND, either express or 
10  * implied. See the Licence for the specific language governing rights and 
11  * limitations under the Licence.
12  * 
13  * The Original Code is within the package zeus.*.
14  * The Initial Developer of the Original Code is British Telecommunications
15  * public limited company, whose registered office is at 81 Newgate Street, 
16  * London, EC1A 7AJ, England. Portions created by British Telecommunications 
17  * public limited company are Copyright 1996-9. All Rights Reserved.
18  * 
19  * THIS NOTICE MUST BE INCLUDED ON ANY COPY OF THIS FILE
20  */
21  
22  
23  
24  package zeus.rete;
25  
26  import java.util.*;
27  import zeus.util.*;
28  import zeus.actors.AgentContext;
29  import zeus.concepts.*;
30  import zeus.concepts.fn.*;
31  import zeus.actors.event.*;
32  import zeus.actors.*;
33  
34  public class ReteEngine {
35     static final int RULE_ACTIVATED   = 0;
36     static final int RULE_DEACTIVATED = 1;
37     static final int RULE_ADDED       = 2;
38     static final int RULE_REMOVED     = 3;
39     static final int RULE_FIRED       = 4;
40  
41     protected Hashtable factDb = new Hashtable();
42     protected static final String PATH = "path#";
43     protected static final String JPATH = "path@";
44  
45     protected Hashtable roots = new Hashtable();
46     protected Hashtable ids = new Hashtable();
47     protected Vector singles = new Vector();
48     protected Vector joins = new Vector();
49     protected Vector tests = new Vector();
50  
51     protected int pathNo = 0;
52     protected int joinPathNo = 0;
53     protected ConflictSet conflictSet;
54     protected Fact initial_fact = null;
55     protected AgentContext context = null;
56     protected OntologyDb ontologyDb = null;
57     private   HSet[] eventMonitor = new HSet[5];
58     private   HSet   localMonitor = new HSet();
59  
60  
61     public ReteEngine () {
62     ;
63     }
64  
65     public ReteEngine(AgentContext context) {
66        Assert.notNull(context);
67        this.context = context;
68        context.set(this);
69  
70        for(int i = 0; i < eventMonitor.length; i++ )
71           eventMonitor[i] = new HSet();
72  
73        conflictSet = new ConflictSet(context);
74        initial_fact = context.OntologyDb().getFact(Fact.FACT,Rule.INITIAL_FACT);
75        ResourceDb database = context.ResourceDb();
76        database.addFactMonitor(new SymFactAction(),
77                                FactEvent.ADD_MASK|FactEvent.DELETE_MASK,true);
78     }
79  
80  
81     public ReteEngine(OntologyDb ontologyDb) {
82        Assert.notNull(ontologyDb);
83        this.ontologyDb = ontologyDb;
84  
85        for(int i = 0; i < eventMonitor.length; i++ )
86           eventMonitor[i] = new HSet();
87  
88        conflictSet = new ConflictSet(this,ontologyDb);
89        initial_fact = ontologyDb.getFact(Fact.FACT,Rule.INITIAL_FACT);
90     }
91     
92  
93     public AgentContext getAgentContext() {
94        return context;
95     }
96  
97     public void run() {
98        update(Node.ADD,initial_fact);
99        conflictSet.start();
100    }
101    
102 
103    public void reset() {
104       conflictSet.reset();
105       // reset all join-nodes
106       JoinNode node;
107       for(int i = 0; i < joins.size(); i++ ) {
108          node = (JoinNode)joins.elementAt(i);
109          node.reset();
110       }
111 
112       // reassert initial facts
113       Enumeration enum = factDb.elements();
114       while( enum.hasMoreElements() )
115          update(Node.ADD,(Fact)enum.nextElement());
116    }
117    
118 
119    ConflictSet getConfictSet() {
120       return conflictSet;
121    }
122 
123 
124    Hashtable getFactDb() {
125       return factDb;
126    }
127 
128 
129    public void update(int tag, ReteFact token) {
130      // System.out.println("called update with retefact "+ token.type); 
131       Fact f = ontology().getFact(Fact.FACT,token.type);
132       String attribute;
133       ValueFunction value;
134       Enumeration enum = token.data.keys();
135       while( enum.hasMoreElements() ) {
136          attribute = (String)enum.nextElement();
137          value = (ValueFunction)token.data.get(attribute);
138          f.setValue(attribute,value);
139       }
140       update(tag,f);
141    }
142 
143 
144     /*** 
145       *  Update is the method which is called to decide if a rule should be fired or not 
146       *  when a fact is added to the ResourceDb. 
147       * Change Log
148       * ----------
149       * 26/06/01 - 1.3 - Simon Thompson added some code to check not only for rootnodes of 
150       * the same type as the added fact, but also the ancestors of the fact to. 
151       * However, note that only one rule will be fired! 
152       */
153    public  void update(int tag, Fact f) {
154     // System.out.println("called update in reteengine " + f.getId() + f.toString()); 
155       if ( context == null ) { // i.e. isLocal
156          if ( tag == Node.ADD )
157             factDb.put(f.getId(),f);
158          else if ( tag == Node.REMOVE )
159             factDb.remove(f.getId());
160       }
161       fireLocalFactMonitor(tag, f);
162       Vector input = new Vector();
163       input.addElement(f);
164       /*** 
165         change */
166         boolean fired = false; 
167     
168       TypeNode node = (TypeNode)roots.get(f.getType()); // originall
169       if ( node != null ) {
170         fired = true; 
171              synchronized(conflictSet.queue) {
172             node.evaluate(null, tag, Node.SINGLE, input, new Bindings());
173              }
174         
175       }// original
176       Iterator supers = ontology().allAncestors(f.getType()); //added
177       while (supers.hasNext()&& !fired) {
178       node = (TypeNode)roots.get((String)supers.next());
179       if ( node != null ) {
180            fired = true;
181            synchronized(conflictSet.queue) {
182                 node.evaluate(null, tag, Node.SINGLE, input, new Bindings());
183         }//added
184       
185       }// end change
186       }
187    }
188 
189 
190    private OntologyDb ontology() {
191       return ((ontologyDb != null) ? ontologyDb : context.OntologyDb());
192    }
193 
194 
195    protected class SymFactAction extends FactAdapter {
196       public void factAddedEvent(FactEvent event) {
197          Fact f = event.getFact();
198          update(Node.ADD,f);
199       }
200       public void factDeletedEvent(FactEvent event) {
201          Fact f = event.getFact();
202          update(Node.REMOVE,f);
203       }
204    }
205 
206    public void add(ReteKB kb) {
207       Rule[] rule = kb.getRules();
208       for(int i = 0; i < rule.length; i++ ) {
209          rule[i].setName(kb.getName() + "$" + rule[i].getName());
210          add(rule[i]);
211       }
212    }
213 
214    public void add(Rule r) {
215       Node node = null;
216       Node[] term;
217       ReteFact token;
218       boolean found;
219       String path;
220       Pattern p;
221 
222       r = r.duplicate(Fact.VAR,ontology().GenSym());
223 
224       Core.DEBUG(2,"Compiling rule " + r.name);
225 
226       term = new Node[r.nTerminals()];
227       int position = 0;
228       for(int i = 0; i < r.patterns.size(); i++, position++ ) {
229          p = (Pattern)r.patterns.elementAt(i);
230          switch(p.tag) {
231             case Pattern.TEST:
232             case Pattern.CMD:
233                  position--;
234                  break;
235 
236             default:
237                  path = PATH + (pathNo + position);
238                  token = (ReteFact)p.data;
239 
240                  // create TypeNode if roots does not already contain an
241                  // appropriate type node;
242                  if ( roots.containsKey(token.type) ) {
243                     term[position] = (Node)roots.get(token.type);
244                     Core.DEBUG(4,"=t");
245                     Core.DEBUG(5,"=t[" + term[position] + "]");
246                  }
247                  else {
248                     term[position] = new TypeNode(this,token.type);
249                     roots.put(token.type,term[position]);
250                     Core.DEBUG(4,"+t");
251                     Core.DEBUG(5,"+t[" + term[position] + "]");
252                  }
253                  term[position].use_count++;
254                  node = term[position];
255 
256                  // if fact id is needed then create id node
257                  // check if ids already contains node
258                  if ( p.id != null ) {
259                     if ( ids.containsKey(p.id) ) {
260                        term[position] = (Node)ids.get(p.id);
261                        Core.DEBUG(4,"=i");
262                        Core.DEBUG(5,"=i[" + term[position] + "]");
263                     }
264                     else {
265                        term[position] = new IdNode(this,p.id);
266                        ids.put(p.id,term[position]);
267                        Core.DEBUG(4,"+i");
268                        Core.DEBUG(5,"+i[" + term[position] + "]");
269                     }
270                     term[position].use_count++;
271                     node.addSuccessor(path,term[position],Node.SINGLE);
272                     node = term[position];
273                  }
274 
275                  // For each attribute value create attribute node
276                  // check if 'singles' already contains node
277                  ValueFunction[] value = token.listValues();
278                  String[] attribute = token.listAttributes();
279 
280                  // For processing efficiency, do determinates first
281                  Vector temp1 = new Vector();
282                  String a;
283                  ValueFunction v;
284 
285                  for(int k = 0; k < value.length; k++ ) {
286                     if ( value[k].isDeterminate() ) {
287                        term[position] =
288                           new AttributeNode(this,attribute[k],value[k]);
289                        found = false;
290                        for(int j = 0; !found && j < singles.size(); j++ ) {
291                           found = term[position].equals(singles.elementAt(j));
292                           if (found)
293                              term[position] = (Node)singles.elementAt(j);
294                        }
295                        if ( !found ) {
296                           singles.addElement(term[position]);
297                           Core.DEBUG(4,"+d");
298                           Core.DEBUG(5,"+d[" + term[position] + "]");
299                        }
300                        else {
301                           Core.DEBUG(4,"=d");
302                           Core.DEBUG(5,"=d[" + term[position] + "]");
303                        }
304                        term[position].use_count++;
305                        node.addSuccessor(path,term[position],Node.SINGLE);
306                        node = term[position];
307                     }
308                     else {
309                        temp1.addElement(attribute[k]);
310                        temp1.addElement(value[k]);
311                     }
312                  }
313 
314                  // Repeat for indeterminates
315                  for(int k = 0; k < temp1.size(); k += 2 ) {
316                     a = (String)temp1.elementAt(k);
317                     v = (ValueFunction)temp1.elementAt(k+1);
318                     term[position] = new AttributeNode(this,a,v);
319                     found = false;
320                     for(int j = 0; !found && j < singles.size(); j++ ) {
321                        found = term[position].equals(singles.elementAt(j));
322                        if (found)
323                           term[position] = (Node)singles.elementAt(j);
324                     }
325                     if ( !found ) {
326                        singles.addElement(term[position]);
327                        Core.DEBUG(4,"+n");
328                        Core.DEBUG(5,"+n[" + term[position] + "]");
329                     }
330                     else {
331                        Core.DEBUG(4,"=n");
332                        Core.DEBUG(5,"=n[" + term[position] + "]");
333                     }
334                     term[position].use_count++;
335                     node.addSuccessor(path,term[position],Node.SINGLE);
336                     node = term[position];
337                  }
338                  break;
339          }
340       }
341 
342       // Now, the pattern nodes for each rule have been created
343       // we create the join nodes
344 
345 //System.out.println("Creating jn nodes for\n" + r);
346 
347       position = 0;
348       JoinNode[] jn = new JoinNode[term.length-1];
349 
350       for(int i = 1; i < r.patterns.size(); i++, position++ ) {
351          p = (Pattern)r.patterns.elementAt(i);
352 
353          switch( p.tag ) {
354             case Pattern.TEST:
355             case Pattern.CMD:
356                  position--;
357                  break;
358 
359             default:
360                  token = (ReteFact)p.data;
361 
362                  jn[position] = (p.tag == Pattern.NONE)
363                               ? new JoinNode(this) : new NotNode(this);
364 
365                  ValueFunction[] r_value = token.listValues();
366                  String[] r_attribute = token.listAttributes();
367 
368                  ValueFunction[] vars = token.variables();
369 
370                  int q = -1;
371                  for(int j = 0; j < i; j++) {
372                     p = (Pattern)r.patterns.elementAt(j);
373                     if ( p.tag == Pattern.NONE ) {
374                        q++;
375                        token = (ReteFact)p.data;
376 
377                        ValueFunction[] l_value = token.listValues();
378                        String[] l_attribute = token.listAttributes();
379 
380                        boolean[][] local =
381                           new boolean[l_value.length][r_value.length];
382 
383                        for(int m = 0; m < local.length; m++ )
384                        for(int n = 0; n < local[m].length; n++ )
385                           local[m][n] = false;
386 
387                        for(int k = 0; k < vars.length; k++ ) {
388                           for(int m = 0; m < l_value.length; m++ ) {
389                              if ( l_value[m].references(vars[k]) ) {
390                                 for(int n = 0; n < r_value.length; n++ ) {
391                                    if ( !local[m][n] &&
392                                         r_value[n].references(vars[k]) ) {
393                                       local[m][n] = true;
394                                       jn[position].add(q,l_attribute[m],l_value[m],
395                                                        0,r_attribute[n],r_value[n]);
396 /*
397 NOT SURE why I used "jn[i-1]" before
398                                       jn[i-1].add(q,l_attribute[m],l_value[m],
399                                                   0,r_attribute[n],r_value[n]);
400 */
401                                    }
402                                 }
403                              }
404                           }
405                        }
406                     }
407                  }
408                  break;
409          }
410       }
411       // all join nodes have been created,
412       // now check for repetition (i.e. node equality) and create graph
413       // in the process, create test & cmd nodes
414 
415       String rightPath;
416 
417       Node leftNode = term[0];
418       String leftPath = PATH+pathNo;
419       position = 0;
420       for(int i = 1; i < r.patterns.size(); i++, position++ ) {
421          p = (Pattern)r.patterns.elementAt(i);
422 
423          switch( p.tag ) {
424             case Pattern.CMD:
425                  position--;
426                  break;
427 
428             case Pattern.TEST:
429                  position--;
430                  node = leftNode; // store previous
431                  leftNode = new TestNode(this,(ValueFunction)p.data);
432                  found = false;
433                  for(int j = 0; !found && j < tests.size(); j++ ) {
434                     found = leftNode.equals(tests.elementAt(j));
435                     if (found) leftNode = (Node)tests.elementAt(j);
436                  }
437                  if ( !found ) {
438                     tests.addElement(leftNode);
439                     Core.DEBUG(4,"+c");
440                     Core.DEBUG(5,"+c[" + leftNode + "]");
441                  }
442                  else {
443                     Core.DEBUG(4,"=c");
444                     Core.DEBUG(5,"=c[" + leftNode + "]");
445                  }
446                  leftNode.use_count++;
447                  node.addSuccessor(leftPath,leftNode,Node.SINGLE);
448                  break;
449 
450             default:
451                  // Repetition check
452                  found = false;
453                  for(int j = 0; !found && j < joins.size(); j++ ) {
454                     found = jn[position].equals(joins.elementAt(j));
455                     if ( found ) jn[position] = (JoinNode)joins.elementAt(j);
456                  }
457                  if ( !found ) {
458                     joins.addElement(jn[position]);
459                     Core.DEBUG(4,"+j");
460                     Core.DEBUG(5,"+j[" + jn[position] + "]");
461                  }
462                  else {
463                     Core.DEBUG(4,"=j");
464                     Core.DEBUG(5,"=j[" + jn[position] + "]");
465                  }
466                  jn[position].use_count++;
467                  path = JPATH + (joinPathNo++);
468                  rightPath = PATH+(pathNo+position+1);
469 
470                  jn[position].addPath(leftPath,rightPath,path);
471                  leftNode.addSuccessor(leftPath,jn[position],Node.LEFT);
472                  term[position+1].addSuccessor(rightPath,jn[position],Node.RIGHT);
473                  leftNode = jn[position];
474                  leftPath = path;
475                  break;
476          }
477       }
478 
479       // now, all that is left is the action node
480       // first determine the last node: which should be 'leftNode'
481 
482       ActionNode action = new ActionNode(this,r.name,r.salience,r.actions);
483       action.use_count++;
484       leftNode.addSuccessor(leftPath,action,Node.ACTION);
485       Core.DEBUG(4,"+a");
486       Core.DEBUG(5,"+a[" + action + "]");
487       pathNo += term.length;
488 
489       String diagnostic = "ADD [" + action.rule_name + "]";
490       fireEvent(RULE_ADDED,action,diagnostic);
491    }
492 
493 
494    public void remove(Rule r) {
495       Core.USER_ERROR("Function ReteEngine.remove(Rule r) not yet implemented");
496    }
497 
498 
499    public void addLocalFactMonitor(LocalFactMonitor monitor) {
500       localMonitor.add(monitor);
501    }
502    
503    
504    public void removeLocalFactMonitor(LocalFactMonitor monitor) {
505       localMonitor.remove(monitor);
506    }
507 
508 
509    void fireLocalFactMonitor(int tag, Fact f) {
510       LocalFactMonitor monitor;
511       Enumeration enum = localMonitor.elements();
512       while( enum.hasMoreElements() ) {
513          monitor = (LocalFactMonitor)enum.nextElement();
514          if ( tag == Node.ADD ) 
515             monitor.reteFactAdded(f);
516          else
517             monitor.reteFactDeleted(f);
518       }
519    }
520 
521 
522    public void addMonitor(ReteEngineMonitor monitor, long event_type) {
523       if ( (event_type & ReteEngineEvent.ADD_MASK) != 0 )
524          eventMonitor[RULE_ADDED].add(monitor);
525       if ( (event_type & ReteEngineEvent.DELETE_MASK) != 0 )
526          eventMonitor[RULE_REMOVED].add(monitor);
527       if ( (event_type & ReteEngineEvent.ACTIVATE_MASK) != 0 )
528          eventMonitor[RULE_ACTIVATED].add(monitor);
529       if ( (event_type & ReteEngineEvent.DEACTIVATE_MASK) != 0 )
530          eventMonitor[RULE_DEACTIVATED].add(monitor);
531       if ( (event_type & ReteEngineEvent.FIRE_MASK) != 0 )
532          eventMonitor[RULE_FIRED].add(monitor);
533    }
534    public void removeMonitor(ReteEngineMonitor monitor, long event_type) {
535       if ( (event_type & ReteEngineEvent.ADD_MASK) != 0 )
536          eventMonitor[RULE_ADDED].remove(monitor);
537       if ( (event_type & ReteEngineEvent.DELETE_MASK) != 0 )
538          eventMonitor[RULE_REMOVED].remove(monitor);
539       if ( (event_type & ReteEngineEvent.ACTIVATE_MASK) != 0 )
540          eventMonitor[RULE_ACTIVATED].remove(monitor);
541       if ( (event_type & ReteEngineEvent.DEACTIVATE_MASK) != 0 )
542          eventMonitor[RULE_DEACTIVATED].remove(monitor);
543       if ( (event_type & ReteEngineEvent.FIRE_MASK) != 0 )
544          eventMonitor[RULE_FIRED].remove(monitor);
545    }
546 
547 
548    void fireEvent(int type, ActionNode node, String diagnostic) {
549       if ( eventMonitor[type].isEmpty() ) return;
550 
551       ReteEngineMonitor monitor;
552       ReteEngineEvent event;
553       Enumeration enum = eventMonitor[type].elements();
554       switch(type) {
555          case RULE_ADDED:
556               event = new ReteEngineEvent(this,node.rule_name,node.salience,diagnostic,ReteEngineEvent.ADD_MASK);
557               while( enum.hasMoreElements() ) {
558                  monitor = (ReteEngineMonitor)enum.nextElement();
559                  monitor.reteRuleAddedEvent(event);
560               }
561               break;
562          case RULE_REMOVED:
563               event = new ReteEngineEvent(this,node.rule_name,node.salience,diagnostic,ReteEngineEvent.DELETE_MASK);
564               while( enum.hasMoreElements() ) {
565                  monitor = (ReteEngineMonitor)enum.nextElement();
566                  monitor.reteRuleDeletedEvent(event);
567               }
568               break;
569          case RULE_ACTIVATED:
570               event = new ReteEngineEvent(this,node.rule_name,node.salience,diagnostic,ReteEngineEvent.ACTIVATE_MASK);
571               while( enum.hasMoreElements() ) {
572                  monitor = (ReteEngineMonitor)enum.nextElement();
573                  monitor.reteRuleActivatedEvent(event);
574               }
575               break;
576          case RULE_DEACTIVATED:
577               event = new ReteEngineEvent(this,node.rule_name,node.salience,diagnostic,ReteEngineEvent.DEACTIVATE_MASK);
578               while( enum.hasMoreElements() ) {
579                  monitor = (ReteEngineMonitor)enum.nextElement();
580                  monitor.reteRuleDeactivatedEvent(event);
581               }
582               break;
583          case RULE_FIRED:
584               event = new ReteEngineEvent(this,node.rule_name,node.salience,diagnostic,ReteEngineEvent.FIRE_MASK);
585               while( enum.hasMoreElements() ) {
586                  monitor = (ReteEngineMonitor)enum.nextElement();
587                  monitor.reteRuleFiredEvent(event);
588               }
589               break;
590       }
591    }
592 
593 }