ThrowableAnyCauseMatcher.java

package fr.sii.ogham.testing.assertion.hamcrest;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

/**
 * A matcher that applies a delegate matcher to the cause of the current
 * Throwable, returning the result of that match.
 *
 * @param <T>
 *            the type of the throwable being matched
 */
public class ThrowableAnyCauseMatcher<T extends Throwable> extends TypeSafeMatcher<T> {
	private final Matcher<? extends Throwable> causeFinder;
	private final Matcher<? extends Throwable> causeMatcher;

	public ThrowableAnyCauseMatcher(Matcher<? extends Throwable> causeFinder, Matcher<? extends Throwable> causeMatcher) {
		this.causeFinder = causeFinder;
		this.causeMatcher = causeMatcher;
	}

	public void describeTo(Description description) {
		description.appendText("exception with cause matching ");
		description.appendDescriptionOf(causeFinder);
		description.appendText(" and matched cause ");
		description.appendDescriptionOf(causeMatcher);
	}

	@Override
	protected boolean matchesSafely(T item) {
		Throwable cause = item;
		while (cause != null) {
			if (causeFinder.matches(cause)) {
				return causeMatcher.matches(cause);
			}
			cause = cause.getCause();
		}
		return false;
	}

	@Override
	protected void describeMismatchSafely(T item, Description description) {
		Throwable cause = item;
		while (cause != null) {
			if (causeFinder.matches(cause)) {
				break;
			}
			cause = cause.getCause();
		}
		if (cause == null) {
			description.appendText("was not found in exception stack");
		} else {
			causeMatcher.describeMismatch(cause, description);
		}
	}

}