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    }