capture
[rrq/gorite.git] / com / intendico / gorite / ContextualGoal.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.*;
23 import java.util.Vector;
24
25 /**
26  * This is a utility class used by BDIGoal execution, to associate a
27  * particular binding context with performing a goal.  It is not
28  * intended for explicit use within goal hierarchies.
29  *
30  * <p> The {@link BDIGoal} creates individual ContextualGoal objects
31  * for each applicable plan option where the plans {@link
32  * Context#context} method returns a {@link Query} that offers
33  * multiple bindings (i.e., not null, {@link Context#EMPTY} or {@link
34  * Context#FALSE}). Plan options whose {@link Context#context} method
35  * returns null are collated without the wrapping of a ContextualGoal
36  * object, and plan options whose {@link Context#context} method
37  * returns {@link Context#EMPTY} or {@link Context#FALSE} are not
38  * applicable.
39  *
40  * <p> The ContextualGoal captures the entry data settings for the
41  * variables as well as the proposed settings that defines the plan
42  * option.  It then manages the set up of the proposed settings into
43  * the data prior to executing the plan body, and the restoration of
44  * these settings if the plan body execution fails. (The variable
45  * settings are not restored if the ContextualGoal execution is
46  * cancelled)
47  *
48  * @see BDIGoal
49  */
50 public class ContextualGoal extends Goal implements Precedence {
51
52     /**
53      * Holds the Data object for the calling BDIInstance that created this
54      * ContextualGoal.
55      */
56     public Data goal_data;
57
58     /**
59      * Holds the actual {@link Ref} objects of the context
60      * {@link Query}.
61      */
62     public Vector/*<Ref>*/ variables;
63
64     /**
65      * Holds {@link Ref} objects with values representing this
66      * invocation context.
67      */
68     public Vector/*<Ref>*/ context;
69
70     /**
71      * The original assignments for the variables.
72      */
73     public Vector/*<Ref>*/ original;
74
75     /**
76      * Two ContextualGoal are equal if they are of the same goal and
77      * have the same context.
78      */
79     public boolean equals(Object c) {
80         return c instanceof ContextualGoal && equals( (ContextualGoal) c );
81     }
82
83     /**
84      * Two ContextualGoal are equal if they are of the same goal
85      * and have the same context.
86      */
87     public boolean equals(ContextualGoal c) {
88         if ( ! c.getGoalSubgoals()[0].equals( getGoalSubgoals()[0] ) )
89             return false;
90         if ( context == null ) {
91             return c.context == null;
92         }
93         return Ref.equals( c.context, context );
94     }
95
96     /**
97      * Constructor.
98      */
99     public ContextualGoal(Vector/*<Ref>*/ orig,Vector/*<Ref>*/ vars,
100         Goal goal,Data data) {
101         super( goal.getGoalName() );
102         goal_data = data;
103         original = orig;
104         variables = vars;
105         if ( variables != null )
106             context = Ref.copy( variables );
107         setGoalSubgoals( new Goal[] { goal } );
108     }
109
110     /**
111      * A wrapping for invoking precedence of the wrapped goal. If the
112      * wrapped goal provides a precedence method, then it is invoked
113      * using the cached {@link #goal_data}, transiently establishing
114      * the invocation context, which is restored upon return.
115      */
116     public int precedence(Data data) {
117         if ( ! ( getGoalSubgoals()[ 0 ] instanceof Precedence ) )
118             return Plan.DEFAULT_PRECEDENCE;
119         if ( context == null )
120             return ((Precedence) getGoalSubgoals()[ 0 ])
121                 .precedence( goal_data );
122         goal_data.set( context );
123         Ref.bind( variables, context );
124         int x = ((Precedence) getGoalSubgoals()[ 0 ]).precedence( goal_data );
125         Ref.bind( variables, original );
126         goal_data.get( context, false );
127         return x;
128     }
129
130     /**
131      * Return the intention instance to be executed.
132      */
133     public Instance instantiate(String head,Data d) {
134         // Assuming data == d without checking it
135         return new ContextualGoalInstance( head, d );
136     }
137
138     /**
139      * Utility class to represent the intention
140      */
141     public class ContextualGoalInstance extends Instance {
142
143         /**
144          * The target intention instance
145          */
146         public Instance instance;
147
148         /**
149          * Constructor. This established the invocation context in the
150          * intention {@link Data}.
151          */
152         public ContextualGoalInstance(String h,Data d) {
153             super( h );
154             if ( context != null ) {
155                 d.set( context );
156                 Ref.bind( variables, context );
157             }
158             String sub = context != null? Ref.toString( context ) : "{}";
159             instance = getGoalSubgoals()[ 0 ].instantiate( h + sub, d );
160         }
161
162         /**
163          * Control callback whereby the intention gets notified that
164          * it is cancelled. This will forward the cancellation to the
165          * currently progressing instance, if any.
166          */
167         public void cancel() {
168             if ( instance != null )
169                 instance.cancel();
170         }
171
172         /**
173          * Excution of this goal, which means to execute the wrapped
174          * goal. If successful, the invocation {@link Ref} objects are
175          * updated from the final {@link Data}.  Execution catches
176          * LoopEndException, but not ParallelEndException, and makes
177          * it cause a failure rather then propagate up. If execution
178          * fails, the {@link Data} context is restored by removing the
179          * local context.
180          */
181         public States action(String head,Data d)
182             throws LoopEndException, ParallelEndException {
183             States s = States.FAILED;
184             try {
185                 s = instance.perform( d );
186             } catch (LoopEndException e) {
187                 s = States.FAILED;
188             } catch (ParallelEndException e) {
189                 s = States.FAILED;
190                 throw e; // Re-thrown
191             } finally {
192                 if ( variables != null ) {
193                     if ( s == States.PASSED ) {
194                         d.get( variables, true );
195                     } else if ( s == States.FAILED ) {
196                         Ref.bind( variables, original );
197                         d.get( context, false );
198                     }
199                 }
200             }
201             return s;
202         }
203
204     } // End of class ContextualGoalInstance
205
206     /**
207      * Textual representation of this goal.
208      */
209     public String toString() {
210         return ( context != null? context.toString() : "{}" ) +
211             getGoalSubgoals()[ 0 ].toString();
212     }
213 }