1 /*
   2  * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.corba.se.impl.orbutil.fsm ;
  27 
  28 import java.util.HashMap ;
  29 import java.util.HashSet ;
  30 import java.util.Set ;
  31 import java.util.Iterator ;
  32 
  33 import org.omg.CORBA.INTERNAL ;
  34 
  35 import com.sun.corba.se.impl.orbutil.ORBUtility ;
  36 
  37 import com.sun.corba.se.spi.orbutil.fsm.Input ;
  38 import com.sun.corba.se.spi.orbutil.fsm.Guard ;
  39 import com.sun.corba.se.spi.orbutil.fsm.Action ;
  40 import com.sun.corba.se.spi.orbutil.fsm.ActionBase ;
  41 import com.sun.corba.se.spi.orbutil.fsm.State ;
  42 import com.sun.corba.se.spi.orbutil.fsm.StateEngine ;
  43 import com.sun.corba.se.spi.orbutil.fsm.StateImpl ;
  44 import com.sun.corba.se.spi.orbutil.fsm.FSM ;
  45 import com.sun.corba.se.spi.orbutil.fsm.FSMImpl ;
  46 
  47 import com.sun.corba.se.impl.orbutil.fsm.GuardedAction ;
  48 
  49 /**
  50  * Encodes the state transition function for a finite state machine.
  51  *
  52  * @author Ken Cavanaugh
  53  */
  54 public class StateEngineImpl implements StateEngine
  55 {
  56     // An action that does nothing at all.
  57     private static Action emptyAction = new ActionBase( "Empty" )
  58     {
  59         public void doIt( FSM fsm, Input in )
  60         {
  61         }
  62     } ;
  63 
  64     private boolean initializing ;
  65     private Action defaultAction ;
  66 
  67     public StateEngineImpl()
  68     {
  69         initializing = true ;
  70         defaultAction = new ActionBase("Invalid Transition")
  71             {
  72                 public void doIt( FSM fsm, Input in )
  73                 {
  74                     throw new INTERNAL(
  75                         "Invalid transition attempted from " +
  76                             fsm.getState() + " under " + in ) ;
  77                 }
  78             } ;
  79     }
  80 
  81     public StateEngine add( State oldState, Input input, Guard guard, Action action,
  82         State newState ) throws IllegalArgumentException,
  83         IllegalStateException
  84     {
  85         mustBeInitializing() ;
  86 
  87         StateImpl oldStateImpl = (StateImpl)oldState ;
  88         GuardedAction ga = new GuardedAction( guard, action, newState ) ;
  89         oldStateImpl.addGuardedAction( input, ga ) ;
  90 
  91         return this ;
  92     }
  93 
  94     public StateEngine add( State oldState, Input input, Action action,
  95         State newState ) throws IllegalArgumentException,
  96         IllegalStateException
  97     {
  98         mustBeInitializing() ;
  99 
 100         StateImpl oldStateImpl = (StateImpl)oldState ;
 101         GuardedAction ta = new GuardedAction( action, newState ) ;
 102         oldStateImpl.addGuardedAction( input, ta ) ;
 103 
 104         return this ;
 105     }
 106 
 107     public StateEngine setDefault( State oldState, Action action, State newState )
 108         throws IllegalArgumentException, IllegalStateException
 109     {
 110         mustBeInitializing() ;
 111 
 112         StateImpl oldStateImpl = (StateImpl)oldState ;
 113         oldStateImpl.setDefaultAction( action ) ;
 114         oldStateImpl.setDefaultNextState( newState ) ;
 115 
 116         return this ;
 117     }
 118 
 119     public StateEngine setDefault( State oldState, State newState )
 120         throws IllegalArgumentException, IllegalStateException
 121     {
 122         return setDefault( oldState, emptyAction, newState ) ;
 123     }
 124 
 125     public StateEngine setDefault( State oldState )
 126         throws IllegalArgumentException, IllegalStateException
 127     {
 128         return setDefault( oldState, oldState ) ;
 129     }
 130 
 131     public void done() throws IllegalStateException
 132     {
 133         mustBeInitializing() ;
 134 
 135         // optimize FSM here if desired.  For example,
 136         // we could choose different strategies for implementing
 137         // the state transition function based on the distribution
 138         // of values for states and input labels.
 139 
 140         initializing = false ;
 141     }
 142 
 143     public void setDefaultAction( Action act ) throws IllegalStateException
 144     {
 145         mustBeInitializing() ;
 146         defaultAction = act ;
 147     }
 148 
 149     public void doIt( FSM fsm, Input in, boolean debug )
 150     {
 151         // This method is present only for debugging.
 152         // innerDoIt does the actual transition.
 153 
 154         if (debug)
 155             ORBUtility.dprint( this, "doIt enter: currentState = " +
 156                 fsm.getState() + " in = " + in ) ;
 157 
 158         try {
 159             innerDoIt( fsm, in, debug ) ;
 160         } finally {
 161             if (debug)
 162                 ORBUtility.dprint( this, "doIt exit" ) ;
 163         }
 164     }
 165 
 166     private StateImpl getDefaultNextState( StateImpl currentState )
 167     {
 168         // Use the currentState defaults if
 169         // set, otherwise use the state engine default.
 170         StateImpl nextState = (StateImpl)currentState.getDefaultNextState() ;
 171         if (nextState == null)
 172             // The state engine default never changes the state
 173             nextState = currentState ;
 174 
 175         return nextState ;
 176     }
 177 
 178     private Action getDefaultAction( StateImpl currentState )
 179     {
 180         Action action = currentState.getDefaultAction() ;
 181         if (action == null)
 182             action = defaultAction ;
 183 
 184         return action ;
 185     }
 186 
 187     private void innerDoIt( FSM fsm, Input in, boolean debug )
 188     {
 189         if (debug) {
 190             ORBUtility.dprint( this, "Calling innerDoIt with input " + in ) ;
 191         }
 192 
 193         // Locals needed for performing the state transition, once we determine
 194         // the required transition.
 195         StateImpl currentState = null ;
 196         StateImpl nextState = null ;
 197         Action action = null ;
 198 
 199         // Do until no guard has deferred.
 200         boolean deferral = false ;
 201         do {
 202             deferral = false ; // clear this after each deferral!
 203             currentState = (StateImpl)fsm.getState() ;
 204             nextState = getDefaultNextState( currentState ) ;
 205             action = getDefaultAction( currentState ) ;
 206 
 207             if (debug) {
 208                 ORBUtility.dprint( this, "currentState      = " + currentState ) ;
 209                 ORBUtility.dprint( this, "in                = " + in ) ;
 210                 ORBUtility.dprint( this, "default nextState = " + nextState    ) ;
 211                 ORBUtility.dprint( this, "default action    = " + action ) ;
 212             }
 213 
 214             Set gas = currentState.getGuardedActions(in) ;
 215             if (gas != null) {
 216                 Iterator iter = gas.iterator() ;
 217 
 218                 // Search for a guard that is not DISABLED.
 219                 // All DISABLED means use defaults.
 220                 while (iter.hasNext()) {
 221                     GuardedAction ga = (GuardedAction)iter.next() ;
 222                     Guard.Result gr = ga.getGuard().evaluate( fsm, in ) ;
 223                     if (debug)
 224                         ORBUtility.dprint( this,
 225                             "doIt: evaluated " + ga + " with result " + gr ) ;
 226 
 227                     if (gr == Guard.Result.ENABLED) {
 228                         // ga has the next state and action.
 229                         nextState = (StateImpl)ga.getNextState() ;
 230                         action = ga.getAction() ;
 231                         if (debug) {
 232                             ORBUtility.dprint( this, "nextState = " + nextState ) ;
 233                             ORBUtility.dprint( this, "action    = " + action ) ;
 234                         }
 235                         break ;
 236                     } else if (gr == Guard.Result.DEFERED) {
 237                         deferral = true ;
 238                         break ;
 239                     }
 240                 }
 241             }
 242         } while (deferral) ;
 243 
 244         performStateTransition( fsm, in, nextState, action, debug ) ;
 245     }
 246 
 247     private void performStateTransition( FSM fsm, Input in,
 248         StateImpl nextState, Action action, boolean debug )
 249     {
 250         StateImpl currentState = (StateImpl)fsm.getState() ;
 251 
 252         // Perform the state transition.  Pre and post actions are only
 253         // performed if the state changes (see UML hidden transitions).
 254 
 255         boolean different = !currentState.equals( nextState ) ;
 256 
 257         if (different) {
 258             if (debug)
 259                 ORBUtility.dprint( this,
 260                     "doIt: executing postAction for state " + currentState ) ;
 261             try {
 262                 currentState.postAction( fsm ) ;
 263             } catch (Throwable thr) {
 264                 if (debug)
 265                     ORBUtility.dprint( this,
 266                         "doIt: postAction threw " + thr ) ;
 267 
 268                 if (thr instanceof ThreadDeath)
 269                     throw (ThreadDeath)thr ;
 270             }
 271         }
 272 
 273         try {
 274             // Note that action may be null in a transition, which simply
 275             // means that no action is needed.  Note that action.doIt may
 276             // throw an exception, in which case the exception is
 277             // propagated after making sure that the transition is properly
 278             // completed.
 279             if (action != null)
 280                 action.doIt( fsm, in ) ;
 281         } finally {
 282             if (different) {
 283                 if (debug)
 284                     ORBUtility.dprint( this,
 285                         "doIt: executing preAction for state " + nextState ) ;
 286 
 287                 try {
 288                     nextState.preAction( fsm ) ;
 289                 } catch (Throwable thr) {
 290                     if (debug)
 291                         ORBUtility.dprint( this,
 292                             "doIt: preAction threw " + thr ) ;
 293 
 294                     if (thr instanceof ThreadDeath)
 295                         throw (ThreadDeath)thr ;
 296                 }
 297 
 298                 ((FSMImpl)fsm).internalSetState( nextState ) ;
 299             }
 300 
 301             if (debug)
 302                 ORBUtility.dprint( this, "doIt: state is now " + nextState ) ;
 303         }
 304     }
 305 
 306     public FSM makeFSM( State startState ) throws IllegalStateException
 307     {
 308         mustNotBeInitializing() ;
 309 
 310         return new FSMImpl( this, startState ) ;
 311     }
 312 
 313     private void mustBeInitializing() throws IllegalStateException
 314     {
 315         if (!initializing)
 316             throw new IllegalStateException(
 317                 "Invalid method call after initialization completed" ) ;
 318     }
 319 
 320     private void mustNotBeInitializing() throws IllegalStateException
 321     {
 322         if (initializing)
 323             throw new IllegalStateException(
 324                 "Invalid method call before initialization completed" ) ;
 325     }
 326 }
 327 
 328 // end of StateEngineImpl.java