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