Skip to content

Commit 1a3f317

Browse files
committed
Changes pattern matching to iterate in reverse, which corrects
a problem where two sequences of different sizes will fail to match from the beginning of the list. fixes #17
1 parent c17d93c commit 1a3f317

3 files changed

Lines changed: 68 additions & 12 deletions

File tree

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

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

2626

27+
import java.util.ArrayList;
2728
import java.util.Arrays;
29+
import java.util.List;
2830

2931
/**
3032
* A state machine which runs on enums. Note that null is a valid state
@@ -58,8 +60,28 @@ public EnumStateMachine(T initial) {
5860
* @param includeSelf if true, will also create loops
5961
*/
6062
public void addAll(Class<T> clazz, boolean includeSelf) {
63+
addAll(clazz, includeSelf, false);
64+
}
65+
66+
/**
67+
* Add transitions between every enum in the class.
68+
* If includeSelf is set, then every enum state will
69+
* also have a transition added which returns to itself.
70+
*
71+
* @param clazz this enum class
72+
* @param includeSelf if true, will also create loops
73+
* @param includeNull if true, will also consider the null state
74+
*/
75+
public void addAll(Class<T> clazz, boolean includeSelf, boolean includeNull) {
6176
setType(clazz);
62-
addAllTransitions(Arrays.asList(clazz.getEnumConstants()), includeSelf);
77+
78+
List<T> allStates = new ArrayList<>(Arrays.asList(clazz.getEnumConstants()));
79+
80+
if (includeNull) {
81+
allStates.add(null);
82+
}
83+
84+
addAllTransitions(allStates, includeSelf);
6385
}
6486

6587
/**

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

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ private void doPatternMatching(StateContainer nextState) {
160160
= Collections.unmodifiableList(new ArrayList<>(recentStates));
161161

162162
for (PatternMatcher matcher : matchers) {
163-
if (matcher.matches(recent.iterator(), recent.size())) {
163+
if (matcher.matches(recent)) {
164164
matcher.handler.onMatch(matcher.pattern);
165165
}
166166
}
@@ -644,21 +644,24 @@ private static class PatternMatcher {
644644
this.handler = handler;
645645
}
646646

647-
boolean matches(Iterator<StateContainer> it, int size) {
648-
if (size < pattern.size()) {
649-
return false;
650-
}
647+
boolean matches(List<StateContainer> states) {
648+
for (int i = pattern.size() - 1; i >= 0; --i) {
649+
int j = states.size() - (pattern.size() - i);
650+
651+
if (j < 0) {
652+
return false;
653+
}
651654

652-
for (State state : pattern) {
653-
State nextState = it.next().state;
655+
State matchState = pattern.get(i);
656+
State recentState = states.get(j).state;
654657

655-
if (state == null) {
656-
if (nextState != null) {
658+
if (matchState == null) {
659+
if (recentState != null) {
657660
return false;
658661
}
659-
} else if (nextState == null) {
662+
} else if (recentState == null) {
660663
return false;
661-
} else if (!state.equals(nextState)) {
664+
} else if (!matchState.equals(recentState)) {
662665
return false;
663666
}
664667
}

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,39 @@ public void test_sequential_match_with_null_initial_state() {
8080
assertEquals(1, counter.get());
8181
}
8282

83+
@Test
84+
public void test_sequential_match_with_differing_list_sizes() {
85+
EnumStateMachine<ZState> esm = new EnumStateMachine<>();
86+
esm.addAll(ZState.class, true, true);
87+
88+
List<ZState> threeStatePattern = Arrays.asList(ZState.One, ZState.Two, ZState.Three);
89+
List<ZState> fourStatePattern = Arrays.asList(ZState.One, ZState.Two, ZState.Three, ZState.Four);
90+
91+
final AtomicInteger counter1 = new AtomicInteger(0);
92+
final AtomicInteger counter2 = new AtomicInteger(0);
93+
94+
esm.onSequence(threeStatePattern, pattern -> counter1.incrementAndGet());
95+
esm.onSequence(fourStatePattern, pattern -> counter2.incrementAndGet());
96+
97+
esm.transition(ZState.One);
98+
esm.transition(ZState.Two);
99+
assertEquals(0, counter1.get());
100+
assertEquals(0, counter2.get());
101+
102+
esm.transition(ZState.Three);
103+
assertEquals(1, counter1.get());
104+
assertEquals(0, counter2.get());
105+
106+
esm.transition(ZState.Four);
107+
assertEquals(1, counter1.get());
108+
assertEquals(1, counter2.get());
109+
}
83110

84111
public enum Color implements State {
85112
Red, Blue, Green, Orange
86113
}
114+
115+
public enum ZState implements State {
116+
One, Two, Three, Four, Five
117+
}
87118
}

0 commit comments

Comments
 (0)