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