-
Notifications
You must be signed in to change notification settings - Fork 716
/
StateMachine.java
184 lines (164 loc) · 4.5 KB
/
StateMachine.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
This file is a part of Angry IP Scanner source code,
see http://www.angryip.org/ for more information.
Licensed under GPLv2.
*/
package net.azib.ipscan.core.state;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Generic StateMachine implementation.
* It holds the current state and performs transitions with corresponding methods.
* <p/>
* Note: the class is abstract because notification of listeners often should happen in the correct thread,
* so subclasses should provide this functionality.
*
* @author Anton Keks
*/
public abstract class StateMachine {
public enum Transition {INIT, START, STOP, NEXT, COMPLETE, RESET, RESCAN, CONTINUE}
private volatile ScanningState state = ScanningState.IDLE;
private ReentrantReadWriteLock listenersLock = new ReentrantReadWriteLock();
private List<StateTransitionListener> transitionListeners = new ArrayList<>();
/**
* @return true if current state is as specified
*/
public boolean inState(ScanningState state) {
return this.state == state;
}
/**
* @return current state
*/
public ScanningState getState() {
return state;
}
/**
* Registers state transition listener.
*/
public void addTransitionListener(StateTransitionListener listener) {
try {
listenersLock.writeLock().lock();
transitionListeners.add(listener);
}
finally {
listenersLock.writeLock().unlock();
}
}
/**
* Unregisters the listener
*/
public void removeTransitionListener(StateTransitionListener listener) {
try {
listenersLock.writeLock().lock();
transitionListeners.remove(listener);
}
finally {
listenersLock.writeLock().unlock();
}
}
/**
* Transitions to the specified state, notifying all listeners.
* Note: this method is intentionally not public, use specific methods to make desired transitions.
*/
void transitionTo(ScanningState newState, Transition transition) {
if (state != newState) {
state = newState;
notifyAboutTransition(transition);
}
}
protected void notifyAboutTransition(Transition transition) {
try {
listenersLock.readLock().lock();
for (StateTransitionListener listener : transitionListeners) {
listener.transitionTo(state, transition);
}
}
finally {
listenersLock.readLock().unlock();
}
}
/**
* Transitions to the next state in the sequence.
* Called when user presses the scan button.
*/
public void transitionToNext() {
// killing state cannot be transitioned from by pressing a button
if (state != ScanningState.KILLING) {
transitionTo(state.next(), Transition.NEXT);
}
}
/**
* Transitions to the stopping state
*/
public void stop() {
if (state == ScanningState.SCANNING) {
transitionTo(ScanningState.STOPPING, Transition.STOP);
}
else if (state == ScanningState.STOPPING) {
// notify anyway to ensure that manual stopping and automatic stopping work well together
notifyAboutTransition(Transition.STOP);
}
else {
throw new IllegalStateException("Attempt to stop from " + state);
}
}
/**
* Transitions back to the idle state
*/
public void complete() {
if (state == ScanningState.STOPPING || state == ScanningState.KILLING) {
transitionTo(ScanningState.IDLE, Transition.COMPLETE);
}
else {
throw new IllegalStateException("Attempt to complete from " + state);
}
}
/**
* Transitions to the RESTARTING state in order to rescan previously scanned results.
*/
public void rescan() {
if (state == ScanningState.IDLE) {
transitionTo(ScanningState.RESTARTING, Transition.RESCAN);
}
else {
throw new IllegalStateException("Attempt to rescan from " + state);
}
}
/**
* Starts the scanning process
*/
public void startScanning() {
if (state == ScanningState.STARTING || state == ScanningState.RESTARTING) {
transitionTo(ScanningState.SCANNING, Transition.START);
}
else {
throw new IllegalStateException("Attempt to go scanning from " + state);
}
}
/**
* Continues previously aborted scanning process
*/
public void continueScanning() {
if (state == ScanningState.IDLE) {
transitionTo(ScanningState.STARTING, Transition.CONTINUE);
}
else {
throw new IllegalStateException("Attempt to continue scanning from " + state);
}
}
/**
* Inits everyone on startup
*/
public void init() {
state = ScanningState.IDLE;
notifyAboutTransition(Transition.INIT);
}
/**
* Resets the machine to the initial state
*/
public void reset() {
// no transition notifications
state = ScanningState.IDLE;
}
}