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.Arrays; 9 import java.util.Collection; 10 import java.util.List; 11 12 import static org.hamcrest.core.IsEqual.equalTo; 13 14 public class IsIterableContainingInAnyOrder<T> extends TypeSafeDiagnosingMatcher<Iterable<? extends T>> { 15 private final Collection<Matcher<? super T>> matchers; 16 17 public IsIterableContainingInAnyOrder(Collection<Matcher<? super T>> matchers) { 18 this.matchers = matchers; 19 } 20 21 @Override 22 protected boolean matchesSafely(Iterable<? extends T> items, Description mismatchDescription) { 23 final Matching<T> matching = new Matching<T>(matchers, mismatchDescription); 24 for (T item : items) { 25 if (! matching.matches(item)) { 26 return false; 27 } 28 } 29 30 return matching.isFinished(items); 31 } 32 33 @Override 34 public void describeTo(Description description) { 35 description.appendText("iterable with items ") 36 .appendList("[", ", ", "]", matchers) 37 .appendText(" in any order"); 38 } 39 40 private static class Matching<S> { 41 private final Collection<Matcher<? super S>> matchers; 42 private final Description mismatchDescription; 43 44 public Matching(Collection<Matcher<? super S>> matchers, Description mismatchDescription) { 45 this.matchers = new ArrayList<Matcher<? super S>>(matchers); 46 this.mismatchDescription = mismatchDescription; 47 } 48 49 public boolean matches(S item) { 50 if (matchers.isEmpty()) { 51 mismatchDescription.appendText("no match for: ").appendValue(item); 52 return false; 53 } 54 return isMatched(item); 55 } 56 57 public boolean isFinished(Iterable<? extends S> items) { 58 if (matchers.isEmpty()) { 59 return true; 60 } 61 mismatchDescription 62 .appendText("no item matches: ").appendList("", ", ", "", matchers) 63 .appendText(" in ").appendValueList("[", ", ", "]", items); 64 return false; 65 } 66 67 private boolean isMatched(S item) { 68 for (Matcher<? super S> matcher : matchers) { 69 if (matcher.matches(item)) { 70 matchers.remove(matcher); 71 return true; 72 } 73 } 74 mismatchDescription.appendText("not matched: ").appendValue(item); 75 return false; 76 } 77 } 78 79 /** 80 * <p> 81 * Creates an order agnostic matcher for {@link Iterable}s that matches when a single pass over 82 * the examined {@link Iterable} yields a series of items, each satisfying one matcher anywhere 83 * in the specified matchers. For a positive match, the examined iterable must be of the same 84 * length as the number of specified matchers. 85 * </p> 86 * <p> 87 * N.B. each of the specified matchers will only be used once during a given examination, so be 88 * careful when specifying matchers that may be satisfied by more than one entry in an examined 89 * iterable. 90 * </p> 91 * <p> 92 * For example: 93 * </p> 94 * <pre>assertThat(Arrays.asList("foo", "bar"), containsInAnyOrder(equalTo("bar"), equalTo("foo")))</pre> 95 * 96 * @param itemMatchers 97 * a list of matchers, each of which must be satisfied by an item provided by an examined {@link Iterable} 98 */ 99 public static <T> Matcher<Iterable<? extends T>> containsInAnyOrder(Matcher<? super T>... itemMatchers) { 100 return containsInAnyOrder((List) Arrays.asList(itemMatchers)); 101 } 102 103 /** 104 * <p> 105 * Creates an order agnostic matcher for {@link Iterable}s that matches when a single pass over 106 * the examined {@link Iterable} yields a series of items, each logically equal to one item 107 * anywhere in the specified items. For a positive match, the examined iterable 108 * must be of the same length as the number of specified items. 109 * </p> 110 * <p> 111 * N.B. each of the specified items will only be used once during a given examination, so be 112 * careful when specifying items that may be equal to more than one entry in an examined 113 * iterable. 114 * </p> 115 * <p> 116 * For example: 117 * </p> 118 * <pre>assertThat(Arrays.asList("foo", "bar"), containsInAnyOrder("bar", "foo"))</pre> 119 * 120 * @param items 121 * the items that must equal the items provided by an examined {@link Iterable} in any order 122 */ 123 public static <T> Matcher<Iterable<? extends T>> containsInAnyOrder(T... items) { 124 List<Matcher<? super T>> matchers = new ArrayList<Matcher<? super T>>(); 125 for (T item : items) { 126 matchers.add(equalTo(item)); 127 } 128 129 return new IsIterableContainingInAnyOrder<T>(matchers); 130 } 131 132 /** 133 * <p> 134 * Creates an order agnostic matcher for {@link Iterable}s that matches when a single pass over 135 * the examined {@link Iterable} yields a series of items, each satisfying one matcher anywhere 136 * in the specified collection of matchers. For a positive match, the examined iterable 137 * must be of the same length as the specified collection of matchers. 138 * </p> 139 * <p> 140 * N.B. each matcher in the specified collection will only be used once during a given 141 * examination, so be careful when specifying matchers that may be satisfied by more than 142 * one entry in an examined iterable. 143 * </p> 144 * <p>For example:</p> 145 * <pre>assertThat(Arrays.asList("foo", "bar"), containsInAnyOrder(Arrays.asList(equalTo("bar"), equalTo("foo"))))</pre> 146 * 147 * @param itemMatchers 148 * a list of matchers, each of which must be satisfied by an item provided by an examined {@link Iterable} 149 */ 150 public static <T> Matcher<Iterable<? extends T>> containsInAnyOrder(Collection<Matcher<? super T>> itemMatchers) { 151 return new IsIterableContainingInAnyOrder<T>(itemMatchers); 152 } 153 } 154 155