    001    package com.hammurapi.eventbus;
    002
    003    import java.lang.reflect.Array;
    004    import java.util.ArrayList;
    005    import java.util.Collection;
    006    import java.util.Collections;
    007    import java.util.Comparator;
    008    import java.util.Iterator;
    009    import java.util.LinkedList;
    010    import java.util.List;
    011    import java.util.Map;
    012    import java.util.Set;
    013    import java.util.concurrent.Callable;
    014    import java.util.concurrent.ExecutorService;
    015    import java.util.concurrent.Future;
    016    import java.util.concurrent.atomic.AtomicReference;
    017    import java.util.logging.Logger;
    018
    019    import com.hammurapi.extract.And;
    020    import com.hammurapi.extract.CommutativeAnd;
    021    import com.hammurapi.extract.ComparisonResult;
    022    import com.hammurapi.extract.Extractor;
    023    import com.hammurapi.extract.False;
    024    import com.hammurapi.extract.Predicate;
    025    import com.hammurapi.extract.True;
    026
    027    /**
    028     *
    029     * @author Pavel Vlasov.
    030     *
    031     * @param <E>
    032     * @param <P>
    033     * @param <C>
    034     */
    035    public abstract class PredicatedInferenceNode<E, P extends Comparable<P>, C, K, H extends EventBus.Handle<E,P,C>, S extends EventStore<E,P,C,H,S>> implements InferenceNode<E, P, C, K, H, S> {
    036            private static final Logger logger = Logger.getLogger(PredicatedInferenceNode.class.getName());
    037
    038            protected abstract List<PredicatedInferenceNode<E, P, C, K, H, S>> getFalseChildren();
    039
    040            protected abstract List<PredicatedInferenceNode<E, P, C, K, H, S>> getTrueChildren();
    041
    042            protected abstract List<EventHandlerWrapper<E, P, C, K, H, S>> getFalseHandlers();
    043
    044            protected abstract List<EventHandlerWrapper<E, P, C, K, H, S>> getTrueHandlers();
    045
    046            protected PredicateChainingMatcher<E, P, C, K, H, S> getMatcher() {
    047                    return matcher;
    048            }
    049
    050            protected abstract Callable<Collection<EventHandlerWrapper<E, P, C, K, H, S>>> createCollectorTask(
    051                            Map<C, Map<Extractor<E, ? super Boolean, C>, ? super Boolean>> cache,
    052                            ExecutorService executor,
    053                            AtomicReference<Collection<Future<Collection<EventHandlerWrapper<E, P, C, K, H, S>>>>> collector,
    054                            E event);
    055
    056            private Predicate<E, C> predicate;
    057
    058            private C context;
    059
    060            private K id;
    061
    062            private boolean isRoot;
    063
    064            private LinkedList<PredicatedInferenceNode<E, P, C, K, H, S>> nodePath;
    065
    066            protected PredicateChainingMatcher<E, P, C, K, H, S> matcher;
    067
    068            public void setRoot(boolean isRoot) {
    069                    this.isRoot = isRoot;
    070            }
    071
    072            public boolean isRoot() {
    073                    return isRoot;
    074            }
    075
    076            public PredicatedInferenceNode(
    077                            PredicatedInferenceNode<E,P,C,K,H,S> parent,
    078                            PredicateChainingMatcher<E,P,C,K,H,S> matcher,
    079                            Predicate<E, C> predicate,
    080                            C context,
    081                            K id) {
    082                    this.predicate = predicate;
    083                    this.context = context;
    084                    this.id = id;
    085                    if (parent==null) {
    086                            this.nodePath = new LinkedList<PredicatedInferenceNode<E,P,C,K,H,S>>();
    087                    } else {
    088                            this.nodePath = new LinkedList<PredicatedInferenceNode<E,P,C,K,H,S>>(parent.getNodePath());
    089                    }
    090                    this.nodePath.add(this);
    091                    this.matcher = matcher;
    092            }
    093
    094            LinkedList<PredicatedInferenceNode<E, P, C, K, H, S>> getNodePath() {
    095                    return nodePath;
    096            }
    097
    098            public K getId() {
    099                    return id;
    100            }
    101
    102            public Predicate<E,C> getPredicate() {
    103                    return predicate;
    104            }
    105
    106            public C getContext() {
    107                    return context;
    108            }
    109
    110            /**
    111             * Returns true if the handler was incorporated into the node.
    112             * @param handler
    113             * @param nodePath If not null, only nodes from the path are considered.
    114             * @return
    115             */
    116            boolean addHandler(EventHandlerWrapper<E, P, C, K, H, S> handler, LinkedList<PredicatedInferenceNode<E,P,C,K,H,S>> nodePath) {
    117                    if (nodePath!=null) {
    118                            if (nodePath.getFirst()!=this) {
    119                                    return false;
    120                            }
    121
    122                            if (nodePath.size()==1) {
    123                                    nodePath = null;
    124                            } else {
    125                                    nodePath = new LinkedList<PredicatedInferenceNode<E,P,C,K,H,S>>(nodePath);
    126                                    nodePath.removeFirst();
    127                            }
    128                    }
    129
    130                    if (handler.getCardinality()!=1) {
    131                            throw new IllegalArgumentException("Only handlers with cardinality 1 can be added to predicated inference node: "+handler);
    132                    }
    133
    134                    if (handler.getPredicate()!=null && !(handler.getPredicate() instanceof True) && !(handler.getPredicate() instanceof False) && !Collections.singleton(0).equals(handler.getPredicate().parameterIndices())) {
    135                            throw new IllegalArgumentException("Only predicates with parameter indices [0] can be added to predicated inference node: "+predicate);
    136                    }
    137
    138                    // Less expensive predicates cannot be chained after more expensive.
    139                    if (!isRoot && this.predicate.getCost()>handler.getPredicate().getCost()) {
    140                            return false;
    141                    }
    142
    143                    CR cr = compare(handler);
    144
    145                    if (cr==null) {
    146                            return false; // Predicates are incompatible.
    147                    }
    148
    149                    EventHandlerWrapperFilter<E,P,C,K,H,S> subHandler = new EventHandlerWrapperFilter<E,P,C,K,H,S>(handler, cr.subPredicate);
    150                    switch (cr.target) {
    151                    case THIS_TRUE:
    152                            getTrueHandlers().add(handler);
    153                            return true;
    154                    case THIS_FALSE:
    155                            getFalseHandlers().add(handler);
    156                            return true;
    157                    case SUB_TRUE:
    158                            for (PredicatedInferenceNode<E, P, C, K, H, S> trueChild: getTrueChildren()) {
    159                                    if (trueChild.addHandler(subHandler, nodePath)) {
    160                                            return true;
    161                                    }
    162                            }
    163
    164                            if (nodePath!=null) {
    165                                    return false;
    166                            }
    167
    168                            if (cr.subPredicate instanceof And && ((And<E,C>) cr.subPredicate).getParts().size()>1) {
    169                                    PredicatedInferenceNode<E, P, C, K, H, S> newTrueChild = getMatcher().createPredicatedInferenceNode(this, ((And<E,C>) cr.subPredicate).getParts().get(0), handler.getContext());
    170                                    getTrueChildren().add(newTrueChild);
    171                                    if (!newTrueChild.addHandler(subHandler, nodePath)) {
    172                                            throw new IllegalStateException("Should never happen!!!");
    173                                    }
    174                                    migrateChildren(newTrueChild.getNodePath());
    175                            } else if (cr.subPredicate instanceof CommutativeAnd && ((CommutativeAnd<E,C>) cr.subPredicate).getParts().size()>1) {
    176                                    List<Predicate<E,C>> parts = new ArrayList<Predicate<E,C>>(((CommutativeAnd<E,C>) cr.subPredicate).getParts());
    177                                    Collections.sort(parts, new Comparator<Predicate<E,C>>() {
    178
    179                                            @Override
    180                                            public int compare(Predicate<E, C> o1, Predicate<E, C> o2) {
    181                                                    double delta =  o1.getCost() - o2.getCost();
    182                                                    if (delta < -Double.MIN_VALUE) {
    183                                                            return -1;
    184                                                    }
    185
    186                                                    if (delta > Double.MIN_VALUE) {
    187                                                            return 1;
    188                                                    }
    189                                                    return o1.hashCode() - o2.hashCode();
    190                                            }
    191
    192                                    });
    193
    194                                    PredicatedInferenceNode<E, P, C, K, H, S> newTrueChild = getMatcher().createPredicatedInferenceNode(this, parts.get(0), handler.getContext());
    195                                    getTrueChildren().add(newTrueChild);
    196                                    if (!newTrueChild.addHandler(subHandler, nodePath)) {
    197                                            throw new IllegalStateException("Should never happen!!!");
    198                                    }
    199                                    migrateChildren(newTrueChild.getNodePath());
    200                            } else {
    201                                    PredicatedInferenceNode<E, P, C, K, H, S> newTrueChild = getMatcher().createPredicatedInferenceNode(this, cr.subPredicate, handler.getContext());
    202                                    newTrueChild.getTrueHandlers().add(subHandler);
    203                                    getTrueChildren().add(newTrueChild);
    204
    205                                    // Try to migrate existing nodes under the new one.
    206                                    migrateChildren(newTrueChild.getNodePath());
    207                            }
    208
    209                            return true;
    210                    case SUB_FALSE:
    211                            for (PredicatedInferenceNode<E, P, C, K, H, S> falseChild: getFalseChildren()) {
    212                                    if (falseChild.addHandler(subHandler, nodePath)) {
    213                                            return true;
    214                                    }
    215                            }
    216
    217                            if (nodePath!=null) {
    218                                    return false;
    219                            }
    220
    221                            if (cr.subPredicate instanceof And && ((And<E,C>) cr.subPredicate).getParts().size()>1) {
    222                                    PredicatedInferenceNode<E, P, C, K, H, S> newFalseChild = getMatcher().createPredicatedInferenceNode(this, ((And<E,C>) cr.subPredicate).getParts().get(0), handler.getContext());
    223                                    getFalseChildren().add(newFalseChild);
    224                                    if (!newFalseChild.addHandler(subHandler, nodePath)) {
    225                                            throw new IllegalStateException("Should never happen!!!");
    226                                    }
    227                                    migrateChildren(newFalseChild.getNodePath());
    228                            } else if (cr.subPredicate instanceof CommutativeAnd && ((CommutativeAnd<E,C>) cr.subPredicate).getParts().size()>1) {
    229                                    List<Predicate<E,C>> parts = new ArrayList<Predicate<E,C>>(((CommutativeAnd<E,C>) cr.subPredicate).getParts());
    230                                    Collections.sort(parts, new Comparator<Predicate<E,C>>() {
    231
    232                                            @Override
    233                                            public int compare(Predicate<E, C> o1, Predicate<E, C> o2) {
    234                                                    double delta =  o1.getCost() - o2.getCost();
    235                                                    if (delta < -Double.MIN_VALUE) {
    236                                                            return -1;
    237                                                    }
    238
    239                                                    if (delta > Double.MIN_VALUE) {
    240                                                            return 1;
    241                                                    }
    242                                                    return o1.hashCode() - o2.hashCode();
    243                                            }
    244
    245                                    });
    246
    247                                    PredicatedInferenceNode<E, P, C, K, H, S> newFalseChild = getMatcher().createPredicatedInferenceNode(this, parts.get(0), handler.getContext());
    248                                    getFalseChildren().add(newFalseChild);
    249                                    if (!newFalseChild.addHandler(subHandler, nodePath)) {
    250                                            throw new IllegalStateException("Should never happen!!!");
    251                                    }
    252                                    migrateChildren(newFalseChild.getNodePath());
    253                            } else {
    254                                    PredicatedInferenceNode<E, P, C, K, H, S> newFalseChild = getMatcher().createPredicatedInferenceNode(this, cr.subPredicate, handler.getContext());
    255                                    newFalseChild.getTrueHandlers().add(subHandler);
    256                                    getFalseChildren().add(newFalseChild);
    257
    258                                    // Try to migrate existing nodes under the new one.
    259                                    migrateChildren(newFalseChild.getNodePath());
    260                            }
    261
    262                            return true;
    263                    default:
    264                            throw new IllegalArgumentException("Unexpected target: "+cr.target);
    265                    }
    266
    267            }
    268
    269            /**
    270             * Rebuilds inference network.
    271             */
    272            public void rebuild() {
    273                    for (EventHandlerWrapper<E, P, C, K, H, S> h: getAllHandlers()) {
    274                            addHandler(EventHandlerWrapperFilter.peel(h), nodePath);
    275                    }
    276            }
    277
    278            /**
    279             * Attempts to migrate this node and child nodes' handlers to the node
    280             * specified by the nodePath parameter.
    281             * @param nodePath
    282             * @return true if the node was fully migrated (empty).
    283             */
    284            boolean migrate(LinkedList<PredicatedInferenceNode<E,P,C,K,H,S>> nodePath) {
    285                    if (nodePath.containsAll(this.nodePath)) {
    286                            return isEmpty(); // Self migration.
    287                    }
    288                    Iterator<EventHandlerWrapper<E, P, C, K, H, S>> thit = getTrueHandlers().iterator();
    289                    while (thit.hasNext()) {
    290                            EventHandlerWrapper<E, P, C, K, H, S> th = thit.next();
    291                            if (this.nodePath.getFirst().addHandler(th, nodePath)) {
    292                                    logger.info("Handler "+th+" migrated from "+this.nodePath+" to "+nodePath);
    293                                    thit.remove();
    294                            }
    295                    }
    296
    297                    Iterator<EventHandlerWrapper<E, P, C, K, H, S>> fhit = getFalseHandlers().iterator();
    298                    while (fhit.hasNext()) {
    299                            EventHandlerWrapper<E, P, C, K, H, S> fh = fhit.next();
    300                            if (this.nodePath.getFirst().addHandler(fh, nodePath)) {
    301                                    logger.info("Handler "+fh+" migrated from "+this.nodePath+" to "+nodePath);
    302                                    fhit.remove();
    303                            }
    304                    }
    305
    306                    migrateChildren(nodePath);
    307
    308                    return isEmpty();
    309            }
    310
    311            private void migrateChildren(LinkedList<PredicatedInferenceNode<E, P, C, K, H, S>> nodePath) {
    312                    Iterator<PredicatedInferenceNode<E, P, C, K, H, S>> tcit = getTrueChildren().iterator();
    313                    while (tcit.hasNext()) {
    314                            PredicatedInferenceNode<E, P, C, K, H, S> tc = tcit.next();
    315                            if (tc.migrate(nodePath)) {
    316                                    tcit.remove();
    317                            }
    318                    }
    319
    320                    Iterator<PredicatedInferenceNode<E, P, C, K, H, S>> fcit = getTrueChildren().iterator();
    321                    while (fcit.hasNext()) {
    322                            PredicatedInferenceNode<E, P, C, K, H, S> fc = fcit.next();
    323                            if (fc.migrate(nodePath)) {
    324                                    fcit.remove();
    325                            }
    326                    }
    327            }
    328
    329            private enum Target {
    330                    // Predicates equal, add to this true
    331                    THIS_TRUE,
    332                    // Predicates opposite, add to this false.
    333                    THIS_FALSE,
    334                    // Parameter predicate is more restrictive than this predicate or
    335                    // composite with extracted part for this predicate, add (merge) to true children.
    336                    SUB_TRUE,
    337                    // Parameter predicate is composite with opposite part extracted,
    338                    // or this predicate is opposite less restrictive than parameter predicate
    339                    // add (merge) to false children.
    340                    SUB_FALSE
    341            }
    342
    343            private class CR {
    344
    345                    // Predicate for sub-node.
    346                    Predicate<E, C> subPredicate;
    347
    348                    Target target;
    349
    350                    public CR(Predicate<E, C> subPredicate, Target target) {
    351                            this.subPredicate = subPredicate;
    352                            this.target = target;
    353                    }
    354
    355            }
    356
    357            private enum CONTEXT_DEPENDENCY {
    358                    BOTH,
    359                    NODE,
    360                    HANDLER,
    361                    NONE
    362            }
    363
    364            /**
    365             * Compares parameter predicate with this predicate.
    366             * @param predicate
    367             * @return
    368             */
    369            private CR compare(EventHandlerWrapper<E, P, C, K, H, S> handler) {
    370                    if (this.predicate==handler.getPredicate()) {
    371                            return new CR(null, Target.THIS_TRUE);
    372                    }
    373
    374                    if (this.predicate.isContextDependent() && !handler.getPredicate().isContextDependent()) {
    375                            return null; // This predicate is automatically more restrictive because it is context dependent.
    376                    }
    377
    378                    CONTEXT_DEPENDENCY contextDependency;
    379                    if (this.predicate.isContextDependent()) {
    380                            if (handler.getPredicate().isContextDependent()) {
    381                                    contextDependency = CONTEXT_DEPENDENCY.BOTH;
    382                            } else {
    383                                    contextDependency = CONTEXT_DEPENDENCY.NODE;
    384                            }
    385                    } else {
    386                            if (handler.getPredicate().isContextDependent()) {
    387                                    contextDependency = CONTEXT_DEPENDENCY.HANDLER;
    388                            } else {
    389                                    contextDependency = CONTEXT_DEPENDENCY.NONE;
    390                            }
    391                    }
    392
    393                    if (CONTEXT_DEPENDENCY.BOTH.equals(contextDependency)) {
    394                            if (this.context==null) {
    395                                    if (handler.getContext()!=null) {
    396                                            return null;
    397                                    }
    398                            } else {
    399                                    if (!this.context.equals(handler.getContext())) {
    400                                            return null;
    401                                    }
    402                            }
    403                    }
    404
    405                    // Both are context independent or
    406                    // contexts are both null or equal.
    407                    Predicate<E, C> handlerPredicate = handler.getPredicate();
    408    //              System.out.println("Comparing ---\n\t"+this.predicate+"\n\t"+handlerPredicate);
    409                    ComparisonResult cr = this.predicate.compareTo(handlerPredicate);
    410                    if (cr!=null && cr.isOneToOneMapping()) {
    411    //                      System.out.println("\t"+cr.getType());
    412                            switch (cr.getType()) {
    413                            case EQUAL:
    414                                    return new CR(null, Target.THIS_TRUE);
    415                            case OPPOSITE:
    416                                    return new CR(null, Target.THIS_FALSE);
    417                            case LESS_RESTRICTIVE:
    418                                    if (handlerPredicate instanceof And) {
    419                                            Predicate<E,C> firstPart = ((And<E,C>) handlerPredicate).getParts().get(0);
    420                                            ComparisonResult pcr = firstPart.compareTo(this.predicate);
    421                                            if (ComparisonResult.EQUAL_NM.equals(pcr)) {
    422                                                    return new CR(((And<E,C>) handlerPredicate).remove(firstPart), Target.SUB_TRUE);
    423                                            }
    424                                    } else if (handlerPredicate instanceof CommutativeAnd) {
    425                                            for (Predicate<E,C> part: ((CommutativeAnd<E, C>) handlerPredicate).getParts()) {
    426                                                    if (ComparisonResult.EQUAL_NM.equals(part.compareTo(this.predicate))) {
    427                                                            return new CR(((CommutativeAnd<E,C>) handlerPredicate).remove(part), Target.SUB_TRUE);
    428                                                    }
    429                                            }
    430                                    }
    431
    432                                    return new CR(handlerPredicate, Target.SUB_TRUE);
    433                            case OPPOSITE_LESS_RESTRICTIVE:
    434                                    if (handlerPredicate instanceof And) {
    435                                            Predicate<E,C> firstPart = ((And<E,C>) handlerPredicate).getParts().get(0);
    436                                            if (ComparisonResult.OPPOSITE_NM.equals(firstPart.compareTo(this.predicate))) {
    437                                                    return new CR(((And<E,C>) handlerPredicate).remove(firstPart), Target.SUB_FALSE);
    438                                            }
    439                                    } else if (handlerPredicate instanceof CommutativeAnd) {
    440                                            for (Predicate<E,C> part: ((CommutativeAnd<E, C>) handlerPredicate).getParts()) {
    441                                                    if (ComparisonResult.OPPOSITE_NM.equals(part.compareTo(this.predicate))) {
    442                                                            return new CR(((And<E,C>) handlerPredicate).remove(part), Target.SUB_FALSE);
    443                                                    }
    444                                            }
    445                                    }
    446
    447                                    return new CR(handlerPredicate, Target.SUB_FALSE);
    448                            }
    449                    }
    450
    451                    if (handlerPredicate instanceof And) {
    452                            // Should have at least one part if reached here
    453                            And<E,C> ahp = (And<E,C>) handlerPredicate;
    454                            Predicate<E, C> firstPart = ahp.getParts().get(0);
    455                            cr = this.predicate.compareTo(firstPart);
    456                            if (cr!=null && cr.isOneToOneMapping()) {
    457    //                              System.out.println("\t firstPart "+cr.getType());
    458                                    switch (cr.getType()) {
    459                                    case EQUAL:
    460                                            return new CR(ahp.remove(firstPart), Target.SUB_TRUE);
    461                                    case LESS_RESTRICTIVE:
    462                                            return new CR(ahp, Target.SUB_TRUE);
    463                                    case OPPOSITE:
    464                                            return new CR(ahp.remove(firstPart), Target.SUB_FALSE);
    465                                    case OPPOSITE_LESS_RESTRICTIVE:
    466                                            return new CR(ahp, Target.SUB_FALSE);
    467                                    }
    468                            }
    469                    } else if (handlerPredicate instanceof CommutativeAnd) {
    470                            // Should have at least one part if reached here
    471                            CommutativeAnd<E,C> cahp = (CommutativeAnd<E,C>) handlerPredicate;
    472                            for (Predicate<E, C> part: cahp.getParts()) {
    473                                    cr = this.predicate.compareTo(part);
    474                                    if (cr!=null && cr.isOneToOneMapping()) {
    475    //                                      System.out.println("\t part "+cr.getType());
    476                                            switch (cr.getType()) {
    477                                            case EQUAL:
    478                                                    return new CR(cahp.remove(part), Target.SUB_TRUE);
    479                                            case LESS_RESTRICTIVE:
    480                                                    return new CR(cahp, Target.SUB_TRUE);
    481                                            case OPPOSITE:
    482                                                    return new CR(cahp.remove(part), Target.SUB_FALSE);
    483                                            case OPPOSITE_LESS_RESTRICTIVE:
    484                                                    return new CR(cahp, Target.SUB_FALSE);
    485                                            }
    486                                    }
    487                            }
    488                    }
    489                    return null;
    490            }
    491
    492            boolean isEmpty() {
    493                    return getTrueChildren().isEmpty()
    494                            && getTrueHandlers().isEmpty()
    495                            && getFalseChildren().isEmpty()
    496                            && getFalseHandlers().isEmpty();
    497            }
    498
    499            /**
    500             * Collects handlers synchronously.
    501             */
    502            @Override
    503            public void collectHandlers(
    504                            Map<C, Map<Extractor<E, ? super Boolean, C>, ? super Boolean>> cache,
    505                            Collection<EventHandlerWrapper<E, P, C, K, H, S>> collector,
    506                            E event) {
    507
    508                    @SuppressWarnings("unchecked")
    509                    E[] events = (E[]) Array.newInstance(matcher.getEventBus().getEventType(), 1);
    510                    events[0] = event;
    511
    512                    if (getPredicate().extract(getContext(), cache, events)) {
    513                            for (PredicatedInferenceNode<E, P, C, K, H, S> trueChild: getTrueChildren()) {
    514                                    trueChild.collectHandlers(cache, collector, event);
    515                            }
    516                            collector.addAll(getTrueHandlers());
    517                    } else {
    518                            for (PredicatedInferenceNode<E, P, C, K, H, S> falseChild: getFalseChildren()) {
    519                                    falseChild.collectHandlers(cache, collector, event);
    520                            }
    521                            collector.addAll(getFalseHandlers());
    522                    }
    523            }
    524
    525            /**
    526             * Collects handlers asynchronously.
    527             */
    528            @Override
    529            public void collectHandlers(
    530                            final Map<C, Map<Extractor<E, ? super Boolean, C>, ? super Boolean>> cache,
    531                            final ExecutorService executor,
    532                            final AtomicReference<Collection<Future<Collection<EventHandlerWrapper<E, P, C, K, H, S>>>>> collector,
    533                            final E event) {
    534
    535                    // Evaluates predicate and submits tasks for sub-nodes.
    536                    Callable<Collection<EventHandlerWrapper<E, P, C, K, H, S>>> task = createCollectorTask(cache, executor, collector, event);
    537                    Collection<Future<Collection<EventHandlerWrapper<E, P, C, K, H, S>>>> c = collector.get();
    538                    if (c==null) {
    539                            logger.severe("Collecting handlers after join()");
    540                            throw new EventBusException("Collecting handlers after join()");
    541                    }
    542                    c.add(executor.submit(task));
    543
    544            }
    545
    546            @Override
    547            public boolean remove(List<PredicatedInferenceNode<E, P, C, K, H, S>> parentTrueChildren, List<PredicatedInferenceNode<E, P, C, K, H, S>> parentFalseChildren, boolean isTrueChild, Iterable<K> keys) {
    548                    for (K key: keys) {
    549                            Iterator<EventHandlerWrapper<E, P, C, K, H, S>> tit = getTrueHandlers().iterator();
    550                            while (tit.hasNext()) {
    551                                    EventHandlerWrapper<E, P, C, K, H, S> eh = tit.next();
    552                                    if (eh.getRegistrationKeys().remove(key)) {
    553                                            if (eh.getRegistrationKeys().isEmpty()) {
    554                                                    tit.remove();
    555                                            }
    556                                    }
    557                            }
    558                            Iterator<EventHandlerWrapper<E, P, C, K, H, S>> fit = getFalseHandlers().iterator();
    559                            while (fit.hasNext()) {
    560                                    EventHandlerWrapper<E, P, C, K, H, S> eh = fit.next();
    561                                    if (eh.getRegistrationKeys().remove(key)) {
    562                                            if (eh.getRegistrationKeys().isEmpty()) {
    563                                                    fit.remove();
    564                                            }
    565                                    }
    566                            }
    567
    568                            Iterator<PredicatedInferenceNode<E, P, C, K, H, S>> tcit = getTrueChildren().iterator();
    569                            while (tcit.hasNext()) {
    570                                    if (!tcit.next().remove(getTrueChildren(), getFalseChildren(), true, keys)) {
    571                                            tcit.remove();
    572                                    }
    573                            }
    574
    575                            Iterator<PredicatedInferenceNode<E, P, C, K, H, S>> fcit = getFalseChildren().iterator();
    576                            while (fcit.hasNext()) {
    577                                    if (!fcit.next().remove(getTrueChildren(), getFalseChildren(), false, keys)) {
    578                                            fcit.remove();
    579                                    }
    580                            }
    581                    }
    582
    583                    if (!getTrueHandlers().isEmpty() || !getFalseHandlers().isEmpty()) {
    584                            return true;
    585                    }
    586
    587                    // TODO implement sub-node promotion to parent. Two-phase - a) collect. b) if nothing remains - promote.
    588
    589                    return !getTrueChildren().isEmpty() || !getFalseChildren().isEmpty();
    590            }
    591
    592            @Override
    593            public void reset() {
    594                    for (EventHandlerWrapper<E, P, C, K, H, S> eh: getTrueHandlers()) {
    595                            eh.reset();
    596                    }
    597                    for (EventHandlerWrapper<E, P, C, K, H, S> eh: getFalseHandlers()) {
    598                            eh.reset();
    599                    }
    600                    for (PredicatedInferenceNode<E, P, C, K, H, S> child: getTrueChildren()) {
    601                            child.reset();
    602                    }
    603                    for (PredicatedInferenceNode<E, P, C, K, H, S> child: getFalseChildren()) {
    604                            child.reset();
    605                    }
    606            }
    607
    608            void takeSnapshot(AbstractEventBus.Snapshot<E, P, C, K, H, S> snapshot, Set<K> taken) {
    609                    if (taken.add(id)) {
    610                            Collection<K> trueChildrenIds = new ArrayList<K>();
    611                            for (PredicatedInferenceNode<E, P, C, K, H, S> tc: getTrueChildren()) {
    612                                    tc.takeSnapshot(snapshot, taken);
    613                                    trueChildrenIds.add(tc.id);
    614                            }
    615
    616                            Collection<K> trueHandlersIds = new ArrayList<K>();
    617                            for (EventHandlerWrapper<E, P, C, K, H, S> th: getTrueHandlers()) {
    618                                    th.takeSnapshot(snapshot, taken);
    619                                    trueHandlersIds.addAll(getMatcher().extractHandlerIds(th));
    620                            }
    621
    622                            Collection<K> falseChildrenIds = new ArrayList<K>();
    623                            for (PredicatedInferenceNode<E, P, C, K, H, S> fc: getFalseChildren()) {
    624                                    fc.takeSnapshot(snapshot, taken);
    625                                    falseChildrenIds.add(fc.id);
    626                            }
    627
    628                            Collection<K> falseHandlersIds = new ArrayList<K>();
    629                            for (EventHandlerWrapper<E, P, C, K, H, S> fh: getFalseHandlers()) {
    630                                    fh.takeSnapshot(snapshot, taken);
    631                                    falseHandlersIds.addAll(getMatcher().extractHandlerIds(fh));
    632                            }
    633
    634                            snapshot.predicateNode(id, predicate, trueChildrenIds, trueHandlersIds, falseChildrenIds, falseHandlersIds, isRoot);
    635                    }
    636            }
    637
    638            @Override
    639            public Collection<EventHandlerWrapper<E, P, C, K, H, S>> getAllHandlers() {
    640                    Collection<EventHandlerWrapper<E, P, C, K, H, S>> ret = new LinkedList<EventHandlerWrapper<E,P,C,K,H,S>>();
    641
    642                    ret.addAll(getTrueHandlers());
    643                    ret.addAll(getFalseHandlers());
    644
    645                    for (PredicatedInferenceNode<E, P, C, K, H, S> tc: getTrueChildren()) {
    646                            ret.addAll(tc.getAllHandlers());
    647                    }
    648
    649                    for (PredicatedInferenceNode<E, P, C, K, H, S> fc: getFalseChildren()) {
    650                            ret.addAll(fc.getAllHandlers());
    651                    }
    652
    653                    return ret;
    654            }
    655    }