/*
* Licensed to Crate under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership. Crate licenses this file
* to you under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial
* agreement.
*/
package io.crate.testing;
import io.crate.analyze.symbol.*;
import io.crate.metadata.Reference;
import io.crate.data.Input;
import io.crate.types.DataType;
import org.apache.lucene.util.BytesRef;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import static org.hamcrest.Matchers.*;
public class SymbolMatchers {
public static Matcher<Symbol> isLiteral(Object expectedValue) {
return isLiteral(expectedValue, null);
}
private static Matcher<Symbol> hasDataType(DataType type) {
return new FeatureMatcher<Symbol, DataType>(equalTo(type), "valueType", "valueType") {
@Override
protected DataType featureValueOf(Symbol actual) {
return actual.valueType();
}
};
}
private static Matcher<Symbol> hasValue(Object expectedValue) {
return new FeatureMatcher<Symbol, Object>(equalTo(expectedValue), "value", "value") {
@Override
protected Object featureValueOf(Symbol actual) {
return ((Input) actual).value();
}
};
}
public static Matcher<Symbol> isLiteral(Object expectedValue, @Nullable final DataType type) {
if (expectedValue instanceof String) {
expectedValue = new BytesRef(((String) expectedValue));
}
if (type == null) {
return Matchers.allOf(Matchers.instanceOf(Literal.class), hasValue(expectedValue));
}
return Matchers.allOf(Matchers.instanceOf(Literal.class), hasValue(expectedValue), hasDataType(type));
}
public static Matcher<Symbol> isInputColumn(final Integer index) {
return both(Matchers.<Symbol>instanceOf(InputColumn.class)).and(
new FeatureMatcher<Symbol, Integer>(equalTo(index), "index", "index") {
@Override
protected Integer featureValueOf(Symbol actual) {
return ((InputColumn) actual).index();
}
});
}
public static Matcher<Symbol> isField(final String expectedName) {
return isField(expectedName, null);
}
public static Matcher<Symbol> isField(final String expectedName, @Nullable final DataType dataType) {
FeatureMatcher<Symbol, String> fm = new FeatureMatcher<Symbol, String>(equalTo(expectedName), "path", "path") {
@Override
protected String featureValueOf(Symbol actual) {
return ((Field) actual).path().outputName();
}
};
if (dataType == null) {
return fm;
}
return both(fm).and(hasDataType(dataType));
}
public static Matcher<Symbol> isFetchRef(int docIdIdx, String ref) {
return isFetchRef(isInputColumn(docIdIdx), isReference(ref));
}
private static Matcher<Symbol> isFetchRef(Matcher<Symbol> docIdMatcher, Matcher<Symbol> refMatcher) {
FeatureMatcher<Symbol, Symbol> m1 = new FeatureMatcher<Symbol, Symbol>(
docIdMatcher, "docId", "docId"
) {
@Override
protected Symbol featureValueOf(Symbol actual) {
return ((FetchReference) actual).fetchId();
}
};
FeatureMatcher<Symbol, Symbol> m2 = new FeatureMatcher<Symbol, Symbol>(
refMatcher, "ref", "ref"
) {
@Override
protected Symbol featureValueOf(Symbol actual) {
return ((FetchReference) actual).ref();
}
};
return allOf(Matchers.<Symbol>instanceOf(FetchReference.class), m1, m2);
}
public static Matcher<Symbol> isReference(String expectedName) {
return isReference(expectedName, null);
}
public static Matcher<Symbol> isReference(final String expectedName, @Nullable final DataType dataType) {
FeatureMatcher<Symbol, String> fm = new FeatureMatcher<Symbol, String>(equalTo(expectedName), "name", "name") {
@Override
protected String featureValueOf(Symbol actual) {
return ((Reference) actual).ident().columnIdent().outputName();
}
};
if (dataType == null) {
return allOf(Matchers.<Symbol>instanceOf(Reference.class), fm);
}
return allOf(Matchers.<Symbol>instanceOf(Reference.class), hasDataType(dataType), fm);
}
@SafeVarargs
public static Matcher<Symbol> isFunction(final String name, Matcher<Symbol>... argMatchers) {
FeatureMatcher<Symbol, Collection<Symbol>> ma = new FeatureMatcher<Symbol, Collection<Symbol>>(
contains(argMatchers), "args", "args") {
@Override
protected Collection<Symbol> featureValueOf(Symbol actual) {
return ((Function) actual).arguments();
}
};
return both(isFunction(name)).and(ma);
}
public static Matcher<Symbol> isFunction(String name) {
FeatureMatcher<Symbol, String> mn = new FeatureMatcher<Symbol, String>(
equalTo(name), "name", "name") {
@Override
protected String featureValueOf(Symbol actual) {
return ((Function) actual).info().ident().name();
}
};
return both(Matchers.<Symbol>instanceOf(Function.class)).and(mn);
}
public static Matcher<Symbol> isFunction(final String name, @Nullable final List<DataType> argumentTypes) {
if (argumentTypes == null) {
return isFunction(name);
}
Matcher[] argMatchers = new Matcher[argumentTypes.size()];
ListIterator<DataType> it = argumentTypes.listIterator();
while (it.hasNext()) {
int i = it.nextIndex();
DataType type = it.next();
argMatchers[i] = hasDataType(type);
}
//noinspection unchecked
return isFunction(name, argMatchers);
}
public static Matcher<Symbol> isAggregation(String name) {
FeatureMatcher<Symbol, String> fm = new FeatureMatcher<Symbol, String>(equalTo(name), "name", "name") {
@Override
protected String featureValueOf(Symbol actual) {
return ((Aggregation) actual).functionIdent().name();
}
};
return both(Matchers.<Symbol>instanceOf(Aggregation.class)).and(fm);
}
}