├── .gitignore
├── framework
├── src
│ ├── test
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── chiralbehaviors
│ │ │ │ └── tron
│ │ │ │ ├── examples
│ │ │ │ ├── simpleProtocol
│ │ │ │ │ ├── BufferHandler.java
│ │ │ │ │ ├── impl
│ │ │ │ │ │ └── SimpleProtocolImpl.java
│ │ │ │ │ ├── SimpleProtocol.java
│ │ │ │ │ ├── SimpleFsm.java
│ │ │ │ │ └── stateMaps
│ │ │ │ │ │ ├── SimpleServer.java
│ │ │ │ │ │ ├── Simple.java
│ │ │ │ │ │ └── SimpleClient.java
│ │ │ │ ├── task
│ │ │ │ │ ├── TaskModel.java
│ │ │ │ │ ├── TaskFsm.java
│ │ │ │ │ └── Task.java
│ │ │ │ └── telephone
│ │ │ │ │ ├── TelephoneFsm.java
│ │ │ │ │ ├── Telephone.java
│ │ │ │ │ ├── Call.java
│ │ │ │ │ └── PhoneNumber.java
│ │ │ │ ├── TestTask.java
│ │ │ │ └── TestSimple.java
│ │ └── resources
│ │ │ └── logback-test.xml
│ └── main
│ │ └── java
│ │ └── com
│ │ └── chiralbehaviors
│ │ └── tron
│ │ ├── FsmExecutor.java
│ │ ├── Exit.java
│ │ ├── Entry.java
│ │ ├── Default.java
│ │ ├── InvalidTransition.java
│ │ └── Fsm.java
└── pom.xml
├── README.md
├── pom.xml
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | **/target
2 | **/.classpath
3 | **/.project
4 | **/.settings
5 | **/src/site
6 | **/.classpath
7 | **/.project
8 | **/.settings
9 | **/src/site
10 | **/.DS_Store
11 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/simpleProtocol/BufferHandler.java:
--------------------------------------------------------------------------------
1 | package com.chiralbehaviors.tron.examples.simpleProtocol;
2 |
3 | public class BufferHandler {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/framework/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 | System.err
7 |
8 | %-5level %msg%n
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/framework/src/main/java/com/chiralbehaviors/tron/FsmExecutor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020, salesforce.com, inc.
3 | * All rights reserved.
4 | * SPDX-License-Identifier: BSD-3-Clause
5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6 | */
7 | package com.chiralbehaviors.tron;
8 |
9 | /**
10 | * @author hal.hildebrand
11 | *
12 | */
13 | public interface FsmExecutor {
14 | default Context context() {
15 | return Fsm.thisContext();
16 | }
17 |
18 | default Fsm fsm() {
19 | return Fsm.thisFsm();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/simpleProtocol/impl/SimpleProtocolImpl.java:
--------------------------------------------------------------------------------
1 | package com.chiralbehaviors.tron.examples.simpleProtocol.impl;
2 |
3 | import com.chiralbehaviors.tron.examples.simpleProtocol.BufferHandler;
4 | import com.chiralbehaviors.tron.examples.simpleProtocol.SimpleProtocol;
5 |
6 | public class SimpleProtocolImpl implements SimpleProtocol {
7 | private BufferHandler handler;
8 |
9 | @Override
10 | public void ackReceived() {
11 | }
12 |
13 | @Override
14 | public void awaitAck() {
15 | }
16 |
17 | @Override
18 | public void enableSend() {
19 | }
20 |
21 | @Override
22 | public void establishClientSession() {
23 | }
24 |
25 | public BufferHandler getHandler() {
26 | return handler;
27 | }
28 |
29 | @Override
30 | public void sendGoodbye() {
31 | }
32 |
33 | @Override
34 | public void setHandler(BufferHandler handler) {
35 | this.handler = handler;
36 | }
37 |
38 | @Override
39 | public void transmitMessage(String message) {
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/task/TaskModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.task;
17 |
18 | /**
19 | *
20 | * @author hhildebrand
21 | *
22 | */
23 | public interface TaskModel {
24 | void blockTask();
25 |
26 | void continueTask();
27 |
28 | void releaseResources();
29 |
30 | void startSliceTimer(long timeslice);
31 |
32 | void stopSliceTimer();
33 |
34 | void stopTask();
35 |
36 | void suspendTask();
37 | }
38 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/simpleProtocol/SimpleProtocol.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.simpleProtocol;
17 |
18 | /**
19 | *
20 | * @author hhildebrand
21 | *
22 | */
23 | public interface SimpleProtocol {
24 | void ackReceived();
25 |
26 | void awaitAck();
27 |
28 | void enableSend();
29 |
30 | void establishClientSession();
31 |
32 | void sendGoodbye();
33 |
34 | void setHandler(BufferHandler handler);
35 |
36 | void transmitMessage(String message);
37 | }
38 |
--------------------------------------------------------------------------------
/framework/src/main/java/com/chiralbehaviors/tron/Exit.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron;
17 |
18 | import static java.lang.annotation.ElementType.METHOD;
19 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
20 |
21 | import java.lang.annotation.Retention;
22 | import java.lang.annotation.Target;
23 |
24 | /**
25 | * The annotation indicating the method is an exit action for the enclosing
26 | * state enumeration.
27 | *
28 | * @author hhildebrand
29 | *
30 | */
31 | @Retention(value = RUNTIME)
32 | @Target(value = { METHOD })
33 | public @interface Exit {
34 | }
35 |
--------------------------------------------------------------------------------
/framework/src/main/java/com/chiralbehaviors/tron/Entry.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron;
17 |
18 | import static java.lang.annotation.ElementType.METHOD;
19 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
20 |
21 | import java.lang.annotation.Retention;
22 | import java.lang.annotation.Target;
23 |
24 | /**
25 | * The annotation indicating the method is an entry action for the enclosing
26 | * state enumeration.
27 | *
28 | * @author hhildebrand
29 | *
30 | */
31 | @Retention(value = RUNTIME)
32 | @Target(value = { METHOD })
33 | public @interface Entry {
34 | }
35 |
--------------------------------------------------------------------------------
/framework/src/main/java/com/chiralbehaviors/tron/Default.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron;
17 |
18 | import static java.lang.annotation.ElementType.METHOD;
19 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
20 |
21 | import java.lang.annotation.Retention;
22 | import java.lang.annotation.Target;
23 |
24 | /**
25 | * The annotation marking a method as the default transition for the state. The
26 | * method must take no arguments
27 | *
28 | * @author hhildebrand
29 | *
30 | */
31 | @Retention(value = RUNTIME)
32 | @Target(value = { METHOD })
33 | public @interface Default {
34 | }
35 |
--------------------------------------------------------------------------------
/framework/src/main/java/com/chiralbehaviors/tron/InvalidTransition.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron;
17 |
18 | /**
19 | * The exception indicating that the transition is invalid for the current state
20 | * of the Fsm
21 | *
22 | * @author hhildebrand
23 | *
24 | */
25 | public class InvalidTransition extends RuntimeException {
26 |
27 | private static final long serialVersionUID = 1L;
28 |
29 | public InvalidTransition(String msg) {
30 | super(msg);
31 | }
32 |
33 | public InvalidTransition(String string, Throwable e) {
34 | super(string, e);
35 | }
36 |
37 | @SuppressWarnings("unused")
38 | private InvalidTransition() {
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Tron
2 | ====
3 |
4 | A simple framework for creating sophisticated Finite State Machines
5 |
6 | ====
7 |
8 | See the [Tron wiki](https://github.com/ChiralBehaviors/Tron/wiki) for useage and design notes.
9 |
10 | Tron is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/).
11 |
12 | Tron is built using Maven 3.x and requires Java 7.
13 |
14 | To build, cd to the top level directory and do:
15 |
16 | mvn clean install
17 |
18 | For prebuilt versions of Tron, add the ChiralBehaviors cloudbees repositories to your project's pom.xml:
19 |
20 | For snapshots:
21 |
22 |
23 | chiralbehaviors-snapshots
24 | ChiralBehaviors Snapshots
25 | http://repository-chiralbehaviors.forge.cloudbees.com/snapshot/
26 |
27 | true
28 |
29 |
30 |
31 | For releases:
32 |
33 |
34 | chiralbehaviors-releases
35 | ChiralBehaviors Releases
36 | http://repository-chiralbehaviors.forge.cloudbees.com/release/
37 |
38 |
39 | The current version of Tron is 0.0.4. To use Tron in your project, add the following dependency in your project's pom.xml:
40 |
41 |
42 |
43 | com.chiralbehaviors
44 | tron
45 | 0.0.4
46 |
47 |
48 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/telephone/TelephoneFsm.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.telephone;
17 |
18 | /**
19 | *
20 | * @author hhildebrand
21 | *
22 | */
23 | public interface TelephoneFsm {
24 | TelephoneFsm clockTimer();
25 |
26 | TelephoneFsm depositMoney();
27 |
28 | TelephoneFsm dialingDone(int callType, String areaCode, String exchange,
29 | String local);
30 |
31 | TelephoneFsm digit(String digit);
32 |
33 | TelephoneFsm emergency();
34 |
35 | TelephoneFsm invalidDigit();
36 |
37 | TelephoneFsm invalidNumber();
38 |
39 | TelephoneFsm leftOfHook();
40 |
41 | TelephoneFsm lineBusy();
42 |
43 | TelephoneFsm loopTimer();
44 |
45 | TelephoneFsm nycTemp();
46 |
47 | TelephoneFsm offHook();
48 |
49 | TelephoneFsm offHookTimer();
50 |
51 | TelephoneFsm onHook();
52 |
53 | TelephoneFsm ringTimer();
54 |
55 | TelephoneFsm time();
56 | }
57 |
--------------------------------------------------------------------------------
/framework/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 |
6 | com.chiralbehaviors
7 | tron.app
8 | 0.0.5-SNAPSHOT
9 |
10 | tron
11 | Tron - A finite state machine framework
12 | A framework for creating finite state machines
13 |
14 | https://github.com/ChiralBehaviors/Tron
15 |
16 |
17 |
18 | Apache License, Version 2.0
19 | http://www.apache.org/licenses/LICENSE-2.0
20 | repo
21 |
22 |
23 |
24 |
25 | https://github.com/ChiralBehaviors/Tron.git
26 |
27 |
28 |
29 |
30 | org.slf4j
31 | slf4j-api
32 | 1.7.5
33 |
34 |
35 |
36 | org.junit.jupiter
37 | junit-jupiter
38 | 5.6.2
39 | test
40 |
41 |
42 | org.mockito
43 | mockito-all
44 | 2.0.2-beta
45 | test
46 |
47 |
48 | ch.qos.logback
49 | logback-classic
50 | 1.2.3
51 | test
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/task/TaskFsm.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.task;
17 |
18 | import com.chiralbehaviors.tron.FsmExecutor;
19 |
20 | /**
21 | *
22 | * @author hhildebrand
23 | *
24 | */
25 | public interface TaskFsm extends FsmExecutor {
26 | default TaskFsm block() {
27 | return null; // loopback transition
28 | }
29 |
30 | default TaskFsm delete() {
31 | return null; // loopback transition
32 | }
33 |
34 | default TaskFsm done() {
35 | return null; // loopback transition
36 | }
37 |
38 | default TaskFsm start(long timeslice) {
39 | return null; // loopback transition
40 | }
41 |
42 | default TaskFsm stop() {
43 | return null; // loopback transition
44 | }
45 |
46 | default TaskFsm stopped() {
47 | return null; // loopback transition
48 | }
49 |
50 | default TaskFsm suspended() {
51 | return null; // loopback transition
52 | }
53 |
54 | default TaskFsm unblock() {
55 | return null; // loopback transition
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/simpleProtocol/SimpleFsm.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.simpleProtocol;
17 |
18 | import com.chiralbehaviors.tron.FsmExecutor;
19 |
20 | /**
21 | *
22 | * @author hhildebrand
23 | *
24 | */
25 | public interface SimpleFsm extends FsmExecutor {
26 | default SimpleFsm accepted(BufferHandler buffer) {
27 | return protocolError();
28 | }
29 |
30 | default SimpleFsm closing() {
31 | return protocolError();
32 | }
33 |
34 | default SimpleFsm connected(BufferHandler buffer) {
35 | return protocolError();
36 | }
37 |
38 | default SimpleFsm protocolError() {
39 | throw fsm().invalidTransitionOn();
40 | }
41 |
42 | default SimpleFsm readError() {
43 | return protocolError();
44 | }
45 |
46 | default SimpleFsm readReady() {
47 | return protocolError();
48 | }
49 |
50 | default SimpleFsm sendGoodbye() {
51 | return protocolError();
52 | }
53 |
54 | default SimpleFsm transmitMessage(String message) {
55 | return protocolError();
56 | }
57 |
58 | default SimpleFsm writeError() {
59 | return protocolError();
60 | }
61 |
62 | default SimpleFsm writeReady() {
63 | return protocolError();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/telephone/Telephone.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.telephone;
17 |
18 | /**
19 | *
20 | * @author hhildebrand
21 | *
22 | */
23 | public interface Telephone {
24 | public enum CallType {
25 | EMERGENCY, LOCAL, LONG_DISTANCE;
26 | }
27 |
28 | String LONG_DISTANCE = null;
29 |
30 | void addDisplay(String string);
31 |
32 | void clearDisplay();
33 |
34 | String getAreaCode();
35 |
36 | String getExchange();
37 |
38 | String getLocal();
39 |
40 | int getType();
41 |
42 | void loop(String string);
43 |
44 | void playDepositMoney();
45 |
46 | void playEmergency();
47 |
48 | void playInvalidNumber();
49 |
50 | void playNYCTemp();
51 |
52 | void playTime();
53 |
54 | void playTT(int d);
55 |
56 | void resetTimer(String string);
57 |
58 | void routeCall(int callType, String areaCode, String exchange, String local);
59 |
60 | void saveAreaCode(int d);
61 |
62 | void saveExchange(int d);
63 |
64 | void saveLocal(int d);
65 |
66 | void setReceiver(String string, String string2);
67 |
68 | void setType(CallType longDistance);
69 |
70 | void startClockTimer();
71 |
72 | void startTimer(String string, int i);
73 |
74 | void stopLoop(String string);
75 |
76 | void stopPlayback();
77 |
78 | void stopTimer(String timer);
79 |
80 | void updateClock();
81 | }
82 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/simpleProtocol/stateMaps/SimpleServer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.simpleProtocol.stateMaps;
17 |
18 | import com.chiralbehaviors.tron.examples.simpleProtocol.BufferHandler;
19 | import com.chiralbehaviors.tron.examples.simpleProtocol.SimpleFsm;
20 |
21 | /**
22 | *
23 | * @author hhildebrand
24 | *
25 | */
26 | public enum SimpleServer implements SimpleFsm {
27 | ACCEPTED, AWAIT_MESSAGE, PROCESS_MESSAGE, SESSION_ESTABLISHED, ;
28 |
29 | @Override
30 | public SimpleFsm accepted(BufferHandler buffer) {
31 | // TODO Auto-generated method stub
32 | return null;
33 | }
34 |
35 | @Override
36 | public SimpleFsm closing() {
37 | // TODO Auto-generated method stub
38 | return null;
39 | }
40 |
41 | @Override
42 | public SimpleFsm connected(BufferHandler buffer) {
43 | // TODO Auto-generated method stub
44 | return null;
45 | }
46 |
47 | @Override
48 | public SimpleFsm protocolError() {
49 | // TODO Auto-generated method stub
50 | return null;
51 | }
52 |
53 | @Override
54 | public SimpleFsm readError() {
55 | // TODO Auto-generated method stub
56 | return null;
57 | }
58 |
59 | @Override
60 | public SimpleFsm readReady() {
61 | // TODO Auto-generated method stub
62 | return null;
63 | }
64 |
65 | @Override
66 | public SimpleFsm sendGoodbye() {
67 | // TODO Auto-generated method stub
68 | return null;
69 | }
70 |
71 | @Override
72 | public SimpleFsm transmitMessage(String message) {
73 | // TODO Auto-generated method stub
74 | return null;
75 | }
76 |
77 | @Override
78 | public SimpleFsm writeError() {
79 | // TODO Auto-generated method stub
80 | return null;
81 | }
82 |
83 | @Override
84 | public SimpleFsm writeReady() {
85 | // TODO Auto-generated method stub
86 | return null;
87 | }
88 | }
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/TestTask.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron;
17 |
18 | import static org.junit.jupiter.api.Assertions.assertEquals;
19 | import static org.mockito.Mockito.mock;
20 | import static org.mockito.Mockito.verify;
21 |
22 | import org.junit.jupiter.api.Test;
23 | import org.mockito.internal.verification.Times;
24 |
25 | import com.chiralbehaviors.tron.examples.task.Task;
26 | import com.chiralbehaviors.tron.examples.task.TaskFsm;
27 | import com.chiralbehaviors.tron.examples.task.TaskModel;
28 |
29 | /**
30 | *
31 | * @author hhildebrand
32 | *
33 | */
34 | public class TestTask {
35 | @Test
36 | public void testIt() {
37 | long timeslice = 100;
38 | TaskModel model = mock(TaskModel.class);
39 | Fsm fsm = Fsm.construct(model, TaskFsm.class, Task.Suspended, false);
40 | TaskFsm transitions = fsm.getTransitions();
41 | assertEquals(Task.Suspended, fsm.getCurrentState());
42 | transitions.start(timeslice);
43 | verify(model).continueTask();
44 | verify(model).startSliceTimer(timeslice);
45 | assertEquals(Task.Running, fsm.getCurrentState());
46 | transitions.suspended();
47 | assertEquals(Task.Suspended, fsm.getCurrentState());
48 | verify(model).stopSliceTimer();
49 | transitions.start(timeslice);
50 | verify(model, new Times(2)).startSliceTimer(timeslice);
51 | transitions.block();
52 | assertEquals(Task.Blocked, fsm.getCurrentState());
53 | verify(model, new Times(2)).stopSliceTimer();
54 | transitions.unblock();
55 | assertEquals(Task.Suspended, fsm.getCurrentState());
56 | transitions.start(timeslice);
57 | verify(model, new Times(3)).startSliceTimer(timeslice);
58 | transitions.stop();
59 | assertEquals(Task.Stopping, fsm.getCurrentState());
60 | verify(model, new Times(3)).stopSliceTimer();
61 | transitions.stopped();
62 | assertEquals(Task.Stopped, fsm.getCurrentState());
63 | transitions.delete();
64 | assertEquals(Task.Deleted, fsm.getCurrentState());
65 | transitions.delete();
66 | assertEquals(Task.Deleted, fsm.getCurrentState());
67 | transitions.stop();
68 | assertEquals(Task.Deleted, fsm.getCurrentState());
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/TestSimple.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron;
17 |
18 | import static org.junit.jupiter.api.Assertions.assertEquals;
19 | import static org.junit.jupiter.api.Assertions.assertNotNull;
20 |
21 | import org.junit.jupiter.api.Test;
22 |
23 | import com.chiralbehaviors.tron.examples.simpleProtocol.BufferHandler;
24 | import com.chiralbehaviors.tron.examples.simpleProtocol.SimpleFsm;
25 | import com.chiralbehaviors.tron.examples.simpleProtocol.SimpleProtocol;
26 | import com.chiralbehaviors.tron.examples.simpleProtocol.impl.SimpleProtocolImpl;
27 | import com.chiralbehaviors.tron.examples.simpleProtocol.stateMaps.Simple;
28 | import com.chiralbehaviors.tron.examples.simpleProtocol.stateMaps.SimpleClient;
29 |
30 | /**
31 | *
32 | * @author hhildebrand
33 | *
34 | */
35 | public class TestSimple {
36 | @Test
37 | public void testIt() {
38 | SimpleProtocol protocol = new SimpleProtocolImpl();
39 | Fsm fsm = Fsm.construct(protocol, SimpleFsm.class, Simple.INITIAL, true);
40 | verifyFsmStates(fsm, protocol);
41 | }
42 |
43 | @Test
44 | public void testItWithCustomClassLoader() {
45 | SimpleProtocol protocol = new SimpleProtocolImpl();
46 | Fsm fsm = Fsm.construct(protocol, SimpleFsm.class, SimpleFsm.class.getClassLoader(),
47 | Simple.INITIAL, true);
48 | verifyFsmStates(fsm, protocol);
49 | }
50 |
51 | private void verifyFsmStates(Fsm fsm, SimpleProtocol protocol) {
52 | assertNotNull(fsm);
53 | BufferHandler handler = new BufferHandler();
54 | fsm.getTransitions().connected(handler);
55 | assertEquals(handler, ((SimpleProtocolImpl) protocol).getHandler());
56 | assertEquals(SimpleClient.CONNECTED, fsm.getCurrentState());
57 | fsm.getTransitions().writeReady();
58 | assertEquals(SimpleClient.ESTABLISH_SESSION, fsm.getCurrentState());
59 | fsm.getTransitions().readReady();
60 | assertEquals(SimpleClient.SEND_MESSAGE, fsm.getCurrentState());
61 | fsm.getTransitions().sendGoodbye();
62 | assertEquals(SimpleClient.SEND_GOODBYE, fsm.getCurrentState());
63 | fsm.getTransitions().readReady();
64 | assertEquals(Simple.CLOSED, fsm.getCurrentState());
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/simpleProtocol/stateMaps/Simple.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.simpleProtocol.stateMaps;
17 |
18 | import com.chiralbehaviors.tron.examples.simpleProtocol.BufferHandler;
19 | import com.chiralbehaviors.tron.examples.simpleProtocol.SimpleFsm;
20 |
21 | /**
22 | *
23 | * @author hhildebrand
24 | *
25 | */
26 | public enum Simple implements SimpleFsm {
27 | CLOSED, CONNECTED() {
28 | @Override
29 | public SimpleFsm closing() {
30 | return CLOSED;
31 | }
32 |
33 | @Override
34 | public SimpleFsm readError() {
35 | return CLOSED;
36 | }
37 |
38 | @Override
39 | public SimpleFsm writeError() {
40 | return CLOSED;
41 | }
42 | },
43 | INITIAL() {
44 | @Override
45 | public SimpleFsm accepted(BufferHandler handler) {
46 | context().setHandler(handler);
47 | fsm().push(SimpleServer.ACCEPTED);
48 | return CONNECTED;
49 | }
50 |
51 | @Override
52 | public SimpleFsm connected(BufferHandler handler) {
53 | context().setHandler(handler);
54 | fsm().push(SimpleClient.CONNECTED);
55 | return CONNECTED;
56 | }
57 | },
58 | PROTOCOL_ERROR() {
59 |
60 | };
61 |
62 | @Override
63 | public SimpleFsm accepted(BufferHandler buffer) {
64 | return PROTOCOL_ERROR;
65 | }
66 |
67 | @Override
68 | public SimpleFsm closing() {
69 | return CLOSED;
70 | }
71 |
72 | @Override
73 | public SimpleFsm connected(BufferHandler buffer) {
74 | return PROTOCOL_ERROR;
75 | }
76 |
77 | @Override
78 | public SimpleFsm protocolError() {
79 | return PROTOCOL_ERROR;
80 | }
81 |
82 | @Override
83 | public SimpleFsm readError() {
84 | return CLOSED;
85 | }
86 |
87 | @Override
88 | public SimpleFsm readReady() {
89 | return CLOSED;
90 | }
91 |
92 | @Override
93 | public SimpleFsm sendGoodbye() {
94 | return PROTOCOL_ERROR;
95 | }
96 |
97 | @Override
98 | public SimpleFsm transmitMessage(String message) {
99 | return PROTOCOL_ERROR;
100 | }
101 |
102 | @Override
103 | public SimpleFsm writeError() {
104 | return CLOSED;
105 | }
106 |
107 | @Override
108 | public SimpleFsm writeReady() {
109 | return PROTOCOL_ERROR;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | com.chiralbehaviors
6 | tron.app
7 | 0.0.5-SNAPSHOT
8 | pom
9 | Tron - Top level app
10 | A framework for creating finite state machines
11 |
12 | UTF-8
13 |
14 |
15 | https://github.com/Hellblazer/Tron
16 |
17 |
18 |
19 | Apache License, Version 2.0
20 | http://www.apache.org/licenses/LICENSE-2.0
21 | repo
22 |
23 |
24 |
25 |
26 | https://github.com/Hellblazer/Tron.git
27 |
28 |
29 |
30 | framework
31 |
32 |
33 |
34 |
35 |
36 | ${project.groupId}
37 | tron
38 | ${project.version}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | org.apache.maven.plugins
47 | maven-compiler-plugin
48 | 3.7.0
49 |
50 | 11
51 |
52 |
53 | org.ow2.asm
54 | asm
55 | 6.2
56 |
57 |
58 |
59 |
60 |
61 | org.apache.maven.plugins
62 | maven-source-plugin
63 | 3.2.0
64 |
65 | false
66 |
67 |
68 |
69 |
70 |
71 |
73 |
74 | org.eclipse.m2e
75 | lifecycle-mapping
76 | 1.0.0
77 |
78 |
79 |
80 |
81 |
82 |
83 | org.apache.felix
84 |
85 |
86 | maven-bundle-plugin
87 |
88 |
89 | [2.4.0,)
90 |
91 |
92 | manifest
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/simpleProtocol/stateMaps/SimpleClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.simpleProtocol.stateMaps;
17 |
18 | import com.chiralbehaviors.tron.Entry;
19 | import com.chiralbehaviors.tron.examples.simpleProtocol.BufferHandler;
20 | import com.chiralbehaviors.tron.examples.simpleProtocol.SimpleFsm;
21 | import com.chiralbehaviors.tron.examples.simpleProtocol.SimpleProtocol;
22 |
23 | /**
24 | *
25 | * @author hhildebrand
26 | *
27 | */
28 | public enum SimpleClient implements SimpleFsm {
29 | ACK_MESSAGE() {
30 | @Entry
31 | public void entry() {
32 | context().ackReceived();
33 | }
34 | },
35 | AWAIT_ACK() {
36 | @Override
37 | public SimpleFsm readReady() {
38 | return ACK_MESSAGE;
39 | }
40 | },
41 | CONNECTED() {
42 | // Context injection into Entry action
43 | @Entry
44 | public void establishClientSession(SimpleProtocol context) {
45 | context.establishClientSession();
46 | }
47 |
48 | @Override
49 | public SimpleFsm writeReady() {
50 | return ESTABLISH_SESSION;
51 | }
52 | },
53 | ESTABLISH_SESSION() {
54 | @Entry
55 | public void entry() {
56 | context().awaitAck();
57 | }
58 |
59 | @Override
60 | public SimpleFsm readReady() {
61 | context().enableSend();
62 | return SEND_MESSAGE;
63 | }
64 | },
65 | MessageSent() {
66 | @Entry
67 | public void entry() {
68 | context().awaitAck();
69 | }
70 |
71 | @Override
72 | public SimpleFsm writeReady() {
73 | return AWAIT_ACK;
74 | }
75 | },
76 | SEND_GOODBYE {
77 | @Entry
78 | public void entry() {
79 | context().sendGoodbye();
80 | }
81 |
82 | @Override
83 | public SimpleFsm readReady() {
84 | SimpleFsm popTransition = fsm().pop();
85 | popTransition.closing();
86 | return null;
87 | }
88 | },
89 | SEND_MESSAGE() {
90 | @Override
91 | public SimpleFsm sendGoodbye() {
92 | return SEND_GOODBYE;
93 | }
94 |
95 | @Override
96 | public SimpleFsm transmitMessage(String message) {
97 | context().transmitMessage(message);
98 | return MessageSent;
99 | }
100 | };
101 |
102 | @Override
103 | public SimpleFsm accepted(BufferHandler buffer) {
104 | return null;
105 | }
106 |
107 | @Override
108 | public SimpleFsm closing() {
109 | SimpleFsm popTransition = fsm().pop();
110 | popTransition.closing();
111 | return null;
112 | }
113 |
114 | @Override
115 | public SimpleFsm protocolError() {
116 | SimpleFsm popTransition = fsm().pop();
117 | popTransition.protocolError();
118 | return null;
119 | }
120 |
121 | @Override
122 | public SimpleFsm sendGoodbye() {
123 | throw fsm().invalidTransitionOn();
124 | }
125 |
126 | @Override
127 | public SimpleFsm transmitMessage(String message) {
128 | throw fsm().invalidTransitionOn();
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/task/Task.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.chiralbehaviors.tron.examples.task;
18 |
19 | import com.chiralbehaviors.tron.Exit;
20 |
21 | /**
22 | *
23 | * @author hhildebrand
24 | *
25 | */
26 | public enum Task implements TaskFsm {
27 | /**
28 | * the uncompleted task is externally prevented from running again. It will stay
29 | * in this state until either stopped or unblocked.
30 | */
31 | Blocked() {
32 | /**
33 | * The task may continue working now. No actions needed.
34 | */
35 | @Override
36 | public TaskFsm unblock() {
37 | return Suspended;
38 | }
39 |
40 | },
41 | /**
42 | * the task is completely stopped and all associated resources returned. The
43 | * task may now be safely deleted. This is the FSM end state.
44 | */
45 | Deleted() {
46 |
47 | @Override
48 | public TaskFsm delete() {
49 | return null;
50 | }
51 |
52 | @Override
53 | public TaskFsm stop() {
54 | return null;
55 | }
56 | },
57 | /**
58 | * the task is actively doing work. The task is allowed to run for a specified
59 | * time limit.
60 | */
61 | Running() {
62 | @Override
63 | public TaskFsm block() {
64 | TaskModel context = context();
65 | context.blockTask();
66 | return Blocked;
67 | }
68 |
69 | @Override
70 | public TaskFsm done() {
71 | TaskModel context = context();
72 | context.releaseResources();
73 | return Stopped;
74 | }
75 |
76 | @Exit
77 | public void exit() {
78 | context().stopSliceTimer();
79 | }
80 |
81 | /**
82 | * Wait for another time slice.
83 | */
84 | @Override
85 | public TaskFsm suspended() {
86 | TaskModel context = context();
87 | context.suspendTask();
88 | return Suspended;
89 | }
90 | },
91 | /**
92 | * the task has either completed running or externally stopped.
93 | */
94 | Stopped() {
95 |
96 | @Override
97 | public TaskFsm stop() {
98 | return null;
99 | }
100 |
101 | },
102 | /**
103 | * the task is cleaning up allocated resources before entering the stop state.
104 | */
105 | Stopping {
106 |
107 | @Override
108 | public TaskFsm stop() {
109 | return null;
110 | }
111 |
112 | @Override
113 | public TaskFsm stopped() {
114 | context().releaseResources();
115 | return Stopped;
116 | }
117 |
118 | },
119 | /**
120 | * the task is waiting to run again since it is not yet completed.
121 | */
122 | Suspended() {
123 | @Override
124 | public TaskFsm block() {
125 | context().blockTask();
126 | return Blocked;
127 | }
128 |
129 | /**
130 | * Time to do more work. The timeslice duration is passed in as a transition
131 | * argument.
132 | */
133 | @Override
134 | public TaskFsm start(long timeslice) {
135 | TaskModel context = context();
136 | context.continueTask();
137 | context.startSliceTimer(timeslice);
138 | return Running;
139 | }
140 |
141 | };
142 |
143 | /**
144 | * All but the Delete state follow this transition. Define it here.
145 | */
146 | @Override
147 | public TaskFsm delete() {
148 | return Deleted;
149 | }
150 |
151 | /**
152 | * Three states follow this transition, three states ignore. So define the
153 | * active definition.
154 | */
155 | @Override
156 | public TaskFsm stop() {
157 | context().stopTask();
158 | return Stopping;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/telephone/Call.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.telephone;
17 |
18 | import com.chiralbehaviors.tron.Default;
19 | import com.chiralbehaviors.tron.Entry;
20 | import com.chiralbehaviors.tron.Exit;
21 | import com.chiralbehaviors.tron.Fsm;
22 |
23 | /**
24 | *
25 | * @author hhildebrand
26 | *
27 | */
28 | public enum Call implements TelephoneFsm {
29 | BusySignal() {
30 | @Entry
31 | public void entry() {
32 | context().loop("busy");
33 | }
34 |
35 | @Exit
36 | public void exit() {
37 | context().stopLoop("busy");
38 | }
39 | },
40 | DepositMoney() {
41 | @Entry
42 | public void entry() {
43 | Telephone context = context();
44 | context.loop("ringing");
45 | context.startTimer("RingTimer", 5000);
46 | }
47 |
48 | @Exit
49 | public void exit() {
50 | Telephone context = context();
51 | context.stopTimer("RingTimer");
52 | context.stopLoop("ringing");
53 | }
54 |
55 | @Override
56 | public TelephoneFsm ringTimer() {
57 | context().playDepositMoney();
58 | return PlayingMessage;
59 | }
60 | },
61 | /**
62 | * The number is being dialed.
63 | */
64 | Dialing() {
65 |
66 | @Override
67 | public TelephoneFsm dialingDone(int callType, String areaCode, String exchange, String local) {
68 | context().routeCall(callType, areaCode, exchange, local);
69 | return Routing;
70 | }
71 |
72 | @Override
73 | public TelephoneFsm invalidDigit() {
74 | return InvalidDigit;
75 | }
76 |
77 | @Override
78 | public TelephoneFsm leftOfHook() {
79 | return LeftOffHook;
80 | }
81 |
82 | },
83 | InvalidDigit() {
84 | @Default
85 | public TelephoneFsm defaultTransition() {
86 | return null;
87 | }
88 |
89 | @Entry
90 | public void entry() {
91 | Telephone context = context();
92 | context.startTimer("LoopTimer", 10000);
93 | context.loop("fast_busy");
94 | }
95 |
96 | @Exit
97 | public void exit() {
98 | Telephone context = context();
99 | context.stopTimer("LoopTimer");
100 | context.stopLoop("fast_busy");
101 | }
102 |
103 | @Override
104 | public TelephoneFsm loopTimer() {
105 | return WaitForOnHook;
106 | }
107 | },
108 | LeftOffHook() {
109 | @Default
110 | public TelephoneFsm defaultTransition() {
111 | return null;
112 | }
113 |
114 | @Entry
115 | public void entry() {
116 | Telephone context = context();
117 | context.startTimer("LoopTimer", 10000);
118 | context.loop("phone_off_hook");
119 | }
120 |
121 | @Exit
122 | public void exit() {
123 | Telephone context = context();
124 | context.stopTimer("LoopTimer");
125 | context.stopLoop("phone_off_hook");
126 | }
127 |
128 | @Override
129 | public TelephoneFsm loopTimer() {
130 | return WaitForOnHook;
131 | }
132 | },
133 | MessagePlayed() {
134 | @Entry
135 | public void entry() {
136 | context().startTimer("OffHookTimer", 10000);
137 | }
138 |
139 | @Exit
140 | public void exit() {
141 | context().stopTimer("OffHookTimer");
142 | }
143 |
144 | @Override
145 | public TelephoneFsm offHookTimer() {
146 | return LeftOffHook;
147 | }
148 | },
149 | NYCTemp() {
150 | @Entry
151 | public void entry() {
152 | Telephone context = context();
153 | context.loop("ringing");
154 | context.startTimer("RingTimer", 10000);
155 | }
156 |
157 | @Exit
158 | public void exit() {
159 | Telephone context = context();
160 | context.stopTimer("RingTimer");
161 | context.stopLoop("ringing");
162 | }
163 |
164 | @Override
165 | public TelephoneFsm ringTimer() {
166 | context().playNYCTemp();
167 | return PlayingMessage;
168 | }
169 | },
170 | OnHook() {
171 | /**
172 | * Time to update the clock's display.
173 | */
174 | @Override
175 | public TelephoneFsm clockTimer() {
176 | Telephone context = context();
177 | context.updateClock();
178 | context.startClockTimer();
179 | return null;
180 | }
181 |
182 | @Entry
183 | public void entry() {
184 | Telephone context = context();
185 | context.updateClock();
186 | context.startClockTimer();
187 | }
188 |
189 | @Exit
190 | public void exit() {
191 | context().stopTimer("ClockTimer");
192 | }
193 |
194 | /**
195 | * We are handling the caller's side of the connection.
196 | */
197 | @Override
198 | public TelephoneFsm offHook() {
199 | fsm().push(PhoneNumber.DialTone);
200 | Telephone context = context();
201 | context.clearDisplay();
202 | context.setReceiver("on hook", "Put down receiver");
203 | return Dialing;
204 | }
205 |
206 | /**
207 | * Oops.
208 | */
209 | @Override
210 | public TelephoneFsm onHook() {
211 | return null;
212 | }
213 | },
214 | PlayingMessage() {
215 | @Override
216 | public TelephoneFsm onHook() {
217 | Telephone context = context();
218 | context.stopPlayback();
219 | context.setReceiver("off hook", "Pick up receiver");
220 | context.clearDisplay();
221 | return OnHook;
222 | }
223 | },
224 | /**
225 | * The call is now being routed.
226 | */
227 | Routing() {
228 | @Override
229 | public TelephoneFsm depositMoney() {
230 | return DepositMoney;
231 | }
232 |
233 | @Override
234 | public TelephoneFsm emergency() {
235 | context().playEmergency();
236 | return PlayingMessage;
237 | }
238 |
239 | @Override
240 | public TelephoneFsm invalidNumber() {
241 | context().playInvalidNumber();
242 | return PlayingMessage;
243 | }
244 |
245 | @Override
246 | public TelephoneFsm lineBusy() {
247 | return BusySignal;
248 | }
249 |
250 | @Override
251 | public TelephoneFsm nycTemp() {
252 | return NYCTemp;
253 | }
254 |
255 | @Override
256 | public TelephoneFsm time() {
257 | return Time;
258 | }
259 | },
260 | Time() {
261 | @Entry
262 | public void entry() {
263 | Telephone context = context();
264 | context.loop("ringing");
265 | context.startTimer("RingTimer", 10000);
266 | }
267 |
268 | @Exit
269 | public void exit() {
270 | Telephone context = context();
271 | context.stopTimer("RingTimer");
272 | context.stopLoop("ringing");
273 | }
274 |
275 | @Override
276 | public TelephoneFsm ringTimer() {
277 | context().playTime();
278 | return PlayingMessage;
279 | }
280 | },
281 | WaitForOnHook() {
282 | @Default
283 | public TelephoneFsm defaultTransition() {
284 | return null;
285 | }
286 | };
287 |
288 | private static Telephone context() {
289 | Telephone context = Fsm.thisContext();
290 | return context;
291 | }
292 |
293 | private static Fsm fsm() {
294 | Fsm fsm = Fsm.thisFsm();
295 | return fsm;
296 | }
297 |
298 | @Override
299 | public TelephoneFsm clockTimer() {
300 | return null;
301 | }
302 |
303 | @Override
304 | public TelephoneFsm depositMoney() {
305 | throw fsm().invalidTransitionOn();
306 | }
307 |
308 | @Override
309 | public TelephoneFsm dialingDone(int callType, String areaCode, String exchange, String local) {
310 | throw null;
311 | }
312 |
313 | @Override
314 | public TelephoneFsm digit(String digit) {
315 | return null;
316 | }
317 |
318 | @Override
319 | public TelephoneFsm emergency() {
320 | throw fsm().invalidTransitionOn();
321 | }
322 |
323 | @Override
324 | public TelephoneFsm invalidDigit() {
325 | throw null;
326 | }
327 |
328 | @Override
329 | public TelephoneFsm invalidNumber() {
330 | throw fsm().invalidTransitionOn();
331 | }
332 |
333 | @Override
334 | public TelephoneFsm leftOfHook() {
335 | throw fsm().invalidTransitionOn();
336 | }
337 |
338 | @Override
339 | public TelephoneFsm lineBusy() {
340 | throw fsm().invalidTransitionOn();
341 | }
342 |
343 | @Override
344 | public TelephoneFsm loopTimer() {
345 | throw fsm().invalidTransitionOn();
346 | }
347 |
348 | @Override
349 | public TelephoneFsm nycTemp() {
350 | throw fsm().invalidTransitionOn();
351 | }
352 |
353 | @Override
354 | public TelephoneFsm offHook() {
355 | throw fsm().invalidTransitionOn();
356 | }
357 |
358 | @Override
359 | public TelephoneFsm offHookTimer() {
360 | throw fsm().invalidTransitionOn();
361 | }
362 |
363 | /**
364 | * No matter when it happens, when the phone is hung up, this call is OVER!
365 | */
366 | @Override
367 | public TelephoneFsm onHook() {
368 | Telephone context = context();
369 | context.setReceiver("off hook", "Pick up receiver");
370 | context.clearDisplay();
371 | return null;
372 | }
373 |
374 | @Override
375 | public TelephoneFsm ringTimer() {
376 | throw fsm().invalidTransitionOn();
377 | }
378 |
379 | @Override
380 | public TelephoneFsm time() {
381 | throw fsm().invalidTransitionOn();
382 | }
383 | }
384 |
--------------------------------------------------------------------------------
/framework/src/test/java/com/chiralbehaviors/tron/examples/telephone/PhoneNumber.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron.examples.telephone;
17 |
18 | import com.chiralbehaviors.tron.Entry;
19 | import com.chiralbehaviors.tron.Exit;
20 | import com.chiralbehaviors.tron.Fsm;
21 |
22 | /**
23 | *
24 | * @author hhildebrand
25 | *
26 | */
27 | public enum PhoneNumber implements TelephoneFsm {
28 | DialTone() {
29 | /**
30 | * If an invalid digit is dialed, give up collecting digits immediately.
31 | */
32 | @Override
33 | public TelephoneFsm digit(String digit) {
34 | int d = Integer.parseInt(digit);
35 | if (d < 0 || d > 9) {
36 | fsm().pop().invalidDigit();
37 | context().clearDisplay();
38 | return null;
39 | } else if (d == 1) {
40 | Telephone context = context();
41 | context.playTT(d);
42 | context.setType(Telephone.CallType.LONG_DISTANCE);
43 | context.saveAreaCode(d);
44 | context.addDisplay("-");
45 | return LongDistance;
46 | } else if (d == 9) {
47 | Telephone context = context();
48 | context.playTT(d);
49 | context.saveExchange(d);
50 | return OneOneStart;
51 | } else {
52 | Telephone context = context();
53 | context.playTT(d);
54 | context.setType(Telephone.CallType.LOCAL);
55 | context.saveExchange(d);
56 | return Exchange;
57 | }
58 | }
59 |
60 | @Entry
61 | public void entry() {
62 | Telephone context = context();
63 | context.loop("dialtone");
64 | context.startTimer("OffHookTimer", 10000);
65 | }
66 |
67 | @Exit
68 | public void exit() {
69 | Telephone context = context();
70 | context.stopTimer("OffHookTimer");
71 | context.stopLoop("dialtone");
72 | }
73 | },
74 | Exchange() {
75 | @Override
76 | public TelephoneFsm digit(String digit) {
77 | int d = Integer.parseInt(digit);
78 | Telephone context = context();
79 | if (d < 0 || d > 9) {
80 | fsm().pop().invalidDigit();
81 | context().clearDisplay();
82 | return null;
83 | } else if (context.getExchange().length() < 2) {
84 | context.playTT(d);
85 | context.saveExchange(d);
86 | context.resetTimer("OffHookTimer");
87 | return null;
88 | } else {
89 | context.playTT(d);
90 | context.saveExchange(d);
91 | context.addDisplay("-");
92 | return LocalCall;
93 | }
94 | }
95 |
96 | @Entry
97 | public void entry() {
98 | context().startTimer("OffHookTimer", 10000);
99 | }
100 |
101 | @Exit
102 | public void exit() {
103 | context().stopTimer("OffHookTimer");
104 | }
105 | },
106 | LocalCall() {
107 | @Override
108 | public TelephoneFsm digit(String digit) {
109 | int d = Integer.parseInt(digit);
110 | Telephone context = context();
111 | if (d < 0 || d > 9) {
112 | fsm().pop().invalidDigit();
113 | context().clearDisplay();
114 | return null;
115 | } else if (context.getLocal().length() < 3) {
116 | context.playTT(d);
117 | context.saveLocal(d);
118 | context.resetTimer("OffHookTimer");
119 | return null;
120 | } else {
121 | fsm().pop()
122 | .dialingDone(context.getType(), context.getAreaCode(), context.getExchange(), context.getLocal());
123 | context.playTT(d);
124 | context.saveLocal(d);
125 | return null;
126 | }
127 | }
128 |
129 | @Entry
130 | public void entry() {
131 | context().startTimer("OffHookTimer", 10000);
132 | }
133 |
134 | @Exit
135 | public void exit() {
136 | context().stopTimer("OffHookTimer");
137 | }
138 | },
139 | LongDistance() {
140 | @Override
141 | public TelephoneFsm digit(String digit) {
142 | int d = Integer.parseInt(digit);
143 | Telephone context = context();
144 | if (d < 0 || d > 9) {
145 | fsm().pop().invalidDigit();
146 | context().clearDisplay();
147 | return null;
148 | } else if (context.getAreaCode().length() < 3) {
149 | context.playTT(d);
150 | context.saveAreaCode(d);
151 | context.resetTimer("OffHookTimer");
152 | return null;
153 | } else {
154 | context.playTT(d);
155 | context.saveAreaCode(d);
156 | context.addDisplay("-");
157 | return Exchange;
158 | }
159 | }
160 |
161 | @Entry
162 | public void entry() {
163 | context().startTimer("OffHookTimer", 10000);
164 | }
165 |
166 | @Exit
167 | public void exit() {
168 | context().stopTimer("OffHookTimer");
169 | }
170 | },
171 | NineOne() {
172 | @Override
173 | public TelephoneFsm digit(String digit) {
174 | int d = Integer.parseInt(digit);
175 | Telephone context = context();
176 | if (d < 0 || d > 9) {
177 | fsm().pop().invalidDigit();
178 | context().clearDisplay();
179 | return null;
180 | } else if (d == 1) {
181 | fsm().pop()
182 | .dialingDone(context.getType(), context.getAreaCode(), context.getExchange(), context.getLocal());
183 | context.playTT(d);
184 | context.setType(Telephone.CallType.EMERGENCY);
185 | context.saveExchange(d);
186 | return null;
187 | } else {
188 | context.playTT(d);
189 | context.setType(Telephone.CallType.LOCAL);
190 | context.saveExchange(d);
191 | context.addDisplay("-");
192 | return LocalCall;
193 | }
194 | }
195 |
196 | @Entry
197 | public void entry() {
198 | context().startTimer("OffHookTimer", 10000);
199 | }
200 |
201 | @Exit
202 | public void exit() {
203 | context().stopTimer("OffHookTimer");
204 | }
205 | },
206 | OneOneStart() {
207 | @Override
208 | public TelephoneFsm digit(String digit) {
209 | int d = Integer.parseInt(digit);
210 | Telephone context = context();
211 | if (d < 0 || d > 9) {
212 | fsm().pop().invalidDigit();
213 | context().clearDisplay();
214 | return null;
215 | } else if (d == 1) {
216 | context.playTT(d);
217 | context.saveExchange(d);
218 | return NineOne;
219 | } else {
220 | context.playTT(d);
221 | context.setType(Telephone.CallType.LOCAL);
222 | context.saveExchange(d);
223 | return Exchange;
224 | }
225 | }
226 |
227 | @Entry
228 | public void entry() {
229 | context().startTimer("OffHookTimer", 10000);
230 | }
231 |
232 | @Exit
233 | public void exit() {
234 | context().stopTimer("OffHookTimer");
235 | }
236 | };
237 |
238 | private static Telephone context() {
239 | Telephone context = Fsm.thisContext();
240 | return context;
241 | }
242 |
243 | private static Fsm fsm() {
244 | Fsm fsm = Fsm.thisFsm();
245 | return fsm;
246 | }
247 |
248 | @Override
249 | public TelephoneFsm clockTimer() {
250 | return null;
251 | }
252 |
253 | @Override
254 | public TelephoneFsm depositMoney() {
255 | throw fsm().invalidTransitionOn();
256 | }
257 |
258 | @Override
259 | public TelephoneFsm dialingDone(int callType, String areaCode, String exchange, String local) {
260 | throw fsm().invalidTransitionOn();
261 | }
262 |
263 | @Override
264 | public TelephoneFsm digit(String digit) {
265 | throw fsm().invalidTransitionOn();
266 | }
267 |
268 | @Override
269 | public TelephoneFsm emergency() {
270 | throw fsm().invalidTransitionOn();
271 | }
272 |
273 | @Override
274 | public TelephoneFsm invalidDigit() {
275 | fsm().pop().invalidDigit();
276 | context().clearDisplay();
277 | return null;
278 | }
279 |
280 | @Override
281 | public TelephoneFsm invalidNumber() {
282 | throw fsm().invalidTransitionOn();
283 | }
284 |
285 | @Override
286 | public TelephoneFsm leftOfHook() {
287 | throw fsm().invalidTransitionOn();
288 | }
289 |
290 | @Override
291 | public TelephoneFsm lineBusy() {
292 | throw fsm().invalidTransitionOn();
293 | }
294 |
295 | @Override
296 | public TelephoneFsm loopTimer() {
297 | throw fsm().invalidTransitionOn();
298 | }
299 |
300 | @Override
301 | public TelephoneFsm nycTemp() {
302 | throw fsm().invalidTransitionOn();
303 | }
304 |
305 | @Override
306 | public TelephoneFsm offHook() {
307 | throw fsm().invalidTransitionOn();
308 | }
309 |
310 | @Override
311 | public TelephoneFsm offHookTimer() {
312 | fsm().pop().leftOfHook();
313 | context().clearDisplay();
314 | return null;
315 | }
316 |
317 | @Override
318 | public TelephoneFsm onHook() {
319 | fsm().pop().onHook();
320 | context().clearDisplay();
321 | return null;
322 | }
323 |
324 | @Override
325 | public TelephoneFsm ringTimer() {
326 | throw fsm().invalidTransitionOn();
327 | }
328 |
329 | @Override
330 | public TelephoneFsm time() {
331 | throw fsm().invalidTransitionOn();
332 | }
333 | }
334 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/framework/src/main/java/com/chiralbehaviors/tron/Fsm.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 ChiralBehaviors LLC, all rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.chiralbehaviors.tron;
17 |
18 | import java.lang.reflect.InvocationHandler;
19 | import java.lang.reflect.InvocationTargetException;
20 | import java.lang.reflect.Method;
21 | import java.lang.reflect.Proxy;
22 | import java.util.ArrayDeque;
23 | import java.util.Deque;
24 | import java.util.concurrent.Callable;
25 | import java.util.concurrent.locks.Lock;
26 | import java.util.concurrent.locks.ReentrantLock;
27 |
28 | import org.slf4j.Logger;
29 | import org.slf4j.LoggerFactory;
30 |
31 | /**
32 | * A Finite State Machine implementation.
33 | *
34 | * @author hhildebrand
35 | *
36 | * @param the transition interface
37 | * @param the fsm context interface
38 | */
39 | public final class Fsm {
40 | private static class State {
41 | private final Context context;
42 | private final Transitions transitions;
43 |
44 | public State(Context context, Transitions transitions) {
45 | this.context = context;
46 | this.transitions = transitions;
47 | }
48 |
49 | }
50 |
51 | private static class PendingTransition implements InvocationHandler {
52 | private volatile Object[] args;
53 | private volatile Method method;
54 |
55 | @Override
56 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
57 | if (this.method != null) {
58 | throw new IllegalStateException(
59 | String.format("Pop transition '%s' has already been established", method.toGenericString()));
60 | }
61 | this.method = method;
62 | this.args = args;
63 | return null;
64 | }
65 | }
66 |
67 | private static final Logger DEFAULT_LOG = LoggerFactory.getLogger(Fsm.class);
68 | private static final ThreadLocal> thisFsm = new ThreadLocal<>();
69 |
70 | /**
71 | * Construct a new instance of a finite state machine.
72 | *
73 | * @param fsmContext - the object used as the action context for this FSM
74 | * @param transitions - the interface class used to define the transitions for
75 | * this FSM
76 | * @param transitionsCL - the class loader to be used to load the transitions
77 | * interface class
78 | * @param initialState - the initial state of the FSM
79 | * @param sync - true if this FSM is to synchronize state transitions.
80 | * This is required for multi-threaded use of the FSM
81 | * @return the Fsm instance
82 | */
83 | public static Fsm construct(Context fsmContext,
84 | Class transitions,
85 | ClassLoader transitionsCL,
86 | Enum> initialState, boolean sync) {
87 | if (!transitions.isAssignableFrom(initialState.getClass())) {
88 | throw new IllegalArgumentException(
89 | String.format("Supplied initial state '%s' does not implement the transitions interface '%s'",
90 | initialState, transitions));
91 | }
92 | Fsm fsm = new Fsm<>(fsmContext, sync, transitions, transitionsCL);
93 | @SuppressWarnings("unchecked")
94 | Transitions initial = (Transitions) initialState;
95 | fsm.current = initial;
96 | return fsm;
97 | }
98 |
99 | /**
100 | * Construct a new instance of a finite state machine with a default
101 | * ClassLoader.
102 | */
103 | public static Fsm construct(Context fsmContext,
104 | Class transitions,
105 | Enum> initialState, boolean sync) {
106 | return construct(fsmContext, transitions, fsmContext.getClass().getClassLoader(), initialState, sync);
107 | }
108 |
109 | /**
110 | *
111 | * @return the Context of the currently executing Fsm
112 | */
113 | public static Context thisContext() {
114 | @SuppressWarnings("unchecked")
115 | Fsm fsm = (Fsm) thisFsm.get();
116 | return fsm.getContext();
117 | }
118 |
119 | /**
120 | *
121 | * @return the currrently executing Fsm
122 | */
123 | public static Fsm thisFsm() {
124 | @SuppressWarnings("unchecked")
125 | Fsm fsm = (Fsm) thisFsm.get();
126 | return fsm;
127 | }
128 |
129 | private Context context;
130 | private Transitions current;
131 | private Logger log;
132 | private String name = "";
133 | private boolean pendingPop = false;
134 | private State pendingPush;
135 | private PendingTransition popTransition;
136 | private Transitions previous;
137 | private final Transitions proxy;
138 | private PendingTransition pushTransition;
139 | private final Deque> stack = new ArrayDeque<>();
140 | private final Lock sync;
141 | private String transition;
142 | private final Class transitionsType;
143 |
144 | Fsm(Context context, boolean sync, Class transitionsType, ClassLoader transitionsCL) {
145 | this.setContext(context);
146 | this.sync = sync ? new ReentrantLock() : null;
147 | this.transitionsType = transitionsType;
148 | this.log = DEFAULT_LOG;
149 | @SuppressWarnings("unchecked")
150 | Transitions facade = (Transitions) Proxy.newProxyInstance(transitionsCL, new Class>[] { transitionsType },
151 | transitionsHandler());
152 | proxy = facade;
153 | }
154 |
155 | /**
156 | * Execute the initial state's entry action. Note that we do not guard against
157 | * multiple invocations.
158 | */
159 | public void enterStartState() {
160 | if (log.isTraceEnabled()) {
161 | log.trace(String.format("[%s] Entering start state %s", name, prettyPrint(current)));
162 | }
163 | executeEntryAction();
164 | }
165 |
166 | /**
167 | *
168 | * @return the action context object of this Fsm
169 | */
170 | public Context getContext() {
171 | return context;
172 | }
173 |
174 | /**
175 | *
176 | * @return the current state of the Fsm
177 | */
178 | public Transitions getCurrentState() {
179 | return locked(() -> {
180 | Transitions transitions = current;
181 | return transitions;
182 | });
183 | }
184 |
185 | /**
186 | *
187 | * @return the logger used by this Fsm
188 | */
189 | public Logger getLog() {
190 | return locked(() -> {
191 | Logger c = log;
192 | return c;
193 | });
194 | }
195 |
196 | public String getName() {
197 | return name;
198 | }
199 |
200 | /**
201 | *
202 | * @return the previous state of the Fsm, or null if no previous state
203 | */
204 | public Transitions getPreviousState() {
205 | return locked(() -> {
206 | Transitions transitions = previous;
207 | return transitions;
208 | });
209 | }
210 |
211 | /**
212 | *
213 | * @return the String representation of the current transition
214 | */
215 | public String getTransition() {
216 | return locked(() -> {
217 | return transition;
218 | });
219 | }
220 |
221 | /**
222 | *
223 | * @return the Transitions object that drives this Fsm through its transitions
224 | */
225 | public Transitions getTransitions() {
226 | return proxy;
227 | }
228 |
229 | /**
230 | * @return the invalid transition excepiton based current transition attempt
231 | */
232 | public InvalidTransition invalidTransitionOn() {
233 | return new InvalidTransition(String.format("[%s] %s.%s", name, prettyPrint(current), transition));
234 | }
235 |
236 | /**
237 | * Pop the state off of the stack of pushed states. This state will become the
238 | * current state of the Fsm. Answer the Transitions object that may be used to
239 | * send a transition to the popped state.
240 | *
241 | * @return the Transitions object that may be used to send a transition to the
242 | * popped state.
243 | */
244 | public Transitions pop() {
245 | if (pendingPop) {
246 | throw new IllegalStateException(String.format("[%s] State has already been popped", name));
247 | }
248 | if (pendingPush != null) {
249 | throw new IllegalStateException(String.format("[%s] Cannot pop after pushing", name));
250 | }
251 | if (stack.size() == 0) {
252 | throw new IllegalStateException(
253 | String.format("[%s] State stack is empty, current state: %s, transition: %s", name,
254 | prettyPrint(current), transition));
255 | }
256 | pendingPop = true;
257 | popTransition = new PendingTransition();
258 | @SuppressWarnings("unchecked")
259 | Transitions pendingTransition = (Transitions) Proxy.newProxyInstance(getContext().getClass().getClassLoader(),
260 | new Class>[] { transitionsType },
261 | popTransition);
262 | return pendingTransition;
263 | }
264 |
265 | public String prettyPrint(Transitions state) {
266 | if (state == null) {
267 | return "null";
268 | }
269 | Class> enclosingClass = state.getClass().getEnclosingClass();
270 | return String.format("%s.%s", (enclosingClass != null ? enclosingClass : state.getClass()).getSimpleName(),
271 | ((Enum>) state).name());
272 | }
273 |
274 | /**
275 | * Push the current state of the Fsm on the state stack. The supplied state
276 | * becomes the current state of the Fsm
277 | *
278 | * @param state - the new current state of the Fsm.
279 | */
280 | public Transitions push(Transitions state) {
281 | return push(state, context);
282 | }
283 |
284 | /**
285 | * Push the current state of the Fsm on the state stack. The supplied state
286 | * becomes the current state of the Fsm
287 | *
288 | * @param state - the new current state of the Fsm.
289 | * @param context - the new current context of the FSM
290 | */
291 | public Transitions push(Transitions state, Context context) {
292 | if (state == null) {
293 | throw new IllegalStateException(String.format("[%s] Cannot push a null state", name));
294 | }
295 | if (pendingPush != null) {
296 | throw new IllegalStateException(String.format("[%s] Cannot push state twice", name));
297 | }
298 | if (pendingPop) {
299 | throw new IllegalStateException(String.format("[%s] Cannot push after pop", name));
300 | }
301 | pushTransition = new PendingTransition();
302 | pendingPush = new State<>(context, state);
303 | @SuppressWarnings("unchecked")
304 | Transitions pendingTransition = (Transitions) Proxy.newProxyInstance(getContext().getClass().getClassLoader(),
305 | new Class>[] { transitionsType },
306 | pushTransition);
307 | return pendingTransition;
308 | }
309 |
310 | /**
311 | * Set the Context of the FSM
312 | */
313 | public void setContext(Context context) {
314 | this.context = context;
315 | }
316 |
317 | /**
318 | * Set the Logger for this Fsm.
319 | *
320 | * @param log - the Logger of this Fsm
321 | */
322 | public void setLog(Logger log) {
323 | this.log = log;
324 | }
325 |
326 | public void setName(String name) {
327 | this.name = name;
328 | }
329 |
330 | public R synchonizeOnState(Callable call) throws Exception {
331 | return locked(call);
332 | }
333 |
334 | public void synchonizeOnState(Runnable call) {
335 | locked(() -> {
336 | call.run();
337 | return null;
338 | });
339 | }
340 |
341 | @Override
342 | public String toString() {
343 | return String.format("Fsm [name = %s, current=%s, previous=%s, transition=%s]", name, prettyPrint(current),
344 | prettyPrint(previous), getTransition());
345 | }
346 |
347 | private void executeEntryAction() {
348 | for (Method action : current.getClass().getDeclaredMethods()) {
349 | if (action.isAnnotationPresent(Entry.class)) {
350 | action.setAccessible(true);
351 | if (log.isTraceEnabled()) {
352 | log.trace(String.format("[%s] Entry action: %s.%s", name, prettyPrint(current),
353 | prettyPrint(action)));
354 | }
355 | try {
356 | // For entry actions with parameters, inject the context
357 | if (action.getParameterTypes().length > 0)
358 | action.invoke(current, getContext());
359 | else
360 | action.invoke(current, new Object[] {});
361 | return;
362 | } catch (IllegalAccessException | IllegalArgumentException e) {
363 | throw new IllegalStateException(e);
364 | } catch (InvocationTargetException e) {
365 | Throwable targetException = e.getTargetException();
366 | if (targetException instanceof RuntimeException) {
367 | throw (RuntimeException) targetException;
368 | }
369 | throw new IllegalStateException(targetException);
370 | }
371 | }
372 | }
373 | }
374 |
375 | private void executeExitAction() {
376 | for (Method action : current.getClass().getDeclaredMethods()) {
377 | if (action.isAnnotationPresent(Exit.class)) {
378 | action.setAccessible(true);
379 | if (log.isTraceEnabled()) {
380 | log.trace(String.format("[%s] Exit action: %s.%s", name, prettyPrint(current),
381 | prettyPrint(action)));
382 | }
383 | try {
384 | // For exit action with parameters, inject the context
385 | if (action.getParameterTypes().length > 0)
386 | action.invoke(current, getContext());
387 | else
388 | action.invoke(current, new Object[] {});
389 | return;
390 | } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
391 | throw new IllegalStateException(e);
392 | }
393 | }
394 | }
395 | }
396 |
397 | /**
398 | * The Jesus Nut
399 | *
400 | * @param t - the transition to fire
401 | * @param arguments - the transition arguments
402 | * @return
403 | */
404 | private Object fire(Method t, Object[] arguments) {
405 | if (t == null) {
406 | return null;
407 | }
408 | Fsm, ?> previousFsm = thisFsm.get();
409 | thisFsm.set(this);
410 | previous = current;
411 | if (!transitionsType.isAssignableFrom(t.getReturnType())) {
412 | try {
413 | return t.invoke(current, arguments);
414 | } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
415 | throw new IllegalStateException(e);
416 | }
417 | }
418 |
419 | try {
420 | transition = prettyPrint(t);
421 | Transitions nextState;
422 | Transitions pinned = current;
423 | try {
424 | nextState = fireTransition(lookupTransition(t), arguments);
425 | } catch (InvalidTransition e) {
426 | nextState = fireTransition(lookupDefaultTransition(e, t), arguments);
427 | }
428 | if (pinned == current) {
429 | transitionTo(nextState);
430 | } else {
431 | if (nextState != null && log.isTraceEnabled()) {
432 | log.trace(String.format("[%s] Eliding Transition %s -> %s, pinned state: %s", name,
433 | prettyPrint(current), prettyPrint(nextState), prettyPrint(pinned)));
434 | }
435 | }
436 | return null;
437 | } finally {
438 | thisFsm.set(previousFsm);
439 | }
440 | }
441 |
442 | /**
443 | * Fire the concrete transition of the current state
444 | *
445 | * @param stateTransition - the transition method to execute
446 | * @param arguments - the arguments of the method
447 | *
448 | * @return the next state
449 | */
450 | @SuppressWarnings("unchecked")
451 | private Transitions fireTransition(Method stateTransition, Object[] arguments) {
452 | if (stateTransition.isAnnotationPresent(Default.class)) {
453 | if (log.isTraceEnabled()) {
454 | log.trace(String.format("[%s] Default transition: %s.%s", prettyPrint(current)), getTransition(), name);
455 | }
456 | try {
457 | return (Transitions) stateTransition.invoke(current, (Object[]) null);
458 | } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
459 | throw new IllegalStateException(String.format("Unable to invoke transition %s,%s", prettyPrint(current),
460 | prettyPrint(stateTransition)),
461 | e);
462 | }
463 | }
464 | if (log.isTraceEnabled()) {
465 | log.trace(String.format("[%s] Transition: %s.%s", name, prettyPrint(current), getTransition()));
466 | }
467 | try {
468 | return (Transitions) stateTransition.invoke(current, arguments);
469 | } catch (IllegalAccessException | IllegalArgumentException e) {
470 | throw new IllegalStateException(String.format("Unable to invoke transition %s.%s", prettyPrint(current),
471 | prettyPrint(stateTransition)),
472 | e.getCause());
473 | } catch (InvocationTargetException e) {
474 | if (e.getTargetException() instanceof InvalidTransition) {
475 | if (log.isTraceEnabled()) {
476 | log.trace(String.format("[%s] Invalid transition %s.%s", name, prettyPrint(current),
477 | getTransition()));
478 | }
479 | throw (InvalidTransition) e.getTargetException();
480 | }
481 | if (e.getTargetException() instanceof RuntimeException) {
482 | throw (RuntimeException) e.getTargetException();
483 | }
484 | throw new IllegalStateException(String.format("[%s] Unable to invoke transition %s.%s", name,
485 | prettyPrint(current), prettyPrint(stateTransition)),
486 | e.getTargetException());
487 | }
488 | }
489 |
490 | private T locked(Callable call) {
491 | final Lock lock = sync;
492 | if (lock != null) {
493 | lock.lock();
494 | }
495 | try {
496 | return call.call();
497 | } catch (RuntimeException e) {
498 | throw e;
499 | } catch (Exception e) {
500 | throw new IllegalStateException(e);
501 | } finally {
502 | if (lock != null) {
503 | lock.unlock();
504 | }
505 | }
506 | }
507 |
508 | private Method lookupDefaultTransition(InvalidTransition previousException, Method t) {
509 | // look for a @Default transition for the state singleton
510 | for (Method defaultTransition : current.getClass().getDeclaredMethods()) {
511 | if (defaultTransition.isAnnotationPresent(Default.class)) {
512 | defaultTransition.setAccessible(true);
513 | return defaultTransition;
514 | }
515 | }
516 | // look for a @Default transition for the state on the enclosing enum class
517 | for (Method defaultTransition : current.getClass().getMethods()) {
518 | if (defaultTransition.isAnnotationPresent(Default.class)) {
519 | defaultTransition.setAccessible(true);
520 | return defaultTransition;
521 | }
522 | }
523 | if (previousException == null) {
524 | throw new InvalidTransition(String.format(prettyPrint(t)));
525 | } else {
526 | throw previousException;
527 | }
528 | }
529 |
530 | /**
531 | * Lookup the transition.
532 | *
533 | * @param t - the transition defined in the interface
534 | * @return the transition Method for the current state matching the interface
535 | * definition
536 | */
537 | private Method lookupTransition(Method t) {
538 | Method stateTransition = null;
539 | try {
540 | // First we try declared methods on the state
541 | stateTransition = current.getClass().getMethod(t.getName(), t.getParameterTypes());
542 | } catch (NoSuchMethodException | SecurityException e1) {
543 | throw new IllegalStateException(
544 | String.format("Inconcievable! The state %s does not implement the transition %s",
545 | prettyPrint(current), prettyPrint(t)));
546 | }
547 | stateTransition.setAccessible(true);
548 | return stateTransition;
549 | }
550 |
551 | /**
552 | * Ye olde tyme state transition
553 | *
554 | * @param nextState - the next state of the Fsm
555 | */
556 | private void normalTransition(Transitions nextState) {
557 | if (nextState == null) { // internal loopback transition
558 | if (log.isTraceEnabled()) {
559 | log.trace(String.format("[%s] Internal loopback: %s", name, prettyPrint(current)));
560 | }
561 | return;
562 | }
563 | executeExitAction();
564 | if (log.isTraceEnabled()) {
565 | log.trace(String.format("[%s] State transition: %s -> %s", name, prettyPrint(current),
566 | prettyPrint(nextState)));
567 | }
568 | current = nextState;
569 | executeEntryAction();
570 | }
571 |
572 | /**
573 | * Execute the exit action of the current state. Set current state to popped
574 | * state of the stack. Execute any pending transition on the current state.
575 | */
576 | private void popTransition() {
577 | pendingPop = false;
578 | previous = current;
579 | State pop = stack.pop();
580 | PendingTransition pendingTransition = popTransition;
581 | popTransition = null;
582 |
583 | executeExitAction();
584 | if (log.isTraceEnabled()) {
585 | log.trace(String.format("[%s] State transition: %s -> %s - Popping(%s)", name, prettyPrint(previous),
586 | prettyPrint(pop), stack.size() + 1));
587 | }
588 | current = pop.transitions;
589 | if (pop.context != null) {
590 | setContext(pop.context);
591 | }
592 | if (pendingTransition != null) {
593 | if (log.isTraceEnabled()) {
594 | log.trace(String.format("[%s] Pop transition: %s.%s", name, prettyPrint(current),
595 | prettyPrint(pendingTransition.method)));
596 | }
597 | fire(pendingTransition.method, pendingTransition.args);
598 | }
599 | }
600 |
601 | private String prettyPrint(State state) {
602 | return prettyPrint(state.transitions) + " [" + state.context == null ? "<>: " + getContext()
603 | : state.context + "]";
604 | }
605 |
606 | private String prettyPrint(Method transition) {
607 | StringBuilder builder = new StringBuilder();
608 | if (transition != null) {
609 | builder.append(transition.getName());
610 | builder.append('(');
611 | Class>[] parameters = transition.getParameterTypes();
612 | for (int i = 0; i < parameters.length; i++) {
613 | builder.append(parameters[i].getSimpleName());
614 | if (i != parameters.length - 1) {
615 | builder.append(", ");
616 | }
617 | }
618 | builder.append(')');
619 | } else {
620 | builder.append("loopback");
621 | }
622 | return builder.toString();
623 | }
624 |
625 | /**
626 | * Push the current state of the Fsm to the stack, with the supplied context as
627 | * the new current context of the FSM, if non null. Transition the Fsm to the
628 | * nextState, execute the entry action of that state. Set the current state of
629 | * the Fsm to the pending push state, executing the entry action on that state
630 | *
631 | * @param nextState
632 | */
633 | private void pushTransition(Transitions nextState) {
634 | State pushed = pendingPush;
635 | pendingPush = null;
636 | normalTransition(nextState);
637 | stack.push(new State<>(context, current));
638 | if (log.isTraceEnabled()) {
639 | log.trace(String.format("[%s] State transition: %s -> %s - Pushing(%s)", name, prettyPrint(current),
640 | prettyPrint(pushed), stack.size()));
641 | }
642 | current = pushed.transitions;
643 | if (pushed.context != null) {
644 | setContext(pushed.context);
645 | }
646 | Transitions pinned = current;
647 | PendingTransition pushTrns = pushTransition;
648 | pushTransition = null;
649 | executeEntryAction();
650 | if (pushTrns != null) {
651 | if (current != pinned) {
652 | log.trace(String.format("[%s] Eliding push transition %s.%s pinned: %s", name, prettyPrint(current),
653 | prettyPrint(pushTrns.method), prettyPrint(pinned)));
654 | } else {
655 | if (log.isTraceEnabled()) {
656 | log.trace(String.format("[%s] Push transition: %s.%s", name, prettyPrint(current),
657 | prettyPrint(pushTrns.method)));
658 | }
659 | fire(pushTrns.method, pushTrns.args);
660 | }
661 | }
662 | }
663 |
664 | private InvocationHandler transitionsHandler() {
665 | return new InvocationHandler() {
666 | @Override
667 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
668 | return locked(() -> fire(method, args));
669 | }
670 | };
671 | }
672 |
673 | /**
674 | * Transition to the next state
675 | *
676 | * @param nextState
677 | */
678 | private void transitionTo(Transitions nextState) {
679 | if (pendingPush != null) {
680 | pushTransition(nextState);
681 | } else if (pendingPop) {
682 | popTransition();
683 | } else {
684 | normalTransition(nextState);
685 | }
686 | }
687 | }
688 |
--------------------------------------------------------------------------------