00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include <spl/io/File.h>
00018 #include <spl/math/StateMachine.h>
00019 #include <spl/xml/XmlElement.h>
00020 #include <spl/xml/XmlDocument.h>
00021
00022 IStateEventListener::~IStateEventListener()
00023 {
00024 }
00025
00026 TransitionActionResolver::TransitionActionResolver()
00027 : m_map()
00028 {
00029 }
00030
00031 TransitionActionResolver::TransitionActionResolver(const TransitionActionResolver& resolver)
00032 : m_map(resolver.m_map)
00033 {
00034 }
00035
00036 TransitionActionResolver::~TransitionActionResolver()
00037 {
00038 }
00039
00040 #ifdef DEBUG
00041 void TransitionActionResolver::ValidateMem() const
00042 {
00043 m_map.ValidateMem();
00044 }
00045
00046 void TransitionActionResolver::CheckMem() const
00047 {
00048 m_map.CheckMem();
00049 }
00050 #endif
00051
00052 State::State
00053 (
00054 const char *name,
00055 const char *enterActionName,
00056 const char *leaveActionName,
00057 IStateEventListener *enterAction,
00058 IStateEventListener *leaveAction
00059 )
00060 : m_name(name),
00061 m_enterActionName(enterActionName),
00062 m_leaveActionName(leaveActionName),
00063 m_enterAction(enterAction),
00064 m_leaveAction(leaveAction),
00065 m_fromThisState(),
00066 m_transitionList()
00067 {
00068 }
00069
00070 State::~State()
00071 {
00072 for ( int x = 0; x < m_transitionList.Count(); x++ )
00073 {
00074 delete m_transitionList.ElementAt(x);
00075 }
00076 m_transitionList.Clear();
00077 }
00078
00079 Transition *State::GetTransition( const String& inputText )
00080 {
00081 Transition *tran = m_fromThisState.Get( inputText );
00082 if ( NULL != tran )
00083 {
00084 return tran;
00085 }
00086
00087 return m_fromThisState.Get(String("*"));
00088 }
00089
00090 void State::FireOnLeave(const String& input, State *to)
00091 {
00092 if ( NULL != m_leaveAction )
00093 {
00094 m_leaveAction->OnStateLeave(input, this, to);
00095 }
00096 }
00097
00098 void State::FireOnEnter(const String& input, State *from)
00099 {
00100 if ( NULL != m_enterAction )
00101 {
00102 m_enterAction->OnStateEnter(input, from, this);
00103 }
00104 }
00105
00106 #ifdef DEBUG
00107 void State::ValidateMem() const
00108 {
00109 m_name.ValidateMem();
00110 m_enterActionName.ValidateMem();
00111 m_leaveActionName.ValidateMem();
00112 m_fromThisState.ValidateMem();
00113 m_transitionList.ValidateMem();
00114 }
00115
00116 void State::CheckMem() const
00117 {
00118 m_name.CheckMem();
00119 m_enterActionName.CheckMem();
00120 m_leaveActionName.CheckMem();
00121 m_fromThisState.CheckMem();
00122 m_transitionList.CheckMem();
00123 }
00124 #endif
00125
00126 Transition::Transition
00127 (
00128 const char *onInput,
00129 State *from,
00130 State *to,
00131 const char *actionName,
00132 IStateEventListener *transitionAction
00133 )
00134 : m_onInput(onInput),
00135 m_from(from),
00136 m_to(to),
00137 m_actionName(actionName),
00138 m_transitionAction(transitionAction)
00139 {
00140 }
00141
00142 Transition::~Transition()
00143 {
00144 }
00145
00146 void Transition::FireOnTransition(const String& input)
00147 {
00148 if ( NULL != m_transitionAction )
00149 {
00150 m_transitionAction->OnStateTransition(input, this);
00151 }
00152 }
00153
00154 #ifdef DEBUG
00155 void Transition::ValidateMem() const
00156 {
00157 m_onInput.ValidateMem();
00158 m_actionName.ValidateMem();
00159 }
00160
00161 void Transition::CheckMem() const
00162 {
00163 m_onInput.CheckMem();
00164 m_actionName.CheckMem();
00165 }
00166 #endif
00167
00168 StateMachine::StateMachine()
00169 : m_resolver(), m_currentState(NULL), m_stateIdx(), m_states()
00170 {
00171 }
00172
00173 StateMachine::~StateMachine()
00174 {
00175 for ( int x = 0; x < m_states.Count(); x++ )
00176 {
00177 delete m_states.ElementAt(x);
00178 }
00179 }
00180
00181 void StateMachine::SetState( const char *name )
00182 {
00183 State *state = m_stateIdx.Get( String(name) );
00184 if ( NULL == state )
00185 {
00186 throw new InvalidArgumentException("State not found");
00187 }
00188 ASSERT_MEM( state, sizeof(State) );
00189 state->ValidateMem();
00190 m_currentState = state;
00191 m_currentState->FireOnEnter(String(""), NULL);
00192 }
00193
00194 void StateMachine::DefineState( const char *name, const char *enterActionName, const char *leaveActionName )
00195 {
00196 IStateEventListener *ea = m_resolver.Get(enterActionName);
00197 if ( NULL == ea && enterActionName[0] != '\0' )
00198 {
00199 throw new InvalidArgumentException("Enter action not found");
00200 }
00201 IStateEventListener *la = m_resolver.Get(leaveActionName);
00202 if ( NULL == la && leaveActionName[0] != '\0' )
00203 {
00204 throw new InvalidArgumentException("Leave action not found");
00205 }
00206 String sname(name);
00207 if ( m_stateIdx.ContainsKey( sname ) )
00208 {
00209 throw new InvalidArgumentException("State already exists -- state names must be unique");
00210 }
00211
00212 State *state = new State(name, enterActionName, leaveActionName, ea, la);
00213 if ( NULL == state )
00214 {
00215 throw OutOfMemoryException();
00216 }
00217 m_stateIdx.Set( sname, state );
00218 m_states.Add( state );
00219 }
00220
00221 void StateMachine::DefineTransition
00222 (
00223 const char *onInput,
00224 const char *from,
00225 const char *to,
00226 const char *actionName
00227 )
00228 {
00229 IStateEventListener *la = m_resolver.Get(actionName);
00230 if ( NULL == la )
00231 {
00232 throw new InvalidArgumentException("Action not found");
00233 }
00234 State *tstate = m_stateIdx.Get(to);
00235 if ( NULL == tstate )
00236 {
00237 throw new InvalidArgumentException("To state not found");
00238 }
00239 State *fstate = m_stateIdx.Get( from );
00240 if ( NULL == fstate )
00241 {
00242 throw new InvalidArgumentException("From state not found");
00243 }
00244 if ( fstate->ContainsTransition( onInput ) )
00245 {
00246 throw new InvalidArgumentException("State already has a transition defined for this input");
00247 }
00248 fstate->AddTransitionFromThisState( new Transition(onInput, fstate, tstate, actionName, la) );
00249 }
00250
00251 void StateMachine::ChangeState(const char *input)
00252 {
00253 String sinput(input);
00254 if ( NULL == m_currentState )
00255 {
00256 throw new StateException("Current state is NULL, use SetState to set the start state");
00257 }
00258 ASSERT_MEM( m_currentState, sizeof(State) );
00259 m_currentState->ValidateMem();
00260
00261 Transition *trans = m_currentState->GetTransition(sinput);
00262 if ( NULL == trans )
00263 {
00264 throw new StateException("There is no transition for that input");
00265 }
00266 ASSERT_MEM( trans, sizeof(Transition) );
00267 trans->ValidateMem();
00268 ASSERT( trans->GetFromState() == m_currentState );
00269 ASSERT( trans->InputText().Equals(input) || trans->InputText().Equals("*") );
00270
00271 m_currentState->FireOnLeave(sinput, trans->GetToState());
00272 trans->FireOnTransition(sinput);
00273 m_currentState = trans->GetToState();
00274 m_currentState->FireOnEnter(sinput, trans->GetFromState());
00275 }
00276
00277 void StateMachine::Load( const char *filename )
00278 {
00279 if ( ! File::Exists(filename) )
00280 {
00281 throw new IOException("File not found");
00282 }
00283 XmlDocumentPtr doc = XmlDocument::Parse(filename);
00284 XmlElementPtr statemachine = doc->FirstChildElement("statemachine");
00285 if ( statemachine.IsNull() )
00286 {
00287 throw new StateException("Invalid XML file -- top-level '<statemachine>' tag not found.");
00288 }
00289 XmlElementPtr state = statemachine->FirstChildElement("state");
00290 while ( state.IsNotNull() )
00291 {
00292 XmlAttributePtr name = state->Attribute("name");
00293 if ( name.IsNull() )
00294 {
00295 throw new StateException("<state> must have a name attribute");
00296 }
00297 XmlAttributePtr enteraction = state->Attribute("enteraction");
00298 XmlAttributePtr exitaction = state->Attribute("exitaction");
00299 DefineState( name->Value()->GetChars(), (enteraction.IsNull()) ? NULL : enteraction->Value()->GetChars(), (exitaction.IsNull()) ? NULL : exitaction->Value()->GetChars() );
00300
00301 state = state->NextSiblingElement("state");
00302 }
00303
00304 state = statemachine->FirstChildElement("state");
00305 while ( state.IsNotNull() )
00306 {
00307 XmlAttributePtr name = state->Attribute("name");
00308
00309 XmlElementPtr transition = state->FirstChildElement("transition");
00310 while ( transition.IsNotNull() )
00311 {
00312 XmlAttributePtr input = transition->Attribute("input");
00313 XmlAttributePtr to = transition->Attribute("to");
00314 if ( input.IsNull() || to.IsNull() )
00315 {
00316 throw new StateException("<transition> must have 'input' and 'to' attributes");
00317 }
00318 XmlAttributePtr enteraction = transition->Attribute("action");
00319 DefineTransition(input->Value()->GetChars(), name->Value()->GetChars(), to->Value()->GetChars(), (enteraction.IsNull()) ? NULL : enteraction->Value()->GetChars());
00320
00321 transition = transition->NextSiblingElement("transition");
00322 }
00323
00324 state = state->NextSiblingElement("state");
00325 }
00326 }
00327
00328 #ifdef DEBUG
00329 void StateMachine::ValidateMem() const
00330 {
00331 m_resolver.ValidateMem();
00332 m_states.ValidateMem();
00333 m_stateIdx.ValidateMem();
00334 }
00335
00336 void StateMachine::CheckMem() const
00337 {
00338 m_resolver.CheckMem();
00339 m_states.CheckMem();
00340 m_stateIdx.CheckMem();
00341 }
00342 #endif