View Javadoc

1   /*
2    *
3    *   Copyright 2006 The Apache Software Foundation.
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  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   */
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       * &lt;invoke&gt; 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                 // docroot
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 &lt;invoke&gt; 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 &lt;invoke&gt; 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 &lt;invoke&gt; 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 &lt;invoke&gt; 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