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