package org.webpieces.javasm.impl;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.event.EventListenerList;
import org.webpieces.javasm.api.NoTransitionListener;
import org.webpieces.javasm.api.State;
import org.webpieces.javasm.api.StateMachineFactory;
/**
*/
public class StateImpl implements State
{
private String name;
private Map<Object, TransitionImpl> evtToTransition = new HashMap<Object, TransitionImpl>();
private EventListenerList entryListeners = new EventListenerList();
private EventListenerList exitListeners = new EventListenerList();
private EventListenerList noTransitionListeners = new EventListenerList();
/**
* Creates an instance of StateImpl.
* @param name
*/
public StateImpl(String name)
{
this.name = name;
}
@Override
public String toString() {
//TODO: return all the events and transition to next states???
return name;
}
/**
* @param evt
* @param transition
*/
public void addTransition(Object evt, TransitionImpl transition)
{
TransitionImpl t = evtToTransition.get(evt);
if(t != null)
throw new IllegalArgumentException("A transition our of state="+this
+" caused from evt="+evt+" has already been added. Cannot add another one.");
evtToTransition.put(evt, transition);
}
/**
* @param smState
* @param evt
*/
public void fireEvent(StateMachineState smState, Object evt)
{
TransitionImpl transition = evtToTransition.get(evt);
if(transition == null) {
transition = evtToTransition.get(StateMachineFactory.ANY_EVENT);
}
if(transition == null) {
// log.fine(smState+"CurrentState="+name+" evt="+evt+" no transition found");
smState.getLogger().debug(() -> smState+"No Transition: "+getName()+" -> <no transition found>, event="+evt);
this.fireNoTransition(smState, evt);
return;
}
StateImpl endState = transition.getEndState();
State nextState = transition.getEndState();
smState.getLogger().debug(() -> smState+"Transition: "+getName()+" -> "+nextState+", event="+evt);
try {
this.fireExitActions(smState);
transition.fireTransitionActions(smState);
endState.fireEntryActions(smState);
smState.setCurrentState(nextState);
} catch(RuntimeException e) {
smState.getLogger().warn(smState+"Transition FAILED: "+getName()+" -> "+nextState+", event="+evt);
throw e;
}
}
private void fireNoTransition(StateMachineState smState, Object event) {
NoTransitionListener[] list = noTransitionListeners.getListeners(NoTransitionListener.class);
for(int ii = list.length-1; ii >= 0; ii--) {
int index = ii;
smState.getLogger().debug(()->smState+"Exit Action: "+list[index].getClass().getName()+", state="+getName());
list[ii].noTransitionFromEvent(this, event);
}
}
/**
* @param smState
*/
private void fireEntryActions(StateMachineState smState)
{
ActionListener[] list = entryListeners.getListeners(ActionListener.class);
ActionEvent evt = new ActionEvent(this, 0, null);
for(int ii = list.length-1; ii >= 0; ii--) {
try {
int index = ii;
smState.getLogger().debug(() -> smState+"Entry Action: "+list[index].getClass().getName()+", state="+getName());
list[ii].actionPerformed(evt);
} catch(RuntimeException e) {
//Do not log stack trace here. It should only be logged at the beginning of a thread
smState.getLogger().warn(smState+"Exception occurred in client ActionListener="+list[ii]+", state="+getName());
//rethrow and stop executing the rest of the Actions
throw e;
}
}
}
/**
* @param smState
*/
private void fireExitActions(StateMachineState smState)
{
ActionListener[] list = exitListeners.getListeners(ActionListener.class);
ActionEvent evt = new ActionEvent(this, 0, null);
for(int ii = list.length-1; ii >= 0; ii--) {
try {
int index = ii;
smState.getLogger().debug(()->smState+"Exit Action: "+list[index].getClass().getName()+", state="+getName());
list[ii].actionPerformed(evt);
} catch(RuntimeException e) {
smState.getLogger().warn(smState+"Exception occurred in client ActionListener="+list[ii]+", state="+getName());
//rethrow and stop executing the rest of the Actions
throw e;
}
}
}
/**
*/
public String getName()
{
return name;
}
/**
* @see org.webpieces.javasm.api.State#addEntryActionListener(java.awt.event.ActionListener)
*/
public State addEntryActionListener(ActionListener listener)
{
if(listener == null)
throw new IllegalArgumentException("listener cannot be null");
entryListeners.add(ActionListener.class, listener);
return this;
}
/**
* @see org.webpieces.javasm.api.State#addExitActionListener(java.awt.event.ActionListener)
*/
public State addExitActionListener(ActionListener listener)
{
if(listener == null)
throw new IllegalArgumentException("listener cannot be null");
exitListeners.add(ActionListener.class, listener);
return this;
}
@Override
public State addNoTransitionListener(NoTransitionListener listener) {
if(listener == null)
throw new IllegalArgumentException("listener cannot be null");
noTransitionListeners.add(NoTransitionListener.class, listener);
return this;
}
}