001/*
002 * VM-Operator
003 * Copyright (C) 2023 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public 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 <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.vmoperator.manager.events;
020
021import java.util.Collection;
022import java.util.Map;
023import java.util.Optional;
024import java.util.Set;
025import java.util.concurrent.ConcurrentHashMap;
026import java.util.function.Function;
027import org.jgrapes.core.Channel;
028
029/**
030 * A channel manager that maintains mappings from a key to a channel.
031 * As a convenience, it is possible to additionally associate arbitrary
032 * data with the entry (and thus with the channel).
033 * 
034 * The manager should be used by a component that defines channels for 
035 * housekeeping. It can be shared between this component and another
036 * component, preferably using the {@link #fixed()} view for the 
037 * second component. Alternatively, the second component can use a
038 * {@link ChannelCache} to track the mappings using events.
039 *
040 * @param <K> the key type
041 * @param <C> the channel type
042 * @param <A> the type of the associated data
043 */
044public class ChannelManager<K, C extends Channel, A> {
045
046    private final Map<K, Both<C, A>> channels = new ConcurrentHashMap<>();
047    private final Function<K, C> supplier;
048    private ChannelManager<K, C, A> readOnly;
049
050    /**
051     * Combines the channel and the associated data.
052     *
053     * @param <C> the generic type
054     * @param <A> the generic type
055     */
056    @SuppressWarnings("PMD.ShortClassName")
057    public static class Both<C extends Channel, A> {
058
059        /** The channel. */
060        public C channel;
061
062        /** The associated. */
063        public A associated;
064
065        /**
066         * Instantiates a new both.
067         *
068         * @param channel the channel
069         * @param associated the associated
070         */
071        public Both(C channel, A associated) {
072            super();
073            this.channel = channel;
074            this.associated = associated;
075        }
076    }
077
078    /**
079     * Instantiates a new channel manager.
080     *
081     * @param supplier the supplier that creates new channels
082     */
083    public ChannelManager(Function<K, C> supplier) {
084        this.supplier = supplier;
085    }
086
087    /**
088     * Instantiates a new channel manager without a default supplier.
089     */
090    public ChannelManager() {
091        this(k -> null);
092    }
093
094    /**
095     * Returns the channel and associates data registered for the key
096     * or an empty optional if no mapping exists.
097     * 
098     * @param key the key
099     * @return the result
100     */
101    public Optional<Both<C, A>> both(K key) {
102        synchronized (channels) {
103            return Optional.ofNullable(channels.get(key));
104        }
105    }
106
107    /**
108     * Store the given data.
109     *
110     * @param key the key
111     * @param channel the channel
112     * @param associated the associated
113     * @return the channel manager
114     */
115    public ChannelManager<K, C, A> put(K key, C channel, A associated) {
116        channels.put(key, new Both<>(channel, associated));
117        return this;
118    }
119
120    /**
121     * Store the given data.
122     *
123     * @param key the key
124     * @param channel the channel
125     * @return the channel manager
126     */
127    public ChannelManager<K, C, A> put(K key, C channel) {
128        put(key, channel, null);
129        return this;
130    }
131
132    /**
133     * Returns the channel registered for the key or an empty optional
134     * if no mapping exists.
135     *
136     * @param key the key
137     * @return the optional
138     */
139    public Optional<C> channel(K key) {
140        return both(key).map(b -> b.channel);
141    }
142
143    /**
144     * Returns the {@link Channel} for the given name, creating it using 
145     * the supplier passed to the constructor if it doesn't exist yet.
146     *
147     * @param key the key
148     * @return the channel
149     */
150    public Optional<C> getChannel(K key) {
151        return getChannel(key, supplier);
152    }
153
154    /**
155     * Returns the {@link Channel} for the given name, creating it using
156     * the given supplier if it doesn't exist yet. 
157     *
158     * @param key the key
159     * @param supplier the supplier
160     * @return the channel
161     */
162    @SuppressWarnings({ "PMD.AssignmentInOperand",
163        "PMD.DataflowAnomalyAnalysis" })
164    public Optional<C> getChannel(K key, Function<K, C> supplier) {
165        synchronized (channels) {
166            return Optional
167                .of(Optional.ofNullable(channels.get(key))
168                    .map(v -> v.channel)
169                    .orElseGet(() -> {
170                        var channel = supplier.apply(key);
171                        channels.put(key, new Both<>(channel, null));
172                        return channel;
173                    }));
174        }
175    }
176
177    /**
178     * Associate the entry for the channel with the given data. The entry
179     * for the channel must already exist.
180     *
181     * @param key the key
182     * @param data the data
183     * @return the channel manager
184     */
185    public ChannelManager<K, C, A> associate(K key, A data) {
186        synchronized (channels) {
187            Optional.ofNullable(channels.get(key))
188                .ifPresent(v -> v.associated = data);
189        }
190        return this;
191    }
192
193    /**
194     * Return the data associated with the entry for the channel.
195     *
196     * @param key the key
197     * @return the data
198     */
199    public Optional<A> associated(K key) {
200        return both(key).map(b -> b.associated);
201    }
202
203    /**
204     * Returns all associated data.
205     *
206     * @return the collection
207     */
208    public Collection<A> associated() {
209        synchronized (channels) {
210            return channels.values().stream()
211                .filter(v -> v.associated != null)
212                .map(v -> v.associated).toList();
213        }
214    }
215
216    /**
217     * Removes the channel with the given name.
218     *
219     * @param name the name
220     */
221    public void remove(String name) {
222        synchronized (channels) {
223            channels.remove(name);
224        }
225    }
226
227    /**
228     * Returns all known keys.
229     *
230     * @return the sets the
231     */
232    public Set<K> keys() {
233        return channels.keySet();
234    }
235
236    /**
237     * Returns a read only view of this channel manager. The methods
238     * that usually create a new entry refrain from doing so. The
239     * methods that change the value of channel and {@link #remove(String)}
240     * do nothing. The associated data, however, can still be changed.
241     *
242     * @return the channel manager
243     */
244    public ChannelManager<K, C, A> fixed() {
245        if (readOnly == null) {
246            readOnly = new ChannelManager<>(supplier) {
247
248                @Override
249                public Optional<Both<C, A>> both(K key) {
250                    return ChannelManager.this.both(key);
251                }
252
253                @Override
254                public ChannelManager<K, C, A> put(K key, C channel,
255                        A associated) {
256                    return associate(key, associated);
257                }
258
259                @Override
260                public Optional<C> getChannel(K key) {
261                    return ChannelManager.this.channel(key);
262                }
263
264                @Override
265                public Optional<C> getChannel(K key, Function<K, C> supplier) {
266                    return ChannelManager.this.channel(key);
267                }
268
269                @Override
270                public ChannelManager<K, C, A> associate(K key, A data) {
271                    return ChannelManager.this.associate(key, data);
272                }
273
274                @Override
275                public Optional<A> associated(K key) {
276                    return ChannelManager.this.associated(key);
277                }
278
279                @Override
280                public Collection<A> associated() {
281                    return ChannelManager.this.associated();
282                }
283
284                @Override
285                public void remove(String name) {
286                    // Do nothing
287                }
288
289                @Override
290                public Set<K> keys() {
291                    return ChannelManager.this.keys();
292                }
293
294                @Override
295                public ChannelManager<K, C, A> fixed() {
296                    return ChannelManager.this.fixed();
297                }
298
299            };
300        }
301        return readOnly;
302    }
303}