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