package org.infinispan.objectfilter.impl.predicateindex;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.infinispan.objectfilter.impl.FilterRegistry;
import org.infinispan.objectfilter.impl.FilterSubscriptionImpl;
import org.infinispan.objectfilter.impl.predicateindex.be.BETree;
/**
* Stores processing state during the matching process of all filters registered with a Matcher.
*
* @param <TypeMetadata> representation of entity type information, ie a Class object or anything that represents a
* type
* @param <AttributeMetadata> representation of attribute type information
* @param <AttributeId> representation of attribute identifiers
* @author anistor@redhat.com
* @since 7.0
*/
public abstract class MatcherEvalContext<TypeMetadata, AttributeMetadata, AttributeId extends Comparable<AttributeId>> {
protected AttributeNode<AttributeMetadata, AttributeId> rootNode;
/**
* Current node during traversal of the attribute tree.
*/
protected AttributeNode<AttributeMetadata, AttributeId> currentNode;
private final Object userContext;
private final Object instance;
private final Object eventType;
private FilterEvalContext singleFilterContext;
/**
* Each filter subscription has its own evaluation context, created on demand.
*/
private FilterEvalContext[] filterContexts;
private List<FilterSubscriptionImpl> filterSubscriptions;
private Map<Predicate<?>, Counter> suspendedPredicateSubscriptionCounts;
protected MatcherEvalContext(Object userContext, Object eventType, Object instance) {
if (instance == null) {
throw new IllegalArgumentException("instance cannot be null");
}
this.userContext = userContext;
this.eventType = eventType;
this.instance = instance;
}
public abstract TypeMetadata getEntityType();
/**
* The instance being matched with the filters.
*/
public Object getInstance() {
return instance;
}
public Object getUserContext() {
return userContext;
}
public Object getEventType() {
return eventType;
}
public void initMultiFilterContext(FilterRegistry<TypeMetadata, AttributeMetadata, AttributeId> filterRegistry) {
rootNode = filterRegistry.getPredicateIndex().getRoot();
suspendedPredicateSubscriptionCounts = new HashMap<>();
filterSubscriptions = filterRegistry.getFilterSubscriptions();
filterContexts = new FilterEvalContext[filterRegistry.getFilterSubscriptions().size()];
}
public FilterEvalContext initSingleFilterContext(FilterSubscriptionImpl filterSubscription) {
singleFilterContext = new FilterEvalContext(this, filterSubscription);
return singleFilterContext;
}
private boolean isSingleFilter() {
return singleFilterContext != null;
}
public FilterEvalContext getFilterEvalContext(FilterSubscriptionImpl filterSubscription) {
if (isSingleFilter()) {
return singleFilterContext;
}
FilterEvalContext filterEvalContext = filterContexts[filterSubscription.index];
if (filterEvalContext == null) {
filterEvalContext = new FilterEvalContext(this, filterSubscription);
filterContexts[filterSubscription.index] = filterEvalContext;
}
return filterEvalContext;
}
public void addSuspendedSubscription(Predicate<?> predicate) {
if (isSingleFilter()) {
return;
}
Counter counter = suspendedPredicateSubscriptionCounts.computeIfAbsent(predicate, k -> new Counter());
counter.value++;
}
public int getSuspendedSubscriptionsCounter(Predicate<AttributeId> predicate) {
if (isSingleFilter()) {
return -1;
}
Counter counter = suspendedPredicateSubscriptionCounts.get(predicate);
return counter == null ? 0 : counter.value;
}
public AttributeNode<AttributeMetadata, AttributeId> getRootNode() {
return rootNode;
}
public void process(AttributeNode<AttributeMetadata, AttributeId> node) {
currentNode = node;
processAttributes(currentNode, instance);
}
public void notifySubscribers() {
if (isSingleFilter()) {
return;
}
for (int i = 0; i < filterContexts.length; i++) {
FilterSubscriptionImpl s = filterSubscriptions.get(i);
FilterEvalContext filterEvalContext = filterContexts[i];
if (filterEvalContext == null) {
if (s.getBETree().getChildCounters()[0] == BETree.EXPR_TRUE) {
// this filter is a tautology and since its FilterEvalContext was never activated that means it also does not have projections
filterEvalContext = new FilterEvalContext(this, s);
filterContexts[i] = filterEvalContext;
} else {
continue;
}
}
if (filterEvalContext.isMatching()) {
s.getCallback().onFilterResult(userContext, eventType, instance, filterEvalContext.getProjection(), filterEvalContext.getSortProjection());
}
}
}
public void notifyDeltaSubscribers(MatcherEvalContext other, Object joiningEvent, Object updateEvent, Object leavingEvent) {
if (isSingleFilter()) {
throw new AssertionError("Single filters contexts do not support delta matching.");
}
for (int i = 0; i < filterContexts.length; i++) {
FilterSubscriptionImpl s = filterSubscriptions.get(i);
FilterEvalContext filterEvalContext1 = filterContexts[i];
if (filterEvalContext1 == null && s.getBETree().getChildCounters()[0] == BETree.EXPR_TRUE) {
// this filter is a tautology and since its FilterEvalContext was never activated that means it also does not have projections
filterEvalContext1 = new FilterEvalContext(this, s);
filterContexts[i] = filterEvalContext1;
}
FilterEvalContext filterEvalContext2 = null;
if (other != null) {
filterEvalContext2 = other.filterContexts[i];
if (filterEvalContext2 == null && s.getBETree().getChildCounters()[0] == BETree.EXPR_TRUE) {
// this filter is a tautology and since its FilterEvalContext was never activated that means it also does not have projections
filterEvalContext2 = new FilterEvalContext(other, s);
other.filterContexts[i] = filterEvalContext2;
}
}
boolean before = filterEvalContext1 != null && filterEvalContext1.isMatching();
boolean after = filterEvalContext2 != null && filterEvalContext2.isMatching();
if (!before && after) {
s.getCallback().onFilterResult(userContext, joiningEvent, other.instance, filterEvalContext2.getProjection(), filterEvalContext2.getSortProjection());
} else if (before && !after) {
s.getCallback().onFilterResult(userContext, leavingEvent, instance, filterEvalContext1.getProjection(), filterEvalContext1.getSortProjection());
} else if (before && after) {
s.getCallback().onFilterResult(userContext, updateEvent, other.instance, filterEvalContext2.getProjection(), filterEvalContext2.getSortProjection());
}
}
}
protected abstract void processAttributes(AttributeNode<AttributeMetadata, AttributeId> node, Object instance);
private static final class Counter {
int value = 0;
}
}