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;
020
021import org.jgrapes.core.annotation.Handler;
022
023/**
024 * Instances of this interface can be used as a communication 
025 * bus for sending events between components. The instances work
026 * as identifiers of channels. Their only functionality is defined
027 * by the {@link Eligible} interface, which allows a channel
028 * (used as attribute of an {@link Event}) to be matched against 
029 * a criterion specified in a {@link Handler}.
030 * 
031 * The need to use the {@link Eligible} interface for comparison
032 * arises from the fact that we cannot use objects as values in
033 * annotations. It must therefore be possible to match channels
034 * (objects) against criteria that can be expressed as constant 
035 * values.
036 * 
037 * Some values have been defined to represent special criteria.
038 * 
039 * * If the value `Channel.class` is specified as criterion in
040 *   a handler, all channel instances match. It is the "catch-all"
041 *   criterion.
042 * 
043 * * If the value `{@link Default}.class` is specified as criterion
044 *   in a handler, the channels from an {@link Event} are
045 *   matched agains the criterion from the component's channel
046 *   (returned by the {@link Manager#channel() channel()} method).  
047 * 
048 * The predefined {@link #BROADCAST} channel is a channel instance
049 * that implements the {@link Eligible} interface in such a way that
050 * all criteria match. Events fired on the {@link #BROADCAST} channel
051 * will therefore be accepted by all handlers (as its name suggests).
052 * 
053 * For ordinary usage, the implementing classes {@link ClassChannel}
054 * and {@link NamedChannel} should be sufficient. If another type of
055 * `Channel` is needed, its implementation must make sure that 
056 * {@link Eligible#isEligibleFor(Object)} returns
057 * `true` if called with `Channel.class` as parameter, else channels 
058 * of the new type will not be delivered to "catch-all" handlers.
059 * 
060 * Objects of type <code>Channel</code> must be immutable.
061 * 
062 * @see Channel#BROADCAST
063 */
064public interface Channel extends Eligible {
065
066    /**
067     * A special channel object that can be passed as argument to 
068     * the constructor of {@link Component#Component(Channel)}. 
069     * Doing this sets the component's channel to the component 
070     * (which is not available as argument when calling the 
071     * constructor).
072     * 
073     * @see Component#Component(Channel)
074     */
075    Channel SELF = new ClassChannel();
076
077    /**
078     * This interface's class can be used to specify the component's 
079     * channel (see {@link Component#channel()}) as criterion in 
080     * handler annotations.
081     * 
082     * Using the component's channel for comparison is the default 
083     * if no channels are specified in the annotation, so specifying 
084     * only this class in the handler annotation is equivalent
085     * to specifying no channel at all. This special channel type is required
086     * if you want to specify a handler that handles events fired on the 
087     * component's channel or on additional channels.
088     */
089    interface Default extends Channel {
090    }
091
092    /**
093     * By default, a channel is eligible for a broadcast and for
094     * its own default criterion (see {@link #defaultCriterion()}.
095     *
096     * @param criterion the criterion
097     * @return true, if is eligible for
098     */
099    @Override
100    default boolean isEligibleFor(Object criterion) {
101        return criterion.equals(BROADCAST.defaultCriterion())
102            || criterion.equals(defaultCriterion());
103    }
104
105    /**
106     * A special channel instance that can be used to send events to
107     * all components.
108     */
109    Channel BROADCAST = new ClassChannel() {
110
111        /**
112         * Returns <code>Channel.class</code>, the value that must
113         * by definition be matched by any channel.
114         * 
115         * @return <code>Channel.class</code>
116         */
117        @Override
118        public Object defaultCriterion() {
119            return Channel.class;
120        }
121
122        /**
123         * Always returns {@code true} because the broadcast channel
124         * is matched by every channel.
125         * 
126         * @return {@code true}
127         */
128        @Override
129        public boolean isEligibleFor(Object criterion) {
130            return true;
131        }
132
133        /*
134         * (non-Javadoc)
135         * 
136         * @see org.jgrapes.core.ClassChannel#toString()
137         */
138        @Override
139        public String toString() {
140            return "BROADCAST";
141        }
142    };
143
144    /**
145     * Returns a textual representation of a channel's criterion.
146     * 
147     * @param criterion the criterion
148     * @return the representation
149     */
150    static String criterionToString(Object criterion) {
151        StringBuilder builder = new StringBuilder();
152        if (criterion instanceof Class) {
153            if (criterion == Channel.class) {
154                builder.append("BROADCAST");
155            } else {
156                builder.append(Components.className((Class<?>) criterion));
157            }
158        } else {
159            builder.append(criterion);
160        }
161        return builder.toString();
162    }
163
164    /**
165     * Returns a textual representation of a channel.
166     * 
167     * @param channel the channel
168     * @return the representation
169     */
170    static String toString(Channel channel) {
171        if (channel == null) {
172            return "null";
173        }
174        StringBuilder builder = new StringBuilder();
175        if (channel instanceof ClassChannel
176            || channel instanceof NamedChannel) {
177            builder.append(criterionToString(channel.defaultCriterion()));
178        } else if (channel == channel.defaultCriterion()) {
179            builder.append(Components.objectName(channel));
180        } else {
181            builder.append(channel.toString());
182        }
183        return builder.toString();
184    }
185
186    /**
187     * Returns a textual representation of an array of channels.
188     * 
189     * @param channels the channels
190     * @return the representation
191     */
192    @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.UseVarargs" })
193    static String toString(Channel[] channels) {
194        StringBuilder builder = new StringBuilder();
195        builder.append('[');
196        boolean first = true;
197        for (Channel c : channels) {
198            if (!first) {
199                builder.append(", ");
200            }
201            builder.append(Channel.toString(c));
202            first = false;
203        }
204        builder.append(']');
205        return builder.toString();
206    }
207
208}