001package org.hamcrest.core;
002
003import static org.hamcrest.core.AllOf.allOf;
004import static org.hamcrest.core.IsEqual.equalTo;
005
006import java.util.ArrayList;
007import java.util.List;
008
009import org.hamcrest.Description;
010import org.hamcrest.Factory;
011import org.hamcrest.Matcher;
012import org.hamcrest.TypeSafeDiagnosingMatcher;
013
014public 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}