package querqy;
import java.util.Arrays;
import java.util.List;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import querqy.model.BooleanClause;
import querqy.model.BooleanQuery;
import querqy.model.BoostQuery;
import querqy.model.Clause.Occur;
import querqy.model.DisjunctionMaxClause;
import querqy.model.DisjunctionMaxQuery;
import querqy.model.Node;
import querqy.model.SubQuery;
import querqy.model.Term;
/**
* {@link Matcher}s for {@link SubQuery}s.
*/
public class QuerqyMatchers {
/**
* Match {@link DisjunctionMaxClause} with {@link Occur#SHOULD}.
*
* @param clauses
* Expected clauses.
*/
@SafeVarargs
public static DMQMatcher dmq(TypeSafeMatcher<? extends DisjunctionMaxClause>... clauses) {
return dmq(should(), clauses);
}
/**
* Match {@link DisjunctionMaxClause}.
*
* @param occur
* Expected occur.
* @param clauses
* Expected clauses.
*/
@SafeVarargs
public static DMQMatcher dmq(OccurMatcher occur, TypeSafeMatcher<? extends DisjunctionMaxClause>... clauses) {
return new DMQMatcher(occur, clauses);
}
/**
* Match {@link BooleanQuery} with {@link Occur#SHOULD}.
*
* @param clauses
* Expected clauses.
*/
@SafeVarargs
public static BQMatcher bq(TypeSafeMatcher<? extends BooleanClause>... clauses) {
return bq(should(), clauses);
}
/**
* Match {@link BooleanQuery}.
*
* @param occur
* Expected occur.
* @param clauses
* Expected clauses.
*/
@SafeVarargs
public static BQMatcher bq(OccurMatcher occur, TypeSafeMatcher<? extends BooleanClause>... clauses) {
return new BQMatcher(occur, clauses);
}
/**
* Match term.
*
* @param field
* Expected field.
* @param value
* Expected value.
*/
public static TermMatcher term(String field, String value) {
return new TermMatcher(field, value);
}
public static TermMatcher term(String field, String value, Boolean generated) {
return new TermMatcher(field, value, generated);
}
/**
* Just match term value.
*
* @param value
* Expected value.
*/
public static TermMatcher term(String value) {
return new TermMatcher(null, value);
}
public static TermMatcher term(String value, Boolean generated) {
return new TermMatcher(null, value, generated);
}
/**
* Match a boost query.
*
* @param queryMatcher The expected query matcher
* @param boost The expected boost factor
*
* @return
*/
public static BoostQueryMatcher boostQ(TypeSafeMatcher<? extends Node> queryMatcher, float boost) {
return new BoostQueryMatcher(queryMatcher, boost);
}
/**
* Expected {@link Occur#MUST}.
*/
public static OccurMatcher must() {
return new OccurMatcher(Occur.MUST);
}
/**
* Expected {@link Occur#MUST_NOT}.
*/
public static final OccurMatcher mustNot() {
return new OccurMatcher(Occur.MUST_NOT);
}
/**
* Expected {@link Occur#SHOULD}.
*/
public static final OccurMatcher should() {
return new OccurMatcher(Occur.SHOULD);
}
//
// Inner classes for matchers.
//
/**
* {@link Matcher} for {@link DisjunctionMaxQuery}s.
*/
private static class DMQMatcher extends SubQueryMatcher<DisjunctionMaxQuery> {
public DMQMatcher(OccurMatcher occur, TypeSafeMatcher<? extends DisjunctionMaxClause>[] clauses) {
super(occur, clauses);
}
@Override
public void describeTo(Description description) {
description.appendText("DMQ:\n ");
super.describeTo(description);
}
}
/**
* {@link Matcher} for {@link BooleanQuery}s.
*/
private static class BQMatcher extends SubQueryMatcher<BooleanQuery> {
public BQMatcher(OccurMatcher occur, TypeSafeMatcher<? extends BooleanClause>[] clauses) {
super(occur, clauses);
}
@Override
public void describeTo(Description description) {
description.appendText("BQ:\n ");
description.appendText("(");
super.describeTo(description);
description.appendText(")\n");
}
}
/**
* {@link Matcher} for {@link Term}s.
*/
private static class TermMatcher extends TypeSafeMatcher<Term> {
private final String expectedField;
private final String expectedValue;
private final Boolean expectedGenerated;
public TermMatcher(String field, String value) {
this(field, value, null);
}
public TermMatcher(String field, String value, Boolean expectedGenerated) {
this.expectedField = field;
this.expectedValue = value;
this.expectedGenerated = expectedGenerated;
}
@Override
public void describeTo(Description description) {
description.appendText("term: " + expectedField + ":" + expectedValue + " (generated:" + expectedGenerated + ")");
}
@Override
protected boolean matchesSafely(Term item) {
return item != null &&
// expectedField == null -> do not compare field name.
(expectedField == null || expectedField.equals(item.getField())) &&
// Using toString() here to avoid incompatibilities between
// equals() of different char sequences.
CharSequenceUtil.equals(expectedValue, item.getValue()) &&
// expectedValue.equals(item.getValue().toString()) &&
((expectedGenerated == null) || (expectedGenerated.booleanValue() == item.isGenerated()))
;
}
}
/**
* {@link Matcher} for {@link BoostQuery}s.
*/
private static class BoostQueryMatcher extends TypeSafeMatcher<BoostQuery> {
final TypeSafeMatcher<? extends Node> queryMatcher;
final float boost;
public BoostQueryMatcher(TypeSafeMatcher<? extends Node> queryMatcher, float boost) {
this.queryMatcher = queryMatcher;
this.boost = boost;
}
@Override
public void describeTo(Description description) {
description.appendText("BoostQuery:\n ");
description.appendText("(");
queryMatcher.describeTo(description);
description.appendText(", boost: ");
description.appendValue(boost);
description.appendText(")\n");
}
@Override
protected boolean matchesSafely(BoostQuery item) {
return queryMatcher.matches(item.getQuery()) && item.getBoost() == boost;
}
}
/**
* {@link Matcher} for {@link Occur}s.
*/
private static class OccurMatcher extends TypeSafeMatcher<Occur> {
private final Occur expected;
public OccurMatcher(Occur expected) {
this.expected = expected;
}
@Override
public void describeTo(Description description) {
description.appendText("occur: " + expected.name());
}
@Override
protected boolean matchesSafely(Occur occur) {
return expected.equals(occur);
}
}
/**
* {@link Matcher} for {@link SubQuery}s.
*/
private static abstract class SubQueryMatcher<T extends SubQuery<?, ?>> extends TypeSafeMatcher<T> {
private final OccurMatcher occur;
private final TypeSafeMatcher<? extends Node>[] clauses;
public SubQueryMatcher(OccurMatcher occur, TypeSafeMatcher<? extends Node>[] clauses) {
this.occur = occur;
this.clauses = clauses;
}
@Override
public void describeTo(Description description) {
occur.describeTo(description);
description.appendList("clauses:[", ",\n", "]", Arrays.asList(clauses));
}
@Override
protected boolean matchesSafely(T item) {
if (!occur.matches(item.occur)) {
return false;
}
List<? extends Node> itemClauses = item.getClauses();
if (itemClauses == null || itemClauses.size() != clauses.length) {
return false;
}
for (int i = 0; i < clauses.length; i++) {
if (!clauses[i].matches(itemClauses.get(i))) {
return false;
}
}
return true;
}
}
}