package org.infinispan.objectfilter.impl.predicateindex; import java.util.List; import org.infinispan.objectfilter.impl.FilterSubscriptionImpl; import org.infinispan.objectfilter.impl.MetadataAdapter; import org.infinispan.objectfilter.impl.predicateindex.be.PredicateNode; /** * Keeps track of all predicates and all projections from all filters of an entity type and determines efficiently which * predicates match a given entity instance. There is a single instance at most of this for class per each entity type. * The predicates are stored in an index-like structure to allow fast matching and are reference counted in order to * allow sharing of predicates between filters rather than duplicating them. * * @param <AttributeMetadata> is the type of the metadata attached to an AttributeNode * @param <AttributeId> is the type used to represent attribute IDs (usually String or Integer) * @author anistor@redhat.com * @since 7.0 */ public final class PredicateIndex<AttributeMetadata, AttributeId extends Comparable<AttributeId>> { private final AttributeNode<AttributeMetadata, AttributeId> root; public PredicateIndex(MetadataAdapter<?, AttributeMetadata, AttributeId> metadataAdapter) { root = new RootNode<>(metadataAdapter); } public AttributeNode<AttributeMetadata, AttributeId> getRoot() { return root; } public Predicates.Subscription<AttributeId> addSubscriptionForPredicate(PredicateNode<AttributeId> predicateNode, FilterSubscriptionImpl filterSubscription) { AttributeNode<AttributeMetadata, AttributeId> attributeNode = addAttributeNodeByPath(predicateNode.getAttributePath()); predicateNode.getPredicate().attributeNode = attributeNode; return attributeNode.addPredicateSubscription(predicateNode, filterSubscription); } public void removeSubscriptionForPredicate(Predicates.Subscription<AttributeId> subscription) { AttributeNode<AttributeMetadata, AttributeId> current = getAttributeNodeByPath(subscription.getPredicateNode().getAttributePath()); current.removePredicateSubscription(subscription); // remove the nodes that no longer have a purpose while (current != root) { if (current.getNumChildren() > 0 || current.hasPredicates() || current.hasProjections()) { break; } AttributeId childId = current.getAttribute(); current = current.getParent(); current.removeChild(childId); } } private AttributeNode<AttributeMetadata, AttributeId> getAttributeNodeByPath(List<AttributeId> attributePath) { AttributeNode<AttributeMetadata, AttributeId> node = root; for (AttributeId attribute : attributePath) { node = node.getChild(attribute); if (node == null) { throw new IllegalStateException("Child not found : " + attribute); } } return node; } private AttributeNode<AttributeMetadata, AttributeId> addAttributeNodeByPath(List<AttributeId> attributePath) { AttributeNode<AttributeMetadata, AttributeId> node = root; for (AttributeId attribute : attributePath) { node = node.addChild(attribute); } return node; } public int addProjections(FilterSubscriptionImpl<?, AttributeMetadata, AttributeId> filterSubscription, List<List<AttributeId>> projection, int i) { for (List<AttributeId> projectionPath : projection) { AttributeNode node = addAttributeNodeByPath(projectionPath); node.addProjection(filterSubscription, i++); } return i; } public void removeProjections(FilterSubscriptionImpl<?, AttributeMetadata, AttributeId> filterSubscription, List<List<AttributeId>> projection) { for (List<AttributeId> projectionPath : projection) { AttributeNode node = getAttributeNodeByPath(projectionPath); node.removeProjections(filterSubscription); } } }