package org.hamcrest.core;

import org.hamcrest.*;

import java.util.ArrayList;

public class CombinableMatcher<T> extends TypeSafeDiagnosingMatcher<T> {
  private final Matcher<? super T> matcher;

  public CombinableMatcher(Matcher<? super T> matcher) {
    this.matcher = matcher;
  }

  @Override
  protected boolean matchesSafely(T item, Description mismatch) {
    if (!matcher.matches(item)) {
      matcher.describeMismatch(item, mismatch);
      return false;
    }
    return true;
  }

  @Override
  public void describeTo(Description description) {
    description.appendDescriptionOf(matcher);
  }

  public CombinableMatcher<T> and(Matcher<? super T> other) {
    return new CombinableMatcher<T>(new AllOf<T>(templatedListWith(other)));
  }

  public CombinableMatcher<T> or(Matcher<? super T> other) {
    return new CombinableMatcher<T>(new AnyOf<T>(templatedListWith(other)));
  }

  private ArrayList<Matcher<? super T>> templatedListWith(Matcher<? super T> other) {
    ArrayList<Matcher<? super T>> matchers = new ArrayList<Matcher<? super T>>();
    matchers.add(matcher);
    matchers.add(other);
    return matchers;
  }

  /**
   * This is useful for fluently combining matchers that must both pass.  For example:
   * <pre>
   *   assertThat(string, both(containsString("a")).and(containsString("b")));
   * </pre>
   */
  @Factory
  public static <LHS> CombinableMatcher<LHS> both(Matcher<? super LHS> matcher) {
    return new CombinableMatcher<LHS>(matcher);
  }

  /**
   * This is useful for fluently combining matchers where either may pass, for example:
   * <pre>
   *   assertThat(string, both(containsString("a")).and(containsString("b")));
   * </pre>
   */
  @Factory
  public static <LHS> CombinableMatcher<LHS> either(Matcher<? super LHS> matcher) {
    return new CombinableMatcher<LHS>(matcher);
  }
}

@@ -1374,32 +1374,36 @@
 %3CLHS%3E Combinable
+Both
 Matcher%3CLHS%3E bot
@@ -1453,32 +1453,36 @@
 n new Combinable
+Both
 Matcher%3CLHS%3E(mat
@@ -1487,24 +1487,356 @@
 atcher);%0A  %7D
+%0A  %0A  public static final class CombinableBothMatcher%3CX%3E %7B%0A    private final Matcher%3C? super X%3E first;%0A    public CombinableBothMatcher(Matcher%3C? super X%3E matcher) %7B%0A        this.first = matcher;%0A    %7D%0A    public CombinableMatcher%3CX%3E and(Matcher%3C? super X%3E other) %7B%0A      return new CombinableMatcher%3CX%3E(first).and(other);%0A    %7D%0A  %7D
 %0A%0A  /**%0A   *
@@ -2062,32 +2062,38 @@
 %3CLHS%3E Combinable
+Either
 Matcher%3CLHS%3E eit
@@ -2145,32 +2145,38 @@
 n new Combinable
+Either
 Matcher%3CLHS%3E(mat
@@ -2186,10 +2186,343 @@
 r);%0A  %7D%0A
+  %0A  public static final class CombinableEitherMatcher%3CX%3E %7B%0A    private final Matcher%3C? super X%3E first;%0A    public CombinableEitherMatcher(Matcher%3C? super X%3E matcher) %7B%0A        this.first = matcher;%0A    %7D%0A    public CombinableMatcher%3CX%3E or(Matcher%3C? super X%3E other) %7B%0A      return new CombinableMatcher%3CX%3E(first).or(other);%0A    %7D%0A  %7D%0A
 %7D
-%0A

address issue 154 (reported on google code project) : Prevent either().and() and both().or(). This should also attend to issue 236 on the junit project https://github.com/KentBeck/junit/issues/236 and pull request 272 on the junit project https://github.com/KentBeck/junit/pull/272