Package de.iritgo.aktera.fsm

Source Code of de.iritgo.aktera.fsm.DefaultEventProvider

/**
* This file is part of the Iritgo/Aktera Framework.
*
* Copyright (C) 2005-2011 Iritgo Technologies.
* Copyright (C) 2003-2005 BueroByte GbR.
*
* Iritgo licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package de.iritgo.aktera.fsm;


import com.evelopers.common.exception.CommonException;
import com.evelopers.unimod.core.stateworks.Event;
import com.evelopers.unimod.core.stateworks.Model;
import com.evelopers.unimod.core.stateworks.State;
import com.evelopers.unimod.core.stateworks.Transition;
import com.evelopers.unimod.runtime.AbstractEventProcessorListener;
import com.evelopers.unimod.runtime.EventProvider;
import com.evelopers.unimod.runtime.ModelEngine;
import com.evelopers.unimod.runtime.StateMachineConfig;
import com.evelopers.unimod.runtime.StateMachinePath;
import com.evelopers.unimod.runtime.context.StateMachineContext;
import java.util.List;


/**
* This event provider is built to solve the following problems:
*
* 1) The start state (i.e. the successor of the initial state) is only entered
* when the first event was send to the state engine. But if the "enter"-action
* of the start state triggers the production of the first event, how can we
* then produce this first event? We cannot and so we need to send an
* initial dummy event to the state engine, that only triggers entering of the
* start state and is then skipped. However if the start state contains an "any"
* event transition, this transition would be fired by the state engine when
* processing the "init"-event. To prevent this, we disable the "any" event
* transition for the first event (by changing the event type from "any" to "no
* event".)
*
* 2) Event generation often depends on the actions performed during entering
* a state. If we need to change the event generation algorithm depending on the
* current state, we need to be informed about entering a state. This is
* simplified by calling a template method prepareForNextEvent() when a new state
* was entered.
*/
public class DefaultEventProvider implements EventProvider, Runnable
{
  /** Special event name for the first init event */
  public static final String INIT_EVENT_NAME = "__INIT__";

  /** Special event name for 'leave sub machine' events */
  public static final String RETURN_EVENT_NAME = "RETURN_";

  /** The model engine for which to generate events */
  private ModelEngine engine;

  /** The state engine model */
  private Model model;

  /** True until we enter the final state */
  private boolean notInFinalState = true;

  /** The state machine's start state (i.e. the successor of the initial state) */
  private State startState = null;

  /** Becomes true on entering the start state */
  private boolean startStateVisited = false;

  /** If not null, contains the name of the sub machine to leave */
  private String returnFromSubMachineName;

  /**
   * Set the state machine model.
   *
   * @param model The state machine model
   */
  public void setModel(Model model)
  {
    this.model = model;
  }

  /**
   * @see com.evelopers.unimod.runtime.EventProvider#dispose()
   */
  public void dispose()
  {
  }

  /**
   * @see com.evelopers.unimod.runtime.EventProvider#init(com.evelopers.unimod.runtime.ModelEngine)
   */
  public void init(ModelEngine engine) throws CommonException
  {
    this.engine = engine;
    engine.getEventProcessor().addEventProcessorListener(new AbstractEventProcessorListener()
    {
      @Override
      public void comeToState(StateMachineContext context, StateMachinePath path, String stateName)
      {
        State state = model.getStateMachine(path.getStateMachine()).findState(stateName);

        if (! state.getName().equals("RETURN"))
        {
          prepareForNextEvent(state);
        }
        else
        {
          returnFromSubMachineName = path.getStateMachine();
        }
      }

      @Override
      public void stateMachineCameToFinalState(StateMachineContext context, StateMachinePath path,
              StateMachineConfig config)
      {
        notInFinalState = false;
      }

      @Override
      public void eventSkipped(StateMachineContext context, StateMachinePath path, String stateName, Event event)
      {
        if (! startStateVisited)
        {
          if (model.getStateMachine(path.getStateMachine()).findState(stateName) == startState)
          {
            startStateVisited = true;
            enableAnyEventTransition(startState);
          }
        }
      }
    });
  }

  /**
   * @see java.lang.Runnable#run()
   */
  public void run()
  {
    startState = findStartState(model);
    disableAnyEventTransition(startState);
    engine.getEventManager().handleAndWait(new Event("_INIT_"), new DefaultContext(null));

    while (notInFinalState)
    {
      Event event = null;

      if (returnFromSubMachineName != null)
      {
        event = new Event(RETURN_EVENT_NAME + returnFromSubMachineName);
        returnFromSubMachineName = null;
      }
      else
      {
        event = produceEvent();
      }

      engine.getEventManager().handleAndWait(event, new DefaultContext(event));
    }
  }

  /**
   * Determine which event type should be provided to trigger the next
   * transition from the specified state.
   *
   * Override this method if you need to change the event production
   * algorithm depending on the possible state transitions.
   *
   * @param state The state to prepare for
   */
  protected void prepareForNextEvent(State state)
  {
  }

  /**
   * Produce the next event that should be sent to the state engine.
   *
   * @return The next event
   */
  protected Event produceEvent()
  {
    return Event.ANY;
  }

  /**
   * Retrieve the start state, i.e. the successor of the initial state.
   *
   * @param model The state engine model
   * @return The start state
   */
  protected State findStartState(Model model)
  {
    return ((Transition) model.getRootStateMachine().getTop().getInitialSubstate().getOutgoingTransitions().get(0))
            .getTargetState();
  }

  /**
   * Disable all "any" event transitions that go out from the specified state.
   *
   *  @param state The state
   */
  private void disableAnyEventTransition(State state)
  {
    for (Transition transition : (List<Transition>) state.getOutgoingTransitions())
    {
      if (transition.getEvent().equals(Event.ANY))
      {
        transition.setEvent(Event.NO_EVENT);
      }
    }
  }

  /**
   * Enable all "any" event transitions that go out from the specified state.
   *
   *  @param state The state
   */
  private void enableAnyEventTransition(State state)
  {
    for (Transition transition : (List<Transition>) state.getOutgoingTransitions())
    {
      if (transition.getEvent().equals(Event.NO_EVENT))
      {
        transition.setEvent(Event.ANY);
      }
    }
  }
}
TOP

Related Classes of de.iritgo.aktera.fsm.DefaultEventProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.