View Javadoc

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 = 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       * &lt;invoke&gt; 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                 // docroot
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 &lt;invoke&gt; 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 &lt;invoke&gt; 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 &lt;invoke&gt; 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 &lt;invoke&gt; 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