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 = 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      SCInstance(final SCXMLExecutor executor) {
94          this.notificationRegistry = new NotificationRegistry();
95          this.contexts = Collections.synchronizedMap(new HashMap());
96          this.histories = Collections.synchronizedMap(new HashMap());
97          this.invokerClasses = Collections.synchronizedMap(new HashMap());
98          this.invokers = Collections.synchronizedMap(new HashMap());
99          this.evaluator = null;
100         this.rootContext = null;
101         this.executor = executor;
102     }
103 
104     /***
105      * Get the <code>Evaluator</code>.
106      *
107      * @return The evaluator.
108      */
109     public Evaluator getEvaluator() {
110         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         this.evaluator = evaluator;
120     }
121 
122     /***
123      * Get the root context.
124      *
125      * @return The root context.
126      */
127     public Context getRootContext() {
128         if (rootContext == null && evaluator != null) {
129             rootContext = evaluator.newContext(null);
130         }
131         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         this.rootContext = context;
141     }
142 
143     /***
144      * Get the notification registry.
145      *
146      * @return The notification registry.
147      */
148     public NotificationRegistry getNotificationRegistry() {
149         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         this.notificationRegistry = notifRegistry;
159     }
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         Context context = (Context) contexts.get(transitionTarget);
170         if (context == null) {
171             TransitionTarget parent = transitionTarget.getParent();
172             if (parent == null) {
173                 // docroot
174                 context = evaluator.newContext(getRootContext());
175             } else {
176                 context = evaluator.newContext(getContext(parent));
177             }
178             Datamodel datamodel = transitionTarget.getDatamodel();
179             SCXMLHelper.cloneDatamodel(datamodel, context, evaluator, null);
180             contexts.put(transitionTarget, context);
181         }
182         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         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         contexts.put(transitionTarget, context);
205     }
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         Set lastConfiguration = (Set) histories.get(history);
215         if (lastConfiguration == null) {
216             lastConfiguration = new HashSet();
217             histories.put(history, lastConfiguration);
218         }
219         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         Set lastConfiguration = getLastConfiguration(history);
231         lastConfiguration.clear();
232         lastConfiguration.addAll(lc);
233     }
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         Set lastConfiguration = (Set) histories.get(history);
243         if (lastConfiguration == null || lastConfiguration.isEmpty()) {
244             return true;
245         }
246         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         Set lastConfiguration = (Set) histories.get(history);
257         if (lastConfiguration != null) {
258             lastConfiguration.clear();
259         }
260     }
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         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         invokerClasses.put(targettype, invokerClass);
282     }
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         invokerClasses.remove(targettype);
293     }
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         Class invokerClass = (Class) invokerClasses.get(targettype);
311         if (invokerClass == null) {
312             throw new InvokerException("No Invoker registered for "
313                 + "targettype \"" + targettype + "\"");
314         }
315         Invoker invoker = null;
316         try {
317             invoker = (Invoker) invokerClass.newInstance();
318         } catch (InstantiationException ie) {
319             throw new InvokerException(ie.getMessage(), ie.getCause());
320         } catch (IllegalAccessException iae) {
321             throw new InvokerException(iae.getMessage(), iae.getCause());
322         }
323         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         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         invokers.put(transitionTarget, invoker);
348     }
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         return invokers;
357     }
358 
359 }
360