|
4 | 4 |
|
5 | 5 | import java.util.ArrayList;
|
6 | 6 | import java.util.Arrays;
|
7 |
| -import java.util.Collection; |
8 |
| -import java.util.Iterator; |
9 | 7 | import java.util.List;
|
10 | 8 |
|
11 | 9 | import org.hamcrest.Description;
|
12 | 10 | import org.hamcrest.Factory;
|
13 | 11 | import org.hamcrest.Matcher;
|
14 |
| -import org.hamcrest.TypeSafeMatcher; |
| 12 | +import org.hamcrest.TypeSafeDiagnosingMatcher; |
15 | 13 |
|
16 |
| -public class IsIterableContainingInOrder<E> extends TypeSafeMatcher<Iterable<E>> { |
17 |
| - private final Collection<Matcher<? super E>> matchers; |
| 14 | +public class IsIterableContainingInOrder<E> extends TypeSafeDiagnosingMatcher<Iterable<E>> { |
| 15 | + private final List<Matcher<? super E>> matchers; |
18 | 16 |
|
19 |
| - public IsIterableContainingInOrder(Collection<Matcher<? super E>> contents) { |
20 |
| - if (contents.isEmpty()) { |
21 |
| - throw new IllegalArgumentException("Should specify at least one expected element"); |
22 |
| - } |
23 |
| - this.matchers = contents; |
| 17 | + public IsIterableContainingInOrder(List<Matcher<? super E>> matchers) { |
| 18 | + this.matchers = matchers; |
24 | 19 | }
|
25 |
| - |
| 20 | + |
26 | 21 | @Override
|
27 |
| - public boolean matchesSafely(Iterable<E> iterable) { |
28 |
| - Iterator<E> items = iterable.iterator(); |
29 |
| - Iterator<Matcher<? super E>> matchersIterator = matchers.iterator(); |
30 |
| - while (items.hasNext() && matchersIterator.hasNext()) { |
31 |
| - if (!matchersIterator.next().matches(items.next())) { |
32 |
| - return false; |
33 |
| - } |
| 22 | + protected boolean matchesSafely(Iterable<E> iterable, Description mismatchDescription) { |
| 23 | + MatchSeries<E> matching = new MatchSeries<E>(matchers, mismatchDescription); |
| 24 | + for (E item : iterable) { |
| 25 | + if (! matching.matches(item)) { |
| 26 | + return false; |
34 | 27 | }
|
35 |
| - return !items.hasNext() && !matchersIterator.hasNext(); |
36 |
| - } |
37 |
| - |
38 |
| - @Override |
39 |
| - public void describeMismatchSafely(Iterable<E> actual, Description mismatchDescription) { |
40 |
| - mismatchDescription.appendText("iterable was ").appendValueList("[", ", ", "]", actual); |
| 28 | + } |
| 29 | + |
| 30 | + return matching.isFinished(); |
41 | 31 | }
|
42 | 32 |
|
43 | 33 | public void describeTo(Description description) {
|
44 |
| - description.appendText("iterable over ") |
45 |
| - .appendList("[", ", ", "]", matchers); |
| 34 | + description.appendText("iterable over ").appendList("[", ", ", "]", matchers); |
| 35 | + } |
| 36 | + |
| 37 | + private static class MatchSeries<F> { |
| 38 | + public final List<Matcher<? super F>> matchers; |
| 39 | + private final Description mismatchDescription; |
| 40 | + public int nextMatchIx = 0; |
| 41 | + |
| 42 | + public MatchSeries(List<Matcher<? super F>> matchers, Description mismatchDescription) { |
| 43 | + this.mismatchDescription = mismatchDescription; |
| 44 | + if (matchers.isEmpty()) { |
| 45 | + throw new IllegalArgumentException("Should specify at least one expected element"); |
| 46 | + } |
| 47 | + this.matchers = matchers; |
| 48 | + } |
| 49 | + |
| 50 | + public boolean matches(F item) { |
| 51 | + return isNotSurplus(item) && isMatched(item); |
| 52 | + } |
| 53 | + |
| 54 | + private boolean isMatched(F item) { |
| 55 | + Matcher<? super F> matcher = matchers.get(nextMatchIx); |
| 56 | + if (!matcher.matches(item)) { |
| 57 | + describeMismatch(matcher, item); |
| 58 | + return false; |
| 59 | + } |
| 60 | + nextMatchIx++; |
| 61 | + return true; |
| 62 | + } |
| 63 | + |
| 64 | + private boolean isNotSurplus(F item) { |
| 65 | + if (matchers.size() <= nextMatchIx) { |
| 66 | + mismatchDescription.appendText("surplus item: ").appendValue(item); |
| 67 | + return false; |
| 68 | + } |
| 69 | + return true; |
| 70 | + } |
| 71 | + |
| 72 | + public boolean isFinished() { |
| 73 | + if (nextMatchIx < matchers.size()) { |
| 74 | + mismatchDescription.appendText("surplus matcher: ").appendDescriptionOf(matchers.get(nextMatchIx)); |
| 75 | + return false; |
| 76 | + } |
| 77 | + return true; |
| 78 | + } |
| 79 | + private void describeMismatch(Matcher<? super F> matcher, F item) { |
| 80 | + mismatchDescription.appendText("item " + nextMatchIx + ": "); |
| 81 | + matcher.describeMismatch(item, mismatchDescription); |
| 82 | + } |
46 | 83 | }
|
47 | 84 |
|
48 | 85 | @Factory
|
@@ -160,7 +197,7 @@ public static <E> Matcher<Iterable<E>> contains(Matcher<E> first, Matcher<? supe
|
160 | 197 | }
|
161 | 198 |
|
162 | 199 | @Factory
|
163 |
| - public static <E> Matcher<Iterable<E>> contains(Collection<Matcher<? super E>> contents) { |
| 200 | + public static <E> Matcher<Iterable<E>> contains(List<Matcher<? super E>> contents) { |
164 | 201 | return new IsIterableContainingInOrder<E>(contents);
|
165 | 202 | }
|
166 | 203 | }
|
0 commit comments