1 package org.hamcrest.collection; 2 3 import org.hamcrest.Description; 4 import org.hamcrest.Matcher; 5 import org.hamcrest.TypeSafeDiagnosingMatcher; 6 7 import java.util.ArrayList; 8 import java.util.List; 9 10 import static java.util.Arrays.asList; 11 import static org.hamcrest.core.IsEqual.equalTo; 12 13 public class IsIterableContainingInRelativeOrder<E> extends TypeSafeDiagnosingMatcher<Iterable<? extends E>> { 14 private final List<Matcher<? super E>> matchers; 15 IsIterableContainingInRelativeOrder(List<Matcher<? super E>> matchers)16 public IsIterableContainingInRelativeOrder(List<Matcher<? super E>> matchers) { 17 this.matchers = matchers; 18 } 19 20 @Override matchesSafely(Iterable<? extends E> iterable, Description mismatchDescription)21 protected boolean matchesSafely(Iterable<? extends E> iterable, Description mismatchDescription) { 22 MatchSeriesInRelativeOrder<E> matchSeriesInRelativeOrder = new MatchSeriesInRelativeOrder<E>(matchers, mismatchDescription); 23 matchSeriesInRelativeOrder.processItems(iterable); 24 return matchSeriesInRelativeOrder.isFinished(); 25 } 26 describeTo(Description description)27 public void describeTo(Description description) { 28 description.appendText("iterable containing ").appendList("[", ", ", "]", matchers).appendText(" in relative order"); 29 } 30 31 private static class MatchSeriesInRelativeOrder<F> { 32 public final List<Matcher<? super F>> matchers; 33 private final Description mismatchDescription; 34 private int nextMatchIx = 0; 35 private F lastMatchedItem = null; 36 MatchSeriesInRelativeOrder(List<Matcher<? super F>> matchers, Description mismatchDescription)37 public MatchSeriesInRelativeOrder(List<Matcher<? super F>> matchers, Description mismatchDescription) { 38 this.mismatchDescription = mismatchDescription; 39 if (matchers.isEmpty()) { 40 throw new IllegalArgumentException("Should specify at least one expected element"); 41 } 42 this.matchers = matchers; 43 } 44 processItems(Iterable<? extends F> iterable)45 public void processItems(Iterable<? extends F> iterable) { 46 for (F item : iterable) { 47 if (nextMatchIx < matchers.size()) { 48 Matcher<? super F> matcher = matchers.get(nextMatchIx); 49 if (matcher.matches(item)) { 50 lastMatchedItem = item; 51 nextMatchIx++; 52 } 53 } 54 } 55 } 56 isFinished()57 public boolean isFinished() { 58 if (nextMatchIx < matchers.size()) { 59 mismatchDescription.appendDescriptionOf(matchers.get(nextMatchIx)).appendText(" was not found"); 60 if (lastMatchedItem != null) { 61 mismatchDescription.appendText(" after ").appendValue(lastMatchedItem); 62 } 63 return false; 64 } 65 return true; 66 } 67 68 } 69 70 /** 71 * Creates a matcher for {@link Iterable}s that matches when a single pass over the 72 * examined {@link Iterable} yields a series of items, that contains items logically equal to the 73 * corresponding item in the specified items, in the same relative order 74 * For example: 75 * <pre>assertThat(Arrays.asList("a", "b", "c", "d", "e"), containsInRelativeOrder("b", "d"))</pre> 76 * 77 * @param items 78 * the items that must be contained within items provided by an examined {@link Iterable} in the same relative order 79 */ containsInRelativeOrder(E... items)80 public static <E> Matcher<Iterable<? extends E>> containsInRelativeOrder(E... items) { 81 List<Matcher<? super E>> matchers = new ArrayList<Matcher<? super E>>(); 82 for (E item : items) { 83 matchers.add(equalTo(item)); 84 } 85 86 return containsInRelativeOrder(matchers); 87 } 88 89 /** 90 * Creates a matcher for {@link Iterable}s that matches when a single pass over the 91 * examined {@link Iterable} yields a series of items, that each satisfying the corresponding 92 * matcher in the specified matchers, in the same relative order. 93 * For example: 94 * <pre>assertThat(Arrays.asList("a", "b", "c", "d", "e"), containsInRelativeOrder(equalTo("b"), equalTo("d")))</pre> 95 * 96 * @param itemMatchers 97 * the matchers that must be satisfied by the items provided by an examined {@link Iterable} in the same relative order 98 */ containsInRelativeOrder(Matcher<? super E>.... itemMatchers)99 public static <E> Matcher<Iterable<? extends E>> containsInRelativeOrder(Matcher<? super E>... itemMatchers) { 100 return containsInRelativeOrder((List) asList(itemMatchers)); 101 } 102 103 /** 104 * Creates a matcher for {@link Iterable}s that matches when a single pass over the 105 * examined {@link Iterable} yields a series of items, that contains items satisfying the corresponding 106 * matcher in the specified list of matchers, in the same relative order. 107 * For example: 108 * <pre>assertThat(Arrays.asList("a", "b", "c", "d", "e"), contains(Arrays.asList(equalTo("b"), equalTo("d"))))</pre> 109 * 110 * @param itemMatchers 111 * a list of matchers, each of which must be satisfied by the items provided by 112 * an examined {@link Iterable} in the same relative order 113 */ containsInRelativeOrder(List<Matcher<? super E>> itemMatchers)114 public static <E> Matcher<Iterable<? extends E>> containsInRelativeOrder(List<Matcher<? super E>> itemMatchers) { 115 return new IsIterableContainingInRelativeOrder<E>(itemMatchers); 116 } 117 } 118