capture
[rrq/gorite.git] / com / intendico / gorite / addon / Reflector.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.gorite.addon;
21
22 import com.intendico.gorite.Performer;
23 import com.intendico.gorite.Data;
24 import com.intendico.gorite.Goal;
25 import com.intendico.gorite.BDIGoal;
26 import com.intendico.data.Query;
27 import com.intendico.data.Ref;
28 import com.intendico.data.Snapshot;
29
30 import java.util.IdentityHashMap;
31 import java.util.Observer;
32 import java.util.Vector;
33 import java.util.Iterator;
34 import java.util.Observable;
35
36 /**
37  * A Reflector is a reasoning faculty to translate situation changes
38  * into percepts. The Reflector is populated with some number of
39  * {@link Reflection} objects, which each combines a {@link Query}
40  * with a {@link Goal}, so as to intend the {@link Goal} as a percept
41  * when the {@link Query} changes state, from false to true or from
42  * true to false, for particular bindings.
43  *
44  * <p> For example, consider the following code snippet:
45  * <pre>
46  *     new Reflector( performer, "grandparent", "inference" ) {{
47  *         Ref p = new Ref( "parent" );
48  *         addQuery( new And( parent.get( new Ref( "gp"), p ),
49  *                            parent.get( p, new Ref( "ch") ) ) );
50  *     }};
51  * </pre>
52  *
53  * The example code would keep observing the "parent" relation, and
54  * when it is updated to present a different grandparent-parent-child
55  * linkage from before, the goal "grandparent" is intended on the
56  * "inference" {@link com.intendico.gorite.Performer.TodoGroup
57  * Performer.TodoGroup}, with the data elements "gp", "parent" and
58  * "ch" set. Further, the data element "reflection event" is set to
59  * "added" or "lost" to indicate whether the change is of adding or
60  * losing a matching binding for the query.
61  *
62  *
63  * <p> Technicallly each added {@link Query} gets wrapped into a
64  * {@link Snapshot} in a {@link Reflector.Reflection} object, which is
65  * set up as {@link Observer} to observe any changes to the {@link
66  * Query} sources. The {@link Snapshot} wrapping acts as a filter to
67  * ensure that only changes cause percepts. The percept will hold the
68  * input query as "percept" data element, and all {@link Ref} object
69  * values of the query as corresponding data elemens.
70  *
71  */
72 public class Reflector extends Perceptor {
73
74     /**
75      * The name of the data element to indicate whether its an
76      * added or lost event
77      */
78     public String REFLECTION_EVENT = "reflection event";
79
80     /**
81      * Constructor that ties the Reflector to the given {@link
82      * Performer}, causing goals as named in the {@link
83      * com.intendico.gorite.Performer.TodoGroup Performer.TodoGroup}
84      * as named.
85      */
86     public Reflector(Performer p,String goal,String todo) {
87         super( p, goal, todo );
88     }
89
90     /**
91      * Utility class for observing the query sources, and trigger
92      * goals when bindings change state.
93      */
94     public class Reflection implements Observer {
95
96         /**
97          * Holds the query wrapped into a {@link Snapshot}
98          */
99         public Snapshot snap;
100
101         /**
102          * Holds the perception goal caused by this reflection.
103          */
104         public Goal goal;
105
106         /**
107          * Holds the reflection mode; postive for reacting to added
108          * bindings, negative for reacting to lost bindings and zero
109          * for both.
110          */
111         public int mode = 1;
112
113         /**
114          * Alternative constructor, with a goal name.
115          */
116         public Reflection(Query q,String g) {
117             this( q, g != null? new BDIGoal( g ) : null );
118         }
119
120         /**
121          * Constructor, which applies the {@link Snapshot} wrapping.
122          */
123         public Reflection(Query q,Goal g) {
124             snap = new Snapshot( q );
125             snap.addObserver( this );
126             goal = g;
127         }
128
129         /**
130          * Implements {@link Observer#update} by reviewing the query
131          * and generating percepts for new bindings.
132          */
133         synchronized public void update(Observable x,Object y) {
134             try {
135                 snap.reset();
136                 Vector/*<Ref>*/ refs = snap.getRefs( new Vector/*<Ref>*/() );
137                 if ( mode >= 0 ) {
138                     // Process added bindings
139                     while ( snap.next() ) {
140                         Data d = new Data();
141                         d.set( refs );
142                         d.setValue( REFLECTION_EVENT, "added" );
143                         perceive( snap.query, d, goal );
144                     }
145                 }
146                 if ( mode <= 0 ) {
147                     // Process lost bindings
148                     for ( Iterator/*<Vector>*/ i =
149                               snap.lost.iterator(); i.hasNext(); ) {
150                         Vector v = (Vector) i.next();
151                         Data d = new Data();
152                         Ref.bind( refs, v );
153                         d.set( refs );
154                         d.setValue( REFLECTION_EVENT, "lost" );
155                         perceive( snap.query, d, goal );
156                     }
157                 }
158             } catch (Exception e) {
159                 e.printStackTrace();
160             }
161         }
162
163         /**
164          * Cancels the observation of query sources.
165          */
166         public void cancel() {
167             snap.deleteObserver( this );
168         }
169     }
170
171     /**
172      * Keeps the current collection of queries, which are contained in
173      * {@link Reflection} objects
174      */
175     public IdentityHashMap/*<Query,Reflection>*/ reflections =
176         new IdentityHashMap/*<Query,Reflection>*/();
177
178     /**
179      * Adds a {@link Reflection} that uses the given {@link Query}.
180      */
181     public void addQuery(Query q) {
182         addQuery( q, (String) null );
183     }
184
185     /**
186      * Adds a {@link Reflection} query using the given {@link
187      * Goal#name}.
188      */
189     public void addQuery(Query q,String goal) {
190         addQuery( q, goal, 1 );
191     }
192
193     /**
194      * Adds a {@link Reflection} query using the given {@link
195      * Goal#name}.
196      */
197     public void addQuery(Query q,String goal,int m) {
198         Reflection r = (Reflection) reflections.get( q );
199         if ( r == null ) {
200             r = new Reflection( q, goal );
201             r.mode = m;
202             reflections.put( q, r );
203             r.update( null, null );
204         } else if ( r.mode != m ) {
205             r.mode = 0;
206             r.update( null, null );
207         }
208     }
209
210     /**
211      * Adds a reflection query using the given Reflection goal.
212      */
213     public void addQuery(Query q,Goal goal) {
214         addQuery( q, goal, 1 );
215     }
216
217     /**
218      * Adds a reflection query using the given Reflection goal.
219      */
220     public void addQuery(Query q,Goal goal,int m) {
221         Reflection r = (Reflection) reflections.get( q );
222         if ( r == null ) {
223             r = new Reflection( q, goal );
224             r.mode = m;
225             reflections.put( q, r );
226             r.update( null, null );
227         } else if ( r.mode != m ) {
228             r.mode = 0;
229             r.update( null, null );
230         }
231     }
232
233     /**
234      * Removes the {@link Reflection} that uses the given {@link
235      * Query}.
236      */
237     public void removeQuery(Query q) {
238         Reflection r = (Reflection) reflections.remove( q );
239         if ( r != null ) {
240             r.cancel();
241         }
242     }
243
244     /**
245      * Returns a String representation of this object.
246      */
247     public String toString() {
248         StringBuilder s = new StringBuilder();
249         s.append( "Reflector(" + performer + "," +
250                   handler + ",\"" + todo_group + "\"):" );
251         for ( Iterator/*<Query>*/ ri = reflections.keySet().iterator();
252               ri.hasNext(); ) {
253             Query q = (Query) ri.next();
254             s.append( "\n  query: " );
255             s.append( q.toString() );
256         }
257         return s.toString();
258     }
259 }