capture
[rrq/gorite.git] / com / intendico / gorite / Team.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 import com.intendico.data.Relation;
25 import java.util.Enumeration;
26 import java.util.HashSet;
27 import java.util.Hashtable;
28 import java.util.Iterator;
29 import java.util.Vector;
30
31 /**
32  * The Team class is a base class for a structured task performer,
33  * i.e. a {@link Performer} that combine sub performers. A particular
34  * team type is defined by extending the Team class. The structure of
35  * an instance of a team type is defined by calls to the {@link
36  * #addPerformer} method. This provides a lookup context for inner
37  * {@link TaskTeam} objects, which are used to define particular sub
38  * team constallations for particular team tasks.
39  */
40
41 public class Team extends Performer {
42
43     /**
44      * Default constructor.
45      */
46     public Team() {
47     }
48
49     /**
50      * Constructor for team of given name.
51      */
52     public Team(String name)
53     {
54         super( name );
55     }
56
57     /**
58      * This is the current role filling for this team -- it's
59      * obligation structure.
60      */
61     public Vector/*<Performer>*/ performers = new Vector/*<Performer>*/();
62
63     /**
64      * This is a table of TaskTeam objects, to support longer term
65      * constallations.
66      */
67     public Hashtable/*<String,TaskTeam>*/ groupings =
68         new Hashtable/*<String,TaskTeam>*/();
69
70     /**
71      * Adds a particular performer in a named role filling.
72      */
73     synchronized public void addPerformer(Performer performer)
74     {
75         performers.add( performer );
76         for ( Iterator/*<TaskTeam>*/ ti = groupings.values().iterator();
77               ti.hasNext(); ) {
78             ((TaskTeam) ti.next()).updateFillers( performer );
79         }
80     }
81
82     /**
83      * Adds multiple, named performers.
84      */
85     public void addPerformers(Performer [] p)
86         throws Exception
87     {
88         if ( p != null ) {
89             for ( int i = 0; i < p.length; i++ )
90                 addPerformer( p[i] );
91         }
92     }
93
94     /**
95      * Utility method to pick a performer. Return null when the index
96      * is out of bounds.
97      */
98     synchronized public Performer getPerformer(int i) {
99         if ( i < 0 || i >= performers.size() )
100             return null;
101         return (Performer) performers.get( i );
102     }
103
104     /**
105      * Utility method to pick a performer by name. Returns the first
106      * Performer of this team with the given name, or null.
107      */
108     public Performer getPerformer(String name) {
109         for ( Iterator/*<Performer>*/ i =
110                   performers.iterator(); i.hasNext(); ) {
111             Performer p = (Performer) i.next();
112             if ( name.equals( p.name ) ) {
113                 return p;
114             }
115         }
116         return null;
117     }
118
119      /**
120       * Utility method to remove a performer by name. Removes and
121       * returns the first Performer with the given name, or null;
122       */
123      public Performer removePerformer(String name) {
124          for ( Iterator/*<Performer>*/ i =
125                    performers.iterator(); i.hasNext(); ) {
126              Performer p = (Performer) i.next();
127              if ( name.equals( p.getName() ) ) {
128                  i.remove();
129                  return p;
130              }
131          }
132          return null;
133      }
134
135     /**
136      * Utility method to access a long term TaskTeam by name.
137      */
138     public TaskTeam getTaskTeam(String name) {
139         return (TaskTeam) groupings.get( name );
140     }
141
142     /**
143      * Utility method to define a long term TaskTeam by unique name.
144      */
145     public void setTaskTeam(String name,TaskTeam group) {
146         groupings.put( name, group );
147         group.updateFillers( performers );
148     }
149
150     /**
151      * A TaskTeam represents a sub-grouping of a team's performers
152      * into roles as appropriate for particular team activities. Each
153      * TaskTeam is populated to hold a selection of performers that
154      * are nominated for the task team roles according to their
155      * abilities. This role filling defines the candidates for acting
156      * in the roles when the TaskTeam is "established" in a particular
157      * intention.
158      *
159      * <p> The role filling in a TaskTeam as well as the established
160      * role filling in an intention may be changed at any time. When
161      * the TaskTeam is installed for a {@link Team}, the current
162      * {@link #performers} collection is revide and performers are
163      * nominated to roles as appropriate. Thereafter each subsequently
164      * added {@link Performer} is automatically considered, and
165      * nominated to roles of installed TaskTeams as appropriate. The
166      * detail nomination decision is made by the overridable {@link
167      * Role#canFill(Performer)} method, which by default ensures
168      * distinct, singular role filling, and that the nominated
169      * {@link Performer} has plans for all required goals.
170      *
171      * <p> The TaskTeam is deployed on demand for intentions,
172      * typically by by means of {@link #deploy(String)} goal in team
173      * plans. When deployed in this way, the TaskTeam role filling is
174      * established in the intention {@link Data} via {@link
175      * #establish(Data)}, with a further filtering through the
176      * overridable {@link Role#canAct(Data,Performer)} method, which
177      * by default is "true".
178      */
179     public class TaskTeam {
180
181         /**
182          * The roles of the task team.
183          */
184         public Vector/*<Role>*/ roles = new Vector/*<Role>*/();
185
186         /**
187          * The population of this task team, held as a {@link
188          * Relation} beteen roles and performers.
189          */
190         public Relation fillers = new Relation( "fillers", 2 );
191
192         /**
193          * Utility method to add a role to the task team.
194          */
195         public void addRole(Role r) {
196             r.group = this;
197             roles.add( r );
198         }
199
200         /**
201          * Utility method to remove a role from the task team. This
202          * also removes any population for the role.
203          */
204         public void removeRole(Role r) {
205             r.group = this;
206             roles.remove( r );
207             filling( r, new Ref( "$p" ) ).remove();
208         }
209
210         /**
211          * Utility method to return a role filling {@link Query}
212          * object for a given role and performer, either of which is
213          * given as a constant of its type, null, or a {@link Ref}
214          * object, which may be (appropriately) bound or unbound.
215          */
216         public Query filling(Object r,Object p) {
217             try {
218                 return fillers.get( new Object [] { r, p } );
219             } catch (Exception e) {
220                 e.printStackTrace();
221                 return null;
222             }
223         }
224
225         /**
226          * Utility method to define a role filler for a role.
227          */
228         public void fillRole(Role r,Performer p) {
229             filling( r, p ).add();
230         }
231
232         /**
233          * Utility method to clear all filling.
234          */
235         public void clearFilling() {
236             filling( null, null ).remove();
237         }
238
239         /**
240          * Updates the role nominations for the given {@link Performer
241          * performers}. This method simply iterates over the
242          * collection and updates each performer one by one via {@link
243          * #updateFillers(Performer)}.
244          */
245         public void updateFillers(Vector/*<Performer>*/ performers) {
246             for ( Iterator/*<Performer>*/ pi = performers.iterator();
247                   pi.hasNext(); ) {
248                 updateFillers( (Performer) pi.next() );
249             }
250         }
251
252         /**
253          * Updates the role nominations for the given {@link
254          * Performer} as appropriate for roles in this taks team.
255          * Which roles it is nominated to, if any, is determined by
256          * {@link Role#canFill(Performer)}.
257          */
258         public void updateFillers(Performer p) {
259             for ( Iterator/*<Role>*/ ri = roles.iterator(); ri.hasNext(); ) {
260                 Role r = (Role) ri.next();
261                 Query q = filling( r, p );
262                 if ( r.canFill( p ) ) {
263                     if ( Goal.isTracing() ) {
264                         System.err.println(
265                             "** can Fill: " + r.name + " " + p );
266                     }
267                     q.add();
268                 } else {
269                     if ( Goal.isTracing() ) {
270                         System.err.println(
271                             "** cannot Fill: " + r.name + " " + p );
272                     }
273                     q.remove();
274                 }
275             }
276         }
277
278         /**
279          * Utility method to establish the role filling in the given
280          * intention data. This will establish the roles one by one in
281          * declaration order, using the current fillers as filtered by
282          * the {@link Role#canAct} method.
283          *
284          * <p> Note that role filling is set up as data elements using
285          * the role names. The value for a role name is the collection
286          * of fillers that can act in the role, qua {@link
287          * Capability}, but actually represented by {@link
288          * Performer.RoleFilling} objects as returned by the {@link
289          * Performer#fillRole(Team.Role)} method.
290          * @return true if all roles have some filling
291          */
292         public boolean establish(Data d) {
293             if ( Goal.isTracing() ) {
294                 System.err.println( "** establish task team" );
295             }
296             for ( Iterator/*<Role>*/ ri = roles.iterator();
297                   ri.hasNext(); ) {
298                 Role role = (Role) ri.next();
299                 Ref $p = new Ref( "$p" );
300                 Query q = filling( role, $p );
301                 d.forget( role.name );
302                 boolean filled = false;
303                 HashSet actors = Team.getActors( role.name, d );
304                 try {
305                     while ( q.next() ) {
306                         Performer candidate = (Performer) $p.get();
307                         if ( actors.contains( candidate ) ) {
308                             if ( Goal.isTracing() ) {
309                                 System.err.println(
310                                     "** already Acting: " + role.name +
311                                     " " + candidate );
312                             }
313                         } else if ( role.canAct( d, candidate ) ) {
314                             if ( Goal.isTracing() ) {
315                                 System.err.println(
316                                     "** can Act: " + role.name +
317                                     " " + candidate );
318                             }
319                             Capability c = candidate.fillRole( role );
320                             if ( c != null ) {
321                                 d.setValue( role.name, c );
322                                 filled = true;
323                             } else if ( Goal.isTracing() ) {
324                                 System.err.println(
325                                     "** refuses to Act: " + role.name +
326                                     " " + candidate );
327                             }
328                         } else if ( Goal.isTracing() ) {
329                             System.err.println(
330                                 "** cannot Act: " + role.name +
331                                 " " + candidate );
332                         }
333                     }
334                 } catch (Exception e) {
335                     e.printStackTrace();
336                 }
337                 if ( ! filled ) {
338                     if ( Goal.isTracing() ) {
339                         System.err.println(
340                             "** Role not established: " + role.name );
341                     }
342                     return false; // couldn't establish the role at
343                                   // all
344                 }
345             }
346             return true;
347         }
348
349         /**
350          * Utility method that creates a {@link Goal} of establishing
351          * this task team in the intention data when the goal is to be
352          * achieved.
353          * @see #establish(Data)
354          */
355         public Goal deploy(String name) {
356             return new Goal( "deploy " + name ) {
357                     public States execute(Data d) {
358                         return establish( d )? States.PASSED : States.FAILED;
359                     }
360                 };
361         }
362
363         /**
364          * Utility method to collate all actors of this TaskTeam's
365          * roles in the given Data.
366          * @return a Hashtable of {@link Performer} HashSets, keyed by
367          * the {@link Role} names of the TaskTeam's roles.
368          */
369         public Hashtable/*<String,HashSet<Performer>>*/ getActors(Data d) {
370             Hashtable result = new Hashtable();
371             for ( Iterator/*<Role>*/ ri = roles.iterator(); ri.hasNext(); ) {
372                 Role r = (Role) ri.next();
373                 HashSet/*<Performer>*/ actors = Team.getActors( r.name, d );
374                 if ( actors != null )
375                     result.put( r.name, actors );
376             }
377             return result;
378         }
379
380         /**
381          * Utility method to return the enclosing team wherein this
382          * TaskTeam object is created.
383          */
384         public Team team() {
385             return Team.this;
386         }
387     }
388
389     /**
390      * Utility method to collate the performers defined to act in the
391      * given role in a given Data.
392      * @see TaskTeam#getActors(Data)
393      */
394     static public HashSet/*<Performer>*/ getActors(String r,Data d) {
395         HashSet/*<Performer>*/ result = new HashSet/*<Performer>*/();
396         Data.Element e = d.find( r );
397         if ( e != null ) {
398             for ( Iterator/*<Object>*/ vi = e.values.iterator();
399                   vi.hasNext(); ) {
400                 result.add( ((Capability) vi.next()).getPerformer() );
401             }
402         }
403         return result;
404     }
405
406     /**
407      * The Role class is a base class for representing team members in
408      * a team. Role extends {@link Capability}, so as to hold {@link
409      * Goal} methods that are tied to the role within the context of a
410      * {@link Team}. It further contains an attribute {@link
411      * #required}, which indicate goals that are required by a {@link
412      * Performer} of the role.
413      */
414     public class Role extends Capability {
415
416         /**
417          * The reference name for this role.
418          */
419         public String name;
420
421         /**
422          * The goal names required by a role filler.
423          */
424         public String [] required;
425
426         /**
427          * The {@link TaskTeam} that this role is a role of. This is
428          * set by the {@link TaskTeam#addRole} method when the role is
429          * added.
430          */
431         public TaskTeam group;
432
433         /**
434          * Constructor that takes an array of required goals.
435          */
436         public Role(String n,String [] r) {
437             name = n;
438             required = r;
439             setPerformer( Team.this );
440         }
441
442         /**
443          * Overrides {@link Capability#addCapability} so as to link up
444          * added capabilities with this team qua performer.
445          */
446         public void addCapability(Capability c) {
447             c.setPerformer( Team.this );
448             super.addCapability( c );
449         }
450
451         /**
452          * Returns the team of the role.
453          */
454         public Team team() {
455             return Team.this;
456         }
457
458         /**
459          * Overridable method that determines if the given performer
460          * can fill this role (within its {@link TaskTeam} {@link
461          * #group}). The default decision implemented by this
462          * method ensures that: <ol>
463          *
464          * <li> the performer is not already filling another role,
465          *
466          * <li> the role is not already filled by another performer,
467          * and
468          *
469          * <li> the performer has some plans to achieve the required
470          * goals.
471          * 
472          * </ol>
473          */
474         public boolean canFill(Performer p) {
475             try {
476                 if ( group.filling( null, p ).next() )
477                     return false;
478                 if ( group.filling( this, null ).next() )
479                     return false;
480                 return p.hasGoals( required );
481             } catch (Exception e) {
482                 e.printStackTrace();
483                 return false;
484             }
485         }
486
487         /**
488          * Overridable method that determines whether the given {@link
489          * Performer} can act in this role within the {@link
490          * Goal.Instance intention} represented by the given {@link
491          * Data}. The default decision is "yes".
492          * @see Team#getActors(String,Data)
493          */
494         public boolean canAct(Data d,Performer p) {
495             return true;
496         }
497
498     }
499
500 }