KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sapia > soto > state > StateMachine


1 package org.sapia.soto.state;
2
3 import org.sapia.soto.Debug;
4 import org.sapia.soto.state.config.Globals;
5
6 import org.sapia.util.xml.confix.ConfigurationException;
7 import org.sapia.util.xml.confix.ObjectHandlerIF;
8
9 import java.util.Iterator JavaDoc;
10
11
12 /**
13  * An instance of this class holds <code>State</code> instances. Each
14  * <code>State</code> is bound under its unique identifier.
15  *
16  * @author Yanick Duchesne
17  * <dl>
18  * <dt><b>Copyright:</b><dd>Copyright &#169; 2002-2003 <a HREF="http://www.sapia-oss.org">Sapia Open Source Software</a>. All Rights Reserved.</dd></dt>
19  * <dt><b>License:</b><dd>Read the license.txt file of the jar or visit the
20  * <a HREF="http://www.sapia-oss.org/license.html">license page</a> at the Sapia OSS web site</dd></dt>
21  * </dl>
22  */

23 public class StateMachine implements ObjectHandlerIF {
24   private static final int TYPE_PRE = 0;
25   private static final int TYPE_POST = 1;
26   private static final int TYPE_ERR = 2;
27   private StaticContext _stmContext = new StaticContext();
28   private boolean _init;
29
30   public StateMachine() {
31   }
32
33   /**
34    * Returns the <code>State</code> that corresponds to the given
35    * identifier, or <code>null</code> if no <code>State</code> is found.
36    *
37    * @param id the identifier of the desired state.
38    * @return a <code>State</code>.
39    */

40   public State getState(String JavaDoc id) {
41     StateHolder sh = (StateHolder) _stmContext.getStates().get(id);
42
43     if (sh == null) {
44       return null;
45     }
46
47     return sh.getState();
48   }
49
50   /**
51    * Adds the given <code>State</code> to this instance.
52    *
53    * @param state a <code>State</code> instance.
54    * @throws ConfigurationException
55    */

56   public void addState(State state) throws ConfigurationException {
57     addStateHolder(new StateHolder(state));
58   }
59
60   /**
61    * Adds the given <code>Module</code> to this instance.
62    *
63    * @param module a <code>Module</code> instance.
64    * @throws ConfigurationException
65    */

66   public void addModule(Module mod) throws ConfigurationException {
67     if (_init) {
68       throw new IllegalStateException JavaDoc(
69         "Modules must be added prior to initialization");
70     }
71
72     if (mod.getName() != null) {
73       if (_stmContext.getModules().get(mod.getName()) != null) {
74         throw new ConfigurationException("Module already exists for: " +
75           mod.getName());
76       }
77
78       if (mod.getStateMachine(false) != null) {
79         mod.getStateMachine(false)._stmContext.setParent(this);
80       }
81
82       _stmContext.getModules().put(mod.getName(), mod);
83     } else {
84             if (mod.getStateMachine(false) != null) {
85                 mod.getStateMachine(false)._stmContext.setParent(this);
86             }
87                     
88       _stmContext.getAnonymousModules().add(mod);
89     }
90   }
91
92   /**
93    * @param gb a <code>Globals</code> instance.
94    */

95   public void addGlobals(Globals gb) {
96     _stmContext._globals.merge(gb);
97   }
98
99   /**
100    * Adds a state listener to this instance.
101
102    * @param listener a <code>StateExecListener</code>.
103    */

104   public void addStateExecListener(StateExecListener listener) {
105     _stmContext._listeners.add(listener);
106   }
107
108   /**
109    * Merges the given <code>StateMachine</code> to this instance; in short, the given
110    * engine's <code>State</code>s will be added to this instance.
111    *
112    * @param machine the <code>StateMachine</code> to merge with this instance.
113    *
114    * @throws ConfigurationException if the given machine holds a state whose identifier
115    * is already taken by a flow within this instance.
116    */

117   public void merge(StateMachine machine) throws ConfigurationException {
118     Iterator JavaDoc itr = machine._stmContext._states.values().iterator();
119
120     while (itr.hasNext()) {
121       addStateHolder((StateHolder) itr.next());
122     }
123
124     itr = machine._stmContext._modules.values().iterator();
125
126     while (itr.hasNext()) {
127       addModule((Module) itr.next());
128     }
129
130     _stmContext._globals.merge(machine._stmContext._globals);
131   }
132
133   /**
134    * Initializes this instance.
135    */

136   public void init() {
137     String JavaDoc id;
138     Iterator JavaDoc itr = _stmContext._states.keySet().iterator();
139     StateHolder sh;
140     State st;
141
142     while (itr.hasNext()) {
143       id = (String JavaDoc) itr.next();
144       sh = (StateHolder) _stmContext._states.get(id);
145       st = sh.getState();
146       st = _stmContext._globals.applySteps(st);
147       sh.setState(st);
148       sh.setVisible(_stmContext._globals.applyRestriction(st));
149     }
150
151     itr = _stmContext._modules.values().iterator();
152
153     Module mod;
154
155     while (itr.hasNext()) {
156       mod = (Module) itr.next();
157
158       if (mod.getStateMachine(false) == null) {
159         throw new IllegalStateException JavaDoc(
160           "State machine instance not specified for module: " + mod.getName());
161       }
162
163       if (mod.isInheritGlobals() &&
164             (mod.getStateMachine(false)._stmContext._globals != null)) {
165         mod.getStateMachine(false)._stmContext._globals.setParent(_stmContext._globals);
166       }
167
168       if (mod.isInheritModules()) {
169         mod.getStateMachine(false)._stmContext._inheritModules = true;
170       }
171
172       mod.getStateMachine(false).init();
173     }
174     _stmContext.init();
175
176     _init = true;
177   }
178
179   /**
180    * Execute a state and returns the latter's execution result in a <code>State</code>
181    * instance.
182    *
183    * @param stateId the identifier of the flow to execute.
184    * @param ctx an execution <code>Context</code>.
185    * @return the <code>Result</code> that results from the state's execution.
186    * @throws UnknownStateException if no state could be found for the given identifier.
187    * @throws StateExecException if an error was generated but not handled.
188    *
189    * @see Result#handleError()
190    */

191   public Result execute(String JavaDoc stateId, Context ctx)
192     throws UnknownStateException, StateExecException {
193     return doExecute(stateId, null, ctx, true);
194   }
195
196   /**
197    * Execute a state and returns the latter's execution result in a <code>State</code>
198    * instance.
199    *
200    * @param stateId the identifier of the flow to execute.
201    * @param module the name of the module to which the execution should be delegated.
202    * @param ctx an execution <code>Context</code>.
203    * @return the <code>Result</code> that results from the state's execution.
204    * @throws UnknownStateException if no state could be found for the given identifier.
205    * @throws StateExecException if an error was generated but not handled.
206    *
207    * @see Result#handleError()
208    */

209   public Result execute(String JavaDoc stateId, String JavaDoc module, Context ctx)
210     throws UnknownStateException, StateExecException {
211     return doExecute(stateId, module, ctx, true);
212   }
213
214   StaticContext getStmContext() {
215     return _stmContext;
216   }
217
218   protected void addStateHolder(StateHolder sh) throws ConfigurationException {
219     if (sh.getState().getId() != null) {
220       if (_stmContext._states.get(sh.getState().getId()) != null) {
221         throw new ConfigurationException("State already exists for: " +
222           sh.getState().getId());
223       }
224
225       _stmContext._states.put(sh.getState().getId(), sh);
226     } else {
227       throw new ConfigurationException("'id' attribute not specified on state");
228     }
229   }
230
231   private Module getModule(String JavaDoc name) {
232     Module mod = (Module) _stmContext._modules.get(name);
233
234     if (_stmContext._inheritModules && (_stmContext._parent != null)) {
235       mod = _stmContext._parent.getModule(name);
236     }
237
238     return mod;
239   }
240
241   private Result doExecute(String JavaDoc stateId, String JavaDoc module, Context ctx,
242     boolean fromOutside) throws UnknownStateException, StateExecException {
243     if (!_init) {
244       throw new IllegalStateException JavaDoc(
245         "State machine not initialized; call init() prior to using it.");
246     }
247
248     if (stateId == null) {
249       throw new IllegalArgumentException JavaDoc("State id cannot be null");
250     }
251
252     if (module != null) {
253       if (Debug.DEBUG) {
254         Debug.debug("Executing state in module: " + module);
255       }
256
257       Module mod = getModule(module);
258
259       if (mod == null) {
260         throw new IllegalArgumentException JavaDoc("Module does not exist: " + module);
261       }
262
263       StateMachine stm = mod.getStateMachine(true);
264
265       return stm.doExecute(stateId, null, ctx, fromOutside);
266     }
267
268     Result rs = new Result(this, ctx);
269     rs.setNextStateId(stateId);
270
271     StateHolder sh;
272     State st;
273     int count = 0;
274
275     while ((rs.getNextStateId() != null) && !rs.isError() && !rs.isAborted()) {
276       sh = (StateHolder) _stmContext.getStateFor(rs.getNextStateId());
277
278       if (sh == null) {
279         throw new UnknownStateException(rs.getNextStateId());
280       }
281
282       st = sh.getState();
283
284       if ((count == 0) && fromOutside && !sh.isVisible()) {
285         throw new StateAccessException("State '" + st.getId() +
286           "' cannot be executed by client application");
287       }
288
289       rs.setCurrentStateId(rs.getNextStateId());
290       rs.reset();
291       notifyListeners(st, rs, TYPE_PRE);
292
293       if (Debug.DEBUG) {
294         Debug.debug("Executing: " + st.getId() + " (" +
295           st.getClass().getName() + ")");
296       }
297
298       st.execute(rs);
299
300       if (rs.isError()) {
301         notifyListeners(st, rs, TYPE_ERR);
302       } else {
303         notifyListeners(st, rs, TYPE_POST);
304       }
305
306       count++;
307     }
308
309     if (rs.isError() && !rs.isErrorHandled()) {
310       if ((rs.getError().getThrowable() != null) &&
311             rs.getError().getThrowable() instanceof StateExecException) {
312         throw (StateExecException) rs.getError().getThrowable();
313       }
314
315       throw new StateExecException(rs.getError());
316     }
317
318     return rs;
319   }
320
321   void execute(String JavaDoc stateId, String JavaDoc module, Result rs)
322     throws StateExecException {
323     doExecute(stateId, module, rs.getContext(), false);
324   }
325
326   private void notifyListeners(State f, Result st, int type) {
327     StateExecListener listener;
328
329     for (int i = 0; i < _stmContext._listeners.size(); i++) {
330       listener = (StateExecListener) _stmContext._listeners.get(i);
331
332       if (type == TYPE_PRE) {
333         listener.onPreExec(st, f.getId());
334       } else if (type == TYPE_POST) {
335         listener.onPostExec(st, f.getId());
336       } else {
337         listener.onError(st, f.getId(), st.getError());
338       }
339     }
340   }
341
342   /**
343    * @see org.sapia.util.xml.confix.ObjectHandlerIF#handleObject(java.lang.String, java.lang.Object)
344    */

345   public void handleObject(String JavaDoc name, Object JavaDoc obj)
346     throws ConfigurationException {
347     if (obj instanceof State) {
348       State state = (State) obj;
349       addState(state);
350     } else if (obj instanceof Globals) {
351       _stmContext._globals.merge((Globals) obj);
352     } else if (obj instanceof Module) {
353       addModule((Module) obj);
354     }
355   }
356 }
357
Popular Tags