1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.scxml;
18
19 import java.io.Serializable;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Set;
25
26 import org.apache.commons.scxml.invoke.Invoker;
27 import org.apache.commons.scxml.invoke.InvokerException;
28 import org.apache.commons.scxml.model.Datamodel;
29 import org.apache.commons.scxml.model.History;
30 import org.apache.commons.scxml.model.TransitionTarget;
31
32 /***
33 * The <code>SCInstance</code> performs book-keeping functions for
34 * a particular execution of a state chart represented by a
35 * <code>SCXML</code> object.
36 */
37 public class SCInstance implements Serializable {
38
39 /***
40 * Serial version UID.
41 */
42 private static final long serialVersionUID = 2L;
43
44 /***
45 * The notification registry.
46 */
47 private NotificationRegistry notificationRegistry;
48
49 /***
50 * The <code>Map</code> of <code>Context</code>s per
51 * <code>TransitionTarget</code>.
52 */
53 private Map contexts;
54
55 /***
56 * The <code>Map</code> of last known configurations per
57 * <code>History</code>.
58 */
59 private Map histories;
60
61 /***
62 * <code>Map</code> for recording the run to completion status of
63 * composite states.
64 */
65 private Map completions;
66
67 /***
68 * The <code>Invoker</code> classes <code>Map</code>, keyed by
69 * <invoke> target types (specified using "targettype" attribute).
70 */
71 private Map invokerClasses;
72
73 /***
74 * The <code>Map</code> of active <code>Invoker</code>s, keyed by
75 * (leaf) <code>State</code>s.
76 */
77 private Map invokers;
78
79 /***
80 * The evaluator for expressions.
81 */
82 private Evaluator evaluator;
83
84 /***
85 * The root context.
86 */
87 private Context rootContext;
88
89 /***
90 * The owning state machine executor.
91 */
92 private SCXMLExecutor executor;
93
94 /***
95 * Constructor.
96 *
97 * @param executor The executor that this instance is attached to.
98 */
99 SCInstance(final SCXMLExecutor executor) {
100 this.notificationRegistry = new NotificationRegistry();
101 this.contexts = Collections.synchronizedMap(new HashMap());
102 this.histories = Collections.synchronizedMap(new HashMap());
103 this.invokerClasses = Collections.synchronizedMap(new HashMap());
104 this.invokers = Collections.synchronizedMap(new HashMap());
105 this.completions = Collections.synchronizedMap(new HashMap());
106 this.evaluator = null;
107 this.rootContext = null;
108 this.executor = executor;
109 }
110
111 /***
112 * Get the <code>Evaluator</code>.
113 *
114 * @return The evaluator.
115 */
116 public Evaluator getEvaluator() {
117 return evaluator;
118 }
119
120 /***
121 * Set the <code>Evaluator</code>.
122 *
123 * @param evaluator The evaluator.
124 */
125 void setEvaluator(final Evaluator evaluator) {
126 this.evaluator = evaluator;
127 }
128
129 /***
130 * Get the root context.
131 *
132 * @return The root context.
133 */
134 public Context getRootContext() {
135 if (rootContext == null && evaluator != null) {
136 rootContext = evaluator.newContext(null);
137 }
138 return rootContext;
139 }
140
141 /***
142 * Set the root context.
143 *
144 * @param context The root context.
145 */
146 void setRootContext(final Context context) {
147 this.rootContext = context;
148 }
149
150 /***
151 * Get the notification registry.
152 *
153 * @return The notification registry.
154 */
155 public NotificationRegistry getNotificationRegistry() {
156 return notificationRegistry;
157 }
158
159 /***
160 * Set the notification registry.
161 *
162 * @param notifRegistry The notification registry.
163 */
164 void setNotificationRegistry(final NotificationRegistry notifRegistry) {
165 this.notificationRegistry = notifRegistry;
166 }
167
168 /***
169 * Get the <code>Context</code> for this <code>TransitionTarget</code>.
170 * If one is not available it is created.
171 *
172 * @param transitionTarget The TransitionTarget.
173 * @return The Context.
174 */
175 public Context getContext(final TransitionTarget transitionTarget) {
176 Context context = (Context) contexts.get(transitionTarget);
177 if (context == null) {
178 TransitionTarget parent = transitionTarget.getParent();
179 if (parent == null) {
180
181 context = evaluator.newContext(getRootContext());
182 } else {
183 context = evaluator.newContext(getContext(parent));
184 }
185 Datamodel datamodel = transitionTarget.getDatamodel();
186 SCXMLHelper.cloneDatamodel(datamodel, context, evaluator, null);
187 contexts.put(transitionTarget, context);
188 }
189 return context;
190 }
191
192 /***
193 * Get the <code>Context</code> for this <code>TransitionTarget</code>.
194 * May return <code>null</code>.
195 *
196 * @param transitionTarget The <code>TransitionTarget</code>.
197 * @return The Context.
198 */
199 Context lookupContext(final TransitionTarget transitionTarget) {
200 return (Context) contexts.get(transitionTarget);
201 }
202
203 /***
204 * Set the <code>Context</code> for this <code>TransitionTarget</code>.
205 *
206 * @param transitionTarget The TransitionTarget.
207 * @param context The Context.
208 */
209 void setContext(final TransitionTarget transitionTarget,
210 final Context context) {
211 contexts.put(transitionTarget, context);
212 }
213
214 /***
215 * Get the last configuration for this history.
216 *
217 * @param history The history.
218 * @return Returns the lastConfiguration.
219 */
220 public Set getLastConfiguration(final History history) {
221 Set lastConfiguration = (Set) histories.get(history);
222 if (lastConfiguration == null) {
223 lastConfiguration = new HashSet();
224 histories.put(history, lastConfiguration);
225 }
226 return lastConfiguration;
227 }
228
229 /***
230 * Set the last configuration for this history.
231 *
232 * @param history The history.
233 * @param lc The lastConfiguration to set.
234 */
235 public void setLastConfiguration(final History history,
236 final Set lc) {
237 Set lastConfiguration = getLastConfiguration(history);
238 lastConfiguration.clear();
239 lastConfiguration.addAll(lc);
240 }
241
242 /***
243 * Check whether we have prior history.
244 *
245 * @param history The history.
246 * @return Whether we have a non-empty last configuration
247 */
248 public boolean isEmpty(final History history) {
249 Set lastConfiguration = (Set) histories.get(history);
250 if (lastConfiguration == null || lastConfiguration.isEmpty()) {
251 return true;
252 }
253 return false;
254 }
255
256 /***
257 * Resets the history state.
258 *
259 * @param history The history.
260 * @see org.apache.commons.scxml.SCXMLExecutor#reset()
261 */
262 public void reset(final History history) {
263 Set lastConfiguration = (Set) histories.get(history);
264 if (lastConfiguration != null) {
265 lastConfiguration.clear();
266 }
267 }
268
269 /***
270 * Get the {@link SCXMLExecutor} this instance is attached to.
271 *
272 * @return The SCXMLExecutor this instance is attached to.
273 * @see org.apache.commons.scxml.SCXMLExecutor
274 */
275 public SCXMLExecutor getExecutor() {
276 return executor;
277 }
278
279 /***
280 * Register an {@link Invoker} class for this target type.
281 *
282 * @param targettype The target type (specified by "targettype"
283 * attribute of <invoke> tag).
284 * @param invokerClass The <code>Invoker</code> <code>Class</code>.
285 */
286 void registerInvokerClass(final String targettype,
287 final Class invokerClass) {
288 invokerClasses.put(targettype, invokerClass);
289 }
290
291 /***
292 * Remove the {@link Invoker} class registered for this target
293 * type (if there is one registered).
294 *
295 * @param targettype The target type (specified by "targettype"
296 * attribute of <invoke> tag).
297 */
298 void unregisterInvokerClass(final String targettype) {
299 invokerClasses.remove(targettype);
300 }
301
302 /***
303 * Get the {@link Invoker} for this {@link TransitionTarget}.
304 * May return <code>null</code>. A non-null <code>Invoker</code> will be
305 * returned if and only if the <code>TransitionTarget</code> is
306 * currently active and contains an <invoke> child.
307 *
308 * @param targettype The type of the target being invoked.
309 * @return An {@link Invoker} for the specified type, if an
310 * invoker class is registered against that type,
311 * <code>null</code> otherwise.
312 * @throws InvokerException When a suitable {@link Invoker} cannot
313 * be instantiated.
314 */
315 public Invoker newInvoker(final String targettype)
316 throws InvokerException {
317 Class invokerClass = (Class) invokerClasses.get(targettype);
318 if (invokerClass == null) {
319 throw new InvokerException("No Invoker registered for "
320 + "targettype \"" + targettype + "\"");
321 }
322 Invoker invoker = null;
323 try {
324 invoker = (Invoker) invokerClass.newInstance();
325 } catch (InstantiationException ie) {
326 throw new InvokerException(ie.getMessage(), ie.getCause());
327 } catch (IllegalAccessException iae) {
328 throw new InvokerException(iae.getMessage(), iae.getCause());
329 }
330 return invoker;
331 }
332
333 /***
334 * Get the {@link Invoker} for this {@link TransitionTarget}.
335 * May return <code>null</code>. A non-null {@link Invoker} will be
336 * returned if and only if the {@link TransitionTarget} is
337 * currently active and contains an <invoke> child.
338 *
339 * @param transitionTarget The <code>TransitionTarget</code>.
340 * @return The Invoker.
341 */
342 public Invoker getInvoker(final TransitionTarget transitionTarget) {
343 return (Invoker) invokers.get(transitionTarget);
344 }
345
346 /***
347 * Set the {@link Invoker} for this {@link TransitionTarget}.
348 *
349 * @param transitionTarget The TransitionTarget.
350 * @param invoker The Invoker.
351 */
352 public void setInvoker(final TransitionTarget transitionTarget,
353 final Invoker invoker) {
354 invokers.put(transitionTarget, invoker);
355 }
356
357 /***
358 * Return the Map of {@link Invoker}s currently "active".
359 *
360 * @return The map of invokers.
361 */
362 public Map getInvokers() {
363 return invokers;
364 }
365
366 /***
367 * Get the completion status for this composite
368 * {@link TransitionTarget}.
369 *
370 * @param transitionTarget The <code>TransitionTarget</code>.
371 * @return The completion status.
372 *
373 * @since 0.7
374 */
375 public boolean isDone(final TransitionTarget transitionTarget) {
376 Boolean done = (Boolean) completions.get(transitionTarget);
377 if (done == null) {
378 return false;
379 } else {
380 return done.booleanValue();
381 }
382 }
383
384 /***
385 * Set the completion status for this composite
386 * {@link TransitionTarget}.
387 *
388 * @param transitionTarget The TransitionTarget.
389 * @param done The completion status.
390 *
391 * @since 0.7
392 */
393 public void setDone(final TransitionTarget transitionTarget,
394 final boolean done) {
395 completions.put(transitionTarget, done ? Boolean.TRUE : Boolean.FALSE);
396 }
397
398 }
399