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