StateEngineImpl.java revision 608:7e06bf1dcb09
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
26package com.sun.corba.se.impl.orbutil.fsm ;
27
28import java.util.HashMap ;
29import java.util.HashSet ;
30import java.util.Set ;
31import java.util.Iterator ;
32
33import org.omg.CORBA.INTERNAL ;
34
35import com.sun.corba.se.impl.orbutil.ORBUtility ;
36
37import com.sun.corba.se.spi.orbutil.fsm.Input ;
38import com.sun.corba.se.spi.orbutil.fsm.Guard ;
39import com.sun.corba.se.spi.orbutil.fsm.Action ;
40import com.sun.corba.se.spi.orbutil.fsm.ActionBase ;
41import com.sun.corba.se.spi.orbutil.fsm.State ;
42import com.sun.corba.se.spi.orbutil.fsm.StateEngine ;
43import com.sun.corba.se.spi.orbutil.fsm.StateImpl ;
44import com.sun.corba.se.spi.orbutil.fsm.FSM ;
45import com.sun.corba.se.spi.orbutil.fsm.FSMImpl ;
46
47import 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 */
54public 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
329