Skip to content

Commit c17d93c

Browse files
committed
Changes to give the initial state correct handling during
sequence matching. fixes #18 fixes #19
1 parent 165950f commit c17d93c

5 files changed

Lines changed: 69 additions & 16 deletions

File tree

src/main/java/unquietcode/tools/esm/EnumStateMachine.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ this software and associated documentation files (the "Software"), to deal in
2424
package unquietcode.tools.esm;
2525

2626

27-
import java.util.*;
27+
import java.util.Arrays;
2828

2929
/**
3030
* A state machine which runs on enums. Note that null is a valid state
@@ -82,7 +82,7 @@ public void setType(Class<T> clazz) {
8282

8383
@Override
8484
protected EnumWrapper<T> wrap(T unwrapped) {
85-
return new EnumWrapper<T>(unwrapped);
85+
return new EnumWrapper<>(unwrapped);
8686
}
8787

8888
@Override

src/main/java/unquietcode/tools/esm/GenericStateMachine.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@ public class GenericStateMachine<T extends State> implements StateMachine<T> {
4040

4141
// states, and the routers that route them
4242
private ExecutorService executor = _newExecutor();
43-
private final Map<StateWrapper, StateContainer> states = new HashMap<StateWrapper, StateContainer>();
44-
private final List<StateRouter<T>> routers = new ArrayList<StateRouter<T>>();
43+
private final Map<StateWrapper, StateContainer> states = new HashMap<>();
44+
private final List<StateRouter<T>> routers = new ArrayList<>();
4545

4646
// sequence matching
4747
private int maxRecent = 0;
48-
private final Queue<State> recentStates = new ArrayDeque<State>();
49-
private final Set<PatternMatcher> matchers = new HashSet<PatternMatcher>();
48+
private final Queue<StateContainer> recentStates = new ArrayDeque<>();
49+
private final Set<PatternMatcher> matchers = new HashSet<>();
5050

5151
// global handlers
52-
private final Set<StateHandler<T>> globalOnEntryHandlers = new HashSet<StateHandler<T>>();
53-
private final Set<StateHandler<T>> globalOnExitHandlers = new HashSet<StateHandler<T>>();
54-
private final Set<TransitionHandler<T>> globalOnTransitionHandlers = new HashSet<TransitionHandler<T>>();
52+
private final Set<StateHandler<T>> globalOnEntryHandlers = new HashSet<>();
53+
private final Set<StateHandler<T>> globalOnExitHandlers = new HashSet<>();
54+
private final Set<TransitionHandler<T>> globalOnTransitionHandlers = new HashSet<>();
5555

5656
// backing data
5757
private StateContainer initial;
@@ -78,6 +78,9 @@ public synchronized void reset() {
7878
current = initial;
7979
recentStates.clear();
8080
executor = _newExecutor();
81+
82+
// add back the initial state for pattern matching
83+
recentStates.add(initial);
8184
}
8285

8386
@Override
@@ -146,15 +149,15 @@ private boolean _transition(final StateContainer nextState) {
146149
@SuppressWarnings("unchecked")
147150
private void doPatternMatching(StateContainer nextState) {
148151
if (maxRecent != 0) {
149-
recentStates.add(nextState.state);
152+
recentStates.add(nextState);
150153
}
151154

152155
if (recentStates.size() > maxRecent) {
153156
recentStates.remove();
154157
}
155158

156-
final List<State> recent
157-
= Collections.unmodifiableList(new ArrayList<State>(recentStates));
159+
final List<StateContainer> recent
160+
= Collections.unmodifiableList(new ArrayList<>(recentStates));
158161

159162
for (PatternMatcher matcher : matchers) {
160163
if (matcher.matches(recent.iterator(), recent.size())) {
@@ -641,13 +644,21 @@ private static class PatternMatcher {
641644
this.handler = handler;
642645
}
643646

644-
boolean matches(Iterator<State> it, int size) {
647+
boolean matches(Iterator<StateContainer> it, int size) {
645648
if (size < pattern.size()) {
646649
return false;
647650
}
648651

649652
for (State state : pattern) {
650-
if (!state.equals(it.next())) {
653+
State nextState = it.next().state;
654+
655+
if (state == null) {
656+
if (nextState != null) {
657+
return false;
658+
}
659+
} else if (nextState == null) {
660+
return false;
661+
} else if (!state.equals(nextState)) {
651662
return false;
652663
}
653664
}

src/main/java/unquietcode/tools/esm/WrappedStateMachine.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public WrappedStateMachine() {
3838
}
3939

4040
public WrappedStateMachine(_Type initial) {
41-
proxy = new GenericStateMachine<>(wrap(initial));
41+
proxy = new GenericStateMachine<>(_wrap(initial));
4242
}
4343

4444
@Override

src/test/java/unquietcode/tools/esm/EnumStateMachine_T.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import java.util.concurrent.ExecutionException;
77
import java.util.concurrent.Future;
8-
import java.util.concurrent.atomic.AtomicBoolean;
98
import java.util.concurrent.atomic.AtomicInteger;
109

1110
/**

src/test/java/unquietcode/tools/esm/Sequence_T.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,49 @@ public void testSequentialMatch() {
3838
assertEquals(1, counter.get());
3939
}
4040

41+
@Test
42+
public void test_sequential_match_on_initial_state() {
43+
EnumStateMachine<Color> sm = new EnumStateMachine<>(Color.Red);
44+
sm.addTransition(Color.Red, Color.Green);
45+
sm.addTransition(Color.Green, Color.Orange);
46+
sm.addTransition(Color.Orange, Color.Red);
47+
48+
final List<Color> _pattern = Arrays.asList(Color.Red, Color.Green, Color.Orange);
49+
final AtomicInteger counter = new AtomicInteger(0);
50+
51+
sm.onSequence(_pattern, pattern -> {
52+
assertEquals(_pattern, pattern);
53+
counter.incrementAndGet();
54+
});
55+
56+
sm.transition(Color.Green);
57+
sm.transition(Color.Orange);
58+
59+
assertEquals(1, counter.get());
60+
}
61+
62+
@Test
63+
public void test_sequential_match_with_null_initial_state() {
64+
EnumStateMachine<Color> sm = new EnumStateMachine<>(null);
65+
sm.addTransition(null, Color.Green);
66+
sm.addTransition(Color.Green, Color.Orange);
67+
sm.addTransition(Color.Orange, Color.Red);
68+
69+
final List<Color> _pattern = Arrays.asList(null, Color.Green, Color.Orange);
70+
final AtomicInteger counter = new AtomicInteger(0);
71+
72+
sm.onSequence(_pattern, pattern -> {
73+
assertEquals(_pattern, pattern);
74+
counter.incrementAndGet();
75+
});
76+
77+
sm.transition(Color.Green);
78+
sm.transition(Color.Orange);
79+
80+
assertEquals(1, counter.get());
81+
}
82+
83+
4184
public enum Color implements State {
4285
Red, Blue, Green, Orange
4386
}

0 commit comments

Comments
 (0)