package org.hamcrest.core;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;

import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.core.AllOf.allOf;
import static org.hamcrest.core.IsEqual.equalTo;

public class IsIterableContaining<T> extends TypeSafeDiagnosingMatcher<Iterable<? super T>> {
    
private final Matcher<? super T> elementMatcher;

    
public IsIterableContaining(Matcher<? super T> elementMatcher) {
        
this.elementMatcher = elementMatcher;
    
}

    
@Override
    
protected boolean matchesSafely(Iterable<? super T> collection, Description mismatchDescription) {
        
if (isEmpty(collection)) {
          
mismatchDescription.appendText("was empty");
          
return false;
        
}

        
for (Object item : collection) {
            
if (elementMatcher.matches(item)) {
                
return true;
            
}
        
}

        
mismatchDescription.appendText("mismatches were: [");
        
boolean isPastFirst = false;
        
for (Object item : collection) {
            
if (isPastFirst) {
              
mismatchDescription.appendText(", ");
            
}
            
elementMatcher.describeMismatch(item, mismatchDescription);
            
isPastFirst = true;
        
}
        
mismatchDescription.appendText("]");
        
return false;
    
}

    
private boolean isEmpty(Iterable<? super T> iterable) {
      
return ! iterable.iterator().hasNext();
    
}

    
@Override
    
public void describeTo(Description description) {
        
description
            
.appendText
("a collection containing ")
            
.appendDescriptionOf(elementMatcher);
    
}

    

    
/**
     
* Creates a matcher for {@link Iterable}s that only matches when a single pass over the
     
* examined {@link Iterable} yields at least one item that is matched by the specified
     
* <code>itemMatcher</code>.
  
Whilst matching, the traversal of the examined {@link Iterable}
     
* will stop as soon as a matching item is found.
     
* For example:
     
* <pre>assertThat(Arrays.asList("foo", "bar"), hasItem(startsWith("ba")))</pre>
     
*
 

     
* @param itemMatcher
     
*the matcher to apply to items provided by the examined {@link Iterable}
     
*/

    
public static <T> Matcher<Iterable<? super T>> hasItem(Matcher<? super T> itemMatcher) {
        
return new IsIterableContaining<>(itemMatcher);
    
}

    
/**
     
* Creates a matcher for {@link Iterable}s that only matches when a single pass over the
     
* examined {@link Iterable} yields at least one item that is equal to the specified
     
* <code>item</code>.
  
Whilst matching, the traversal of the examined {@link Iterable}
     
* will stop as soon as a matching item is found.
     
* For example:
     
* <pre>assertThat(Arrays.asList("foo", "bar"), hasItem("bar"))</pre>
     
*
 

     
* @param item
     
*the item to compare against the items provided by the examined {@link Iterable}
     
*/

    
public static <T> Matcher<Iterable<? super T>> hasItem(T item) {
        
// Doesn't forward to hasItem() method so compiler can sort out generics.
        
return new IsIterableContaining<>(equalTo(item));
    
}

    
/**
     
* Creates a matcher for {@link Iterable}s that matches when consecutive passes over the
     
* examined {@link Iterable} yield at least one item that is matched by the corresponding
     
* matcher from the specified <code>itemMatchers</code>.
  
Whilst matching, each traversal of
     
* the examined {@link Iterable} will stop as soon as a matching item is found.
     
* For example:
     
* <pre>assertThat(Arrays.asList("foo", "bar", "baz"), hasItems(endsWith("z"), endsWith("o")))</pre>
     
*
 

     
* @param itemMatchers
     
*the matchers to apply to items provided by the examined {@link Iterable}
     
*/

    
@SafeVarargs
    
public static <T> Matcher<Iterable<T>> hasItems(Matcher<? super T>... itemMatchers) {
        
List<Matcher<? super Iterable<T>>> all = new ArrayList<>(itemMatchers.length);
        

        
for (Matcher<? super T> elementMatcher : itemMatchers) {
          
// Doesn't forward to hasItem() method so compiler can sort out generics.
          
all.add(new IsIterableContaining<>(elementMatcher));
        
}
        

        
return allOf(all);
    
}
    

    
/**
     
* Creates a matcher for {@link Iterable}s that matches when consecutive passes over the
     
* examined {@link Iterable} yield at least one item that is equal to the corresponding
     
* item from the specified <code>items</code>.
  
Whilst matching, each traversal of the
     
* examined {@link Iterable} will stop as soon as a matching item is found.
     
* For example:
     
* <pre>assertThat(Arrays.asList("foo", "bar", "baz"), hasItems("baz", "foo"))</pre>
     
*
 

     
* @param items
     
*the items to compare against the items provided by the examined {@link Iterable}
     
*/

    
@SafeVarargs
    
public static <T> Matcher<Iterable<T>> hasItems(T... items) {
        
List<Matcher<? super Iterable<T>>> all = new ArrayList<>(items.length);
        
for (T item : items) {
            
all.add(hasItem(item));
        
}
        

        
return allOf(all);
    
}

}