capture
[rrq/gorite.git] / com / intendico / data / RuleSet.java
1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
3
4 This file is part of GORITE.
5
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14 License for more details.
15
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
19
20 package com.intendico.data;
21
22 import java.util.Observable;
23 import java.util.Observer;
24 import java.util.Vector;
25 import java.util.HashSet;
26 import java.util.Iterator;
27
28 /**
29  * A RuleSet is a reason maintenance unit, which attempts to uphold
30  * given rules. In normal use, the RuleSet is populated with some
31  * number of Rule objects, via either {@link #add(Rule)} or {@link
32  * #add(Query,Query)}, which tie the {@link Rule} objects to the
33  * RuleSet. Each {@link Rule} object, qua {@link Observer}, gets
34  * notified about possible changes by their antecedent source , which
35  * result in them invoking the {@link #activate} method. The RuleSet
36  * {@link #run} method then pops and processes all activated rules to
37  * achieve their belief propagation effects.
38  */
39 public class RuleSet extends Observable implements Runnable {
40
41     /**
42      * Utility class to observe source update
43      */
44     class RuleObserver implements Observer {
45
46         /**
47          * The {@link Rule} concerned.
48          */
49         Rule rule;
50
51         /**
52          * Constructor.
53          */
54         RuleObserver(Rule r) {
55             rule = r ;
56         }
57
58         /**
59          * Implements {@link Observer#update} by activating the rule
60          * concerned. However, if the {@link Rule} is not contained in
61          * {@link #all_rules} then this observer just deletes itself
62          * from the calling {@link Observable}.
63          */
64         public void update(Observable x,Object y) {
65             if ( ! all_rules.contains( rule ) ) {
66                 x.deleteObserver( this );
67             } else {
68                 activate( rule );
69             }
70         }
71
72         /**
73          * Returns a String representation of this object.
74          */
75         public String toString() {
76             return "RuleObserver: " + rule;
77         }
78     }
79
80     /**
81      * The collection of {@link Rule} objects explicitly added to this
82      * RuleSet.
83      */
84     public HashSet/*<Rule>*/ all_rules = new HashSet/*<Rule>*/();
85
86     /**
87      * The current set of activated rules.
88      */
89     public HashSet/*<Rule>*/ active = new HashSet/*<Rule>*/();
90
91     /**
92      * Utility method to activate a {@link Rule} in this RuleSet. This
93      * includes adding the {@link Rule} to the {@link #active}
94      * collection, then both {@link Object#notify} on that object, and
95      * invoke {@link Observable#notifyObservers} to signal that this
96      * RuleSet has activated rules.
97      * @see #run
98      */
99     public void activate(Rule r) {
100         synchronized ( active ) {
101             if ( ! active.contains( r ) ) {
102                 active.add( r );
103                 active.notifyAll();
104                 setChanged();
105                 notifyObservers();
106             }
107         }
108     }
109
110     /**
111      * Returns true if there are any activated rules.
112      */
113     public boolean hasActive() {
114         return active.size() > 0;
115     }
116
117     /**
118      * This is a flag to signal that the run method should keep
119      * waiting for next update rather than returning.
120      */
121     public boolean wait = false;
122
123     /**
124      * The reset method calls {@link Rule#reset} on all given rules so
125      * as to reset their antecedent snapshots.
126      */
127     public void reset(Vector/*<Rule>*/ rules) throws Exception {
128         for ( Iterator/*<Rule>*/ i = rules.iterator(); i.hasNext(); ) {
129             Rule r = (Rule) i.next();
130             r.reset();
131         }
132     }
133
134     /**
135      * The propagate method calls {@link Rule#propagate} on all rules
136      * so as to apply the rules for all their new bindings, which
137      * previosuly have been snapshot by {@link Rule#reset} calls.
138      */
139     public void propagate(Vector/*<Rule>*/ rules) throws Exception {
140         for ( Iterator/*<Rule>*/ i = rules.iterator(); i.hasNext(); ) {
141             Rule r = (Rule) i.next();
142             r.propagate();
143         }
144     }
145
146     /**
147      * Returns a String representation of this RuleSet.
148      */
149     public String toString() {
150         StringBuilder s = new StringBuilder();
151         s.append( "{" );
152         boolean first = true;
153         for ( Iterator/*<Rule>*/ i = all_rules.iterator(); i.hasNext(); ) {
154             Rule r = (Rule) i.next();
155             if ( ! first )
156                 s.append( "; " );
157             else
158                 first = false;
159             s.append( r.toString() );
160         }
161         s.append( "}" );
162         return s.toString();
163     }
164
165     /**
166      * Utility method to add a {@link Rule} object and tie it to this
167      * RuleSet.
168      */
169     public void add(Rule r) {
170         if ( all_rules.contains( r ) ) {
171             return;
172         }
173         all_rules.add( r );
174         r.antecedent.addObserver( new RuleObserver( r ) );
175     }
176
177     /**
178      * Utility method to create a new {@link Rule} object with given
179      * antecedent and consequent {@link Query} objects, and add it to
180      * this RuleSet.
181      */
182     public void add(Query a,Query c) {
183         add( new Rule( a, c ) );
184     }
185
186     /**
187      * Deactivates this rule.
188      */
189     public void cancel(Rule r) {
190         all_rules.remove( r );
191     }
192
193     /**
194      * Utility method that adds all currently active rules to a
195      * {@link Vector}, and clears the active set.
196      */
197     public boolean popActivated(Vector/*<Rule>*/ v,boolean w) {
198         synchronized ( active ) {
199             while ( active.size() == 0 ) {
200                 if ( w ) {
201                     try {
202                         active.wait();
203                     } catch (InterruptedException e) {
204                     }
205                 } else {
206                     return false;
207                 }
208             }
209             v.addAll( active );
210             active.clear();
211         }
212         return true;
213     }
214
215     ///
216     /// Runnable implementation
217     ///
218
219     /**
220      * Utility method to keep refreshing the rules if and while there
221      * are source updates. The {@link #wait} flag controls whether
222      * this method should block and wait for a new update (true) or
223      * return (false) when a propagation session ends. Note that if
224      * there are rules to propagate competing assertions in a cyclic
225      * manner, then the propagation will not end and the overall
226      * application will suffer badly.
227      */
228     public void run() {
229         try {
230             Vector/*<Rule>*/ rules = new Vector/*<Rule>*/();
231             for ( ;; ) {
232                 if ( ! popActivated( rules, wait ) )
233                     return;
234                 reset( rules );
235                 propagate( rules );
236                 rules.clear();
237             }
238         } catch (Exception e) {
239             e.printStackTrace();
240         }
241     }
242
243 }