当前最新发行版本
<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.8</version>
</dependency>
最新快照版本
<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.9-SNAPSHOT</version>
</dependency>
public class QuickStartSample {
// 1. 定义状态机事件
enum FSMEvent {
ToA, ToB, ToC, ToD
}
// 2. 定义状态机类
@StateMachineParameters(stateType=String.class, eventType=FSMEvent.class, contextType=Integer.class)
static class StateMachineSample extends AbstractUntypedStateMachine {
protected void fromAToB(String from, String to, FSMEvent event, Integer context) {
System.out.println("Transition from '"+from+"' to '"+to+"' on event '"+event+
"' with context '"+context+"'.");
}
protected void ontoB(String from, String to, FSMEvent event, Integer context) {
System.out.println("Entry State \'"+to+"\'.");
}
}
public static void main(String[] args) {
// 3. 创建状态过渡流程
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineSample.class);
builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");
builder.externalTransition().from("B").to("C").on(FSMEvent.ToC).callMethod("fromAToB");
builder.onEntry("B").callMethod("ontoB");
// 4. 使用状态机
UntypedStateMachine fsm = builder.newStateMachine("A");
fsm.fire(FSMEvent.ToB, 10);
fsm.fire(FSMEvent.ToC);
System.out.println("Current state is "+fsm.getCurrentState());
}
}
控制台打印
Transition from 'A' to 'B' on event 'ToB' with context '10'.
Entry State 'B'.
Transition from 'B' to 'C' on event 'ToC' with context 'null'.
Current state is C
这样一个简单的demo就运行起来了。
squirrel-foundation不仅支持fluent API和声明性方式来声明状态机,还能让用户能够以直接的方式定义操作方法。
StateMachineBuilder<MyStateMachine, MyState, MyEvent, MyContext> builder =
StateMachineBuilderFactory.create(MyStateMachine.class, MyState.class, MyEvent.class, MyContext.class);
State Machine Builder创建完成后,可以用Fluent API去定义状态/转换/动作
builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");
这段代码的含义是:从状态A通过ToB事件转变为状态B,同时触发fromAToB方法。
builder.internalTransition(TransitionPriority.HIGH).within(MyState.A).on(MyEvent.WithinA).perform(new MyAction());
internalTransition可以设置优先级,优先级用于在覆盖原始过渡,状态A在触发"WithinA"事件时调用"myAction",通过myAction实现一定的逻辑,使用internalTransition时在过渡完成后没有状态进入或退出
builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.CTOD).when(
new Condition<MyContext>() {
@Override
public boolean isSatisfied(MyContext context) {
return context!=null && context.getValue()>80;
}
@Override
public String name() {
return "MyCondition";
}
}).callMethod("myInternalTransitionCall");
builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.CTOD).whenMvel(
"MyCondition:::(context!=null && context.getValue()>80)").callMethod("myInternalTransitionCall");
当isSatisfied为true时触发状态变化。
默认方法调用,通过函数名即可确认调用函数。
protected void transitFromAToBOnATOB(MyState from, MyState to, MyEvent event, MyContext context)
protected void transitFromAnyToBOnGoToB(MyState from, MyState to, MyEvent event, MyContext context)
其他的默认方法格式
transitFrom[fromStateName]To[toStateName]On[eventName]When[conditionName]
transitFrom[fromStateName]To[toStateName]On[eventName]
transitFromAnyTo[toStateName]On[eventName]
transitFrom[fromStateName]ToAnyOn[eventName]
transitFrom[fromStateName]To[toStateName]
on[eventName]
@States({
@State(name = "A", entryCallMethod = "entryA", exitCallMethod = "exitA", initialState = true),
@State(name = "B", entryCallMethod = "entryB", exitCallMethod = "exitB"),
@State(name = "C", entryCallMethod = "entryC", exitCallMethod = "exitC"),
@State(name = "D", entryCallMethod = "entryD", exitCallMethod = "exitD")
})
@Transitions({
@Transit(from = "A", to = "B", on = "ATOB", callMethod = "toB"),
@Transit(from = "B", to = "C", on = "BTOC", callMethod = "toC"),
@Transit(from = "C", to = "D", on = "CTOD", callMethod = "toD")
})
@StateMachineParameters(stateType = MyState.class, eventType = MyEvent.class, contextType = MyContext.class)
public class DeclarativeMachine extends AbstractUntypedStateMachine {
public void entryA(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---entryA");
}
public void exitA(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---exitA");
}
public void entryB(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---entryB");
}
public void exitB(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---exitB");
}
public void entryC(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---entryC");
}
public void exitC(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---exitC");
}
public void entryD(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---entryD");
}
public void exitD(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---exitD");
}
public void toB(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---toB");
}
public void toC(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---toC");
}
public void toD(MyState from, MyState to, MyEvent on, MyContext context){
System.out.println("---toD");
}
}
@Test
public void testDeclarativeMachine(){
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(DeclarativeMachine.class);
UntypedStateMachine fsm = builder.newAnyStateMachine(MyState.A);
fsm.start();
fsm.fire(MyEvent.ATOB);
fsm.fire(MyEvent.BTOC);
fsm.fire(MyEvent.CTOD);
}
api
builder.defineSequentialStatesOn(MyState.A, MyState.XINA, MyState.YINA);
annotation
@States({
@State(name="A", entryCallMethod="entryA", exitCallMethod="exitA"),
@State(parent="A", name="XINA", entryCallMethod="entryXINA", exitCallMethod="exitXINA", initialState=true),
@State(parent="A", name="YINA", entryCallMethod="entryYINA", exitCallMethod="exitYINA")
})
api
builder.defineParallelStatesOn(MyState.Root, MyState.RegionState1, MyState.RegionState2);
builder.defineSequentialStatesOn(MyState.RegionState1, MyState.State11, MyState.State12);
builder.externalTransition().from(MyState.State11).to(MyState.State12).on(MyEvent.Event1);
builder.defineSequentialStatesOn(MyState.RegionState2, MyState.State21, MyState.State22);
builder.externalTransition().from(MyState.State21).to(MyState.State22).on(MyEvent.Event2);
annotation
@States({
@State(name="Root", entryCallMethod="enterRoot", exitCallMethod="exitRoot", compositeType=StateCompositeType.PARALLEL),
@State(parent="Root", name="RegionState1", entryCallMethod="enterRegionState1", exitCallMethod="exitRegionState1"),
@State(parent="Root", name="RegionState2", entryCallMethod="enterRegionState2", exitCallMethod="exitRegionState2")
})
针对不同用例定义了三种类型的上下文事件。
启动/终止事件:状态机启动/终止时,将使用声明为启动/终止事件的事件。因此,用户可以区分调用的动作触发器,例如,当状态机正在启动并进入其初始状态时,用户可以区分这些状态进入动作是由启动事件调用的。
完成事件:当所有并行状态都达到最终状态时,将自动触发完成事件。用户可以根据完成事件定义以下过渡。
api
builder.defineFinishEvent(HEvent.Start);
builder.defineTerminateEvent(HEvent.Terminate);
builder.defineStartEvent(HEvent.Finish);
annotation
@ContextEvent(terminateEvent = "RootToA")
public class ParallelStateMachine extends AbstractUntypedStateMachine {
}
使用历史记录保存和还原当前状态
过渡类型
过渡分为三种类型:
api
builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.A2B);
builder.internalTransition().within(MyState.A).on(MyEvent.innerA);
builder.localTransition().from(MyState.A).to(MyState.CinA).on(MyEvent.intoC)
annotation
@Transitions({
@Transition(from="A", to="B", on="A2B"),
@Transition(from="A", on="innerA", type=TransitionType.INTERNAL),
@Transition(from="A", to="CinA", on="intoC", type=TransitionType.LOCAL),
})
|--StateMachineEvent /* 所有状态机的事件 */
|--StartEvent /* 当状态机启动时触发 */
|--TerminateEvent /* 当状态机终止时触发 */
|--TransitionEvent /* 过渡时触发的事件 */
|--TransitionBeginEvent /* 过渡开始时触发 */
|--TransitionCompleteEvent /* 过渡完成时触发 */
|--TransitionExceptionEvent /* 抛出异常时触发 */
|--TransitionDeclinedEvent /* 过渡拒绝时触发 */
|--TransitionEndEvent /* 结束过渡时触发(无论何种情况结束)*/
static class ExternalModule {
@OnTransitionEnd
@ListenerOrder(10) // Since 0.3.1 ListenerOrder can be used to insure listener invoked orderly
public void transitionEnd() {
// method annotated with TransitionEnd will be invoked when transition end...
// the method must be public and return nothing
}
@OnTransitionBegin
public void transitionBegin(TestEvent event) {
// method annotated with TransitionBegin will be invoked when transition begin...
}
// 'event'(E), 'from'(S), 'to'(S), 'context'(C) and 'stateMachine'(T)可以用于MVEL脚本
@OnTransitionBegin(when="event.name().equals(\"toB\")")
public void transitionBeginConditional() {
// method will be invoked when transition begin while transition caused by event "toB"
}
@OnTransitionComplete
public void transitionComplete(String from, String to, TestEvent event, Integer context) {
// method annotated with TransitionComplete will be invoked when transition complete...
}
@OnTransitionDecline
public void transitionDeclined(String from, TestEvent event, Integer context) {
// method annotated with TransitionDecline will be invoked when transition declined...
}
@OnBeforeActionExecuted
public void onBeforeActionExecuted(Object sourceState, Object targetState,
Object event, Object context, int[] mOfN, Action<?, ?, ?,?> action) {
// method annotated with OnAfterActionExecuted will be invoked before action invoked
}
@OnAfterActionExecuted
public void onAfterActionExecuted(Object sourceState, Object targetState,
Object event, Object context, int[] mOfN, Action<?, ?, ?,?> action) {
// method annotated with OnAfterActionExecuted will be invoked after action invoked
}
@OnActionExecException
public void onActionExecException(Action<?, ?, ?,?> action, TransitionException e) {
// method annotated with OnActionExecException will be invoked when action thrown exception
}
}
ExternalModule externalModule = new ExternalModule();
fsm.addDeclarativeListener(externalModule);
...
fsm.removeDeclarativeListener(externalModule);
protected void afterTransitionCausedException(Exception e, S fromState, S toState, E event, C context) {
}
protected void beforeTransitionBegin(S fromState, E event, C context) {
}
protected void afterTransitionCompleted(S fromState, S toState, E event, C context) {
}
protected void afterTransitionEnd(S fromState, S toState, E event, C context) {
}
protected void afterTransitionDeclined(S fromState, E event, C context) {
}
protected void beforeActionInvoked(S fromState, S toState, E event, C context) {
}
public class WeightedActionTest {
@Transitions({
@Transit(from="A", to="B", on="ToB", callMethod="fromAToB"),
@Transit(from="A", to="C", on="ToC", callMethod="fromAToC"),
@Transit(from="A", to="D", on="ToD")
})
@States({
@State(name="D", entryCallMethod="entryD")
})
@StateMachineParameters(stateType=String.class, eventType=String.class, contextType=Void.class)
@ContextInsensitive
static class UntypedStateMachineBase extends AbstractUntypedStateMachine {
protected StringBuilder logger = new StringBuilder();
protected void fromAToB(String from, String to, String event) {
logger.append("fromAToB");
}
protected void transitFromAToBOnToB(String from, String to, String event) {
logger.append("transitFromAToBOnToB");
}
protected void fromAToC(String from, String to, String event) {
logger.append("fromAToC");
}
protected void entryD(String from, String to, String event) {
logger.append("entryD");
}
@Override
protected void beforeActionInvoked(Object fromState, Object toState, Object event, Object context) {
addOptionalDot();
}
private void addOptionalDot() {
if (logger.length() > 0) {
logger.append('.');
}
}
public String consumeLog() {
final String result = logger.toString();
logger = new StringBuilder();
return result;
}
}
@Transitions({
@Transit(from="A", to="B", on="ToB", callMethod="beforeFromAToB"),
@Transit(from="A", to="B", on="ToB", callMethod="afterFromAToB"),
@Transit(from="A", to="C", on="ToC", callMethod="goAToC1:+150"),
@Transit(from="A", to="C", on="ToC", callMethod="goAToC2:-150"),
})
@States({
@State(name="D", entryCallMethod="beforeEntryD"),
@State(name="D", entryCallMethod="goEntryD:-150"),
})
static class UntypedStateMachineExt extends UntypedStateMachineBase {
protected void beforeFromAToB(String from, String to, String event) {
logger.append("beforeFromAToB");
}
protected void afterFromAToB(String from, String to, String event) {
logger.append("afterFromAToB");
}
protected void goAToC1(String from, String to, String event) {
logger.append("goAToC1");
}
protected void goAToC2(String from, String to, String event) {
logger.append("goAToC2");
}
protected void beforeEntryD(String from, String to, String event) {
logger.append("beforeEntryD");
}
protected void goEntryD(String from, String to, String event) {
for(int i=0; i<10000; ++i) {
RandomStringUtils.randomAlphabetic(10);
}
logger.append("goEntryD");
}
}
@States({
@State(name="D", entryCallMethod="entryD:+200") // override extension method weight
})
static class UntypedStateMachineExt2 extends UntypedStateMachineExt {
}
private UntypedStateMachineExt fsm;
private StateMachineLogger logger;
@After
public void teardown() {
if(fsm.getStatus()!=StateMachineStatus.TERMINATED)
fsm.terminate(null);
}
@Before
public void setup() {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineExt.class);
fsm = builder.newUntypedStateMachine("A",
StateMachineConfiguration.create().enableDebugMode(true),
new Object[0]);
}
/**
* 测试 before:100 注解 默认方法 after:-100优先级
*/
@Test
public void testBeforeExtension() {
fsm.fire("ToB");
assertThat(fsm.consumeLog(), is(equalTo("beforeFromAToB.fromAToB.transitFromAToBOnToB.afterFromAToB")));
}
/**
* 测试设置优先级的调用顺序
*/
@Test
public void testWeightTransitionAction() {
fsm.fire("ToC");
assertThat(fsm.consumeLog(), is(equalTo("goAToC1.fromAToC.goAToC2")));
}
/**
*
*/
@Test
public void testWeightStateAction() {
fsm.fire("ToD");
assertThat(fsm.consumeLog(), is(equalTo("beforeEntryD.entryD.goEntryD")));
}
/**
* 测试重写优先级的调用顺序
*/
@Test
public void testOverrideWeight() {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineExt2.class);
fsm = builder.newUntypedStateMachine("A");
logger = new StateMachineLogger(fsm);
logger.startLogging();
fsm.fire("ToD");
assertThat(fsm.consumeLog(), is(equalTo("entryD.beforeEntryD.goEntryD")));
}
/**
* 测试ignore来忽略调用方法
*/
@Test
public void testIgnoreWeight() {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineExt2.class);
builder.onEntry("D").callMethod("entryD:ignore");
fsm = builder.newUntypedStateMachine("A");
logger = new StateMachineLogger(fsm);
logger.startLogging();
fsm.fire("ToD");
assertThat(fsm.consumeLog(), is(equalTo("beforeEntryD.goEntryD")));
}
}
// 1 User defined a state machine interface
interface MyStateMachine extends StateMachine<MyStateMachine, MyState, MyEvent, MyContext> {
. . .
}
// 2 Both MyStateMachineImpl and MyStateMachineImplEx are implemented MyStateMachine
class MyStateMachineImpl implements MyStateMachine {
. . .
}
class MyStateMachineImplEx implements MyStateMachine {
. . .
}
// 3 User define a state machine post processor
MyStateMachinePostProcessor implements SquirrelPostProcessor<MyStateMachine> {
void postProcess(MyStateMachine component) {
. . .
}
}
// 4 User register state machine post process
SquirrelPostProcessorProvider.getInstance().register(MyStateMachine.class, MyStateMachinePostProcessor.class);
SCXMLVisitor visitor = SquirrelProvider.getInstance().newInstance(SCXMLVisitor.class);
stateMachine.accept(visitor);
visitor.convertSCXMLFile("MyStateMachine", true);
DotVisitor可以用来生成状态图,可以通过GraphViz查看。
DotVisitor visitor = SquirrelProvider.getInstance().newInstance(DotVisitor.class);
stateMachine.accept(visitor);
visitor.convertDotFile("SnakeStateMachine");
UntypedStateMachineBuilder builder = new UntypedStateMachineImporter().importDefinition(scxmlDef);
ATMStateMachine stateMachine = builder.newAnyStateMachine(ATMState.Idle);
注意:UntypedStateMachineImporter提供了XML样式来定义状态机,就像状态机构建器API或声明性注释一样。与SCXML类似的定义不等于标准SCXML。
@Test
public void testSavedData() {
stateMachine.fire(LEvent.A12A2, 0);
assertThat(stateMachine.getCurrentState(), equalTo(LState.A2));
StateMachineData.Reader<TestStateMachine, LState, LEvent, Integer> savedData =
stateMachine.dumpSavedData();
assertThat(savedData.linkedStates(), contains(LState.A));
stateMachine.terminate(null);
try {
// use buffering
OutputStream file = new FileOutputStream("data.sqr");
OutputStream buffer = new BufferedOutputStream(file);
OutputStreamWriter osw = new OutputStreamWriter(buffer, "UTF-8");
osw.write(ObjectSerializableSupport.serialize(savedData));
osw.flush();
} catch (IOException ex) {
Assert.fail();
}
setup();
try {
// use buffering
InputStream file = new FileInputStream("data.sqr");
InputStream buffer = new BufferedInputStream(file);
InputStreamReader isr = new InputStreamReader(buffer, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String fileContent = br.readLine();
// deserialize the List
StateMachineData.Reader<TestStateMachine, LState, LEvent, Integer> loadedSavedData =
ObjectSerializableSupport.deserialize(fileContent);
stateMachine.loadSavedData(loadedSavedData);
stateMachine.fire(LEvent.A22A3, 0);
assertThat(stateMachine.getCurrentState(), equalTo(LState.A3));
} catch (Exception ex) {
ex.printStackTrace();
Assert.fail();
}
}
UntypedStateMachine fsm = builder.newUntypedStateMachine("a",
StateMachineConfiguration.create().enableAutoStart(false)
.setIdProvider(IdProvider.UUIDProvider.getInstance()),
new Object[0]); // since 0.3.0
fsm.fire(TestEvent.toA);
上面的示例代码用于创建一个以UUID作为其标识符的状态机实例,并禁用自动启动功能。
StateMachineConfigure也可以在状态机构建器上设置,这意味着由该配置创建builder.newStateMachine(S initialStateId)或builder.newStateMachine(S initialStateId, Object… extraParams)将使用此配置的所有状态机实例。
状态机诊断
Timed State
定时触发状态。定时任务将被提交给ScheduledExecutorService。用户可以通过SquirrelSingletonProvider注册您的ScheduledExecutorService实现实例,如果未注册ScheduledExecutorService实例,则SquirrelConfiguration将提供默认实例。之后,可以由状态机构建器定义定时状态。
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
SquirrelSingletonProvider.getInstance().register(ScheduledExecutorService.class, scheduler);
builder.defineTimedState("A", 50, 100, "FIRST", null);
builder.internalTransition().within("A").on("FIRST");
public class LinkedStateMachineTest {
enum LState {
A, B, C, D, A1, A2, A3
}
enum LEvent {
A2B, B2C, C2D, D2A, A12A2, A22A3, A32A1
}
@States({
@State(name = "A", entryCallMethod = "enterA", exitCallMethod = "leftA"),
@State(name = "B", entryCallMethod = "enterB", exitCallMethod = "leftB"),
@State(name = "C", entryCallMethod = "enterC", exitCallMethod = "leftC"),
@State(name = "D", entryCallMethod = "enterD", exitCallMethod = "leftD") })
@Transitions({
@Transit(from = "A", to = "B", on = "A2B", callMethod = "transitA2B"),
@Transit(from = "B", to = "C", on = "B2C", callMethod = "transitB2C"),
@Transit(from = "C", to = "D", on = "C2D", callMethod = "transitC2D"),
@Transit(from = "D", to = "A", on = "D2A", callMethod = "transitD2A") })
static class TestStateMachine extends
AbstractStateMachine<TestStateMachine, LState, LEvent, Integer> {
private StringBuilder logger;
protected TestStateMachine(StringBuilder logger) {
this.logger = logger;
}
public void transitA2B(LState from, LState to, LEvent event,
Integer context) {
addOptionalDot();
logger.append("transitA2B");
}
public void transitB2C(LState from, LState to, LEvent event,
Integer context) {
addOptionalDot();
logger.append("transitB2C");
}
public void transitC2D(LState from, LState to, LEvent event,
Integer context) {
addOptionalDot();
logger.append("transitC2D");
}
public void transitD2A(LState from, LState to, LEvent event,
Integer context) {
addOptionalDot();
logger.append("transitD2A");
}
public void enterA(LState from, LState to, LEvent event, Integer context) {
addOptionalDot();
logger.append("enterA");
}
public void leftA(LState from, LState to, LEvent event, Integer context) {
addOptionalDot();
logger.append("leftA");
}
public void enterB(LState from, LState to, LEvent event, Integer context) {
addOptionalDot();
logger.append("enterB");
}
public void leftB(LState from, LState to, LEvent event, Integer context) {
addOptionalDot();
logger.append("leftB");
}
public void enterC(LState from, LState to, LEvent event, Integer context) {
addOptionalDot();
logger.append("enterC");
}
public void leftC(LState from, LState to, LEvent event, Integer context) {
addOptionalDot();
logger.append("leftC");
}
public void enterD(LState from, LState to, LEvent event, Integer context) {
addOptionalDot();
logger.append("enterD");
}
public void leftD(LState from, LState to, LEvent event, Integer context) {
addOptionalDot();
logger.append("leftD");
}
@Override
public void start(Integer context) {
logger.append("start1");
super.start(context);
}
@Override
public void terminate(Integer context) {
addOptionalDot();
logger.append("terminate1");
super.terminate(context);
}
private void addOptionalDot() {
if (logger.length() > 0) {
logger.append('.');
}
}
}
@States({
@State(name = "A1", entryCallMethod = "enterA1", exitCallMethod = "leftA1"),
@State(name = "A2", entryCallMethod = "enterA2", exitCallMethod = "leftA2"),
@State(name = "A3", entryCallMethod = "enterA3", exitCallMethod = "leftA3") })
@Transitions({
@Transit(from = "A1", to = "A2", on = "A12A2", callMethod = "transitA12A2"),
@Transit(from = "A2", to = "A3", on = "A22A3", callMethod = "transitA22A3"),
@Transit(from = "A3", to = "A1", on = "A32A1", callMethod = "transitA32A1") })
static class LinkedStateMachine extends
AbstractStateMachine<LinkedStateMachine, LState, LEvent, Integer> {
private StringBuilder logger;
protected LinkedStateMachine(StringBuilder logger) {
this.logger = logger;
}
public void transitA12A2(LState from, LState to, LEvent event,
Integer context) {
logger.append("transitA12A2");
}
public void transitA22A3(LState from, LState to, LEvent event,
Integer context) {
logger.append("transitA22A3");
}
public void transitA32A1(LState from, LState to, LEvent event,
Integer context) {
logger.append("transitA32A1");
}
public void enterA1(LState from, LState to, LEvent event,
Integer context) {
logger.append("enterA1");
}
public void leftA1(LState from, LState to, LEvent event, Integer context) {
logger.append("leftA1");
}
public void enterA2(LState from, LState to, LEvent event,
Integer context) {
logger.append("enterA2");
}
public void leftA2(LState from, LState to, LEvent event, Integer context) {
logger.append("leftA2");
}
public void enterA3(LState from, LState to, LEvent event,
Integer context) {
logger.append("enterA3");
}
public void leftA3(LState from, LState to, LEvent event, Integer context) {
logger.append("leftA3");
}
@Override
protected void beforeActionInvoked(LState from, LState to, LEvent event, Integer context) {
addOptionalDot();
}
@Override
public void start(Integer context) {
addOptionalDot();
logger.append("start2");
super.start(context);
}
@Override
public void terminate(Integer context) {
addOptionalDot();
logger.append("terminate2");
super.terminate(context);
}
private void addOptionalDot() {
if (logger.length() > 0) {
logger.append('.');
}
}
}
TestStateMachine stateMachine;
TestStateMachine stateMachine2;
StringBuilder logger;
@Before
public void setup() {
logger = new StringBuilder();
StateMachineBuilder<LinkedStateMachine, LState, LEvent, Integer> builderOfLinkedStateMachine =
StateMachineBuilderFactory.create(LinkedStateMachine.class, LState.class, LEvent.class,
Integer.class, new Class<?>[] { StringBuilder.class });
StateMachineBuilder<TestStateMachine, LState, LEvent, Integer> builderOfTestStateMachine =
StateMachineBuilderFactory.create(TestStateMachine.class, LState.class, LEvent.class,
Integer.class, new Class<?>[] { StringBuilder.class });
// 定义链接状态
builderOfTestStateMachine.defineLinkedState(LState.A, builderOfLinkedStateMachine, LState.A1, logger);
builderOfTestStateMachine.defineLinkedState(LState.C, builderOfLinkedStateMachine, LState.A2, logger);
stateMachine = builderOfTestStateMachine.newStateMachine(LState.A, logger);
stateMachine2 = builderOfTestStateMachine.newStateMachine(LState.A, logger);
}
@Test
public void testInitialLinkedState() {
doTestInitialLinkedState(stateMachine, logger);
logger.append("|");
doTestInitialLinkedState(stateMachine2, logger);
assertThat(logger.toString(), equalTo("start1.enterA.start2.enterA1|start1.enterA.start2.enterA1"));
}
private void doTestInitialLinkedState(TestStateMachine stateMachine, StringBuilder logger) {
stateMachine.start(0);
assertThat(stateMachine.getCurrentState(), equalTo(LState.A1));
assertThat(stateMachine.getCurrentRawState().getStateId(), equalTo(LState.A1));
}
@Test
public void testLinkedStateMachineProcessEvent() {
stateMachine.fire(LEvent.A12A2, 0);
assertThat(
logger.toString(),
equalTo("start1.enterA.start2.enterA1.leftA1.transitA12A2.enterA2"));
}
@Test
public void testTestStateMachineProcessEvent() {
stateMachine.fire(LEvent.A2B, 0);
assertThat(
logger.toString(),
equalTo("start1.enterA.start2.enterA1.terminate2.leftA1.leftA.transitA2B.enterB"));
}
@Test
public void testInitialLinkedState2() {
stateMachine.fire(LEvent.A2B, 0);
System.out.println(logger);
stateMachine.fire(LEvent.B2C, 0);
System.out.println(logger);
System.out.println(stateMachine.getCurrentState());
stateMachine.fire(LEvent.A22A3, 0);
System.out.println(logger);
assertThat(stateMachine.getCurrentState(), equalTo(LState.A3));
assertThat(stateMachine.getCurrentRawState().getStateId(), equalTo(LState.A3));
}
}