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