Coverage Report - org.apache.commons.scxml.SCInstance

Classes in this File Line Coverage Branch Coverage Complexity
SCInstance
86% 
100% 
1.619

 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  48
     SCInstance(final SCXMLExecutor executor) {
 88  48
         this.notificationRegistry = new NotificationRegistry();
 89  48
         this.contexts = new HashMap();
 90  48
         this.histories = new HashMap();
 91  48
         this.invokerClasses = new HashMap();
 92  48
         this.invokers = new HashMap();
 93  48
         this.evaluator = null;
 94  48
         this.rootContext = null;
 95  48
         this.executor = executor;
 96  48
     }
 97  
 
 98  
     /**
 99  
      * Get the <code>Evaluator</code>.
 100  
      *
 101  
      * @return The evaluator.
 102  
      */
 103  
     public Evaluator getEvaluator() {
 104  293
         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  40
         this.evaluator = evaluator;
 114  40
     }
 115  
 
 116  
     /**
 117  
      * Get the root context.
 118  
      *
 119  
      * @return The root context.
 120  
      */
 121  
     public Context getRootContext() {
 122  695
         if (rootContext == null && evaluator != null) {
 123  1
             rootContext = evaluator.newContext(null);
 124  
         }
 125  695
         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  40
         this.rootContext = context;
 135  40
     }
 136  
 
 137  
     /**
 138  
      * Get the notification registry.
 139  
      *
 140  
      * @return The notification registry.
 141  
      */
 142  
     public NotificationRegistry getNotificationRegistry() {
 143  280
         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  0
         this.notificationRegistry = notifRegistry;
 153  0
     }
 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  350
         Context context = (Context) contexts.get(transitionTarget);
 164  350
         if (context == null) {
 165  149
             TransitionTarget parent = transitionTarget.getParent();
 166  149
             if (parent == null) {
 167  
                 // docroot
 168  61
                 context = evaluator.newContext(getRootContext());
 169  
             } else {
 170  88
                 context = evaluator.newContext(getContext(parent));
 171  
             }
 172  149
             Datamodel datamodel = transitionTarget.getDatamodel();
 173  149
             SCXMLHelper.cloneDatamodel(datamodel, context, evaluator, null);
 174  149
             contexts.put(transitionTarget, context);
 175  
         }
 176  350
         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  189
         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  1
         contexts.put(transitionTarget, context);
 199  1
     }
 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  23
         Set lastConfiguration = (Set) histories.get(history);
 209  23
         if (lastConfiguration == null) {
 210  8
             lastConfiguration = new HashSet();
 211  8
             histories.put(history, lastConfiguration);
 212  
         }
 213  23
         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  13
         Set lastConfiguration = getLastConfiguration(history);
 225  13
         lastConfiguration.clear();
 226  13
         lastConfiguration.addAll(lc);
 227  13
     }
 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  14
         Set lastConfiguration = (Set) histories.get(history);
 237  14
         if (lastConfiguration == null || lastConfiguration.isEmpty()) {
 238  5
             return true;
 239  
         }
 240  9
         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  8
         Set lastConfiguration = (Set) histories.get(history);
 251  8
         if (lastConfiguration != null) {
 252  3
             lastConfiguration.clear();
 253  
         }
 254  8
     }
 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  0
         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  1
         invokerClasses.put(targettype, invokerClass);
 276  1
     }
 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  0
         invokerClasses.remove(targettype);
 287  0
     }
 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  1
         Class invokerClass = (Class) invokerClasses.get(targettype);
 305  1
         if (invokerClass == null) {
 306  0
             throw new InvokerException("No Invoker registered for "
 307  
                 + "targettype \"" + targettype + "\"");
 308  
         }
 309  1
         Invoker invoker = null;
 310  
         try {
 311  1
             invoker = (Invoker) invokerClass.newInstance();
 312  0
         } catch (InstantiationException ie) {
 313  0
             throw new InvokerException(ie.getMessage(), ie.getCause());
 314  0
         } catch (IllegalAccessException iae) {
 315  0
             throw new InvokerException(iae.getMessage(), iae.getCause());
 316  1
         }
 317  1
         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  1
         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  1
         invokers.put(transitionTarget, invoker);
 342  1
     }
 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  565
         return invokers;
 351  
     }
 352  
 
 353  
 }
 354