##### Coverage Report - org.apache.commons.scxml.SCInstance

Classes in this File | Line Coverage | Branch Coverage | Complexity
SCInstance |
86% |
100% |
| 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 |  |
          * &lt;invoke&gt; 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 &lt;invoke&gt; 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 &lt;invoke&gt; 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 &lt;invoke&gt; 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 &lt;invoke&gt; 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 |  |