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  /*
25   * @(#)ResourceDb.java 1.00
26   */
27  package zeus.actors;
28  
29  import java.util.*;
30  import zeus.util.*;
31  import zeus.concepts.*;
32  import zeus.concepts.fn.*;
33  import zeus.actors.event.*;
34  
35  
36  /***
37   * This class implements the Resource Database component, whose role is to
38   * store the resources owned by the agent, (which are {@link Fact} objects).
39   * The Resource Database is simply an extended {@link Hashtable} with the fact
40   * objects held indexed by their types. <p>
41   *
42   * Developers will probably interact the Resource Database on a regular basis,
43   * as it both contains the agent's knowledge and provides a means of altering it. 
44   * A reference to the Resource Database can be obtained from the agent's 
45   * {@link AgentContext} object, allowing access to the methods described below; 
46   * (The methods without descriptions are primarily used by other agent components
47   * and so are unlikely to be useful to developers).
48   
49   <p> <i> Change log </i> <p> 
50   19/08/00 - added some code to the any(Fact) method at about line 580 - 590
51              this is to stop an unnecessary exception being thrown, the new 
52              behavior is that if the method is called and there are no facts of that type
53              in the resourcedb then a new fact of that type will be returned instead. 
54   <P> 
55   Change log 
56   ----------
57   13/06/01 added a number of fields (name,planner,ontologyDb, gensym,externaldb) which are 
58   used to store references that would be obtained from the AgentContext object, but 
59   cannot be if the context is not available at initialisation.
60   I hope that this will enable better extensibility, but it might cause problems if the agent 
61   architecture is altered at run time. 
62   Added isSet method to allow programmers to check to see if 
63   Added setPlanner to permit this reference to be updated easily 
64   Added setName 
65   Added setOntologyDb
66   Added setGenSym 
67   */
68  
69  public class ResourceDb extends Hashtable
70  {
71     private HSet[] eventMonitor = new HSet[4];
72  
73     private static final int ADD    = 0;
74     private static final int MODIFY = 1;
75     private static final int DELETE = 2;
76     private static final int ACCESS = 3;
77  
78     protected AgentContext context = null;
79     protected String name = null;
80     protected Planner planner = null; 
81     protected OntologyDb ontologyDb = null; 
82     protected GenSym gensym = null; 
83     
84  
85     public ResourceDb () {
86       super(); 
87        for(int i = 0; i < eventMonitor.length; i++ )
88           eventMonitor[i] = new HSet();
89     }
90  
91     public ResourceDb(AgentContext context) {
92        super(); 
93        this.context = context;
94        this.name = context.whoami(); 
95        this.gensym = context.GenSym(); 
96        this.planner = context.getPlanner(); 
97        this.ontologyDb = context.getOntologyDb(); 
98        context.set(this);
99  
100       for(int i = 0; i < eventMonitor.length; i++ )
101          eventMonitor[i] = new HSet();
102    }
103 
104 
105     /*** 
106        * this allows you to set the planner reference 
107        */
108     public void setPlanner(Planner planner) { 
109         this.planner = planner; 
110     }
111     
112     
113     /*** 
114         this allows you to set the name reference
115         */
116     public void setName (String name) { 
117         this.name = name; 
118     }
119     
120     
121     /*** 
122         this allows you to set the gensym referencce
123         */
124     public void setGenSym(GenSym gensym) { 
125         this.gensym = gensym; 
126     }
127     
128     
129     /***
130        this allows you to set the ontolgyDb reference
131        */
132     public void setOntologyDb(OntologyDb ontologyDb) { 
133         this.ontologyDb = ontologyDb; 
134     }
135     
136     /*** 
137         check that the name refernce is set
138         */
139     public boolean isNameSet() { 
140         if (name == null) 
141             return false;
142             else 
143                 return true;
144     }
145     
146     /*** 
147         check that the gensym refernece is set
148         */
149     public boolean isGenSymSet() { 
150         if (gensym == null) 
151             return false; 
152             else
153                return true;
154     }
155     
156 
157     /*** 
158         check that the planner reference is set 
159         */
160     public boolean isPlannerSet() { 
161         if (planner == null) 
162             return false; 
163             else 
164                return true; 
165          }
166     
167         
168     /*** 
169         check that the ontologyDb reference is set
170         */
171     public boolean isOntologyDbSet (){ 
172         if (ontologyDb == null) 
173             return false; 
174             else
175                 return true; 
176     }
177     
178     
179     /*** 
180         check that the AgentContext context field is set. 
181         Note: this is not necessary for the rdb to function, but 
182         is included so that 
183         */
184     public boolean isContextSet() { 
185         if (context == null) 
186             return false; 
187             else 
188                 return true; 
189     }   
190     
191     
192     /*** 
193         check to see if all necessary fields are set 
194         */ 
195     public boolean isSet () { 
196         if (isContextSet ()) return true; 
197         else {
198          if (!isGenSymSet () ) {
199                 System.out.println("gensym not set"); 
200                 return false; 
201          }
202          if (!isPlannerSet () ) {
203             System.out.println("planner not set"); 
204             return false; 
205             }
206          if (!isOntologyDbSet () ) {
207             System.out.println("ontologydb not set"); 
208             return false; 
209          }
210          if (!isNameSet () ) {
211              System.out.println("name not set"); 
212              return false; 
213          }
214                 
215         }
216          return true; 
217     }
218         
219         
220      
221         
222 
223    /*** Use this to obtain handles to the other internal components of agent */
224    public AgentContext getAgentContext() { return context; }
225 
226     
227    /*** 
228     was synchronized
229     */ 
230    public synchronized ResourceItem add(Vector List) {
231       for(int i = 0; i < List.size(); i++ )
232          add( (Fact)List.elementAt(i) );
233       return null;
234    }
235    
236    
237    /*** 
238     was synchronized
239     */
240    public synchronized ResourceItem add(Fact[] List) {
241       for(int i = 0; i < List.length; i++ )
242          add( List[i] );
243       return null;
244    }
245 
246    /*** The primary method for adding new facts to the Resource Database 
247        was synchronized
248    */
249    public synchronized ResourceItem add(Fact fact) {
250       String type = fact.getType();
251       ResourceItem item;
252       Vector List;
253       PrimitiveNumericFn numericFn;
254 
255       if ( (List = (Vector)this.get(type)) == null ) {
256          List = new Vector();
257          this.put(type,List);
258       }
259 
260       if ( type.equals(OntologyDb.MONEY) && !List.isEmpty() ) {
261          for(int i = 0; i < List.size(); i++ ) {
262             item = (ResourceItem) List.elementAt(i);
263             notifyMonitors(item,ACCESS);
264             if ( !item.isReserved() ) {
265                Fact f1 = item.getFact();
266                f1.resolve(new Bindings(name));
267                numericFn = (PrimitiveNumericFn)f1.getFn(OntologyDb.AMOUNT);
268                double db_amount = numericFn.doubleValue();
269 
270                fact.resolve(new Bindings(name));
271                numericFn = (PrimitiveNumericFn)fact.getFn(OntologyDb.AMOUNT);
272                double input = numericFn.doubleValue();
273 
274                double total = db_amount + input;
275                if ( total == 0 ) {
276                   // remove item
277                   List.removeElementAt(i--);
278                   item.deleted();
279                   notifyMonitors(item,DELETE);
280                   if ( List.isEmpty() ) this.remove(type);
281                   return null;
282                }
283                else {
284                   List.removeElementAt(i--);
285                   item.deleted();
286                   notifyMonitors(item,DELETE);
287                   f1.setValue(OntologyDb.AMOUNT,total);
288                   f1.resolve(new Bindings(name));
289                   List.addElement(item);
290                   notifyMonitors(item,ADD);
291                   return item;
292                }
293             }
294          }
295       }
296       item = new ResourceItem(fact);
297       List.addElement(item);
298       notifyMonitors(item,ADD);
299       return item;
300    }
301 
302     /*** 
303         was synchronized
304         */
305    synchronized void  replaceOrAdd(Fact fact) {
306       ExternalDb externalDb = context.ExternalDb();
307       if ( externalDb != null && externalDb.put(fact) ) {
308          // ResourceItem item = new ResourceItem(fact);
309          // notifyMonitors(item,ADD);
310       }
311       else
312          add(fact);
313    }
314 
315    public synchronized void del(Vector List) {
316       for(int i = 0; i < List.size(); i++ )
317          del((Fact)List.elementAt(i));
318    }
319 
320    public synchronized void del(Fact[] List) {
321       for(int i = 0; i < List.length; i++ )
322          del(List[i]);
323    }
324 
325 
326    /*** The primary method for permanently removing facts from the Resource Database */
327    public synchronized void del(Fact fact) {
328       String type = fact.getType();
329       Vector List;
330 
331       if ( (List = (Vector)this.get(type)) == null )
332          return;
333 
334       ResourceItem item;
335       Fact f1;
336       for(int i = 0; i < List.size(); i++ ) {
337          item = (ResourceItem)List.elementAt(i);
338          f1 = item.getFact();
339          notifyMonitors(item,ACCESS);
340          if ( f1.equals(fact) ) {
341             List.removeElementAt(i--);
342             item.deleted();
343             notifyMonitors(item,DELETE);
344          }
345       }
346       if ( List.isEmpty() ) this.remove(type);
347    }
348 
349    /*** Use this if facts have changed and you need to update the Resource Database */
350    public synchronized void modify(Fact f1, Fact f2) {
351       del(f1);
352       add(f2);
353    }
354 
355     // synchronized 
356    public void free(DataRec rec) {
357       if ( rec == null ) return;
358       Vector List = rec.available();
359       ResourceItem item;
360       ExternalDb externalDb = context.ExternalDb();
361 
362       Fact f1;
363       for(int i = 0; i < List.size(); i++ ) {
364          item = (ResourceItem)List.elementAt(i);
365          item.cancelReservation(rec);
366          f1 = item.getFact();
367          if ( externalDb != null && !item.isReserved() &&
368               externalDb.put(f1) ) {
369             List.removeElementAt(i--);
370             item.deleted();
371             notifyMonitors(item,DELETE);
372          }
373       }
374       rec.free();
375    }
376 
377    public void consume(DataRec rec) {
378       if ( rec == null ) return;
379       Vector List = rec.available();
380       Vector data;
381       Fact f1;
382       for(int i = 0; i < List.size(); i++ ) {
383          ResourceItem item = (ResourceItem) List.elementAt(i);
384          f1 = item.getFact();
385          notifyMonitors(item,ACCESS);
386          notifyMonitors(item,DELETE);
387          data = (Vector)this.get(f1.getType());
388          switch( item.consumed(rec) ) {
389             case ResourceItem.UNCHANGED:
390 	         notifyMonitors(item,ADD); // replace unchanged
391                  break;
392             case ResourceItem.MODIFY:
393 	         notifyMonitors(item,ADD); // replace changed
394                  break;
395 	          case ResourceItem.DELETE: // no replacement
396                  data.removeElement(item);
397                  if ( data.isEmpty() )
398                  this.remove(item.getFact().getType());
399                  break;
400          }
401       }
402    }
403 
404    public int findAll(PlanRecord rec, int precond_position,
405                                    int required) {
406       Core.ERROR(required > 0,1001,this);
407       DataRec datarec = rec.getDatarec(precond_position);
408       int start = rec.getStartTime();
409       return reserve(datarec,rec,start,required);
410    }
411 
412    /*** Enables a resource to be secured at a certain time period, (this is
413        provided primarily for internal use) */
414    public int reserve(DataRec datarec, PlanRecord rec,
415                                    int start, int required) {
416       Vector List;
417       ResourceItem item;
418       Fact f1, f2, f3;
419       ExternalDb externalDb = context.ExternalDb();
420 
421       Fact fact = datarec.getFact();
422       boolean consumed = !fact.isReadOnly();
423 
424       if ( (List = (Vector)this.get(fact.getType())) == null )
425          List = new Vector();
426 
427       int amt;
428       Bindings b = new Bindings(name);
429       for(int i = 0; required > 0 && i < List.size(); i++ ) {
430          item = (ResourceItem)List.elementAt(i);
431          f1 = item.getFact();
432          notifyMonitors(item,ACCESS);
433          int available;
434          Core.DEBUG(2,"Fact before test \n" + f1); 
435          b.clear();
436          if ( (available = item.unreservedAmount(start,consumed)) > 0 &&
437               f1.unifiesWith(fact,b) && rec.applyConstraints(b) ) {
438             amt = Math.min(available,required);
439             Core.ERROR(datarec.add(item,start,amt),1002,this);
440             if ( consumed )
441                rec.updateCost(amt*f1.getUnitCost());
442             required -= amt;
443 
444             Core.DEBUG(2,"Required to find Fact:\n" + fact);
445             Core.DEBUG(2,"Fact:\n" + f1 + "\nassigned to datarec " +
446                          datarec.getId());
447          }
448       }
449 
450       if ( required > 0 && externalDb != null ) {
451          Enumeration enum = externalDb.all(fact);
452          while( enum != null && enum.hasMoreElements() && required > 0 ) {
453             f1 = (Fact)enum.nextElement();
454             b.clear();
455             if ( f1.unifiesWith(fact,b) && rec.applyConstraints(b) ) {
456                int available = f1.getNumber();
457                f3 = externalDb.remove(f1);
458                if ( f3 != null ) {
459                   item = add(f3);
460                   amt = Math.min(available,required);
461                   Core.ERROR(datarec.add(item,start,amt),1003,this);
462                   if ( consumed )
463                      rec.updateCost(amt*f3.getUnitCost());
464                   required -= amt;
465    
466                   Core.DEBUG(2,"Required to find Fact:\n" + fact);
467                   Core.DEBUG(2,"Fact:\n" + f3 + "\nassigned to datarec " +
468                             datarec.getId());
469                }
470                else {
471                   Core.USER_ERROR("Improperly defined externalDb: fact \"" +
472                      f1 + "\" should be in externaldb, but not found");
473                }
474             }
475          }
476       }
477       return required;
478    }
479 
480    public synchronized int reserve(DataRec datarec, int start, int required) {
481       Vector List;
482       ResourceItem item;
483       Fact f1, f2;
484       ExternalDb externalDb = context.ExternalDb();
485 
486       Fact fact = datarec.getFact();
487       boolean consumed = !fact.isReadOnly();
488 
489       if ( (List = (Vector)this.get(fact.getType())) == null )
490          List = new Vector();
491 
492       int amt;
493       for(int i = 0; required > 0 && i < List.size(); i++ ) {
494          item = (ResourceItem)List.elementAt(i);
495          f1 = item.getFact();
496          notifyMonitors(item,ACCESS);
497          Bindings b = new Bindings(name);
498          int available;
499          if ( (available = item.unreservedAmount(start,consumed)) > 0 &&
500               f1.unifiesWith(fact,b) ) {
501             amt = Math.min(available,required);
502             Core.ERROR(datarec.add(item,start,amt),1005,this);
503             required -= amt;
504 
505             Core.DEBUG(2,"Required to find Fact:\n" + fact);
506             Core.DEBUG(2,"Fact:\n" + f1 + "\nassigned to datarec " +
507                          datarec.getId());
508          }
509       }
510 
511       if ( required > 0 && externalDb != null ) {
512          Enumeration enum = externalDb.all(fact);
513          while( enum.hasMoreElements() && required > 0 ) {
514             f1 = (Fact)enum.nextElement();
515             Bindings b = new Bindings(name);
516             if ( f1.unifiesWith(fact,b) ) {
517                int available = f1.getNumber();
518                f1 = externalDb.remove(f1);
519                item = add(f1);
520                amt = Math.min(available,required);
521                Core.ERROR(datarec.add(item,start,amt),1006,this);
522                required -= amt;
523 
524                Core.DEBUG(2,"Required to find Fact:\n" + fact);
525                Core.DEBUG(2,"Fact:\n" + f1 + "\nassigned to datarec " +
526                          datarec.getId());
527             }
528          }
529       }
530       return required;
531    }
532 
533     // was sychronized - but caused deadlock!
534    public Vector allocateResources(PlanRecord rec) {
535      // zeus.actors.rtn.Engine engine = context.getEngine(); 
536      // synchronized (engine) { 
537       Vector subgoals = new Vector();
538       Goal g;
539       int position, required;
540 
541       PrimitiveTask task = rec.getTask();
542       Fact[][] consumed = task.orderPreconditions();
543 
544       for(int i = 0; i < consumed.length; i++) {
545          for(int j = 0; j < consumed[i].length; j++) {
546             if ( !consumed[i][j].isNegative() ) {
547                position = task.getConsumedPos(consumed[i][j]);
548                required = rec.noRequiredItems(position);
549                if ( required > 0) {
550                   g = allocateResource(rec,position,required);
551                   if ( g != null ) subgoals.addElement(g);
552                }
553             }
554          }
555          if ( !subgoals.isEmpty() ) break;
556       }
557       return subgoals;
558     //  }
559    }
560 
561 
562     // synchronized
563    public Goal allocateResource(PlanRecord rec, int position,
564                                              int required) {
565       int s;
566       required = findAll(rec,position,required);
567 
568       if ( required > 0 ) {
569         // since 1.3 - check that the planner is extant.
570          if (!isPlannerSet())  
571             if (!isContextSet()) { 
572                     try {
573                         throw new Exception ("Planner and AgentContext not set in ResourceDb.\n Agent is improperly initialised, sorry"); 
574                         } 
575                         catch (Exception e) { 
576                                 e.printStackTrace(); 
577                             } }
578                 else { 
579                     this.planner = context.getPlanner(); } 
580                     // ends addition
581          Planner planner = this.planner;
582          debug("planner = " + planner); 
583          // check planner for seredipituous side effects
584          DataRec datarec = rec.getDatarec(position);
585          Fact desc = datarec.getFact();
586          if ( (s = planner.anySideEffect(desc,rec,position,required)) != 0 ) {
587             Fact g = new Fact(desc);
588             g.setNumber(s);
589             return rec.createSubgoal(g,position);
590          }
591       }
592       return null;
593    }
594 
595    /*** Special purpose method to decrease the value of the amount attribute of MONEY facts */
596    public synchronized Fact debit(double amount) {
597       Vector List;
598       ResourceItem item;
599       Fact f1, f2, debit;
600       PrimitiveNumericFn numericFn;
601 
602       Core.DEBUG(3,"About to debit: " + amount);
603       Core.ERROR(amount>=0,1006,this);
604 
605       OntologyDb ontology = ontologyDb;
606       debit = ontology.getFact(Fact.FACT,OntologyDb.MONEY);
607       debit.setValue(OntologyDb.AMOUNT,amount);
608 
609       List = (Vector)this.get(OntologyDb.MONEY);
610       for(int i = 0; List != null && amount > 0 && i < List.size(); i++ ) {
611          item = (ResourceItem)List.elementAt(i);
612          notifyMonitors(item,ACCESS);
613          if ( !item.isReserved() ) {
614             f1 = item.getFact();
615             f1.resolve(new Bindings(name));
616             numericFn = (PrimitiveNumericFn)f1.getFn(OntologyDb.AMOUNT);
617             double available = numericFn.doubleValue();
618             if ( available > amount ) {
619                f2 = f1.duplicate(Fact.VAR,gensym);
620                f2.setValue(OntologyDb.AMOUNT,available-amount);
621                f2.resolve(new Bindings(name));
622 
623                // delete f1 from db
624                List.removeElementAt(i--);
625                item.deleted();
626                notifyMonitors(item,DELETE);
627                f1.setValue(OntologyDb.AMOUNT,amount);
628 
629                ResourceItem new_item = new ResourceItem(f2);
630                List.addElement(new_item);
631                notifyMonitors(new_item,ADD);
632                Core.DEBUG(3,"Debit completed: " + debit);
633                return debit;
634             }
635             else if ( available >= 0 ) {
636                // delete f1 from db
637                List.removeElementAt(i--);
638 	       item.deleted();
639                notifyMonitors(item,DELETE);
640                amount = amount - available;
641             }
642          }
643       }
644 
645       // if amount > 0 then add -ve amount to db and return
646       if ( amount > 0 ) {
647          boolean done = false;
648          for(int i = 0; List != null && !done && i < List.size(); i++ ) {
649             item = (ResourceItem)List.elementAt(i);
650             notifyMonitors(item,ACCESS);
651             if ( !item.isReserved() ) {
652                f1 = item.getFact();
653                f1.resolve(new Bindings(name));
654                numericFn = (PrimitiveNumericFn)f1.getFn(OntologyDb.AMOUNT);
655                double available = numericFn.doubleValue();
656 
657                // delete f1 from db
658                List.removeElementAt(i--);
659                item.deleted();
660                notifyMonitors(item,DELETE);
661                f1.setValue(OntologyDb.AMOUNT,available-amount);
662                f1.resolve(new Bindings(name));
663                List.addElement(item);
664                notifyMonitors(item,ADD);
665                done = true;
666             }
667          }
668 
669          if ( !done ) {
670             f1 = ontology.getFact(Fact.FACT,OntologyDb.MONEY);
671             f1.setValue(OntologyDb.AMOUNT,0 - amount);
672             f1.resolve(new Bindings(name));
673             add(f1);
674          }
675       }
676       Core.DEBUG(3,"Debit completed: " + debit);
677       return debit;
678    }
679 
680    public synchronized Fact evalLocal(Fact fact) {
681       Fact[] answer = all(fact);
682       if ( answer.length == 0 ) return null;
683       OntologyDb ontology = ontologyDb;
684       Fact result = new Fact(answer[0]);
685       for( int i = 1; i < answer.length; i++ )
686          Core.ERROR(result.disjoin(answer[i]),1008,this);
687       return result;
688    }
689 
690    public synchronized boolean evalNegative(Fact fact) {
691       Fact[] answer = all(fact);
692       return ( answer.length == 0 );
693    }
694 
695    public synchronized Fact[] all(String type) {
696       OntologyDb ontology = ontologyDb;
697       Fact f = ontology.getFact(Fact.VARIABLE,type);
698       return all(f);
699    }
700 
701    /*** Use this to retrieve all the facts in the database that match the parameter */
702    public synchronized Fact[] all(Fact fact) {
703       Vector List;
704       ResourceItem item;
705       Fact f1;
706       ExternalDb externalDb = context.ExternalDb();
707 
708       if ( (List = (Vector)this.get(fact.getType())) == null )
709          List = new Vector();
710 
711       Vector answer = new Vector();
712       Bindings b = new Bindings(name);
713       for(int i = 0; i < List.size(); i++, b.clear() ) {
714          item = (ResourceItem)List.elementAt(i);
715          f1 = item.getFact();
716          notifyMonitors(item,ACCESS);
717          if ( f1.unifiesWith(fact,b) )
718             answer.addElement(f1);
719       }
720       if ( externalDb != null ) {
721          Enumeration enum = externalDb.all(fact);
722          while( enum.hasMoreElements() )
723             answer.addElement((Fact)enum.nextElement());
724       }
725 
726       Fact[] results = new Fact[answer.size()];
727       for(int i = 0; i < answer.size(); i++ )
728          results[i] = (Fact) answer.elementAt(i);
729       return results;
730    }
731 
732    /*** Deletes all facts matching the parameter type, should obviously
733        be used with caution */
734    public synchronized void deleteAll(String type) {
735       Vector List;
736       ResourceItem item;
737       Fact f1;
738 
739       if ( (List = (Vector)this.get(type)) == null )
740          return;
741 
742       // delete all unallocated first then delete the allocated
743       for(int i = 0; i < List.size(); i++ ) {
744          item = (ResourceItem)List.elementAt(i);
745          f1 = item.getFact();
746          notifyMonitors(item,ACCESS);
747          if ( !item.isReserved() ) {
748             List.removeElementAt(i--);
749             item.deleted();
750             notifyMonitors(item,DELETE);
751          }
752       }
753       for(int i = 0; i < List.size(); i++ ) {
754          item = (ResourceItem)List.elementAt(i);
755          f1 = item.getFact();
756          notifyMonitors(item,ACCESS);
757          List.removeElementAt(i--);
758          item.deleted();
759          notifyMonitors(item,DELETE);
760       }
761    }
762 
763    /*** Randomly retrieves a fact with the same type as the parameter */
764    public synchronized Fact any(String type) {
765       OntologyDb ontology = ontologyDb;
766       Fact f = ontology.getFact(Fact.VARIABLE,type);
767       return any(f);
768    }
769 
770    /*** Use this to randomly retrieve a fact that matches the parameter */
771    public synchronized Fact any(Fact fact) {
772       Fact[] answer = all(fact);
773       if ( answer == null ) return null;
774       int pos = (int) (Math.random()*answer.length);
775       // added by simon to stop an exception being thrown. 
776       //19/8/00
777       if (pos>answer.length-1) {
778         // in this case the array is out of bounds
779         return fact; }
780         // end addition
781       else 
782             return answer[pos];
783    }
784 
785    /*** Use this to test whether a particular fact exists in the database */
786    public synchronized boolean contains(Fact fact, int start) {
787       Vector List;
788       ResourceItem item;
789       Fact f1;
790       ExternalDb externalDb = context.ExternalDb();
791 
792       if ( (List = (Vector)this.get(fact.getType())) == null )
793          List = new Vector();
794 
795       Bindings b = new Bindings(name);
796       int required = fact.getNumber();
797       boolean consumed = !fact.isReadOnly();
798       int available;
799       for(int i = 0; required > 0 && i < List.size(); i++, b.clear()) {
800          item = (ResourceItem)List.elementAt(i);
801          f1 = item.getFact();
802          notifyMonitors(item,ACCESS);
803          if ( (available = item.unreservedAmount(start,consumed)) > 0 &&
804               f1.unifiesWith(fact,b) ) {
805             required = required - Math.min(available,required);
806          }
807       }
808 
809       if ( required > 0 && externalDb != null ) {
810          Enumeration enum = externalDb.all(fact);
811          while( enum.hasMoreElements() && required > 0 ) {
812             f1 = (Fact)enum.nextElement();
813             available = f1.getNumber();
814             required = required - Math.min(available,required);
815          }
816       }
817 
818       return required <= 0;
819    }
820 
821 
822    /***
823     * Use this if your code needs to react to changes in the Resource Database.
824     * This provides a programatic alternative to writing reaction rules
825     */
826 
827    public void addFactMonitor(FactMonitor monitor, long event_type,
828                               boolean notify_previous)  {
829       addFactMonitor(monitor,event_type);
830       if ( !notify_previous ) return;
831 
832       Enumeration enum = elements();
833       Vector List;
834       ResourceItem item;
835       FactEvent event;
836 
837       while( enum.hasMoreElements() ) {
838          List = (Vector)enum.nextElement();
839          for(int i = 0; i < List.size(); i++ ) {
840             item = (ResourceItem) List.elementAt(i);
841             event = new FactEvent(this,item,FactEvent.ACCESS_MASK);
842             monitor.factAccessedEvent(event);
843             event = new FactEvent(this,item,FactEvent.ADD_MASK);
844             monitor.factAddedEvent(event);
845          }
846       }
847    }
848 
849    public void addFactMonitor(FactMonitor monitor, long event_type) {
850       if ( (event_type & FactEvent.ADD_MASK) != 0 )
851          eventMonitor[ADD].add(monitor);
852       if ( (event_type & FactEvent.MODIFY_MASK) != 0 )
853          eventMonitor[MODIFY].add(monitor);
854       if ( (event_type & FactEvent.DELETE_MASK) != 0 )
855          eventMonitor[DELETE].add(monitor);
856       if ( (event_type & FactEvent.ACCESS_MASK) != 0 )
857          eventMonitor[ACCESS].add(monitor);
858    }
859 
860    public void removeFactMonitor(FactMonitor monitor, long event_type) {
861       if ( (event_type & FactEvent.ADD_MASK) != 0 )
862          eventMonitor[ADD].remove(monitor);
863       if ( (event_type & FactEvent.MODIFY_MASK) != 0 )
864          eventMonitor[MODIFY].remove(monitor);
865       if ( (event_type & FactEvent.DELETE_MASK) != 0 )
866          eventMonitor[DELETE].remove(monitor);
867       if ( (event_type & FactEvent.ACCESS_MASK) != 0 )
868          eventMonitor[ACCESS].remove(monitor);
869    }
870 
871    private void notifyMonitors(ResourceItem item, int type) {
872       if ( eventMonitor[type].isEmpty() ) return;
873 
874       FactMonitor monitor;
875       FactEvent event;
876       Enumeration enum = eventMonitor[type].elements();
877 
878       switch(type) {
879          case ADD:
880               event = new FactEvent(this,item,FactEvent.ADD_MASK);
881               while( enum.hasMoreElements() ) {
882                  monitor = (FactMonitor)enum.nextElement();
883                  monitor.factAddedEvent(event);
884               }
885               break;
886          case MODIFY:
887               event = new FactEvent(this,item,FactEvent.MODIFY_MASK);
888               while( enum.hasMoreElements() ) {
889                  monitor = (FactMonitor)enum.nextElement();
890                  monitor.factModifiedEvent(event);
891               }
892               break;
893          case DELETE:
894               event = new FactEvent(this,item,FactEvent.DELETE_MASK);
895               while( enum.hasMoreElements() ) {
896                  monitor = (FactMonitor)enum.nextElement();
897                  monitor.factDeletedEvent(event);
898               }
899               break;
900          case ACCESS:
901               event = new FactEvent(this,item,FactEvent.ACCESS_MASK);
902               while( enum.hasMoreElements() ) {
903                  monitor = (FactMonitor)enum.nextElement();
904                  monitor.factAccessedEvent(event);
905               }
906               break;
907       }
908    }
909    
910    private void debug(String str) { 
911   //  System.out.println(str); 
912    }
913 
914 }