001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2016-2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.core.internal;
020
021import java.lang.annotation.Annotation;
022import java.lang.reflect.Method;
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.Collections;
026import java.util.ConcurrentModificationException;
027import java.util.Iterator;
028import java.util.List;
029import java.util.NoSuchElementException;
030import java.util.Stack;
031import java.util.concurrent.ExecutorService;
032import org.jgrapes.core.Channel;
033import org.jgrapes.core.ComponentType;
034import org.jgrapes.core.Components;
035import org.jgrapes.core.Event;
036import org.jgrapes.core.EventPipeline;
037import org.jgrapes.core.HandlerScope;
038import org.jgrapes.core.Manager;
039import org.jgrapes.core.annotation.HandlerDefinition;
040import org.jgrapes.core.annotation.HandlerDefinition.ChannelReplacements;
041import org.jgrapes.core.events.Attached;
042import org.jgrapes.core.events.Detached;
043import org.jgrapes.core.events.Start;
044
045/**
046 * ComponentVertex is the base class for all nodes in the component tree.
047 * ComponentVertex is extended by {@link org.jgrapes.core.Component}
048 * for the use as base class for component implementations. As an 
049 * alternative for implementing components with an independent base class,
050 * the derived class {@link org.jgrapes.core.internal.ComponentProxy} can be
051 * used. 
052 */
053@SuppressWarnings({ "PMD.TooManyMethods", "PMD.DataflowAnomalyAnalysis",
054    "PMD.AvoidDuplicateLiterals", "PMD.GodClass",
055    "PMD.CouplingBetweenObjects" })
056public abstract class ComponentVertex implements Manager, Channel {
057
058    /** The component's (optional) name. */
059    private String name;
060    /** Reference to the common properties of the tree nodes. */
061    private ComponentTree tree;
062    /** Reference to the parent node. */
063    private ComponentVertex parent;
064    /** All the node's children */
065    private final List<ComponentVertex> children = new ArrayList<>();
066    /** The handlers provided by this component. */
067    private List<HandlerReference> handlers;
068    /** The channel replacements. */
069    private final ChannelReplacements channelReplacements;
070
071    /**
072     *  
073     * Initialize the ComponentVertex. By default it forms a stand-alone
074     * tree, i.e. the root is set to the component itself.
075     *
076     * @param channelReplacements the channel replacements
077     */
078    protected ComponentVertex(ChannelReplacements channelReplacements) {
079        this.channelReplacements = channelReplacements;
080    }
081
082    /**
083     * Return the channel replacements passed to the constructor.
084     *
085     * @return the channel replacements
086     */
087    public ChannelReplacements channelReplacements() {
088        return channelReplacements;
089    }
090
091    /**
092     * Initialize the handler list of this component. May only be called
093     * when {@link #component()} can be relied on to return the
094     * correct value.
095     */
096    @SuppressWarnings("PMD.AvoidDuplicateLiterals")
097    protected void initComponentsHandlers() {
098        handlers = new ArrayList<>();
099        // Have a look at all methods.
100        for (Method m : component().getClass().getMethods()) {
101            maybeAddHandler(m, channelReplacements);
102        }
103        handlers = Collections.synchronizedList(handlers);
104    }
105
106    private void maybeAddHandler(
107            Method method, ChannelReplacements channelReplacements) {
108        for (Annotation annotation : method.getDeclaredAnnotations()) {
109            Class<?> annoType = annotation.annotationType();
110            HandlerDefinition hda = annoType.getAnnotation(
111                HandlerDefinition.class);
112            if (hda == null) {
113                continue;
114            }
115            HandlerDefinition.Evaluator evaluator
116                = CoreUtils.definitionEvaluator(hda);
117            HandlerScope scope = evaluator.scope(component(), method,
118                channelReplacements);
119            if (scope == null) {
120                continue;
121            }
122            handlers.add(HandlerReference.newRef(
123                component(), method, evaluator.priority(annotation),
124                scope));
125        }
126    }
127
128    /*
129     * (non-Javadoc)
130     * 
131     * @see org.jgrapes.core.Manager#setName(java.lang.String)
132     */
133    @Override
134    public ComponentType setName(String name) {
135        this.name = name;
136        return component();
137    }
138
139    /*
140     * (non-Javadoc)
141     * 
142     * @see org.jgrapes.core.Manager#name()
143     */
144    @Override
145    public String name() {
146        return name;
147    }
148
149    /*
150     * (non-Javadoc)
151     * 
152     * @see org.jgrapes.core.Manager#path()
153     */
154    @Override
155    public String componentPath() {
156        StringBuilder res = new StringBuilder();
157        buildPath(res);
158        return res.toString();
159    }
160
161    private void buildPath(StringBuilder builder) {
162        if (parent != null) {
163            parent.buildPath(builder);
164        }
165        builder.append('/')
166            .append(name == null ? getClass().getSimpleName() : name);
167    }
168
169    /**
170     * Return the component node for a given component.
171     * 
172     * @param component the component
173     * @param componentChannel the component's channel
174     * @return the node representing the component in the tree
175     */
176    public static ComponentVertex componentVertex(
177            ComponentType component, Channel componentChannel) {
178        if (component instanceof ComponentVertex) {
179            return (ComponentVertex) component;
180        }
181        return ComponentProxy.getComponentProxy(component, componentChannel);
182    }
183
184    /**
185     * Returns the component represented by this node in the tree.
186     * 
187     * @return the component
188     */
189    public abstract ComponentType component();
190
191    /*
192     * (non-Javadoc)
193     * 
194     * @see org.jgrapes.core.Manager#getChildren()
195     */
196    @Override
197    public List<ComponentType> children() {
198        synchronized (this) {
199            List<ComponentType> children = new ArrayList<>();
200            for (ComponentVertex child : this.children) {
201                children.add(child.component());
202            }
203            return Collections.unmodifiableList(children);
204        }
205    }
206
207    /*
208     * (non-Javadoc)
209     * 
210     * @see org.jgrapes.core.Manager#getParent()
211     */
212    @Override
213    public ComponentType parent() {
214        synchronized (this) {
215            if (parent == null) {
216                return null;
217            }
218            return parent.component();
219        }
220    }
221
222    /*
223     * (non-Javadoc)
224     * 
225     * @see org.jgrapes.core.Manager#getRoot()
226     */
227    @Override
228    public ComponentType root() {
229        return tree().root().component();
230    }
231
232    /**
233     * Return the tree that this node belongs to. If the node does not
234     * belong to a tree yet, a tree is automatically created.
235     * 
236     * @return the tree
237     */
238    /* default */ ComponentTree tree() {
239        if (tree != null) {
240            return tree;
241        }
242        // Build complete tree before assigning it.
243        ComponentTree newTree = new ComponentTree(this);
244        newTree.setEventPipeline(new BufferingEventPipeline(newTree));
245        tree = newTree;
246        fire(new Attached(component(), null), channel());
247        return tree;
248    }
249
250    /**
251     * Set the reference to the common properties of this component 
252     * and all its children to the given value.
253     * 
254     * @param comp the new root
255     */
256    private void setTree(ComponentTree tree) {
257        synchronized (this) {
258            this.tree = tree;
259            for (ComponentVertex child : children) {
260                child.setTree(tree);
261            }
262        }
263    }
264
265    /*
266     * (non-Javadoc)
267     * 
268     * @see org.jgrapes.core.Manager#attach(Component)
269     */
270    @Override
271    @SuppressWarnings({ "PMD.CyclomaticComplexity", "PMD.NcssCount",
272        "PMD.NPathComplexity", "PMD.CognitiveComplexity",
273        "PMD.ConfusingArgumentToVarargsMethod" })
274    public <T extends ComponentType> T attach(T child) {
275        synchronized (this) {
276            ComponentVertex childNode = componentVertex(child, null);
277            List<Channel> attachedAsChannels = new ArrayList<>();
278            synchronized (childNode) {
279                if (tree != null && tree.isStarted()) {
280                    for (TreeIterator itr = new TreeIterator(childNode);
281                            itr.hasNext();) {
282                        attachedAsChannels.add(itr.next());
283                    }
284                }
285                synchronized (tree()) {
286                    if (childNode.tree == null) {
287                        // Newly created, stand-alone child node
288                        childNode.parent = this;
289                        childNode.setTree(tree);
290                        children.add(childNode);
291                    } else {
292                        // Attaching a tree...
293                        synchronized (childNode.tree) {
294                            if (childNode.parent != null) {
295                                throw new IllegalStateException(
296                                    "Cannot attach a node with a parent.");
297                            }
298                            if (childNode.tree.isStarted()) {
299                                throw new IllegalStateException(
300                                    "Cannot attach a started subtree.");
301                            }
302                            childNode.parent = this;
303                            ComponentTree childTree = childNode.tree;
304                            childNode.setTree(tree);
305                            children.add(childNode);
306                            tree.mergeEvents(childTree);
307                        }
308                    }
309                    tree.clearHandlerCache();
310                }
311            }
312            Channel parentChan = channel();
313            if (parentChan == null) {
314                parentChan = Channel.BROADCAST;
315            }
316            Channel childChan = childNode.channel();
317            if (childChan == null) {
318                parentChan = Channel.BROADCAST;
319            }
320            Attached evt = new Attached(childNode.component(), component());
321            if (parentChan.equals(Channel.BROADCAST)
322                || childChan.equals(Channel.BROADCAST)) {
323                fire(evt, Channel.BROADCAST);
324            } else if (parentChan.equals(childChan)) {
325                fire(evt, parentChan);
326            } else {
327                fire(evt, parentChan, childChan);
328            }
329            if (!attachedAsChannels.isEmpty()) {
330                fire(new Start(), attachedAsChannels.toArray(new Channel[0]));
331            }
332            return child;
333        }
334    }
335
336    /*
337     * (non-Javadoc)
338     * 
339     * @see org.jgrapes.core.Manager#detach()
340     */
341    @Override
342    public ComponentType detach() {
343        synchronized (this) {
344            if (parent != null) {
345                ComponentVertex oldParent = parent;
346                synchronized (tree) {
347                    if (!tree.isStarted()) {
348                        throw new IllegalStateException(
349                            "Components may not be detached from a tree before"
350                                + " a Start event has been fired on it.");
351                    }
352                    synchronized (oldParent) {
353                        parent.children.remove(this);
354                        parent.tree.clearHandlerCache();
355                        parent = null;
356                    }
357                    ComponentTree newTree = new ComponentTree(this);
358                    newTree.setEventPipeline(new FeedBackPipelineFilter(
359                        newTree, new EventProcessor(newTree)));
360                    setTree(newTree);
361                }
362                Detached evt = new Detached(component(), oldParent.component());
363                oldParent.fire(evt);
364                evt = new Detached(component(), oldParent.component());
365                fire(evt);
366            }
367            return component();
368        }
369    }
370
371    /*
372     * (non-Javadoc)
373     * 
374     * @see java.lang.Iterable#iterator()
375     */
376    @Override
377    public Iterator<ComponentType> iterator() {
378        return new ComponentIterator(new TreeIterator(this));
379    }
380
381    /**
382     * A simple wrapper that converts a component vertex iterator
383     * to a component (type) iterator.
384     */
385    private static class ComponentIterator implements Iterator<ComponentType> {
386
387        private final TreeIterator baseIterator;
388
389        /**
390         * @param baseIterator
391         */
392        public ComponentIterator(TreeIterator baseIterator) {
393            this.baseIterator = baseIterator;
394        }
395
396        @Override
397        public boolean hasNext() {
398            return baseIterator.hasNext();
399        }
400
401        @Override
402        public ComponentType next() {
403            return baseIterator.next().component();
404        }
405
406    }
407
408    /**
409     * An iterator for getting all nodes of the tree.
410     */
411    private static class TreeIterator implements Iterator<ComponentVertex> {
412
413        @SuppressWarnings({ "PMD.LooseCoupling", "PMD.ReplaceVectorWithList" })
414        private final Stack<CurPos> stack = new Stack<>();
415        private final ComponentTree tree;
416
417        /**
418         * The current position.
419         */
420        private class CurPos {
421            public final ComponentVertex current;
422            public final Iterator<ComponentVertex> childIter;
423
424            /**
425             * Instantiates a new current position
426             *
427             * @param vertex the cm
428             */
429            public CurPos(ComponentVertex vertex) {
430                current = vertex;
431                childIter = current.children.iterator();
432            }
433        }
434
435        /**
436         * Instantiates a new tree iterator.
437         *
438         * @param root the root
439         */
440        public TreeIterator(ComponentVertex root) {
441            tree = root.tree();
442            stack.push(new CurPos(root));
443        }
444
445        /*
446         * (non-Javadoc)
447         * 
448         * @see java.util.Iterator#hasNext()
449         */
450        @Override
451        public boolean hasNext() {
452            return !stack.empty();
453        }
454
455        /*
456         * (non-Javadoc)
457         * 
458         * @see java.util.Iterator#next()
459         */
460        @Override
461        @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
462        public ComponentVertex next() {
463            if (stack.empty()) {
464                throw new NoSuchElementException();
465            }
466            CurPos pos = stack.peek();
467            @SuppressWarnings("PMD.PrematureDeclaration")
468            ComponentVertex res = pos.current;
469            while (true) {
470                synchronized (pos.current) {
471                    if (pos.current.tree != tree) {
472                        throw new ConcurrentModificationException();
473                    }
474                    if (pos.childIter.hasNext()) {
475                        stack.push(new CurPos(pos.childIter.next()));
476                        break;
477                    }
478                }
479                stack.pop();
480                if (stack.empty()) {
481                    break;
482                }
483                pos = stack.peek();
484            }
485            return res;
486        }
487
488        /*
489         * (non-Javadoc)
490         * 
491         * @see java.util.Iterator#remove()
492         */
493        @Override
494        public void remove() {
495            throw new UnsupportedOperationException();
496        }
497    }
498
499    @Override
500    public void addHandler(Method method, HandlerScope scope, int priority) {
501        handlers.add(HandlerReference.newRef(component(),
502            method, priority, scope));
503    }
504
505    /*
506     * (non-Javadoc)
507     * 
508     * @see org.jgrapes.core.core.Manager#fire
509     * (org.jgrapes.core.Event, org.jgrapes.core.Channel)
510     */
511    @Override
512    public <T> Event<T> fire(Event<T> event, Channel... channels) {
513        if (channels.length == 0) {
514            channels = event.channels();
515            if (channels.length == 0) {
516                channels = new Channel[] { channel() };
517            }
518        }
519        event.setChannels(channels);
520        tree().fire(event, channels);
521        return event;
522    }
523
524    /**
525     * Collects all handlers. Iterates over the tree with this object
526     * as root and for all child components adds the matching handlers to
527     * the result set recursively.
528     * 
529     * @param hdlrs the result set
530     * @param event the event to match
531     * @param channels the channels to match
532     */
533    @SuppressWarnings("PMD.UseVarargs")
534    /* default */ void collectHandlers(Collection<HandlerReference> hdlrs,
535            EventBase<?> event, Channel[] channels) {
536        for (HandlerReference hdlr : handlers) {
537            if (hdlr.handles(event, channels)) {
538                hdlrs.add(hdlr);
539            }
540        }
541        for (ComponentVertex child : children) {
542            child.collectHandlers(hdlrs, event, channels);
543        }
544    }
545
546    /*
547     * (non-Javadoc)
548     * 
549     * @see org.jgrapes.core.Manager#activeEventPipeline()
550     */
551    @Override
552    public EventPipeline activeEventPipeline() {
553        return new CheckingPipelineFilter(tree(),
554            tree().eventPipeline(), channel());
555    }
556
557    /*
558     * (non-Javadoc)
559     * 
560     * @see org.jgrapes.core.Manager#newEventPipeline()
561     */
562    @Override
563    public EventPipeline newEventPipeline() {
564        return new CheckingPipelineFilter(tree(), new EventProcessor(tree()),
565            channel());
566    }
567
568    /*
569     * (non-Javadoc)
570     * 
571     * @see org.jgrapes.core.Manager#newEventPipeline(java.util.concurrent.
572     * ExecutorService)
573     */
574    @Override
575    public EventPipeline newEventPipeline(ExecutorService executorService) {
576        return new CheckingPipelineFilter(tree(),
577            new EventProcessor(tree(), executorService), channel());
578    }
579
580    /**
581     * If a name has been set for this component 
582     * (see {@link Manager#setName(String)}), return the name,
583     * else return the object name provided by 
584     * {@link Components#objectName(Object)}, using
585     * {@link #component()} as argument.
586     */
587    @Override
588    public String toString() {
589        if (name != null) {
590            return name;
591        }
592        return Components.objectName(component());
593    }
594
595    /*
596     * (non-Javadoc)
597     * 
598     * @see org.jgrapes.core.Manager#registerAsGenerator()
599     */
600    @Override
601    public void registerAsGenerator() {
602        GeneratorRegistry.instance().add(component());
603    }
604
605    /*
606     * (non-Javadoc)
607     * 
608     * @see org.jgrapes.core.Manager#unregisterAsGenerator()
609     */
610    @Override
611    public void unregisterAsGenerator() {
612        GeneratorRegistry.instance().remove(component());
613    }
614
615}