capture
[rrq/gorite.git] / com / intendico / gorite / BranchingGoal.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;
21
22 import java.util.Vector;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.Observable;
26 import java.util.Observer;
27
28 /**
29  * The BranchingGoal class is an intermediate, abstract class that
30  * provides common execution support for parallel branches.
31  */
32 abstract public class BranchingGoal extends Goal {
33
34     /**
35      * Constructor.
36      */
37     public BranchingGoal(String n,Goal [] sg) {
38         super( n, sg );
39     }
40
41     /**
42      * Convenience constructor without sub goals.
43      */
44     public BranchingGoal(String n) {
45         this( n, null );
46     }
47
48     /**
49      * Helper class that implements suport for parallel goal execution
50      * with interrupt control. 
51      */
52     abstract public class MultiInstance extends Instance {
53
54         /**
55          * States of execution; the number of branches remaining.
56          */
57         public int counter;
58
59         /**
60          * Currently active branches.
61          */
62         public Vector/*<Branch>*/ branches = null;
63
64         /**
65          * Names of data elements that are output from any branch.
66          */
67         public HashSet/*<String>*/ outs = new HashSet/*<String>*/();
68
69         /**
70          * Utility method for counting branches when they are created.
71          */
72         public synchronized void increment() {
73             counter++;
74         }
75
76         /**
77          * Constructor.
78          */
79         public MultiInstance(String h) {
80             super( h );
81         }
82
83         /**
84          * A class to monitor the combined runnability of all the
85          * branches, by the logic that this intention is runnable if
86          * any of the branch intentions are runnable.
87          */
88         class RunnableMonitor extends Observable implements Observer {
89
90             private boolean some_runnable = false;
91
92             public void update(Observable x,Object y) {
93                 if ( ! some_runnable ) {
94                     some_runnable = true;
95                     setChanged();
96                     notifyObservers();
97                 }
98                 if ( x != null ) {
99                     x.deleteObserver( this );
100                 }
101             }
102         }
103
104         /**
105          * Keeps the monitor for branch runnability state.
106          */
107         private RunnableMonitor runnable_monitor = null;
108
109         /**
110          * Utility class to set up runnability monitoring.
111          */
112         private void setupMonitor(Data d) {
113             runnable_monitor = new RunnableMonitor();
114             d.setTrigger( runnable_monitor );
115             boolean some = false;
116             for ( Iterator/*<Branch>*/ i =
117                       branches.iterator(); i.hasNext(); ) {
118                 some |= ((Branch)i.next()).setupObserver( d );
119             }
120             if ( some ) {
121                 removeMonitor( d );
122                 d.clearTrigger();
123             }
124         }
125
126         /**
127          * Utility method to remove the runnability monitoring.
128          */
129         private void removeMonitor(Data d) {
130             runnable_monitor = null;
131             for ( Iterator/*<Branch>*/ i =
132                       branches.iterator(); i.hasNext(); ) {
133                 ((Branch)i.next()).removeObserver( d );
134             }
135         }
136
137         /**
138          * Branch head and execution.
139          */
140         public class Branch {
141
142             /**
143              * Collecting all outputs.
144              */
145             public HashSet/*<String>*/ outputs;
146
147             /**
148              * The goal instance to execute.
149              */
150             public Instance instance;
151
152             /**
153              * Branch thread name. This needs to be distinct from the
154              * actual branch Instance in order to join data properly.
155              */
156             public String thread_name;
157
158             /**
159              * The Data operated on.
160              */
161             public Data data;
162
163             /**
164              * Propagate cancellation to actual branch.
165              */
166             public void cancel() {
167                 if ( instance != null ) {
168                     instance.cancel();
169                 }
170             }
171
172             /**
173              * Constructor, tying the branch to a goal instance.
174              */
175             public Branch(int ix,Instance i,Data d,HashSet/*<String>*/ outs) {
176                 instance = i;
177                 data = d;
178                 outputs = outs;
179                 increment();
180                 thread_name = instance.thread_name + "(branch)";
181                 data.fork( getGoalControl(), ix, thread_name );
182             }
183
184             /**
185              * Branch main method: performs the associated goal
186              * instance, and manages current "thread name".
187              */
188             public States action()
189                 throws LoopEndException, ParallelEndException {
190                 String old = data.setThreadName( thread_name );
191                 try {
192                     States s = instance.perform( data );
193                     // TODO: collate all result data for join at end
194                     if ( s == States.PASSED ) {
195                         //data.join( getGoalControl(), outputs, thread_name );
196                         data.join( getGoalControl(), null, thread_name );
197                     } else if ( s == States.FAILED ) {
198                         data.join( getGoalControl(), null, thread_name );
199                     }
200                     return s;
201                 } catch (ParallelEndException e) {
202                     //data.join( getGoalControl(), outputs, thread_name );
203                     data.join( getGoalControl(), null, thread_name );
204                     throw e;
205                 } finally {
206                     data.setThreadName( old );
207                 }
208             }
209
210             /**
211              * Utility method to set up the runnable_monitor as
212              * observer of runnability change for this branch, and
213              * then return the current runnability state.
214              */
215             boolean setupObserver(Data d) {
216                 d.addObserver( instance.thread_name, runnable_monitor );
217                 return d.isRunning( instance.thread_name );
218             }
219
220             /**
221              * Utility method to remove the runnable_monitor as
222              * runnability observer.
223              */
224             void removeObserver(Data d) {
225                 d.deleteObserver( instance.thread_name, runnable_monitor );
226             }
227         }
228
229         /**
230          * Cancels the MultiInstance (Parallel or Repeat)
231          */
232         public void cancel() {
233             super.cancel();
234             propagateCancel( null );
235         }
236
237         /**
238          * Propagates cancel to all branches
239          */
240         public void propagateCancel(Branch skip)
241         {
242             if ( branches == null )
243                 return;
244             for ( Iterator/*<Branch>*/ i =
245                       branches.iterator(); i.hasNext(); ) {
246                 Branch b = (Branch) i.next();
247                 if ( b != skip )
248                     b.cancel();
249             }
250         }
251
252         /**
253          * Process all subgoals in parallel, and complete when they
254          * complete.
255          */
256         public States action(String head,Data d)
257             throws LoopEndException, ParallelEndException {
258             if ( branches == null ) {
259                 branches = new Vector/*<Branch>*/();
260                 for ( int i = 0; more( i, d ); i++ ) {
261                     Instance instance = getBranch( i, head + ":" + i, d );
262                     Branch b = new Branch( i, instance, d, outs );
263                     branches.add( b );
264                 }
265             }
266
267             if ( runnable_monitor != null ) {
268                 removeMonitor( d );
269             }
270
271             int icount = 0;
272             int bcount = 0;
273
274             try {
275                 while ( branches.size() > 0 ) {
276                     States s = ((Branch)branches.get( 0 )).action();
277                     if ( s == States.STOPPED || s == States.BLOCKED ) {
278                         branches.add( branches.remove( 0 ) );
279                         icount += 1;
280                         if ( s == States.BLOCKED )
281                             bcount += 1;
282                         if ( icount < branches.size() )
283                             continue;
284                         if ( bcount != icount )
285                             return States.STOPPED;
286                         setupMonitor( d );
287                         return States.BLOCKED;
288                     }
289                     icount = bcount = 0;
290                     if ( s == States.PASSED ) {
291                         branches.remove( 0 );
292                         continue;
293                     }
294                     propagateCancel( (Branch)branches.get( 0 ) );
295                     return s;
296                 }
297             } catch (LoopEndException e) {
298                 propagateCancel( null );
299                 throw e;
300             } catch (ParallelEndException e) {
301                 propagateCancel( (Branch) branches.get( 0 ) );
302                 // Discard data from cancelled branches
303             } finally {
304                 // Signal elevated branch outputs in calling data context.
305                 for ( Iterator/*<String>*/ i =
306                           outs.iterator(); i.hasNext(); ) {
307                     String key = (String) i.next();
308                     d.ready( key );
309                 }
310             }
311             // TODO join collated data
312             return States.PASSED;
313         }
314
315         /**
316          * The method for determining whether there is another branch.
317          */
318         abstract public boolean more(int i,Data d);
319
320         /**
321          * Utility method to obtain the actual branch instance.
322          */
323         abstract public Instance getBranch(int i,String head,Data d);
324
325     }
326
327 }