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;
032
033import org.jgrapes.core.Channel;
034import org.jgrapes.core.ComponentType;
035import org.jgrapes.core.Components;
036import org.jgrapes.core.Event;
037import org.jgrapes.core.EventPipeline;
038import org.jgrapes.core.HandlerScope;
039import org.jgrapes.core.Manager;
040import org.jgrapes.core.annotation.HandlerDefinition;
041import org.jgrapes.core.annotation.HandlerDefinition.ChannelReplacements;
042import org.jgrapes.core.events.Attached;
043import org.jgrapes.core.events.Detached;
044import org.jgrapes.core.events.Start;
045
046/**
047 * ComponentVertex is the base class for all nodes in the component tree.
048 * ComponentVertex is extended by {@link org.jgrapes.core.Component}
049 * for the use as base class for component implementations. As an 
050 * alternative for implementing components with an independent base class,
051 * the derived class {@link org.jgrapes.core.internal.ComponentProxy} can be
052 * used. 
053 */
054@SuppressWarnings({ "PMD.TooManyMethods", "PMD.DataflowAnomalyAnalysis",
055    "PMD.AvoidDuplicateLiterals" })
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    protected 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" })
260    public <T extends ComponentType> T attach(T child) {
261        synchronized (this) {
262            ComponentVertex childNode = componentVertex(child, null);
263            List<Channel> attachedAsChannels = new ArrayList<>();
264            synchronized (childNode) {
265                if (tree != null && tree.isStarted()) {
266                    for (TreeIterator itr = new TreeIterator(childNode);
267                            itr.hasNext();) {
268                        attachedAsChannels.add(itr.next());
269                    }
270                }
271                synchronized (tree()) {
272                    if (childNode.tree == null) {
273                        // Newly created, stand-alone child node
274                        childNode.parent = this;
275                        childNode.setTree(tree);
276                        children.add(childNode);
277                    } else {
278                        // Attaching a tree...
279                        synchronized (childNode.tree) {
280                            if (childNode.parent != null) {
281                                throw new IllegalStateException(
282                                    "Cannot attach a node with a parent.");
283                            }
284                            if (childNode.tree.isStarted()) {
285                                throw new IllegalStateException(
286                                    "Cannot attach a started subtree.");
287                            }
288                            childNode.parent = this;
289                            ComponentTree childTree = childNode.tree;
290                            childNode.setTree(tree);
291                            children.add(childNode);
292                            tree.mergeEvents(childTree);
293                        }
294                    }
295                    tree.clearHandlerCache();
296                }
297            }
298            Channel parentChan = channel();
299            if (parentChan == null) {
300                parentChan = Channel.BROADCAST;
301            }
302            Channel childChan = childNode.channel();
303            if (childChan == null) {
304                parentChan = Channel.BROADCAST;
305            }
306            Attached evt = new Attached(childNode.component(), component());
307            if (parentChan.equals(Channel.BROADCAST)
308                || childChan.equals(Channel.BROADCAST)) {
309                fire(evt, Channel.BROADCAST);
310            } else if (parentChan.equals(childChan)) {
311                fire(evt, parentChan);
312            } else {
313                fire(evt, parentChan, childChan);
314            }
315            if (!attachedAsChannels.isEmpty()) {
316                fire(new Start(), attachedAsChannels.toArray(new Channel[0]));
317            }
318            return child;
319        }
320    }
321
322    /*
323     * (non-Javadoc)
324     * 
325     * @see org.jgrapes.core.Manager#detach()
326     */
327    @Override
328    public ComponentType detach() {
329        synchronized (this) {
330            if (parent != null) {
331                ComponentVertex oldParent = parent;
332                synchronized (tree) {
333                    if (!tree.isStarted()) {
334                        throw new IllegalStateException(
335                            "Components may not be detached from a tree before"
336                                + " a Start event has been fired on it.");
337                    }
338                    synchronized (oldParent) {
339                        parent.children.remove(this);
340                        parent.tree.clearHandlerCache();
341                        parent = null;
342                    }
343                    ComponentTree newTree = new ComponentTree(this);
344                    newTree.setEventPipeline(new FeedBackPipelineFilter(
345                        newTree, new EventProcessor(newTree)));
346                    setTree(newTree);
347                }
348                Detached evt = new Detached(component(), oldParent.component());
349                oldParent.fire(evt);
350                evt = new Detached(component(), oldParent.component());
351                fire(evt);
352            }
353            return component();
354        }
355    }
356
357    /*
358     * (non-Javadoc)
359     * 
360     * @see java.lang.Iterable#iterator()
361     */
362    @Override
363    public Iterator<ComponentType> iterator() {
364        return new ComponentIterator(new TreeIterator(this));
365    }
366
367    /**
368     * A simple wrapper that converts a component vertex iterator
369     * to a component (type) iterator.
370     */
371    private static class ComponentIterator implements Iterator<ComponentType> {
372
373        private final TreeIterator baseIterator;
374
375        /**
376         * @param baseIterator
377         */
378        public ComponentIterator(TreeIterator baseIterator) {
379            this.baseIterator = baseIterator;
380        }
381
382        @Override
383        public boolean hasNext() {
384            return baseIterator.hasNext();
385        }
386
387        @Override
388        public ComponentType next() {
389            return baseIterator.next().component();
390        }
391
392    }
393
394    /**
395     * An iterator for getting all nodes of the tree.
396     */
397    private static class TreeIterator implements Iterator<ComponentVertex> {
398
399        private final Stack<CurPos> stack = new Stack<>();
400        private final ComponentTree tree;
401
402        /**
403         * The current position.
404         */
405        private class CurPos {
406            public ComponentVertex current;
407            public Iterator<ComponentVertex> childIter;
408
409            /**
410             * Instantiates a new current position
411             *
412             * @param vertex the cm
413             */
414            public CurPos(ComponentVertex vertex) {
415                current = vertex;
416                childIter = current.children.iterator();
417            }
418        }
419
420        /**
421         * Instantiates a new tree iterator.
422         *
423         * @param root the root
424         */
425        public TreeIterator(ComponentVertex root) {
426            tree = root.tree();
427            stack.push(new CurPos(root));
428        }
429
430        /*
431         * (non-Javadoc)
432         * 
433         * @see java.util.Iterator#hasNext()
434         */
435        @Override
436        public boolean hasNext() {
437            return !stack.empty();
438        }
439
440        /*
441         * (non-Javadoc)
442         * 
443         * @see java.util.Iterator#next()
444         */
445        @Override
446        @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
447        public ComponentVertex next() {
448            if (stack.empty()) {
449                throw new NoSuchElementException();
450            }
451            CurPos pos = stack.peek();
452            @SuppressWarnings("PMD.PrematureDeclaration")
453            ComponentVertex res = pos.current;
454            while (true) {
455                synchronized (pos.current) {
456                    if (pos.current.tree != tree) {
457                        throw new ConcurrentModificationException();
458                    }
459                    if (pos.childIter.hasNext()) {
460                        stack.push(new CurPos(pos.childIter.next()));
461                        break;
462                    }
463                }
464                stack.pop();
465                if (stack.empty()) {
466                    break;
467                }
468                pos = stack.peek();
469            }
470            return res;
471        }
472
473        /*
474         * (non-Javadoc)
475         * 
476         * @see java.util.Iterator#remove()
477         */
478        @Override
479        public void remove() {
480            throw new UnsupportedOperationException();
481        }
482    }
483
484    @Override
485    public void addHandler(Method method, HandlerScope scope, int priority) {
486        handlers.add(HandlerReference.newRef(component(),
487            method, priority, scope));
488    }
489
490    /*
491     * (non-Javadoc)
492     * 
493     * @see org.jgrapes.core.core.Manager#fire
494     * (org.jgrapes.core.Event, org.jgrapes.core.Channel)
495     */
496    @Override
497    public <T> Event<T> fire(Event<T> event, Channel... channels) {
498        if (channels.length == 0) {
499            channels = event.channels();
500            if (channels.length == 0) {
501                channels = new Channel[] { channel() };
502            }
503        }
504        event.setChannels(channels);
505        tree().fire(event, channels);
506        return event;
507    }
508
509    /**
510     * Collects all handlers. Iterates over the tree with this object
511     * as root and for all child components adds the matching handlers to
512     * the result set recursively.
513     * 
514     * @param hdlrs the result set
515     * @param event the event to match
516     * @param channels the channels to match
517     */
518    @SuppressWarnings("PMD.UseVarargs")
519    /* default */ void collectHandlers(Collection<HandlerReference> hdlrs,
520            EventBase<?> event, Channel[] channels) {
521        for (HandlerReference hdlr : handlers) {
522            if (hdlr.handles(event, channels)) {
523                hdlrs.add(hdlr);
524            }
525        }
526        for (ComponentVertex child : children) {
527            child.collectHandlers(hdlrs, event, channels);
528        }
529    }
530
531    /*
532     * (non-Javadoc)
533     * 
534     * @see org.jgrapes.core.Manager#activeEventPipeline()
535     */
536    @Override
537    public EventPipeline activeEventPipeline() {
538        return new CheckingPipelineFilter(tree(),
539            tree().eventPipeline(), channel());
540    }
541
542    /*
543     * (non-Javadoc)
544     * 
545     * @see org.jgrapes.core.Manager#newSynchronousPipeline()
546     */
547    @Override
548    public EventPipeline newSyncEventPipeline() {
549        return new CheckingPipelineFilter(tree(),
550            new SynchronousEventProcessor(tree()), channel());
551    }
552
553    /*
554     * (non-Javadoc)
555     * 
556     * @see org.jgrapes.core.Manager#newEventPipeline()
557     */
558    @Override
559    public EventPipeline newEventPipeline() {
560        return new CheckingPipelineFilter(tree(), new EventProcessor(tree()),
561            channel());
562    }
563
564    /*
565     * (non-Javadoc)
566     * 
567     * @see org.jgrapes.core.Manager#newEventPipeline(java.util.concurrent.
568     * ExecutorService)
569     */
570    @Override
571    public EventPipeline newEventPipeline(ExecutorService executorService) {
572        return new CheckingPipelineFilter(tree(),
573            new EventProcessor(tree(), executorService), channel());
574    }
575
576    /**
577     * If a name has been set for this component 
578     * (see {@link Manager#setName(String)}), return the name,
579     * else return the object name provided by 
580     * {@link Components#objectName(Object)}, using
581     * {@link #component()} as argument.
582     */
583    @Override
584    public String toString() {
585        if (name != null) {
586            return name;
587        }
588        return Components.objectName(component());
589    }
590
591    /*
592     * (non-Javadoc)
593     * 
594     * @see org.jgrapes.core.Manager#registerAsGenerator()
595     */
596    @Override
597    public void registerAsGenerator() {
598        GeneratorRegistry.instance().add(component());
599    }
600
601    /*
602     * (non-Javadoc)
603     * 
604     * @see org.jgrapes.core.Manager#unregisterAsGenerator()
605     */
606    @Override
607    public void unregisterAsGenerator() {
608        GeneratorRegistry.instance().remove(component());
609    }
610
611}