Spring cleaning, take 1
- Polish, cleaning, tidy up - Split listener and context events into new StateMachineObjectSupport - Polish pseudostate handling - Extract isSubstate into static util function - Remove event from State exit/entry. Event not needed as function argument because it's already wrapped in state context. - Polish how StateContext is created - Polish state exit/entry
This commit is contained in:
@@ -153,10 +153,10 @@ public abstract class AbstractState<S, E> implements State<S, E> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void exit(E event, StateContext<S, E> context);
|
||||
public abstract void exit(StateContext<S, E> context);
|
||||
|
||||
@Override
|
||||
public abstract void entry(E event, StateContext<S, E> context);
|
||||
public abstract void entry(StateContext<S, E> context);
|
||||
|
||||
@Override
|
||||
public S getId() {
|
||||
|
||||
@@ -118,7 +118,7 @@ public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractSim
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(E event, StateContext<S, E> context) {
|
||||
public void exit(StateContext<S, E> context) {
|
||||
Collection<? extends Action<S, E>> actions = getExitActions();
|
||||
if (actions != null) {
|
||||
for (Action<S, E> action : actions) {
|
||||
@@ -128,7 +128,7 @@ public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractSim
|
||||
}
|
||||
|
||||
@Override
|
||||
public void entry(E event, StateContext<S, E> context) {
|
||||
public void entry(StateContext<S, E> context) {
|
||||
Collection<? extends Action<S, E>> actions = getEntryActions();
|
||||
if (actions != null) {
|
||||
for (Action<S, E> action : actions) {
|
||||
|
||||
@@ -106,9 +106,9 @@ public class RegionState<S, E> extends AbstractState<S, E> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(E event, StateContext<S, E> context) {
|
||||
public void exit(StateContext<S, E> context) {
|
||||
for (Region<S, E> region : getRegions()) {
|
||||
region.getState().exit(event, context);
|
||||
region.getState().exit(context);
|
||||
region.stop();
|
||||
}
|
||||
Collection<? extends Action<S, E>> actions = getExitActions();
|
||||
@@ -120,7 +120,7 @@ public class RegionState<S, E> extends AbstractState<S, E> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void entry(E event, StateContext<S, E> context) {
|
||||
public void entry(StateContext<S, E> context) {
|
||||
Collection<? extends Action<S, E>> actions = getEntryActions();
|
||||
if (actions != null) {
|
||||
for (Action<S, E> action : actions) {
|
||||
@@ -135,7 +135,7 @@ public class RegionState<S, E> extends AbstractState<S, E> {
|
||||
} else {
|
||||
for (Region<S, E> region : getRegions()) {
|
||||
if (region.getState() != null) {
|
||||
region.getState().entry(event, context);
|
||||
region.getState().entry(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,18 +42,16 @@ public interface State<S, E> {
|
||||
/**
|
||||
* Initiate an exit sequence for the state.
|
||||
*
|
||||
* @param event the event
|
||||
* @param context the context
|
||||
* @param context the state context
|
||||
*/
|
||||
void exit(E event, StateContext<S, E> context);
|
||||
void exit(StateContext<S, E> context);
|
||||
|
||||
/**
|
||||
* Initiate an entry sequence for the state.
|
||||
*
|
||||
* @param event the event
|
||||
* @param context the context
|
||||
* @param context the state context
|
||||
*/
|
||||
void entry(E event, StateContext<S, E> context);
|
||||
void entry(StateContext<S, E> context);
|
||||
|
||||
/**
|
||||
* Gets the state identifier.
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.support.StateMachineUtils;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
|
||||
@@ -131,13 +132,15 @@ public class StateMachineState<S, E> extends AbstractState<S, E> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(E event, StateContext<S, E> context) {
|
||||
public void exit(StateContext<S, E> context) {
|
||||
// don't stop if it looks like we're coming back
|
||||
// stop would cause start with entry which would
|
||||
// enable default transition and state
|
||||
if (getSubmachine().getState() != null && context.getTransition().getSource().getId() != getSubmachine().getState().getId()) {
|
||||
if (getSubmachine().getState() != null
|
||||
&& context.getTransition().getSource().getId() != getSubmachine().getState().getId()) {
|
||||
getSubmachine().stop();
|
||||
} else if (!isSubstate(context.getTransition().getTarget(), context.getTransition().getSource())) {
|
||||
} else if (!StateMachineUtils.isSubstate(context.getTransition().getTarget(), context.getTransition()
|
||||
.getSource())) {
|
||||
getSubmachine().stop();
|
||||
}
|
||||
Collection<? extends Action<S, E>> actions = getExitActions();
|
||||
@@ -149,7 +152,7 @@ public class StateMachineState<S, E> extends AbstractState<S, E> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void entry(E event, StateContext<S, E> context) {
|
||||
public void entry(StateContext<S, E> context) {
|
||||
Collection<? extends Action<S, E>> actions = getEntryActions();
|
||||
if (actions != null && !isLocal(context)) {
|
||||
for (Action<S, E> action : actions) {
|
||||
@@ -180,12 +183,6 @@ public class StateMachineState<S, E> extends AbstractState<S, E> {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSubstate(State<S, E> left, State<S, E> right) {
|
||||
Collection<State<S, E>> c = left.getStates();
|
||||
c.remove(left);
|
||||
return c.contains(right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StateMachineState [getIds()=" + getIds() + ", toString()=" + super.toString() + ", getClass()="
|
||||
|
||||
@@ -43,8 +43,6 @@ import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
import org.springframework.statemachine.event.StateMachineEventPublisher;
|
||||
import org.springframework.statemachine.listener.CompositeStateMachineListener;
|
||||
import org.springframework.statemachine.listener.StateMachineListener;
|
||||
import org.springframework.statemachine.processor.StateMachineHandler;
|
||||
import org.springframework.statemachine.processor.StateMachineOnTransitionHandler;
|
||||
@@ -72,7 +70,7 @@ import org.springframework.util.Assert;
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport implements StateMachine<S, E> {
|
||||
public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSupport<S, E> implements StateMachine<S, E> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(AbstractStateMachine.class);
|
||||
|
||||
@@ -92,8 +90,6 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
private final LinkedList<Message<E>> deferList = new LinkedList<Message<E>>();
|
||||
|
||||
private final CompositeStateMachineListener<S, E> stateListener = new CompositeStateMachineListener<S, E>();
|
||||
|
||||
private volatile State<S,E> currentState;
|
||||
|
||||
private volatile PseudoState<S, E> history;
|
||||
@@ -108,8 +104,6 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
private final Map<Trigger<S, E>, Transition<S,E>> triggerToTransitionMap = new HashMap<Trigger<S,E>, Transition<S,E>>();
|
||||
|
||||
private boolean contextEventsEnabled = true;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state machine.
|
||||
*
|
||||
@@ -231,8 +225,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
switchToState(initialState, initialEvent, null, this);
|
||||
// TODO: for now execute outside of switchToState
|
||||
if (initialTransition != null) {
|
||||
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(initialEvent != null ? initialEvent.getPayload() : null,
|
||||
initialEvent != null ? initialEvent.getHeaders() : null, extendedState, initialTransition, this);
|
||||
StateContext<S, E> stateContext = buildStateContext(initialEvent, initialTransition, this);
|
||||
initialTransition.transit(stateContext);
|
||||
}
|
||||
notifyStateMachineStarted(this);
|
||||
@@ -246,7 +239,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
@Override
|
||||
public void addStateListener(StateMachineListener<S, E> listener) {
|
||||
stateListener.register(listener);
|
||||
getStateListener().register(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -288,26 +281,15 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if context application events are enabled. Events
|
||||
* are enabled by default. Set this to false if you don't
|
||||
* want state machine to send application context events.
|
||||
*
|
||||
* @param contextEventsEnabled the enabled flag
|
||||
*/
|
||||
public void setContextEventsEnabled(boolean contextEventsEnabled) {
|
||||
this.contextEventsEnabled = contextEventsEnabled;
|
||||
}
|
||||
protected boolean acceptEvent(Message<E> message) {
|
||||
|
||||
protected boolean acceptEvent(Message<E> event) {
|
||||
|
||||
boolean accepted = currentState.sendEvent(event);
|
||||
boolean accepted = currentState.sendEvent(message);
|
||||
if (accepted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Queue event " + event);
|
||||
log.debug("Queue event " + message);
|
||||
}
|
||||
|
||||
Message<E> defer = null;
|
||||
@@ -316,11 +298,11 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
Trigger<S, E> trigger = transition.getTrigger();
|
||||
|
||||
if (StateMachineUtils.containsAtleastOne(source.getIds(), currentState.getIds())) {
|
||||
if (trigger != null && trigger.evaluate(new DefaultTriggerContext<S, E>(event.getPayload()))) {
|
||||
triggerQueue.add(new TriggerQueueItem(trigger, event));
|
||||
if (trigger != null && trigger.evaluate(new DefaultTriggerContext<S, E>(message.getPayload()))) {
|
||||
triggerQueue.add(new TriggerQueueItem(trigger, message));
|
||||
return true;
|
||||
} else if (source.getDeferredEvents() != null && source.getDeferredEvents().contains(event.getPayload())) {
|
||||
defer = event;
|
||||
} else if (source.getDeferredEvents() != null && source.getDeferredEvents().contains(message.getPayload())) {
|
||||
defer = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,32 +315,17 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
return false;
|
||||
}
|
||||
|
||||
private void switchToState(State<S,E> state, Message<E> event, Transition<S,E> transition, StateMachine<S, E> stateMachine) {
|
||||
private void switchToState(State<S,E> state, Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine) {
|
||||
// TODO: need to make below more clear when
|
||||
// we figure out rest of a pseudostates
|
||||
if (state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.CHOICE) {
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(event != null ? event.getPayload() : null,
|
||||
messageHeaders, extendedState, transition, stateMachine);
|
||||
State<S, E> entry = state.getPseudoState().entry(event.getPayload(), stateContext);
|
||||
setCurrentState(entry, event, transition, true, stateMachine);
|
||||
} else if (state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.HISTORY_SHALLOW) {
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(event != null ? event.getPayload() : null,
|
||||
messageHeaders, extendedState, transition, stateMachine);
|
||||
State<S, E> entry = state.getPseudoState().entry(event.getPayload(), stateContext);
|
||||
setCurrentState(entry, event, transition, true, stateMachine);
|
||||
} else if (state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.HISTORY_DEEP) {
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(event != null ? event.getPayload() : null,
|
||||
messageHeaders, extendedState, transition, stateMachine);
|
||||
State<S, E> entry = state.getPseudoState().entry(event.getPayload(), stateContext);
|
||||
setCurrentState(entry, event, transition, true, stateMachine);
|
||||
PseudoStateKind kind = state.getPseudoState() != null ? state.getPseudoState().getKind() : null;
|
||||
if (kind == PseudoStateKind.CHOICE || kind == PseudoStateKind.HISTORY_SHALLOW
|
||||
|| kind == PseudoStateKind.HISTORY_DEEP) {
|
||||
StateContext<S, E> stateContext = buildStateContext(message, transition, stateMachine);
|
||||
State<S, E> toState = state.getPseudoState().entry(message.getPayload(), stateContext);
|
||||
setCurrentState(toState, message, transition, true, stateMachine);
|
||||
} else {
|
||||
setCurrentState(state, event, transition, true, stateMachine);
|
||||
setCurrentState(state, message, transition, true, stateMachine);
|
||||
}
|
||||
|
||||
// TODO: should handle triggerles transition some how differently
|
||||
@@ -366,7 +333,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
State<S,E> source = t.getSource();
|
||||
State<S,E> target = t.getTarget();
|
||||
if (t.getTrigger() == null && source.equals(currentState)) {
|
||||
switchToState(target, event, t, stateMachine);
|
||||
switchToState(target, message, t, stateMachine);
|
||||
}
|
||||
}
|
||||
if (isComplete()) {
|
||||
@@ -374,6 +341,13 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
}
|
||||
|
||||
private StateContext<S, E> buildStateContext(Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine) {
|
||||
E event = message != null ? message.getPayload() : null;
|
||||
MessageHeaders messageHeaders = message != null ? message.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
return new DefaultStateContext<S, E>(event, messageHeaders, extendedState, transition, stateMachine);
|
||||
}
|
||||
|
||||
private State<S, E> findDeepParent(State<S, E> state) {
|
||||
for (State<S, E> s : states) {
|
||||
if (s.getStates().contains(state)) {
|
||||
@@ -383,11 +357,11 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
return null;
|
||||
}
|
||||
|
||||
void setCurrentState(State<S, E> state, Message<E> event, Transition<S, E> transition, boolean exit, StateMachine<S, E> stateMachine) {
|
||||
void setCurrentState(State<S, E> state, Message<E> message, Transition<S, E> transition, boolean exit, StateMachine<S, E> stateMachine) {
|
||||
State<S, E> findDeep = findDeepParent(state);
|
||||
boolean isTargetSubOf = false;
|
||||
if (transition != null) {
|
||||
isTargetSubOf = isSubstate(state, transition.getSource());
|
||||
isTargetSubOf = StateMachineUtils.isSubstate(state, transition.getSource());
|
||||
if (isTargetSubOf && currentState == transition.getTarget()) {
|
||||
state = transition.getSource();
|
||||
}
|
||||
@@ -395,37 +369,37 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
if (states.contains(state)) {
|
||||
if (exit) {
|
||||
exitCurrentState(state, event, transition, stateMachine);
|
||||
exitCurrentState(state, message, transition, stateMachine);
|
||||
}
|
||||
State<S, E> notifyFrom = currentState;
|
||||
currentState = state;
|
||||
if (!isRunning()) {
|
||||
start();
|
||||
}
|
||||
entryToState(state, event, transition, stateMachine);
|
||||
entryToState(state, message, transition, stateMachine);
|
||||
notifyStateChanged(notifyFrom, state);
|
||||
} else if (currentState != null) {
|
||||
if (findDeep != null) {
|
||||
if (exit) {
|
||||
exitCurrentState(state, event, transition, stateMachine);
|
||||
exitCurrentState(state, message, transition, stateMachine);
|
||||
}
|
||||
if (currentState == findDeep) {
|
||||
StateMachine<S, E> submachine = ((AbstractState<S, E>)currentState).getSubmachine();
|
||||
if (submachine.getState() == state) {
|
||||
if (currentState == findDeep) {
|
||||
if (isTargetSubOf) {
|
||||
entryToState(currentState, event, transition, stateMachine);
|
||||
entryToState(currentState, message, transition, stateMachine);
|
||||
}
|
||||
currentState = findDeep;
|
||||
((AbstractStateMachine<S, E>)submachine).setCurrentState(state, event, transition, false, stateMachine);
|
||||
((AbstractStateMachine<S, E>)submachine).setCurrentState(state, message, transition, false, stateMachine);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
currentState = findDeep;
|
||||
entryToState(currentState, event, transition, stateMachine);
|
||||
entryToState(currentState, message, transition, stateMachine);
|
||||
StateMachine<S, E> submachine = ((AbstractState<S, E>)currentState).getSubmachine();
|
||||
((AbstractStateMachine<S, E>)submachine).setCurrentState(state, event, transition, false, stateMachine);
|
||||
((AbstractStateMachine<S, E>)submachine).setCurrentState(state, message, transition, false, stateMachine);
|
||||
}
|
||||
}
|
||||
if (history != null) {
|
||||
@@ -437,86 +411,81 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
}
|
||||
|
||||
void exitCurrentState(State<S, E> state, Message<E> event, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
void exitCurrentState(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
if (currentState == null) {
|
||||
return;
|
||||
}
|
||||
if (currentState.isSubmachineState()) {
|
||||
StateMachine<S, E> submachine = ((AbstractState<S, E>)currentState).getSubmachine();
|
||||
((AbstractStateMachine<S, E>)submachine).exitCurrentState(state, event, transition, stateMachine);
|
||||
exitFromState(currentState, event, transition, stateMachine);
|
||||
((AbstractStateMachine<S, E>)submachine).exitCurrentState(state, message, transition, stateMachine);
|
||||
exitFromState(currentState, message, transition, stateMachine);
|
||||
} else {
|
||||
exitFromState(currentState, event, transition, stateMachine);
|
||||
exitFromState(currentState, message, transition, stateMachine);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSubstate(State<S, E> left, State<S, E> right) {
|
||||
Collection<State<S, E>> c = left.getStates();
|
||||
c.remove(left);
|
||||
return c.contains(right);
|
||||
private void exitFromState(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
log.trace("Trying Exit state=[" + state + "]");
|
||||
StateContext<S, E> stateContext = buildStateContext(message, transition, stateMachine);
|
||||
|
||||
State<S, E> findDeep = findDeepParent(transition.getTarget());
|
||||
boolean isTargetSubOfOtherState = findDeep != null && findDeep != currentState;
|
||||
boolean isTargetSubOfSource = StateMachineUtils.isSubstate(transition.getSource(), transition.getTarget());
|
||||
boolean isSubOfSource = StateMachineUtils.isSubstate(transition.getSource(), currentState);
|
||||
boolean isSubOfTarget = StateMachineUtils.isSubstate(transition.getTarget(), currentState);
|
||||
|
||||
// TODO: this and entry below should be done via a separate
|
||||
// voter of some sort which would reveal transition path
|
||||
// we could make a choice on.
|
||||
if (currentState == transition.getSource() && currentState == transition.getTarget()) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getSource()) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) {
|
||||
} else if (isTargetSubOfOtherState) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget && findDeep == null) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (transition.getSource() == currentState && isTargetSubOfSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Exit state=[" + state + "]");
|
||||
state.exit(stateContext);
|
||||
notifyStateExited(state);
|
||||
}
|
||||
|
||||
private void exitFromState(State<S, E> state, Message<E> event, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
if (state != null) {
|
||||
log.trace("Exit state=[" + state + "]");
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(event != null ? event.getPayload() : null,
|
||||
messageHeaders, extendedState, transition, stateMachine);
|
||||
private void entryToState(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
log.trace("Trying Enter state=[" + state + "]");
|
||||
StateContext<S, E> stateContext = buildStateContext(message, transition, stateMachine);
|
||||
|
||||
State<S, E> findDeep = findDeepParent(transition.getTarget());
|
||||
boolean isTargetSubOfOtherState = findDeep != null && findDeep != currentState;
|
||||
if (transition != null) {
|
||||
State<S, E> findDeep1 = findDeepParent(transition.getTarget());
|
||||
State<S, E> findDeep2 = findDeepParent(transition.getSource());
|
||||
boolean isComingFromOtherSubmachine = findDeep1 != null && findDeep2 != null && findDeep2 != currentState;
|
||||
|
||||
boolean isSubOfSource = isSubstate(transition.getSource(), currentState);
|
||||
boolean isSubOfTarget = isSubstate(transition.getTarget(), currentState);
|
||||
boolean isSubOfSource = StateMachineUtils.isSubstate(transition.getSource(), currentState);
|
||||
boolean isSubOfTarget = StateMachineUtils.isSubstate(transition.getTarget(), currentState);
|
||||
if (currentState == transition.getSource() && currentState == transition.getTarget()) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getSource()) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) {
|
||||
} else if (isTargetSubOfOtherState) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget && findDeep == null) {
|
||||
} else if (isComingFromOtherSubmachine) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget && findDeep2 == null) {
|
||||
} else if (isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) {
|
||||
return;
|
||||
} else if (!isSubOfSource && !isSubOfTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isTargetSubOfSource = isSubstate(transition.getSource(), transition.getTarget());
|
||||
if (transition.getSource() == currentState && isTargetSubOfSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.exit(event != null ? event.getPayload() : null, stateContext);
|
||||
notifyStateExited(state);
|
||||
}
|
||||
}
|
||||
|
||||
private void entryToState(State<S, E> state, Message<E> event, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
if (state != null) {
|
||||
log.trace("Enter state=[" + state + "]");
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(event != null ? event.getPayload() : null,
|
||||
messageHeaders, extendedState, transition, stateMachine);
|
||||
|
||||
if (transition != null) {
|
||||
State<S, E> findDeep1 = findDeepParent(transition.getTarget());
|
||||
State<S, E> findDeep2 = findDeepParent(transition.getSource());
|
||||
boolean isComingFromOtherSubmachine = findDeep1 != null && findDeep2 != null && findDeep2 != currentState;
|
||||
|
||||
boolean isSubOfSource = isSubstate(transition.getSource(), currentState);
|
||||
boolean isSubOfTarget = isSubstate(transition.getTarget(), currentState);
|
||||
if (currentState == transition.getSource() && currentState == transition.getTarget()) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) {
|
||||
} else if (isComingFromOtherSubmachine) {
|
||||
} else if (!isSubOfSource && !isSubOfTarget && findDeep2 == null) {
|
||||
} else if (isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) {
|
||||
return;
|
||||
} else if (!isSubOfSource && !isSubOfTarget) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
notifyStateEntered(state);
|
||||
state.entry(event != null ? event.getPayload() : null, stateContext);
|
||||
}
|
||||
notifyStateEntered(state);
|
||||
log.debug("Enter state=[" + state + "]");
|
||||
state.entry(stateContext);
|
||||
}
|
||||
|
||||
private void processEventQueue() {
|
||||
@@ -590,8 +559,8 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
continue;
|
||||
}
|
||||
|
||||
Message<E> queuedEvent = queueItem.message;
|
||||
E event = queuedEvent != null ? queuedEvent.getPayload() : null;
|
||||
Message<E> queuedMessage = queueItem.message;
|
||||
E event = queuedMessage != null ? queuedMessage.getPayload() : null;
|
||||
|
||||
// need all transitions trigger could match, event trigger may match multiple
|
||||
// need to go up from substates and ask if trigger transit, if not check super
|
||||
@@ -622,8 +591,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
// go through candidates and transit max one
|
||||
for (Transition<S, E> t : trans) {
|
||||
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(queuedEvent != null ? queuedEvent.getPayload() : null,
|
||||
queuedEvent != null ? queuedEvent.getHeaders() : null, extendedState, t, this);
|
||||
StateContext<S, E> stateContext = buildStateContext(queuedMessage, t, this);
|
||||
if (t == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -640,9 +608,9 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
// if transition will transit so that we can post
|
||||
// accurate notifyTransitionStart
|
||||
notifyTransitionStart(t);
|
||||
callHandlers(t.getSource(), t.getTarget(), queuedEvent);
|
||||
callHandlers(t.getSource(), t.getTarget(), queuedMessage);
|
||||
if (t.getKind() != TransitionKind.INTERNAL) {
|
||||
switchToState(t.getTarget(), queuedEvent, t, this);
|
||||
switchToState(t.getTarget(), queuedMessage, t, this);
|
||||
}
|
||||
notifyTransition(t);
|
||||
notifyTransitionEnd(t);
|
||||
@@ -654,16 +622,15 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
}
|
||||
|
||||
private void callHandlers(State<S,E> sourceState, State<S,E> targetState, Message<E> event) {
|
||||
private void callHandlers(State<S,E> sourceState, State<S,E> targetState, Message<E> message) {
|
||||
if (sourceState != null && targetState != null) {
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(event != null ? event.getPayload() : null, messageHeaders, extendedState, null, this);
|
||||
StateContext<S, E> stateContext = buildStateContext(message, null, this);
|
||||
getStateMachineHandlerResults(getStateMachineHandlers(sourceState, targetState), stateContext);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler<S, E>> stateMachineHandlers, final StateContext<S, E> stateContext) {
|
||||
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler<S, E>> stateMachineHandlers,
|
||||
final StateContext<S, E> stateContext) {
|
||||
StateMachineRuntime<S, E> runtime = new StateMachineRuntime<S, E>() {
|
||||
@Override
|
||||
public StateContext<S, E> getStateContext() {
|
||||
@@ -677,7 +644,8 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
return results;
|
||||
}
|
||||
|
||||
private synchronized List<StateMachineHandler<S, E>> getStateMachineHandlers(State<S, E> sourceState, State<S, E> targetState) {
|
||||
private synchronized List<StateMachineHandler<S, E>> getStateMachineHandlers(State<S, E> sourceState,
|
||||
State<S, E> targetState) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
// TODO think how to handle null bf
|
||||
@@ -766,86 +734,6 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyStateChanged(State<S,E> source, State<S,E> target) {
|
||||
stateListener.stateChanged(source, target);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateChanged(this, source, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyStateEntered(State<S,E> state) {
|
||||
stateListener.stateEntered(state);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateEntered(this, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyStateExited(State<S,E> state) {
|
||||
stateListener.stateExited(state);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateExited(this, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyTransitionStart(Transition<S,E> transition) {
|
||||
stateListener.transitionStarted(transition);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishTransitionStart(this, transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyTransition(Transition<S,E> transition) {
|
||||
stateListener.transition(transition);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishTransitionEnd(this, transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyTransitionEnd(Transition<S,E> transition) {
|
||||
stateListener.transitionEnded(transition);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishTransition(this, transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyStateMachineStarted(StateMachine<S, E> stateMachine) {
|
||||
stateListener.stateMachineStarted(stateMachine);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateMachineStart(this, stateMachine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyStateMachineStopped(StateMachine<S, E> stateMachine) {
|
||||
stateListener.stateMachineStopped(stateMachine);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateMachineStop(this, stateMachine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TriggerQueueItem {
|
||||
Trigger<S, E> trigger;
|
||||
Message<E> message;
|
||||
@@ -855,53 +743,4 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to relay listener events from a submachines which works
|
||||
* as its own listener context. User only connects to main root machine and
|
||||
* expects to get events for all machines from there.
|
||||
*/
|
||||
private class StateMachineListenerRelay implements StateMachineListener<S,E> {
|
||||
|
||||
@Override
|
||||
public void stateChanged(State<S, E> from, State<S, E> to) {
|
||||
stateListener.stateChanged(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateEntered(State<S, E> state) {
|
||||
stateListener.stateEntered(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateExited(State<S, E> state) {
|
||||
stateListener.stateExited(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(Transition<S, E> transition) {
|
||||
stateListener.transition(transition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionStarted(Transition<S, E> transition) {
|
||||
stateListener.transitionStarted(transition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionEnded(Transition<S, E> transition) {
|
||||
stateListener.transitionEnded(transition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateMachineStarted(StateMachine<S, E> stateMachine) {
|
||||
stateListener.stateMachineStarted(stateMachine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateMachineStopped(StateMachine<S, E> stateMachine) {
|
||||
stateListener.stateMachineStopped(stateMachine);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.statemachine.event.StateMachineEventPublisher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -56,9 +55,6 @@ public abstract class LifecycleObjectSupport implements InitializingBean, SmartL
|
||||
// to access bean factory
|
||||
private volatile BeanFactory beanFactory;
|
||||
|
||||
/** Context application event publisher if exist */
|
||||
private volatile StateMachineEventPublisher stateMachineEventPublisher;
|
||||
|
||||
@Override
|
||||
public final void afterPropertiesSet() {
|
||||
try {
|
||||
@@ -230,31 +226,6 @@ public abstract class LifecycleObjectSupport implements InitializingBean, SmartL
|
||||
return taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state machine event publisher.
|
||||
*
|
||||
* @return the state machine event publisher
|
||||
*/
|
||||
protected StateMachineEventPublisher getStateMachineEventPublisher() {
|
||||
if(stateMachineEventPublisher == null && getBeanFactory() != null) {
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("getting stateMachineEventPublisher service from bean factory " + getBeanFactory());
|
||||
}
|
||||
stateMachineEventPublisher = StateMachineContextUtils.getEventPublisher(getBeanFactory());
|
||||
}
|
||||
return stateMachineEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state machine event publisher.
|
||||
*
|
||||
* @param stateMachineEventPublisher the new state machine event publisher
|
||||
*/
|
||||
public void setStateMachineEventPublisher(StateMachineEventPublisher stateMachineEventPublisher) {
|
||||
Assert.notNull(stateMachineEventPublisher, "StateMachineEventPublisher cannot be null");
|
||||
this.stateMachineEventPublisher = stateMachineEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may implement this for initialization logic. Called
|
||||
* during the {@link InitializingBean} phase. Implementor should
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed 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 org.springframework.statemachine.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.event.StateMachineEventPublisher;
|
||||
import org.springframework.statemachine.listener.CompositeStateMachineListener;
|
||||
import org.springframework.statemachine.listener.StateMachineListener;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Support and helper class for base state machine implementation.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSupport {
|
||||
|
||||
private static final Log log = LogFactory.getLog(StateMachineObjectSupport.class);
|
||||
|
||||
private final CompositeStateMachineListener<S, E> stateListener = new CompositeStateMachineListener<S, E>();
|
||||
|
||||
/** Context application event publisher if exist */
|
||||
private volatile StateMachineEventPublisher stateMachineEventPublisher;
|
||||
|
||||
/** Flag for application context events */
|
||||
private boolean contextEventsEnabled = true;
|
||||
|
||||
/**
|
||||
* Gets the state machine event publisher.
|
||||
*
|
||||
* @return the state machine event publisher
|
||||
*/
|
||||
protected StateMachineEventPublisher getStateMachineEventPublisher() {
|
||||
if(stateMachineEventPublisher == null && getBeanFactory() != null) {
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("getting stateMachineEventPublisher service from bean factory " + getBeanFactory());
|
||||
}
|
||||
stateMachineEventPublisher = StateMachineContextUtils.getEventPublisher(getBeanFactory());
|
||||
}
|
||||
return stateMachineEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state machine event publisher.
|
||||
*
|
||||
* @param stateMachineEventPublisher the new state machine event publisher
|
||||
*/
|
||||
public void setStateMachineEventPublisher(StateMachineEventPublisher stateMachineEventPublisher) {
|
||||
Assert.notNull(stateMachineEventPublisher, "StateMachineEventPublisher cannot be null");
|
||||
this.stateMachineEventPublisher = stateMachineEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if context application events are enabled. Events
|
||||
* are enabled by default. Set this to false if you don't
|
||||
* want state machine to send application context events.
|
||||
*
|
||||
* @param contextEventsEnabled the enabled flag
|
||||
*/
|
||||
public void setContextEventsEnabled(boolean contextEventsEnabled) {
|
||||
this.contextEventsEnabled = contextEventsEnabled;
|
||||
}
|
||||
|
||||
protected CompositeStateMachineListener<S, E> getStateListener() {
|
||||
return stateListener;
|
||||
}
|
||||
|
||||
protected void notifyStateChanged(State<S,E> source, State<S,E> target) {
|
||||
stateListener.stateChanged(source, target);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateChanged(this, source, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyStateEntered(State<S,E> state) {
|
||||
stateListener.stateEntered(state);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateEntered(this, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyStateExited(State<S,E> state) {
|
||||
stateListener.stateExited(state);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateExited(this, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyTransitionStart(Transition<S,E> transition) {
|
||||
stateListener.transitionStarted(transition);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishTransitionStart(this, transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyTransition(Transition<S,E> transition) {
|
||||
stateListener.transition(transition);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishTransitionEnd(this, transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyTransitionEnd(Transition<S,E> transition) {
|
||||
stateListener.transitionEnded(transition);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishTransition(this, transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyStateMachineStarted(StateMachine<S, E> stateMachine) {
|
||||
stateListener.stateMachineStarted(stateMachine);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateMachineStart(this, stateMachine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyStateMachineStopped(StateMachine<S, E> stateMachine) {
|
||||
stateListener.stateMachineStopped(stateMachine);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishStateMachineStop(this, stateMachine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to relay listener events from a submachines which works
|
||||
* as its own listener context. User only connects to main root machine and
|
||||
* expects to get events for all machines from there.
|
||||
*/
|
||||
protected class StateMachineListenerRelay implements StateMachineListener<S,E> {
|
||||
|
||||
@Override
|
||||
public void stateChanged(State<S, E> from, State<S, E> to) {
|
||||
stateListener.stateChanged(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateEntered(State<S, E> state) {
|
||||
stateListener.stateEntered(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateExited(State<S, E> state) {
|
||||
stateListener.stateExited(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(Transition<S, E> transition) {
|
||||
stateListener.transition(transition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionStarted(Transition<S, E> transition) {
|
||||
stateListener.transitionStarted(transition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionEnded(Transition<S, E> transition) {
|
||||
stateListener.transitionEnded(transition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateMachineStarted(StateMachine<S, E> stateMachine) {
|
||||
stateListener.stateMachineStarted(stateMachine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateMachineStopped(StateMachine<S, E> stateMachine) {
|
||||
stateListener.stateMachineStopped(stateMachine);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.statemachine.support;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -28,6 +29,19 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
public abstract class StateMachineUtils {
|
||||
|
||||
/**
|
||||
* Checks if right hand side is a substate of a left hand side.
|
||||
*
|
||||
* @param left the super state
|
||||
* @param right the sub state
|
||||
* @return if sub is child of super
|
||||
*/
|
||||
public static <S, E> boolean isSubstate(State<S, E> left, State<S, E> right) {
|
||||
Collection<State<S, E>> c = left.getStates();
|
||||
c.remove(left);
|
||||
return c.contains(right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if right hand collection has atleast one same item as left hand
|
||||
* collection.
|
||||
|
||||
Reference in New Issue
Block a user