| package org.hamcrest.collection; |
| |
| import org.hamcrest.Description; |
| import org.hamcrest.Matcher; |
| import org.hamcrest.TypeSafeDiagnosingMatcher; |
| import org.hamcrest.internal.NullSafety; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import static java.util.Arrays.asList; |
| import static org.hamcrest.core.IsEqual.equalTo; |
| |
| public class IsIterableContainingInOrder<E> extends TypeSafeDiagnosingMatcher<Iterable<? extends E>> { |
| private final List<Matcher<? super E>> matchers; |
| |
| public IsIterableContainingInOrder(List<Matcher<? super E>> matchers) { |
| this.matchers = matchers; |
| } |
| |
| @Override |
| protected boolean matchesSafely(Iterable<? extends E> iterable, Description mismatchDescription) { |
| final MatchSeries<E> matchSeries = new MatchSeries<E>(matchers, mismatchDescription); |
| for (E item : iterable) { |
| if (!matchSeries.matches(item)) { |
| return false; |
| } |
| } |
| |
| return matchSeries.isFinished(); |
| } |
| |
| @Override |
| public void describeTo(Description description) { |
| description.appendText("iterable containing ").appendList("[", ", ", "]", matchers); |
| } |
| |
| private static class MatchSeries<F> { |
| private final List<Matcher<? super F>> matchers; |
| private final Description mismatchDescription; |
| private int nextMatchIx = 0; |
| |
| public MatchSeries(List<Matcher<? super F>> matchers, Description mismatchDescription) { |
| this.mismatchDescription = mismatchDescription; |
| if (matchers.isEmpty()) { |
| throw new IllegalArgumentException("Should specify at least one expected element"); |
| } |
| this.matchers = matchers; |
| } |
| |
| public boolean matches(F item) { |
| if (matchers.size() <= nextMatchIx) { |
| mismatchDescription.appendText("not matched: ").appendValue(item); |
| return false; |
| } |
| |
| return isMatched(item); |
| } |
| |
| public boolean isFinished() { |
| if (nextMatchIx < matchers.size()) { |
| mismatchDescription.appendText("no item was ").appendDescriptionOf(matchers.get(nextMatchIx)); |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean isMatched(F item) { |
| final Matcher<? super F> matcher = matchers.get(nextMatchIx); |
| if (!matcher.matches(item)) { |
| describeMismatch(matcher, item); |
| return false; |
| } |
| nextMatchIx++; |
| return true; |
| } |
| |
| private void describeMismatch(Matcher<? super F> matcher, F item) { |
| mismatchDescription.appendText("item " + nextMatchIx + ": "); |
| matcher.describeMismatch(item, mismatchDescription); |
| } |
| } |
| |
| /** |
| * Creates a matcher for {@link Iterable}s that matches when a single pass over the |
| * examined {@link Iterable} yields a series of items, each logically equal to the |
| * corresponding item in the specified items. For a positive match, the examined iterable |
| * must be of the same length as the number of specified items. |
| * For example: |
| * <pre>assertThat(Arrays.asList("foo", "bar"), contains("foo", "bar"))</pre> |
| * |
| * @param items |
| * the items that must equal the items provided by an examined {@link Iterable} |
| */ |
| public static <E> Matcher<Iterable<? extends E>> contains(E... items) { |
| List<Matcher<? super E>> matchers = new ArrayList<Matcher<? super E>>(); |
| for (E item : items) { |
| matchers.add(equalTo(item)); |
| } |
| |
| return contains(matchers); |
| } |
| |
| /** |
| * Creates a matcher for {@link Iterable}s that matches when a single pass over the |
| * examined {@link Iterable} yields a single item that satisfies the specified matcher. |
| * For a positive match, the examined iterable must only yield one item. |
| * For example: |
| * <pre>assertThat(Arrays.asList("foo"), contains(equalTo("foo")))</pre> |
| * |
| * @param itemMatcher |
| * the matcher that must be satisfied by the single item provided by an |
| * examined {@link Iterable} |
| */ |
| @SuppressWarnings("unchecked") |
| public static <E> Matcher<Iterable<? extends E>> contains(final Matcher<? super E> itemMatcher) { |
| return contains(new ArrayList<Matcher<? super E>>(asList(itemMatcher))); |
| } |
| |
| /** |
| * Creates a matcher for {@link Iterable}s that matches when a single pass over the |
| * examined {@link Iterable} yields a series of items, each satisfying the corresponding |
| * matcher in the specified matchers. For a positive match, the examined iterable |
| * must be of the same length as the number of specified matchers. |
| * For example: |
| * <pre>assertThat(Arrays.asList("foo", "bar"), contains(equalTo("foo"), equalTo("bar")))</pre> |
| * |
| * @param itemMatchers |
| * the matchers that must be satisfied by the items provided by an examined {@link Iterable} |
| */ |
| public static <E> Matcher<Iterable<? extends E>> contains(Matcher<? super E>... itemMatchers) { |
| // required for JDK 1.6 |
| //noinspection RedundantTypeArguments |
| final List<Matcher<? super E>> nullSafeWithExplicitTypeMatchers = NullSafety.<E>nullSafe(itemMatchers); |
| return contains(nullSafeWithExplicitTypeMatchers); |
| } |
| |
| /** |
| * Creates a matcher for {@link Iterable}s that matches when a single pass over the |
| * examined {@link Iterable} yields a series of items, each satisfying the corresponding |
| * matcher in the specified list of matchers. For a positive match, the examined iterable |
| * must be of the same length as the specified list of matchers. |
| * For example: |
| * <pre>assertThat(Arrays.asList("foo", "bar"), contains(Arrays.asList(equalTo("foo"), equalTo("bar"))))</pre> |
| * |
| * @param itemMatchers |
| * a list of matchers, each of which must be satisfied by the corresponding item provided by |
| * an examined {@link Iterable} |
| */ |
| public static <E> Matcher<Iterable<? extends E>> contains(List<Matcher<? super E>> itemMatchers) { |
| return new IsIterableContainingInOrder<E>(itemMatchers); |
| } |
| } |