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