capture
[rrq/gorite.git] / com / intendico / gorite / Goal.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 com.intendico.data.Query;
23 import com.intendico.data.Ref;
24
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.Vector;
28
29 /**
30  * The Goal class is base class for a goal oriented process modelling
31  * through a hierarchy that reflects the decomposition of a goal as an
32  * abstraction of sub goals.  A process model is a hierarchy with a
33  * top level goal that is sucessively decomposed as particular
34  * combinations of sub goals. The top level goal is achieved by means
35  * of processing its sub goals according to the goal type.
36  *
37  * <p> A goal is processed by instantiating it via its {@link
38  * #instantiate} method, which creates the {@link Instance} object
39  * that represents an actual intention to achieve the goal. The
40  * intention is then pursued via the {@link Instance#perform} method,
41  * which eventually results in invoking one of the particular {@link
42  * Instance#action} methods that implement the particular execution
43  * semantics.
44  * 
45  * <p> The base class is also used for task goals, where the {@link
46  * #execute} method is overridden to perform the intended task. In
47  * many cases, the task execution is a matter of processing one or
48  * more input data elements to proved one or more result data
49  * elements, as illustrated by the following example:
50  * <pre>
51  * public States execute(Data data) {
52  *     data.setValue( "result", process( data.getValue( "input" ) ) );
53  *     return PASSED;
54  * }
55  * </pre>
56  *
57  * Of course, the data element names need to be designed and defined
58  * in the full application context, since all goal executions of the
59  * same intention use the same {@link Data} object. Typically you
60  * would employ some data naming scheme to ensure appropriate name
61  * separation.
62  *
63  * <p> Note that an Goal may be augmented with a reimplemented {@link
64  * Goal#instantiate(String,Data)} method as way of defining data
65  * values, a reimplemented {@link Goal#execute(Data,Goal.Instance)}
66  * method as way of defining post processing, and a reimplemented
67  * {@link Goal#cancelled(Goal.Instance)} method as way of restoring
68  * state if the goal execution is cancelled.
69  *
70  * In particular, if the implementation requires a dynamic lexical
71  * environment for each execution, then the {@link
72  * Goal#instantiate(String,Data)} needs to create a vacuous Goal
73  * extension with that environment and ultimately containing the
74  * associated {@link Goal#execute(Data,Goal.Instance)} method. The
75  * following is an illustration example of this.
76  * <pre>
77  * public Goal.Instance instantiate(String head,Data data) {
78  *     return new Goal( "achieving " + getGoalName() ) {
79  *         int x;
80  *         public States execute(Data data) {
81  *             // Sees a dynamically instantiated x
82  *             while ( x++ < 10 ) {
83  *                 return BLOCKED;
84  *             }
85  *             return PASSED;
86  *         }
87  *     }.instantiate( head, goal );
88  * }
89  * </pre>
90  *
91  * Using that approach, a vacuous Goal object, with the "int x"
92  * member, is created for each instantation of the surrounding goal,
93  * and then that goal is instantiated for providing the actual
94  * Instance object.
95  */
96 public class Goal {
97
98     /**
99      * Interface for intrusive tracing of goal execution.
100      * @see #tracer
101      */
102     public interface Tracer {
103
104         /**
105          * Invoked before performing an instance for given data. This
106          * method is invoked by the gorite executor thread prior to
107          * entering or re-entering the goal instance execution. A
108          * Tracer implementation may, by hogging the thread, hold back
109          * the execution while inspecting the model state. The Data is
110          * the instance data to be; this data object becomes the
111          * {@link Instance#data} upon the first entry (after the call
112          * to this method).
113          */
114         public void enterPerform(Instance instance,Data d);
115
116         /**
117          * Invoked after performing instance returns. This method is
118          * invoked when the execution thread stops performing the
119          * instance, which is either because it is finished, or
120          * because it is blocked or stopped. The given {@link States}
121          * is the execution return value, which in many cases is also
122          * cached in {@link Instance#state}.
123          */
124         public void exitPerform(Instance instance,States s);
125
126         /**
127          * Invoked after performing instance throws exception. This
128          * method is invoked when the execution thread stops
129          * performing the instance due to it throwing an exception or
130          * ({@link Throwable}).  If it happens to be a {@link
131          * LoopEndException} or {@link ParallelEndException}, then the
132          * exception is passed in on the call, otherwise null is
133          * passed in. Note that this invocation happens in the
134          * "finally block" for the pending exception propagation,
135          * which thus gets propagated when the method returns.
136          */
137         public void exceptionPerform(Instance instance,Exception e);
138
139     }
140
141     /**
142      * The global intrusive goal execution tracer, if any. This may be
143      * set via the command line property <tt>-Dgorite.tracer=XX</tt>
144      * where <tt>XX</tt> is the class name for the Tracer
145      * implementation to instantiate. It may also be set in code.
146      */
147     public static Tracer tracer = null;
148
149     /**
150      * These are the return values of the {@link #execute(Data)}
151      * method, to report on the state of a goal instance execution.
152      */
153     public /*enum*/ static class States {
154
155         /**
156          * A "name" of a States object.
157          */
158         protected String name;
159
160         /**
161          * The PASSED state is returned to indicate that the goal
162          * execution has completed successfully.
163          */
164         public final static States PASSED =
165             new States() {{ name = "PASSED"; }};
166
167         /**
168          * The FAILED state is returned to indicate that the goal
169          * execution has completed without success.
170          */
171         public final static States FAILED =
172             new States() {{ name = "FAILED"; }};
173
174         /**
175          * The CANCEL state is not returned by goal instance
176          * execution, but is used to mark an {@link Instance} when the
177          * execution has been cancelled.
178          */
179         public final static States CANCEL =
180             new States() {{ name = "CANCEL"; }};
181
182         /**
183          * The BLOCKED state is returned to indicate that the goal
184          * execution has not completed, and that it cannot progress
185          * until a blocking condition has been removed. This return
186          * code is further a promise that something will notify the
187          * execution system via {@link Data#setFlag} call when the
188          * blocking condition possibly has changed. Having returned
189          * this code, the goal instance execution may be re-entered
190          * whenever the execution system so wishes, regardless of the
191          * blocking condition, and the execution method needs to
192          * confirm the condition or return BLOCKED again.
193          * @see Data#setTrigger(Observable)
194          * @see Data#setTrigger(Query)
195          */
196         public final static States BLOCKED =
197             new States() {{ name = "BLOCKED"; }};
198
199         /**
200          * The STOPPED state is returned to indicate that the goal
201          * execution has not completed, but is interrupted for some
202          * reason other than a blocking condition. The goal instance
203          * execution is thus expecting re-entry as soon as possible.
204          */
205         public final static States STOPPED =
206             new States() {{ name = "STOPPED"; }};
207
208         /**
209          * Returns the name.
210          */
211         public String toString() {
212             return name;
213         }
214     }
215
216     /**
217      * The property name for tracing goal execution.
218      */
219     public static final String TRACE = "gorite.goal.trace";
220
221     /**
222      * The name of the data element that identifies the executing
223      * performer.
224      */
225     public static final String PERFORMER = "this performer";
226
227     /**
228      * The name of this goal. There are no restrictions on which names
229      * goals have, but it is the name that identifies the goal when
230      * resolving a {@link BDIGoal} or a {@link TeamGoal} into some
231      * plan to achieve it. Conceptually it is the name that *is* the
232      * goal, wheras a Goal object rather is a node in a goal hierarchy
233      * to represent the idea of achieving the named goal.
234      *
235      * <p> An actual attempt to achieve the named goal is also
236      * separate both from the goal (name) itself, and from the Goal
237      * object (the idea of achieving it). Such an attempt is
238      * understood as an "intention", which in GORITE gets represented
239      * by a {@link Goal.Instance}.
240      */
241     private String name = null;
242
243     /**
244      * The control data, if any. This is only used by certain kinds of
245      * goals, to carry some certain definition detail separate from
246      * the goal type.
247      *
248      * <p> It is for instance used by {@link ParallelGoal} and {@link
249      * RepeatGoal} goals to name the data element that identifies each
250      * branch.
251      * @see Data#fork(String,int,String)
252      * @see Data#join(String,HashSet,String)
253      */
254     private String control = null;
255
256     /**
257      * The execution group to be used for pursuing the goal. If set,
258      * it is the name of the {@link Performer.TodoGroup} to use for
259      * goal instance execution. Goals of the same group are then
260      * managed by the execution management associated with the {@link
261      * Performer.TodoGroup}.
262      */
263     private String group = null;
264
265     /**
266      * The sub goals of this goal.
267      */
268     private Goal [] subgoals;
269
270     /**
271      * Where is this created?
272      */
273     final public StackTraceElement created;
274
275     /**
276      * Constructor.
277      */
278     public Goal(String n,Goal [] sg) {
279         name = n;
280         subgoals = sg;
281         StackTraceElement [] where = new Error().getStackTrace();
282         for ( int i = 0; i < where.length; i++ ) {
283             String c = where[i].getClassName();
284             if ( ! c.startsWith( "com.intendico.gorite." ) ) {
285                 created = where[i];
286                 return;
287             }
288         }
289         created = null;
290     }
291
292     /**
293      * Convenience constructor without sub goals. The sub goals may be
294      * set explicitly as value to the {@link #subgoals} member.
295      */
296     public Goal(String n) {
297         this( n, null );
298     }
299
300     /**
301      * This flag captures whether or not the {@link #TRACE} property
302      * set (to any value) upon class initialisation, and it may be set
303      * or reset subsequently for controlling the amount of goal
304      * execution logging to generate.
305      * 
306      * <p> Ordinarily one will turn on standard goal execution tracing
307      * by using the command line argument:
308      * <pre>
309      * -Dgorite.goal.trace=yes
310      * </pre>
311      */
312     public static boolean tracing = System.getProperty( TRACE, null ) != null;
313
314     // Set up a tracer if declared
315     static {
316         String c = System.getProperty( "gorite.tracer", null );
317         if ( c != null ) {
318             try {
319                 tracer = (Tracer) Class.forName( c ).newInstance();
320             } catch (Exception e) {
321                 e.printStackTrace();
322             }
323         }
324     }
325     /**
326      * This is a utility method to discover whether tracing has been
327      * requested by defining the {@link #TRACE} property, or not.
328      */
329     public static boolean isTracing() {
330         return tracing;
331     }
332
333     //
334     // Execution support
335     //
336
337     /**
338      * This is the primary execution method for a goal. It is invoked
339      * from the {@link Instance} object for a goal so as to achieve
340      * the goal, and it is told about both the calling goal {@link
341      * Instance} and {@link Data}. This base implementation invokes
342      * {@link Instance#action(String,Data)} for actual progress of the
343      * intention.
344      *
345      * <p> This method may be overridden by a user method when
346      * defining a task goal that needs access to the {@link Instance}
347      * object. See {@link #execute(Data)} for details.
348      */
349     public States execute(Data d,Instance i)
350         throws LoopEndException, ParallelEndException {
351         return i.action( i.head, d );
352     }
353
354     /**
355      * This is the simplified execution method for task goal. It is
356      * invoked from the {@link Instance} object for a task goal so as
357      * to achieve the goal by means of Java code, and it is told only
358      * about the intention {@link Data}. This base implementation
359      * merely returns {@link States#PASSED}, and it should be
360      * overridden by a user method when defining an actual task goal.
361      *
362      * <p> The method returns either {@link States#PASSED} or {@link
363      * States#FAILED} to indicate that the execution has terminated
364      * and the goal is achieved or not, or it returns {@link
365      * States#STOPPED} or {@link States#BLOCKED} to indicate that the
366      * attempt of achieving the goal is still going on, but execution
367      * control is relinquished temporarily (so as to allow other
368      * intentions to progress). By returning {@link States#BLOCKED},
369      * it also indicates that this code need not be invoked unless
370      * some other intention has progressed, but the {@link Executor}
371      * may choose to go idle. This is typically combined with the
372      * setting of an execution trigger, which is done via {@link
373      * Data#setTrigger(Query)} or {@link Data#setTrigger(Observable)},
374      * to ensure that the {@link Executor} wakes up when something
375      * interesting has happened.
376      */
377     public States execute(Data d)
378         throws InterruptedException, LoopEndException, ParallelEndException {
379         return States.PASSED;
380     }
381
382     /**
383      * Utility method for instantiating a goal according to its
384      * type. Each goal type has its own instance class extension,
385      * which implement the ways in which a goal is achieved through
386      * its sub goals.
387      */
388     public Instance instantiate(String head,Data d) {
389         return new Instance( head );
390     }
391
392     /**
393      * Control callback whereby the goal gets notified that an (its)
394      * intention is cancelled.
395      */
396     public void cancelled(Instance which) {
397     }
398
399     /**
400      * Base class for goal instance execution procedures.
401      */
402     public class Instance {
403
404         /**
405          * The trace name of the execution point. This defines where
406          * in the abstract goal execution tree this instance appears.
407          * The head is a kind of cursor into the abstract tree, set up
408          * as a sequence of numbers that tell which branch to follow
409          * so as to eventually arrive at this node. Branches are
410          * numbered from 0 and up, i.e. branch #3 is the fourth
411          * branch; for some goal types the branch number is also an
412          * index into the {@link #subgoals} array for the goal the
413          * execution is an instantiation of.
414          * The numbers are separated by characters that mark which
415          * kind of expansion the abstract tree has at the point.
416          * <ul>
417          * <li> ".n" marks insantiation of subgoal n
418          * <li> ":n" marks repetition n or branch n, for {@link
419          * LoopGoal}, {@link ParallelGoal}, {@link RepeatGoal} and
420          * {@link TeamGoal}.
421          * <li> "*n" marks attempt n for a {@link BDIGoal}
422          * </ul>
423          */
424         final public String head;
425
426         /**
427          * A more verbose intention name, which consists of the head
428          * and the goal name. This is used as key for the {@link
429          * Data.Bindings} of this intention.
430          */
431         final public String thread_name;
432
433         /**
434          * The Data object for this instance.
435          */
436         public Data data = null;
437
438         /**
439          * The monitoring state. This is the state an intention
440          * monitor should return; it starts off as BLOCKED, and turns
441          * into PASSED, FAILED or STOPPED (upon exception).
442          */
443         public States state = States.BLOCKED;
444
445         /**
446          * Returns the goal of this instance.
447          */
448         public Goal getGoal() {
449             return Goal.this;
450         }
451
452         /**
453          * Returns the plan argument object, if any, for this
454          * instance.
455          */
456         public Object getArgs() {
457             return data.getArgs( head );
458         }
459
460         /**
461          * Determine the plan-local variable name for the given short
462          * name.
463          */
464         public String local(String name) {
465             int i = head.lastIndexOf( "*" );
466             if ( i < 0 )
467                 i = head.length();
468             return head.substring( 0, i ) + "-" + name;
469         }
470
471         /**
472          * Returns the value of a data element as seen by this
473          * intention.
474          */
475         public Object getValue(String name) {
476             if ( data == null )
477                 return null;
478             return data.getValue( name, thread_name );
479         }
480
481         /**
482          * Sets a data value onto the innermost lookup path as seen by
483          * this intention. If an element of the name is found, then
484          * that element gains a new value, otherwise a new element is
485          * created for the most local binding of this intention.
486          */
487         public void setValue(String name,Object value) {
488             data.setValue( thread_name, name, value );
489         }
490
491         /**
492          * Utility method for updating the monitoring state, which
493          * also notifies any waiting thread.
494          */
495         synchronized public void setMonitoring(States s) {
496             if ( isTracing() )
497                 trace( "-- noting", " (" + s + ")" );
498             state = s;
499             if ( state == States.BLOCKED )
500                 state = s;
501             else
502                 data.setFlag();
503         }
504
505         /**
506          * Utility method to cancel the execution of this instance.
507          * This method is typically overridden by extension classes in
508          * order to propagate cancellation.
509          */
510         public void cancel() {
511             if ( isTracing() )
512                 System.err.println( "** cancel " + head + " " + this );
513             if ( state == States.BLOCKED || state == States.STOPPED )
514                 state = States.CANCEL;
515             // Notify Goal about cancelling this intention.
516             cancelled( this );
517             if ( data != null )
518                 data.dropBindings( thread_name );
519         }
520
521         /**
522          * Utility thread control method to wait until this instance
523          * is done.
524          */
525         synchronized public States waitDone()
526             throws LoopEndException, ParallelEndException {
527             if ( state == States.BLOCKED ) {
528                 trace( "-- waiting on ", "" );
529                 data.waitFlag();
530             }
531             throwSavedException();
532             return state;
533         }
534
535         /**
536          * Utility funtion that throws the particular exception, if
537          * any, that has been saved for this instance, or just
538          * returns.
539          */
540         public void throwSavedException()
541             throws LoopEndException, ParallelEndException {
542             if ( exception instanceof LoopEndException )
543                 throw (LoopEndException) exception;
544             if ( exception instanceof ParallelEndException )
545                 throw (ParallelEndException) exception;
546         }
547             
548         /**
549          * Keeps any LoopEndException or ParallelEndException thrown
550          * by a sub goal.
551          */
552         public Exception exception = null;
553
554         /**
555          * The instance performer.
556          */
557         public Performer performer = null;
558
559         /**
560          * Constructor, taking the execution point name as argument.
561          */
562         public Instance(String h) {
563             head = h.replace( '\"', ':' );
564             thread_name = head + " " + nameString( name );
565         }
566
567         /**
568          * Utility method to issue a standard trace message.
569          */
570         public void trace(String prefix,String suffix) {
571             if ( isTracing() ) {
572                 System.err.println(
573                     prefix + " " +
574                     ( performer == null? "" : ( performer.name + ":" ) ) +
575                     thread_name + suffix );
576             }
577         }
578
579         /**
580          * This is the primary entry point for executing this goal
581          * instance. It is invoked repeatedly by the calling goal
582          * instance for the purpose of progressing this goal instance
583          * via its {@link Goal#execute(Data,Instance)} method. On the
584          * first invokation, which is detected by virtue of {@link
585          * #data} being unset, this method registers the instance to
586          * the intention data and sets up the {@link #PERFORMER} data
587          * element.
588          *
589          * <p> This method also invokes {@link
590          * Performer#propagateBeliefs()} before progressing the
591          * intention.
592          *
593          * <p> If the enclosing goal's {@link Goal#group} attribute is
594          * non-null, then it is understood to name a {@link
595          * Performer.TodoGroup} for coordinating instances of this
596          * goal. Therefore, instead of actually progressing the
597          * intention, this instance gets added to the named {@link
598          * Performer.TodoGroup} (by {@link Performer#execute}), and
599          * subsequently all progress will occur "in parallel" via
600          * calls to the {@link #action()} method, while this method
601          * henceforth results in merely polling the instance state
602          * (which happens indirectly via the subsequent calls to
603          * {@link Performer#execute}).
604          */
605         public States perform(Data d)
606             throws LoopEndException, ParallelEndException {
607             Tracer t = tracer;
608             if ( t == null ) {
609                 try {
610                     return performIt( d );
611                 } catch(LoopEndException x) {
612                     throw x;
613                 } catch(ParallelEndException x) {
614                     throw x;
615                 } catch (Throwable x) {
616                     throw new GoriteError( this, x );
617                 }
618             }
619             t.enterPerform( this, d );
620             States b = States.STOPPED;
621             boolean returns = false;
622             Exception e = null;
623             try {
624                 b = performIt( d );
625                 returns = true;
626                 return b;
627             } catch (LoopEndException x) {
628                 e = x;
629                 throw x;
630             } catch (ParallelEndException x) {
631                 e = x;
632                 throw x;
633             } catch (Throwable x) {
634                 throw new GoriteError( this, x );
635             } finally {
636                 if ( returns ) {
637                     t.exitPerform( this, b );
638                 } else {
639                     t.exceptionPerform( this, e );
640                 }
641             }
642         }
643         
644         /**
645          * The working method for performing this Instance. It used to
646          * be {@link #perform}, but nowadays that method only deals
647          * with the {@link #tracer}, while invoking this method for
648          * the actual work.
649          * @see #perform
650          */
651         private States performIt(Data d)
652             throws LoopEndException, ParallelEndException {
653             try {
654                 if ( state == States.CANCEL ) {
655                     trace( "--", " (cancel)" );
656                     cancel();
657                     return States.CANCEL;
658                 }
659
660                 boolean reentry = true;
661                 if ( data == null ) {
662                     trace( "=>", " (group=" + group + ")" );
663                     data = d;
664                     data.link( thread_name );
665                     reentry = false;
666                     performer = (Performer) data.getValue( PERFORMER );
667                     if ( performer == null ) {
668                         System.err.println(
669                             "** Missing performer for goal \"" +
670                             name + "\" at " + head );
671                         System.err.println( "---------------------\n" + data );
672                         //System.exit( 1 );
673                     }
674                 } else {
675                     if ( state == States.BLOCKED &&
676                          ! data.isRunning( thread_name ) )
677                         return States.BLOCKED;
678                     trace( "=>", " (resume)" );
679                 }
680
681                 if ( ! reentry && performer.changeFocus() ) {
682                     if ( group == null )
683                         return States.STOPPED ;
684                 }
685
686                 performer.propagateBeliefs();
687
688                 States b;
689                 if ( group == null ) {
690
691                     b = execute( data, this );
692                     if ( b == States.PASSED || b == States.FAILED ) {
693                         data.dropBindings( thread_name );
694                     }
695                 } else {
696                     b = performer.execute( reentry, this, group );
697                 }
698                 setMonitoring( b );
699                 
700                 if ( isTracing() ) {
701                     trace( "<=", " (" + b + ")" );
702                 }
703
704                 return b;
705             } catch (LoopEndException e) {
706                 if ( data != null )
707                     data.dropBindings( thread_name );
708                 throw e;
709             } catch (ParallelEndException e) {
710                 if ( data != null )
711                     data.dropBindings( thread_name );
712                 throw e;
713             }
714         }
715
716         /**
717          * Utility method that runs this instance's
718          * action(String,Data). This is used for resuming the instance
719          * from a {@link Performer.TodoGroup}.
720          */
721         public States action() {
722             if ( state == States.CANCEL ) {
723                 trace( "--*", " (cancel)" );
724                 cancel();
725                 return States.CANCEL;
726             }
727             state = States.BLOCKED;
728             try {
729                 data.setThreadName( thread_name );
730                 if ( performer == null ) {
731                     throw new Error(
732                         "** Missing performer for goal \"" +
733                         name + "\" at " + head );
734                 }
735                 data.replaceValue( Goal.PERFORMER, performer );
736                 trace( "=>*", " (resume)" );
737                 performer.propagateBeliefs();
738                 return execute( data, this );
739             } catch (LoopEndException e) {
740                 exception = e;
741                 return States.STOPPED;
742             } catch (ParallelEndException e) {
743                 exception = e;
744                 return States.STOPPED;
745             }
746         }
747
748         /**
749          * Utility method to access a data element relative to this
750          * thread.
751          */
752         public Data.Element getDataElement(String name) {
753             return data.getLocalElement( thread_name, name );
754         }
755
756         /**
757          * Implements how a goal is achieved by means of its sub
758          * goals.  This base implementation invokes the goal's {@link
759          * #execute(Data)}.
760          */
761         public States action(String head,Data d)
762             throws LoopEndException, ParallelEndException {
763             try {
764                 return execute( d );
765             } catch (InterruptedException e) {
766                 e.printStackTrace();
767                 return States.STOPPED;
768             }
769         }
770     }
771
772     /**
773      * Utility method to quote a string.
774      */
775     public static String nameString(String n)
776     {
777         if ( n == null )
778             return "null";
779         return "\"" + n + "\"";
780     }
781
782     /**
783      * Returns a {@link java.lang.String} identifying the type of the
784      * goal.
785      */
786     public String getType()
787     {
788         String s = getClass().getName();
789         int i = s.lastIndexOf( "." );
790         return s.substring( i + 1 );
791     }
792
793     /**
794      * Makes a deep-structure string representation of this goal and
795      * all its sub goals, using a given depth prefix. It does not
796      * check for cyclic goal structure.
797      */
798     public String toString(String counter) {
799         StringBuffer s = new StringBuffer();
800         String x = "";
801         if ( counter != null ) {
802             s.append( counter );
803             s.append( " " );
804             x = counter + ".";
805         }
806         s.append( nameString( name ) );
807         s.append( " (" );
808         s.append( getType() );
809         s.append( ")" );
810         if ( subgoals != null ) {
811             for ( int i = 0; i < subgoals.length; i++ ) {
812                 s.append( "\n" );
813                 s.append( subgoals[ i ].toString( x + (i+1) ) );
814             }
815             //s.append( " // end " + counter + "\n" );
816         }
817         return s.toString();
818     }
819
820     /**
821      * Makes a deep-structure string representation of this goal and
822      * all its sub goals.
823      */
824     public String toString() {
825         return toString( null );
826     }
827
828     /**
829      * Returns the goal {@link #name} attribute.
830      */
831     public String getGoalName() {
832         return name;
833     }
834
835     /**
836      * Returns the goal {@link #control} attribute.
837      */
838     public String getGoalControl() {
839         return control;
840     }
841
842     /**
843      * Returns the {@link #subgoals} attribute.
844      */
845     public Goal [] getGoalSubgoals() {
846         return subgoals;
847     }
848
849     /**
850      * Returns the {@link #group} attribute.
851      */
852     public String getGoalGroup() {
853         return group;
854     }
855
856     /**
857      * Assigns the {@link #control} attribute.
858      */
859     public void setGoalControl(String v) {
860         control = v;
861     }
862
863     /**
864      * Assigns the {@link #subgoals} attribute.
865      */
866     public void setGoalSubgoals(Goal [] s) {
867         subgoals = s;
868     }
869
870     /**
871      * Assigns the {@link #group} attribute.
872      */
873     public void setGoalGroup(String v) {
874         group = v;
875     }
876 }