package org.infinispan.objectfilter.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.infinispan.objectfilter.FilterCallback;
import org.infinispan.objectfilter.ObjectFilter;
import org.infinispan.objectfilter.SortField;
import org.infinispan.objectfilter.impl.aggregation.FieldAccumulator;
import org.infinispan.objectfilter.impl.logging.Log;
import org.infinispan.objectfilter.impl.predicateindex.AttributeNode;
import org.infinispan.objectfilter.impl.predicateindex.FilterEvalContext;
import org.infinispan.objectfilter.impl.predicateindex.MatcherEvalContext;
import org.infinispan.objectfilter.impl.predicateindex.PredicateIndex;
import org.infinispan.objectfilter.impl.predicateindex.be.BETree;
import org.infinispan.objectfilter.impl.predicateindex.be.BETreeMaker;
import org.infinispan.objectfilter.impl.syntax.BooleanExpr;
import org.infinispan.objectfilter.impl.syntax.BooleanFilterNormalizer;
import org.infinispan.objectfilter.impl.syntax.parser.IckleParsingResult;
import org.infinispan.objectfilter.impl.util.StringHelper;
import org.jboss.logging.Logger;
/**
* @author anistor@redhat.com
* @since 7.0
*/
final class ObjectFilterImpl<TypeMetadata, AttributeMetadata, AttributeId extends Comparable<AttributeId>>
extends ObjectFilterBase<TypeMetadata> implements ObjectFilter {
private static final Log log = Logger.getMessageLogger(Log.class, ObjectFilterImpl.class.getName());
private static final FilterCallback emptyCallback = (userContext, eventType, instance, projection, sortProjection) -> {
// do nothing
};
private final BaseMatcher<TypeMetadata, AttributeMetadata, AttributeId> matcher;
private final MetadataAdapter<TypeMetadata, AttributeMetadata, AttributeId> metadataAdapter;
private final FieldAccumulator[] acc;
private final String[] projection;
private final Class<?>[] projectionTypes;
private final List<List<AttributeId>> translatedProjections;
private final SortField[] sortFields;
private final List<List<AttributeId>> translatedSortFields;
private final BooleanExpr normalizedQuery;
private FilterSubscriptionImpl<TypeMetadata, AttributeMetadata, AttributeId> filterSubscription;
private AttributeNode<AttributeMetadata, AttributeId> root;
ObjectFilterImpl(BaseMatcher<TypeMetadata, AttributeMetadata, AttributeId> matcher,
MetadataAdapter<TypeMetadata, AttributeMetadata, AttributeId> metadataAdapter,
IckleParsingResult<TypeMetadata> parsingResult,
FieldAccumulator[] acc) {
super(parsingResult, null);
this.projection = parsingResult.getProjections();
this.projectionTypes = parsingResult.getProjectedTypes();
this.sortFields = parsingResult.getSortFields();
if (acc != null) {
if (projectionTypes == null) {
throw new IllegalArgumentException("Accumulators can only be used with projections");
}
if (sortFields != null) {
throw new IllegalArgumentException("Accumulators cannot be used with sorting");
}
}
this.matcher = matcher;
this.metadataAdapter = metadataAdapter;
this.acc = acc;
if (projection != null && projection.length != 0) {
translatedProjections = new ArrayList<>(projection.length);
for (String projectionPath : projection) {
translatedProjections.add(metadataAdapter.mapPropertyNamePathToFieldIdPath(StringHelper.split(projectionPath)));
}
} else {
translatedProjections = null;
}
if (sortFields != null) {
// deduplicate sort fields
LinkedHashMap<String, SortField> sortFieldMap = new LinkedHashMap<>();
for (SortField sf : sortFields) {
String path = sf.getPath().asStringPath();
if (!sortFieldMap.containsKey(path)) {
sortFieldMap.put(path, sf);
}
}
SortField[] sortFields = sortFieldMap.values().toArray(new SortField[sortFieldMap.size()]);
// translate sort field paths
translatedSortFields = new ArrayList<>(sortFields.length);
for (SortField sortField : sortFields) {
translatedSortFields.add(metadataAdapter.mapPropertyNamePathToFieldIdPath(sortField.getPath().asArrayPath()));
}
} else {
translatedSortFields = null;
}
BooleanFilterNormalizer booleanFilterNormalizer = new BooleanFilterNormalizer();
normalizedQuery = booleanFilterNormalizer.normalize(parsingResult.getWhereClause());
if (getParameterNames().isEmpty()) {
subscribe();
}
}
private ObjectFilterImpl(ObjectFilterImpl<TypeMetadata, AttributeMetadata, AttributeId> other, Map<String, Object> namedParameters) {
super(other.parsingResult, Collections.unmodifiableMap(namedParameters));
this.matcher = other.matcher;
this.metadataAdapter = other.metadataAdapter;
this.acc = other.acc;
this.projection = other.projection;
this.projectionTypes = other.projectionTypes;
this.sortFields = other.sortFields;
this.translatedProjections = other.translatedProjections;
this.translatedSortFields = other.translatedSortFields;
this.normalizedQuery = other.normalizedQuery;
subscribe();
}
private void subscribe() {
BETreeMaker<AttributeId> treeMaker = new BETreeMaker<>(metadataAdapter, false);
BETree beTree = treeMaker.make(normalizedQuery, namedParameters);
PredicateIndex<AttributeMetadata, AttributeId> predicateIndex = new PredicateIndex<>(metadataAdapter);
root = predicateIndex.getRoot();
filterSubscription = new FilterSubscriptionImpl<>(parsingResult.getQueryString(), namedParameters, false, metadataAdapter, beTree,
emptyCallback, false, projection, projectionTypes, translatedProjections, sortFields, translatedSortFields, null);
filterSubscription.registerProjection(predicateIndex);
filterSubscription.subscribe(predicateIndex);
filterSubscription.index = 0;
}
@Override
public String getEntityTypeName() {
return metadataAdapter.getTypeName();
}
@Override
public String[] getProjection() {
return projection;
}
@Override
public Class<?>[] getProjectionTypes() {
return projectionTypes;
}
@Override
public ObjectFilter withParameters(Map<String, Object> namedParameters) {
if (namedParameters == null) {
throw log.getNamedParametersCannotBeNull();
}
for (String paramName : getParameterNames()) {
if (namedParameters.get(paramName) == null) {
throw new IllegalArgumentException("Query parameter '" + paramName + "' was not set");
}
}
return new ObjectFilterImpl<>(this, namedParameters);
}
@Override
public SortField[] getSortFields() {
return sortFields;
}
@Override
public Comparator<Comparable[]> getComparator() {
if (filterSubscription == null) {
throw new IllegalStateException("Parameter values were not bound yet.");
}
return filterSubscription.getComparator();
}
@Override
public FilterResult filter(Object instance) {
if (filterSubscription == null) {
throw new IllegalStateException("Parameter values were not bound yet.");
}
if (instance == null) {
throw new IllegalArgumentException("instance cannot be null");
}
MatcherEvalContext<TypeMetadata, AttributeMetadata, AttributeId> matcherEvalContext = matcher.startSingleTypeContext(null, null, instance, filterSubscription.getMetadataAdapter());
if (matcherEvalContext != null) {
FilterEvalContext filterEvalContext = matcherEvalContext.initSingleFilterContext(filterSubscription);
if (acc != null) {
filterEvalContext.acc = acc;
for (FieldAccumulator a : acc) {
if (a != null) {
a.init(filterEvalContext.getProjection());
}
}
}
matcherEvalContext.process(root);
if (filterEvalContext.isMatching()) {
Object o = filterEvalContext.getProjection() == null ? matcher.convert(instance) : null;
return new FilterResultImpl(o, filterEvalContext.getProjection(), filterEvalContext.getSortProjection());
}
}
return null;
}
}