capture
[rrq/gorite.git] / com / intendico / gorite / Performer.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.Hashtable;
24 import java.util.Iterator;
25 import java.util.Observable;
26 import java.util.Observer;
27 import com.intendico.data.Ref;
28 import com.intendico.data.RuleSet;
29 import com.intendico.data.Rule;
30 import com.intendico.data.Query;
31 import com.intendico.gorite.Goal.Instance;
32 import com.intendico.gorite.addon.TodoGroupSkipBlocked;
33 import com.intendico.gorite.addon.TodoGroupRoundRobin;
34 import com.intendico.gorite.addon.TodoGroupParallel;
35
36 /**
37  * The Performer class is base class for {@link Team} member
38  * implementations. Conceptually a performer is considered to be the
39  * agent for {@link Goal} executions, and it contains the belief
40  * structures, if any, that agent reasoning may refer to.
41  *
42  * <p> A Performer is a {@link Capability} in that it has {@link Goal}
43  * hierarchies, and inner capabilities that define its reasoning
44  * processes.
45  *
46  * <p> The goals of performer may be grouped to belong to named "to-do
47  * groups", which are represented by the inner class {@link
48  * Performer.TodoGroup}. The goals of a TodoGroup are managed such
49  * that only one at a time is performed, and the group is associated
50  * with meta-level reasoning for deciding which goal to perform next.
51  */
52 public class Performer extends Capability {
53
54     /**
55      * The {@link Data.Element} name of the Todogroup object concerned
56      * when performing TodoGroups meta-level reasoning.
57      */
58     public static final String TODOGROUP = "todogroup";
59
60     /**
61      * Holds the Performer's name.
62      */
63     public String name;
64
65     /**
66      * Default constructor. When this is used, the {@link #name} field
67      * should be assigned before the performer is made to fill a role.
68      */
69     public Performer() {
70         this( "nobody" );
71     }
72
73     /**
74      * Constructor with {@link #name} given.
75      */
76     public Performer(String n) {
77         name = n;
78         performer = this;
79     }
80
81     /**
82      * Returns a text representation of this performer.
83      */
84     public String toString() {
85         return getClass().getName() + ": " + name;
86     }
87
88     /**
89      * Overrides {@link Capability#addCapability} so as to link up
90      * added capabilities with this performer.
91      */
92     public void addCapability(Capability c) {
93         c.setPerformer( this );
94         super.addCapability( c );
95     }
96
97     //
98     // The performer's rule set, if any.
99     //
100
101     /**
102      * The Performer's rule set.
103      */
104     public RuleSet rule_set = new RuleSet() {{
105         addObserver( new Observer() {
106                 public void update(Observable x,Object y) {
107                     signalExecutor();
108                 }
109             } );
110     }};
111
112     /**
113      * Utility method to apply all rules exhaustively.
114      */
115     public void propagateBeliefs() {
116         rule_set.run();
117     }
118
119     //
120     // TodoGroup based execution support
121     //
122
123     /**
124      * A constant goal name for a generic TodoGroup meta goal.
125      * Presently "todogroup meta goal".
126      */
127     public final static String TODOGROUP_META_GOAL =
128         TODOGROUP + " meta goal";
129
130     /**
131      * This performer's {link TodoGroup} objects.
132      */
133     public Hashtable/*<String,TodoGroup>*/ todogroups =
134         new Hashtable/*<String,TodoGroup>*/();
135
136     /**
137      * Utility method to find a named {@link TodoGroup}. This method
138      * will create a new {@link TodoGroup} rather than return
139      * <tt>null</tt>, and the new {@link TodoGroup} will have its
140      * {@link TodoGroup#meta_goal} string set to <tt>null</tt> by
141      * default (meaning no meta goal), unless the system property
142      * <tt>gorite.todo.classic</tt> is set, in which case {@link
143      * #TODOGROUP_META_GOAL} is used as meta goal name.
144      * @param rn the {@link TodoGroup} name
145      * @return the {@link TodoGroup} object of that name among this
146      * performer's {@link #todogroups}.
147      */
148     public TodoGroup getTodoGroup(String rn) {
149         if ( rn == null ) 
150             return null;
151         TodoGroup r = (TodoGroup) todogroups.get( rn );
152         if ( r == null ) {
153             if ( Goal.isTracing() ) {
154                 System.err.println( "-- " + name + " adds TodoGroup " + rn );
155             }
156             String mg = null;
157             if ( System.getProperty( "gorite.todo.classic" ) != null )
158                 mg = TODOGROUP_META_GOAL;
159             r = new TodoGroup( rn, mg );
160             addTodoGroup( r );
161         }
162         return r;
163     }
164
165     /**
166      * Utility method to add a TodoGroup with given name and meta goal.
167      */
168     public void addTodoGroup(String n,String mg) {
169         addTodoGroup( new TodoGroup( n, mg ) );
170     }
171     
172     /**
173      * Utility method to add a given TodoGroup.
174      */
175     public void addTodoGroup(TodoGroup r) {
176         if ( todogroups == null )
177             todogroups = new Hashtable/*<String,TodoGroup>*/();
178         todogroups.put( r.name, r );
179     }
180
181     /**
182      * Control flag for TodoGroup execution.
183      */
184     private long todo_added = 0;
185
186     /**
187      * Utility method to add an instance to its todogroup.
188      */
189     public Goal.States execute(
190         boolean reentry,Goal.Instance instance,String group)
191         throws LoopEndException, ParallelEndException
192     {
193         if ( Goal.isTracing() )
194             System.err.println( "-- execute: " + group + " " + reentry );
195         //todo_added += 1;
196         return getTodoGroup( group ).execute( reentry, instance );
197     }
198
199     /**
200      * Index of TodoGroup to execute next.
201      */
202     private int todo_index = 0;
203
204     /**
205      * Control method to run all todogroups until stopped or blocked.
206      */
207     synchronized public Goal.States runPerformer() {
208         if ( Goal.isTracing() )
209             System.err.println(
210                 "-- " + name + " running " + 
211                 ( todogroups == null? 0 : todogroups.size() ) );
212         propagateBeliefs();
213         if ( todogroups == null || todogroups.size() == 0 )
214             return Goal.States.BLOCKED;
215         boolean running = true;
216         Goal.States s = Goal.States.BLOCKED;
217         while ( running ) {
218             long todo_added_cache = todo_added;
219             TodoGroup [] groups = (TodoGroup []) todogroups.values().toArray(
220                 new TodoGroup [ todogroups.size() ] );
221             running = false;
222             propagateBeliefs();
223             for ( int i = 0; i < groups.length; i++ ) {
224                 if ( todo_index >= groups.length )
225                     todo_index = 0;
226                 s = groups[ todo_index++ ].runGroup();
227                 if ( Goal.isTracing() )
228                     System.err.println( "-- runGroup = " + s );
229                 propagateBeliefs();
230                 running = todo_added != todo_added_cache;
231                 if ( s == Goal.States.BLOCKED ) {
232                     continue;
233                 }
234                 if ( s == Goal.States.STOPPED )
235                     break;
236                 running = true;
237                 s = Goal.States.BLOCKED;
238             }
239         }
240         if ( Goal.isTracing() )
241             System.err.println( "-- " + name + " (" + s + ")" );
242         return s;
243     }
244
245     /**
246      * The TodoGroup class provides a meta-level goal execution
247      * facility, allowing multiple parallel executions be
248      * synchronised.
249      *
250      * <p> A Goal that has its {@link Goal#group} attribute set will
251      * be executed by adding its execution instance to the nominated
252      * TodoGroup. The instance execution will be progressed as a
253      * TodoGroup execution task, when the execution thread gets around
254      * to it.
255      *
256      * <p> The TodoGroup is associated with a meta goal {@link
257      * #meta_goal}, which is performed by the execution machinery when
258      * the TodoGroup state changes: when a goal instance is added,
259      * when its execution gets blocked, and when it completes. The
260      * meta goal execution may inspect the TodoGroup and promote any
261      * of the pending goal instance executions to be the one to
262      * progress next.
263       */
264     public class TodoGroup implements Observer {
265
266         /**
267          * A name attribute for the group. There is no particular
268          * significance in this name except that it uniquely
269          * identifies a TodoGroup object for a {@link Performer}.
270          */
271         public String name = null;
272
273         /**
274          * This names the {@link BDIGoal} to be invoked when {@link
275          * Goal.Instance} objects are added to, or removed from the
276          * TodoGroup, as well as when the intention execution returns
277          * blocked. The meta goal is invoked for the purpose of
278          * deciding which intention to pursue next.
279          * 
280          * <p> The default name is {@link
281          * Performer#TODOGROUP_META_GOAL}.
282          *
283          * <p> When the meta goal is invoked, it is provided with the
284          * following data elements:
285          * <dl>
286          * <dt>named by {@link Goal#PERFORMER}</dt><dd>this {@link
287          * Performer} object</dd>
288          * <dt>named by {@link #TODOGROUP}</dt><dd>this TodoGroup
289          * object</dd>
290          * <dt>"added"</dt><dd>{@link Vector} of the {@link
291          * Goal.Instance} objects just added to the group, if any.</dd>
292          * <dt>"removed"</dt><dd>{@link Vector} of the {@link
293          * Goal.Instance} objects just removed from the group, if any.</dd>
294          * <dt>"top"</dt><dd>the current top-of-stack {@link
295          * Goal.Instance} object. When an instance terminates, it is
296          * added to the "removed" set, then removed from the stack
297          * before the meta goal gets invoked.</dd>
298          * </dl>
299          *
300          * <p> Note that the "added" and "removed" collections have
301          * been processed, and their contents have been added and
302          * removed respectively before the meta goal is performed.
303          *
304          * <p> A meta goal plan typically uses {@link #promote} to
305          * change the {@link #stack}. Still, no other code is supposed
306          * to operate on the stack concurrently, so the meta goal plan
307          * may change the stack in more complex ways.
308          *
309          * @see TodoGroupSkipBlocked
310          * @see TodoGroupRoundRobin
311          * @see TodoGroupParallel
312          */
313         public String meta_goal = null;
314
315         /**
316          * Constructor given name and meta goal name. If the meta goal
317          * name is null, then the default meta goal name ({@link
318          * Performer#TODOGROUP_META_GOAL}) is used.
319          */
320         public TodoGroup(String n,String mg) {
321             name = n;
322             meta_goal = mg;
323         }
324
325         /**
326          * This is the stack of executions in this TodoGroup.
327          */
328         public Vector/*<Goal.Instance>*/ stack =
329             new Vector/*<Goal.Instance>*/();
330
331         public Vector/*<Goal.Instance>*/ added =
332             new Vector/*<Goal.Instance>*/();
333
334         public Vector/*<Goal.Instance>*/ removed =
335             new Vector/*<Goal.Instance>*/();
336
337         /**
338          * Keeping track of an ongoing meta goal execution. This is in
339          * particular used if a meta goal execution is blocked or
340          * stopped, in which case the pursuit of TodoGroup tasks is
341          * stopped. The ongoing meta goal execution must terminate
342          * (passed, failed or cancel) before the next TodoGroup task
343          * is pursued.
344          *
345          * <p> Note that new TodoGroup tasks can be added during an
346          * ongoing meta goal execution, and that this will not trigger
347          * any new meta goal execution. However, it will cause {@link
348          * #added_while_ongoing} to be set, which in turn results in
349          * an additional meta goal execution to follow the ongoing
350          * one.
351          */
352         public Goal.Instance ongoing_meta_goal = null;
353
354         /**
355          * A flag that indicates that a TodoGroup task was added
356          * during an ongoing meta goal execution. This will cause a
357          * subsequent meta goal execution before any TodoGroup task is
358          * progressed.
359          */
360         public boolean added_while_ongoing = false;
361
362         /**
363          * Invoked prior to pursuing the top element of the
364          * stack. This method adds the added instances onto the stack,
365          * cleares out the removed instances. It creates a meta goal
366          * instance if either forced is true, or the some instance was
367          * added or removed.
368          *
369          * <p> Note that this method will change the {@link #stack} by
370          * removing completed tasks, and actually adding tasks pending
371          * to be added. This happens before the invocation of the meta
372          * goal (if any). 
373          */
374         private Goal.Instance enterGroup(boolean forced) {
375             Vector/*<Goal.Instance>*/ coming = null;
376             Vector/*<Goal.Instance>*/ gone = null;
377
378             // pick up the added and removed instances
379             // the added vector changes by parallel threads
380             synchronized ( added ) {
381                 coming = added;
382                 added = new Vector/*<Goal.Instance>*/();
383                 gone = removed;
384                 removed = new Vector/*<Goal.Instance>*/();
385             }
386
387             // handle removed instances
388             for ( Iterator/*<Goal.Instance>*/ i = gone.iterator();
389                   i.hasNext(); ) {
390                 Goal.Instance instance = (Goal.Instance) i.next();
391                 instance.data.deleteObserver( instance.thread_name, this );
392             }
393
394             // handle added instances
395             for ( Iterator/*<Goal.Instance>*/ i = coming.iterator();
396                   i.hasNext(); ) {
397                 Goal.Instance instance = (Goal.Instance) i.next();
398                 stack.add( instance );
399                 instance.data.addObserver( instance.thread_name, this );
400             }
401
402             if ( meta_goal == null )
403                 return null;
404
405             if ( coming.size() + gone.size() == 0 ) {
406                 if ( ! forced )
407                     return null;
408                 if ( System.getProperty( "gorite.todo.classic" ) != null )
409                     return null;
410             }
411
412             if ( Goal.isTracing() ) {
413                 System.err.println(
414                     "-- " + Performer.this.name + "/" + name +
415                     " meta goal " + added + " \"" + meta_goal + "\"" );
416             }
417
418             Goal.Instance mg = new BDIGoal( meta_goal )
419                 .instantiate( name, null );
420             mg.performer = Performer.this;
421             mg.data = new Data()
422                 .setValue( Goal.PERFORMER, Performer.this )
423                 .setValue( TODOGROUP, this )
424                 .setValue( "added", coming )
425                 .setValue( "removed", gone )
426                 .setValue( "top", stack.size() == 0? null : stack.get( 0 ) );
427             mg.data.link( mg.thread_name );
428
429             return mg;
430         }
431
432         /**
433          * A transient handle to the most recent blocking intention on
434          * the actual stack.
435          */
436         public Goal.Instance blocking = null;
437
438         /**
439          * Run the group. This means to run its top-of-stack goal
440          * instance. If that passes or fails, it is removed from the
441          * group, and the meta goal is called upon to possibly
442          * re-order remaining goal instances. If it returns STOPPED,
443          * the group execution returns STOPPED. If it returns BLOCKED,
444          * the meta goal is invoked in order to possibly re-arrange
445          * the group, and if so, the group execution continues with
446          * the new top goal instance. Otherwise the group execution
447          * returns blocked.
448          */
449         public Goal.States runGroup() {
450             if ( Goal.isTracing() ) {
451                 System.err.println(
452                     "-- TodoGroup " + Performer.this.name + "/" + name +
453                     " " + stack.size() );
454             }
455             for ( ;; ) {
456                 if ( ongoing_meta_goal != null ) {
457                     if ( Goal.isTracing() ) {
458                         System.err.println(
459                             "-- TodoGroup " + Performer.this.name +
460                             "/" + name + " ongoing meta goal " +
461                             stack.size() );
462                     }
463                     Goal.States s = ongoing_meta_goal.action();
464                     if ( Goal.isTracing() ) {
465                         System.err.println(
466                             "-- " + Performer.this.name + "/" + name +
467                             " meta goal " + " \"" + meta_goal + "\" " +
468                             s );
469                     }
470                     if ( s == Goal.States.STOPPED ) {
471                         continue; // Don't interrupt meta goal
472                     }
473                     if ( s == Goal.States.BLOCKED ) {
474                         return s;
475                     }
476                 }
477                 ongoing_meta_goal = enterGroup( ongoing_meta_goal == null );
478                 if ( ongoing_meta_goal != null )
479                     continue;
480                 if ( stack.size() == 0 )
481                     return Goal.States.BLOCKED;
482
483                 Goal.Instance x = (Goal.Instance) stack.get( 0 );
484
485                 if ( x == blocking ) {
486                     if ( ! x.data.isRunning( x.thread_name ) ) {
487                         return Goal.States.BLOCKED;
488                     }
489                     if ( Goal.isTracing() ) {
490                         System.err.println( "-- BLOCKING " + x.head +
491                                             " without trigger" );
492                     }
493                     blocking = null;
494                     //return Goal.States.BLOCKED;
495                 }
496                 blocking = null;
497                 Goal.States s = x.action();
498                 if ( Goal.isTracing() ) {
499                     System.err.println( "-- noting " + x.head + " " + s );
500                 }
501                 if ( s == Goal.States.STOPPED ) {
502                     return s;
503                 }
504                 if ( s == Goal.States.BLOCKED ) {
505                     blocking = x;
506                     continue;
507                 }
508                 // s == Goal.States.PASSED
509                 // s == Goal.States.FAILED
510                 // s == Goal.States.CANCEL
511                 x.setMonitoring( s );
512                 if ( stack.remove( x ) )
513                     removed.add( x );
514             }
515         }
516
517         /**
518          * Adds a goal for execution as part of this todo
519          * group. Returns the instance monitoring state.
520          */
521         public Goal.States execute(
522             boolean reentry,Goal.Instance instance)
523             throws LoopEndException, ParallelEndException
524         {
525             if ( ! reentry ) {
526                 todo_added += 1;
527                 signalExecutor();
528                 synchronized ( added ) {
529                     instance.setMonitoring( Goal.States.STOPPED );
530                     added.add( instance );
531                 }
532                 signalExecutor();
533             } else {
534                 instance.throwSavedException();
535             }
536             return instance.state;
537         }
538
539         /**
540          * Utility method to obtain stack size.
541          */
542         public int size() {
543             return stack.size();
544         }
545
546         /**
547          * Utility method to access the i:th stack element.
548          * @throws ArrayIndexOutOfBoundsException if the stack is
549          * empty, or i is negative.
550          */
551         public Instance get(int i) {
552             return (Instance) stack.get( i );
553         }
554
555         /**
556          * Utility method to remove the i:th stack element.
557          * @throws ArrayIndexOutOfBoundsException if appropriate
558          */
559         public Instance remove(int i) {
560             return (Instance) stack.remove( i );
561         }
562
563         /**
564          * Utility method to insert an element to be the i:th stack
565          * element.
566          * @throws ArrayIndexOutOfBoundsException if appropriate
567          */
568         public void add(int i,Instance x) {
569             stack.add( i, x );
570         }
571
572         /**
573          * Utility method to move a selected {@link Goal.Instance} to
574          * be the one to pursue next.
575          */
576         public void promote(int index) {
577             if ( index != 0 )
578                 stack.insertElementAt( stack.remove( index ), 0 );
579         }
580
581         /**
582          * Utility method to move the top {@link Goal.Instance} to
583          * stack bottom.
584          */
585         public void rotate() {
586             if ( stack.size() > 1 )
587                 stack.add( stack.remove( 0 ) );
588         }
589
590         ///
591         /// Observer interface implementation
592         ///
593
594         /**
595          * Implements {@link Observer#update} by pretending that a
596          * todo group is added, as way of signalling that the
597          * performer should process its todogroups again
598          */
599         public void update(Observable x,Object y) {
600             todo_added += 1;
601         }
602
603     } // End class TodoGroup
604
605     /**
606      * The {@link Executor} for this performer.
607      */
608     public Executor executor = null;
609
610     /**
611      * Utility method to signal the {@link #executor} that this
612      * performer wants some attention. This will use the current
613      * executor, and attach to the default executor if none is
614      * assigned.
615      */
616     public void signalExecutor() {
617         if ( executor == null ) {
618             executor = Executor.getDefaultExecutor();
619             shareInquirables( null );
620             executor.addPerformer( this );
621         }
622         executor.signal();
623     }
624
625     /**
626      * Utility method to instantiate the goal for given the head, and
627      * then keep invoking the perform method with the given data
628      * context, until it passes or fails.
629      */
630     synchronized public boolean performGoal(Goal goal,String head,Data d) {
631         signalExecutor();
632         try {
633             Goal.Instance instance = goal.instantiate( head, d );
634             Goal.States s = Goal.States.PASSED;
635             for ( ;; ) {
636                 if ( Goal.isTracing() )
637                     System.err.println( "** perform " + instance.head );
638                 d.replaceValue( Goal.PERFORMER, this );
639                 s = instance.perform( d );
640                 if ( s == Goal.States.PASSED || s == Goal.States.FAILED )
641                     break;
642                 boolean idle = s == Goal.States.BLOCKED;
643                 if ( executor != null ) {
644                     if ( executor.runPerformersBlocked() )
645                         idle = false;
646                 }
647                 if ( idle )
648                     instance.waitDone(); // ignore result
649             }
650             if ( executor != null ) {
651                 while ( executor.runPerformersBlocked() )
652                     ;
653             }
654             return s == Goal.States.PASSED;
655         } catch (LoopEndException e) {
656             e.printStackTrace();
657             return false;
658         } catch (ParallelEndException e) {
659             e.printStackTrace();
660             return false;
661         }
662     }
663
664     /**
665      * Alternative performGoal method, with input and output held as
666      * Vector<Ref> collections.
667      */
668     public boolean performGoal(
669         Goal goal,String head, Vector/*<Ref>*/ ins,Vector/*<Ref>*/ outs) {
670         Data d = new Data();
671         d.set( ins );
672         boolean b = performGoal( goal, head, d );
673         d.get( outs, true );
674         return b;
675     }
676
677     /**
678      * Alternative performGoal method, with goal given as a {@link
679      * String}, implying a BDI goal, and input and output held as
680      * Vector<Ref> collections.
681      */
682     public boolean performGoal(
683         String goal,String head, Vector/*<Ref>*/ ins,Vector/*<Ref>*/ outs) {
684         return performGoal( new BDIGoal( goal ), head, ins, outs );
685     }
686
687     /**
688      * Returns true if the performer execution should change focus.
689      */
690     public boolean changeFocus() {
691         return executor != null && executor.changeFocus();
692     }
693
694     /**
695      * Returns a {@link RoleFilling} object for filling a given {@link
696      * Team.Role}. The object represents that this performer fills the
697      * given {@link Team.Role}. This method may be overridden in a
698      * specific performer, either as way of providing an extended
699      * {@link RoleFilling} object, or to possibly refuse the {@link
700      * Team.Role} filling, by returning <em>null</em>.
701      */
702     public RoleFilling fillRole(Team.Role r) {
703         return new RoleFilling( r );
704     }
705
706     /**
707      * This class is used for representing this performer when acting
708      * in a given {@link Team.Role}. It in particular provides a
709      * {@link #lookup(String)} method that provides a combined plan
710      * lookup in both the filled {@link Team.Role} (qua {@link
711      * Capability}) and the enclosing {@link Performer} (qua {@link
712      * Capability}), and thereby extending the performer's capability
713      * with the role plans.
714      */
715     public class RoleFilling extends Capability {
716
717         /**
718          * The {@link Team.Role} being filled.
719          */
720         public Team.Role role;
721
722         /**
723          * Constructor. The enclosing performer is set to fill the
724          * given role.
725          */
726         public RoleFilling(Team.Role r) {
727             role = r;
728         }
729
730         /**
731          * Extends the base class method {@link Capability#lookup} by
732          * wrapping the result goals into {@link TransferGoal} goals.
733          */
734         public Vector/*<Goal>*/ lookup(String name) {
735             Vector/*<Goal>*/ v = Performer.this.lookup( name );
736             Vector/*<Goal>*/ vx = role.lookup( name );
737             vx.addAll( super.lookup( name ) );
738             for ( Iterator/*<Goal>*/ gi = v.iterator(); gi.hasNext(); ) {
739                 vx.add( goalForPerformer( (Goal) gi.next() ) );
740             }
741             return vx;
742         }
743
744         /**
745          * Extends the base class method {@link Capability#hasGoals} by
746          * formarding to the performer
747          */
748         public boolean hasGoal(String name) {
749             return role.hasGoal( name ) ||
750                 super.hasGoal( name ) ||
751                 Performer.this.hasGoal( name );
752         }
753
754         /**
755          * Returns the {@link Performer} of this RoleFilling.
756          */
757         public Performer getPerformer() {
758             return Performer.this;
759         }
760
761         /**
762          * Returns a text representation of this performer object.
763          */
764         public String toString()
765         {
766             return "role filler " + Performer.this;
767         }
768     }
769
770     /**
771      * Utility method to obtain the {@link Team} that has established
772      * a {@link Team.Role} filler under a given name in the {@link
773      * Data}.
774      */
775     static public Team team(String rolename,Data d) {
776         RoleFilling r = (RoleFilling) d.getValue( rolename );
777         return r.role.team();
778     }
779
780     /**
781      * Utility method to create a {@link TransferGoal} object aimed
782      * at this performer.
783      */
784     public Goal goalForPerformer(Goal g) {
785         return new TransferGoal( this, g );
786     }
787
788     /**
789      * A method that returns {@link #name}.
790      */
791     public String getName() {
792         return name;
793     }
794
795     ///
796     /// Plan choice support
797     ///
798
799     /**
800      * The association between (BDI) goals and their plan choice
801      * modifiers. These take effect in the BDI plan choice processing,
802      * to provide the decision for choosing a next applicable plan
803      * instance. The modifier should be either a {@link
804      * java.util.Random Random} object
805      * (for making a random draw among highest precedence plan
806      * options0, or a {@link String} (for making the choice by meta
807      * level plan processing).
808      *
809      * @see BDIGoal
810      */
811     public Hashtable/*<String,Object>*/ plan_choices = null;
812
813     /**
814      * Utility method to declare a plan choice modifier for a
815      * goal. The modifier should be either a {@link java.util.Random
816      * Random} object, or a {@link String}.
817      *
818      * @see BDIGoal
819      */
820     public void setPlanChoice(String goal,Object choice_method) {
821         if ( plan_choices == null ) {
822             plan_choices = new Hashtable/*<String,String>*/();
823         }
824         plan_choices.put( goal, choice_method );
825     }
826
827     /**
828      * Utility method to obtain the plan choice modifier, if any,
829      * associated with the given goal.
830      */
831     public Object getPlanChoice(String goal) {
832         return plan_choices == null? null : plan_choices.get( goal );
833     }
834 }