Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
SCInstance |
|
| 1.619047619047619;1.619 |
1 | /* |
|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more |
|
3 | * contributor license agreements. See the NOTICE file distributed with |
|
4 | * this work for additional information regarding copyright ownership. |
|
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 |
|
6 | * (the "License"); you may not use this file except in compliance with |
|
7 | * the License. You may obtain a copy of the License at |
|
8 | * |
|
9 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
10 | * |
|
11 | * Unless required by applicable law or agreed to in writing, software |
|
12 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 | * See the License for the specific language governing permissions and |
|
15 | * limitations under the License. |
|
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 = 1L; |
|
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 | * The <code>Invoker</code> classes <code>Map</code>, keyed by |
|
63 | * <invoke> target types (specified using "targettype" attribute). |
|
64 | */ |
|
65 | private Map invokerClasses; |
|
66 | ||
67 | /** |
|
68 | * The <code>Map</code> of active <code>Invoker</code>s, keyed by |
|
69 | * (leaf) <code>State</code>s. |
|
70 | */ |
|
71 | private Map invokers; |
|
72 | ||
73 | /** |
|
74 | * The evaluator for expressions. |
|
75 | */ |
|
76 | private Evaluator evaluator; |
|
77 | ||
78 | /** |
|
79 | * The root context. |
|
80 | */ |
|
81 | private Context rootContext; |
|
82 | ||
83 | /** |
|
84 | * The owning state machine executor. |
|
85 | */ |
|
86 | private SCXMLExecutor executor; |
|
87 | ||
88 | /** |
|
89 | * Constructor. |
|
90 | * |
|
91 | * @param executor The executor that this instance is attached to. |
|
92 | */ |
|
93 | 59 | SCInstance(final SCXMLExecutor executor) { |
94 | 59 | this.notificationRegistry = new NotificationRegistry(); |
95 | 59 | this.contexts = Collections.synchronizedMap(new HashMap()); |
96 | 59 | this.histories = Collections.synchronizedMap(new HashMap()); |
97 | 59 | this.invokerClasses = Collections.synchronizedMap(new HashMap()); |
98 | 59 | this.invokers = Collections.synchronizedMap(new HashMap()); |
99 | 59 | this.evaluator = null; |
100 | 59 | this.rootContext = null; |
101 | 59 | this.executor = executor; |
102 | 59 | } |
103 | ||
104 | /** |
|
105 | * Get the <code>Evaluator</code>. |
|
106 | * |
|
107 | * @return The evaluator. |
|
108 | */ |
|
109 | public Evaluator getEvaluator() { |
|
110 | 390 | return evaluator; |
111 | } |
|
112 | ||
113 | /** |
|
114 | * Set the <code>Evaluator</code>. |
|
115 | * |
|
116 | * @param evaluator The evaluator. |
|
117 | */ |
|
118 | void setEvaluator(final Evaluator evaluator) { |
|
119 | 51 | this.evaluator = evaluator; |
120 | 51 | } |
121 | ||
122 | /** |
|
123 | * Get the root context. |
|
124 | * |
|
125 | * @return The root context. |
|
126 | */ |
|
127 | public Context getRootContext() { |
|
128 | 928 | if (rootContext == null && evaluator != null) { |
129 | 1 | rootContext = evaluator.newContext(null); |
130 | } |
|
131 | 928 | return rootContext; |
132 | } |
|
133 | ||
134 | /** |
|
135 | * Set the root context. |
|
136 | * |
|
137 | * @param context The root context. |
|
138 | */ |
|
139 | void setRootContext(final Context context) { |
|
140 | 51 | this.rootContext = context; |
141 | 51 | } |
142 | ||
143 | /** |
|
144 | * Get the notification registry. |
|
145 | * |
|
146 | * @return The notification registry. |
|
147 | */ |
|
148 | public NotificationRegistry getNotificationRegistry() { |
|
149 | 363 | return notificationRegistry; |
150 | } |
|
151 | ||
152 | /** |
|
153 | * Set the notification registry. |
|
154 | * |
|
155 | * @param notifRegistry The notification registry. |
|
156 | */ |
|
157 | void setNotificationRegistry(final NotificationRegistry notifRegistry) { |
|
158 | 0 | this.notificationRegistry = notifRegistry; |
159 | 0 | } |
160 | ||
161 | /** |
|
162 | * Get the <code>Context</code> for this <code>TransitionTarget</code>. |
|
163 | * If one is not available it is created. |
|
164 | * |
|
165 | * @param transitionTarget The TransitionTarget. |
|
166 | * @return The Context. |
|
167 | */ |
|
168 | public Context getContext(final TransitionTarget transitionTarget) { |
|
169 | 438 | Context context = (Context) contexts.get(transitionTarget); |
170 | 438 | if (context == null) { |
171 | 192 | TransitionTarget parent = transitionTarget.getParent(); |
172 | 192 | if (parent == null) { |
173 | // docroot |
|
174 | 102 | context = evaluator.newContext(getRootContext()); |
175 | } else { |
|
176 | 90 | context = evaluator.newContext(getContext(parent)); |
177 | } |
|
178 | 192 | Datamodel datamodel = transitionTarget.getDatamodel(); |
179 | 192 | SCXMLHelper.cloneDatamodel(datamodel, context, evaluator, null); |
180 | 192 | contexts.put(transitionTarget, context); |
181 | } |
|
182 | 438 | return context; |
183 | } |
|
184 | ||
185 | /** |
|
186 | * Get the <code>Context</code> for this <code>TransitionTarget</code>. |
|
187 | * May return <code>null</code>. |
|
188 | * |
|
189 | * @param transitionTarget The <code>TransitionTarget</code>. |
|
190 | * @return The Context. |
|
191 | */ |
|
192 | Context lookupContext(final TransitionTarget transitionTarget) { |
|
193 | 238 | return (Context) contexts.get(transitionTarget); |
194 | } |
|
195 | ||
196 | /** |
|
197 | * Set the <code>Context</code> for this <code>TransitionTarget</code>. |
|
198 | * |
|
199 | * @param transitionTarget The TransitionTarget. |
|
200 | * @param context The Context. |
|
201 | */ |
|
202 | void setContext(final TransitionTarget transitionTarget, |
|
203 | final Context context) { |
|
204 | 1 | contexts.put(transitionTarget, context); |
205 | 1 | } |
206 | ||
207 | /** |
|
208 | * Get the last configuration for this history. |
|
209 | * |
|
210 | * @param history The history. |
|
211 | * @return Returns the lastConfiguration. |
|
212 | */ |
|
213 | public Set getLastConfiguration(final History history) { |
|
214 | 23 | Set lastConfiguration = (Set) histories.get(history); |
215 | 23 | if (lastConfiguration == null) { |
216 | 8 | lastConfiguration = new HashSet(); |
217 | 8 | histories.put(history, lastConfiguration); |
218 | } |
|
219 | 23 | return lastConfiguration; |
220 | } |
|
221 | ||
222 | /** |
|
223 | * Set the last configuration for this history. |
|
224 | * |
|
225 | * @param history The history. |
|
226 | * @param lc The lastConfiguration to set. |
|
227 | */ |
|
228 | public void setLastConfiguration(final History history, |
|
229 | final Set lc) { |
|
230 | 13 | Set lastConfiguration = getLastConfiguration(history); |
231 | 13 | lastConfiguration.clear(); |
232 | 13 | lastConfiguration.addAll(lc); |
233 | 13 | } |
234 | ||
235 | /** |
|
236 | * Check whether we have prior history. |
|
237 | * |
|
238 | * @param history The history. |
|
239 | * @return Whether we have a non-empty last configuration |
|
240 | */ |
|
241 | public boolean isEmpty(final History history) { |
|
242 | 14 | Set lastConfiguration = (Set) histories.get(history); |
243 | 14 | if (lastConfiguration == null || lastConfiguration.isEmpty()) { |
244 | 5 | return true; |
245 | } |
|
246 | 9 | return false; |
247 | } |
|
248 | ||
249 | /** |
|
250 | * Resets the history state. |
|
251 | * |
|
252 | * @param history The history. |
|
253 | * @see org.apache.commons.scxml.SCXMLExecutor#reset() |
|
254 | */ |
|
255 | public void reset(final History history) { |
|
256 | 8 | Set lastConfiguration = (Set) histories.get(history); |
257 | 8 | if (lastConfiguration != null) { |
258 | 3 | lastConfiguration.clear(); |
259 | } |
|
260 | 8 | } |
261 | ||
262 | /** |
|
263 | * Get the {@link SCXMLExecutor} this instance is attached to. |
|
264 | * |
|
265 | * @return The SCXMLExecutor this instance is attached to. |
|
266 | * @see org.apache.commons.scxml.SCXMLExecutor |
|
267 | */ |
|
268 | public SCXMLExecutor getExecutor() { |
|
269 | 0 | return executor; |
270 | } |
|
271 | ||
272 | /** |
|
273 | * Register an {@link Invoker} class for this target type. |
|
274 | * |
|
275 | * @param targettype The target type (specified by "targettype" |
|
276 | * attribute of <invoke> tag). |
|
277 | * @param invokerClass The <code>Invoker</code> <code>Class</code>. |
|
278 | */ |
|
279 | void registerInvokerClass(final String targettype, |
|
280 | final Class invokerClass) { |
|
281 | 1 | invokerClasses.put(targettype, invokerClass); |
282 | 1 | } |
283 | ||
284 | /** |
|
285 | * Remove the {@link Invoker} class registered for this target |
|
286 | * type (if there is one registered). |
|
287 | * |
|
288 | * @param targettype The target type (specified by "targettype" |
|
289 | * attribute of <invoke> tag). |
|
290 | */ |
|
291 | void unregisterInvokerClass(final String targettype) { |
|
292 | 0 | invokerClasses.remove(targettype); |
293 | 0 | } |
294 | ||
295 | /** |
|
296 | * Get the {@link Invoker} for this {@link TransitionTarget}. |
|
297 | * May return <code>null</code>. A non-null <code>Invoker</code> will be |
|
298 | * returned if and only if the <code>TransitionTarget</code> is |
|
299 | * currently active and contains an <invoke> child. |
|
300 | * |
|
301 | * @param targettype The type of the target being invoked. |
|
302 | * @return An {@link Invoker} for the specified type, if an |
|
303 | * invoker class is registered against that type, |
|
304 | * <code>null</code> otherwise. |
|
305 | * @throws InvokerException When a suitable {@link Invoker} cannot |
|
306 | * be instantiated. |
|
307 | */ |
|
308 | public Invoker newInvoker(final String targettype) |
|
309 | throws InvokerException { |
|
310 | 1 | Class invokerClass = (Class) invokerClasses.get(targettype); |
311 | 1 | if (invokerClass == null) { |
312 | 0 | throw new InvokerException("No Invoker registered for " |
313 | + "targettype \"" + targettype + "\""); |
|
314 | } |
|
315 | 1 | Invoker invoker = null; |
316 | try { |
|
317 | 1 | invoker = (Invoker) invokerClass.newInstance(); |
318 | 0 | } catch (InstantiationException ie) { |
319 | 0 | throw new InvokerException(ie.getMessage(), ie.getCause()); |
320 | 0 | } catch (IllegalAccessException iae) { |
321 | 0 | throw new InvokerException(iae.getMessage(), iae.getCause()); |
322 | 1 | } |
323 | 1 | return invoker; |
324 | } |
|
325 | ||
326 | /** |
|
327 | * Get the {@link Invoker} for this {@link TransitionTarget}. |
|
328 | * May return <code>null</code>. A non-null {@link Invoker} will be |
|
329 | * returned if and only if the {@link TransitionTarget} is |
|
330 | * currently active and contains an <invoke> child. |
|
331 | * |
|
332 | * @param transitionTarget The <code>TransitionTarget</code>. |
|
333 | * @return The Invoker. |
|
334 | */ |
|
335 | public Invoker getInvoker(final TransitionTarget transitionTarget) { |
|
336 | 1 | return (Invoker) invokers.get(transitionTarget); |
337 | } |
|
338 | ||
339 | /** |
|
340 | * Set the {@link Invoker} for this {@link TransitionTarget}. |
|
341 | * |
|
342 | * @param transitionTarget The TransitionTarget. |
|
343 | * @param invoker The Invoker. |
|
344 | */ |
|
345 | public void setInvoker(final TransitionTarget transitionTarget, |
|
346 | final Invoker invoker) { |
|
347 | 1 | invokers.put(transitionTarget, invoker); |
348 | 1 | } |
349 | ||
350 | /** |
|
351 | * Return the Map of {@link Invoker}s currently "active". |
|
352 | * |
|
353 | * @return The map of invokers. |
|
354 | */ |
|
355 | public Map getInvokers() { |
|
356 | 733 | return invokers; |
357 | } |
|
358 | ||
359 | } |
|
360 |