    001    package org.hamcrest.core;
    002
    003    import static org.hamcrest.core.AllOf.allOf;
    004    import static org.hamcrest.core.IsEqual.equalTo;
    005
    006    import java.util.ArrayList;
    007    import java.util.List;
    008
    009    import org.hamcrest.Description;
    010    import org.hamcrest.Factory;
    011    import org.hamcrest.Matcher;
    012    import org.hamcrest.TypeSafeDiagnosingMatcher;
    013
    014    public class IsCollectionContaining<T> extends TypeSafeDiagnosingMatcher<Iterable<? super T>> {
    015        private final Matcher<? super T> elementMatcher;
    016
    017        public IsCollectionContaining(Matcher<? super T> elementMatcher) {
    018            this.elementMatcher = elementMatcher;
    019        }
    020
    021        @Override
    022        protected boolean matchesSafely(Iterable<? super T> collection, Description mismatchDescription) {
    023            boolean isPastFirst = false;
    024            for (Object item : collection) {
    025                if (elementMatcher.matches(item)){
    026                    return true;
    027                }
    028                if (isPastFirst) {
    029                  mismatchDescription.appendText(", ");
    030                }
    031                elementMatcher.describeMismatch(item, mismatchDescription);
    032                isPastFirst = true;
    033            }
    034            return false;
    035        }
    036
    037        @Override
    038        public void describeTo(Description description) {
    039            description
    040                .appendText("a collection containing ")
    041                .appendDescriptionOf(elementMatcher);
    042        }
    043
    044
    045        /**
    046         * Creates a matcher for {@link Iterable}s that only matches when a single pass over the
    047         * examined {@link Iterable} yields at least one item that is matched by the specified
    048         * <code>itemMatcher</code>.  Whilst matching, the traversal of the examined {@link Iterable}
    049         * will stop as soon as a matching item is found.
    050         * <p/>
    051         * For example:
    052         * <pre>assertThat(Arrays.asList("foo", "bar"), hasItem(startsWith("ba")))</pre>
    053         *
    054         * @param itemMatcher
    055         *     the matcher to apply to items provided by the examined {@link Iterable}
    056         */
    057        @Factory
    058        public static <T> Matcher<Iterable<? super T>> hasItem(Matcher<? super T> itemMatcher) {
    059            return new IsCollectionContaining<T>(itemMatcher);
    060        }
    061
    062        /**
    063         * Creates a matcher for {@link Iterable}s that only matches when a single pass over the
    064         * examined {@link Iterable} yields at least one item that is equal to the specified
    065         * <code>item</code>.  Whilst matching, the traversal of the examined {@link Iterable}
    066         * will stop as soon as a matching item is found.
    067         * <p/>
    068         * For example:
    069         * <pre>assertThat(Arrays.asList("foo", "bar"), hasItem("bar"))</pre>
    070         *
    071         * @param item
    072         *     the item to compare against the items provided by the examined {@link Iterable}
    073         */
    074        @Factory
    075        public static <T> Matcher<Iterable<? super T>> hasItem(T item) {
    076            // Doesn't forward to hasItem() method so compiler can sort out generics.
    077            return new IsCollectionContaining<T>(equalTo(item));
    078        }
    079
    080        /**
    081         * Creates a matcher for {@link Iterable}s that matches when consecutive passes over the
    082         * examined {@link Iterable} yield at least one item that is matched by the corresponding
    083         * matcher from the specified <code>itemMatchers</code>.  Whilst matching, each traversal of
    084         * the examined {@link Iterable} will stop as soon as a matching item is found.
    085         * <p/>
    086         * For example:
    087         * <pre>assertThat(Arrays.asList("foo", "bar", "baz"), hasItems(endsWith("z"), endsWith("o")))</pre>
    088         *
    089         * @param itemMatchers
    090         *     the matchers to apply to items provided by the examined {@link Iterable}
    091         */
    092        @Factory
    093        public static <T> Matcher<Iterable<T>> hasItems(Matcher<? super T>... itemMatchers) {
    094            List<Matcher<? super Iterable<T>>> all = new ArrayList<Matcher<? super Iterable<T>>>(itemMatchers.length);
    095
    096            for (Matcher<? super T> elementMatcher : itemMatchers) {
    097              // Doesn't forward to hasItem() method so compiler can sort out generics.
    098              all.add(new IsCollectionContaining<T>(elementMatcher));
    099            }
    100
    101            return allOf(all);
    102        }
    103
    104        /**
    105         * Creates a matcher for {@link Iterable}s that matches when consecutive passes over the
    106         * examined {@link Iterable} yield at least one item that is equal to the corresponding
    107         * item from the specified <code>items</code>.  Whilst matching, each traversal of the
    108         * examined {@link Iterable} will stop as soon as a matching item is found.
    109         * <p/>
    110         * For example:
    111         * <pre>assertThat(Arrays.asList("foo", "bar", "baz"), hasItems("baz", "foo"))</pre>
    112         *
    113         * @param items
    114         *     the items to compare against the items provided by the examined {@link Iterable}
    115         */
    116        @Factory
    117        public static <T> Matcher<Iterable<T>> hasItems(T... items) {
    118            List<Matcher<? super Iterable<T>>> all = new ArrayList<Matcher<? super Iterable<T>>>(items.length);
    119            for (T element : items) {
    120                all.add(hasItem(element));
    121            }
    122
    123            return allOf(all);
    124        }
    125
    126    }