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 * Provides an actively managed implementation of the {@link ChannelDictionary}.
031 * 
032 * The {@link ChannelManager} can be used for housekeeping by any component
033 * that creates channels. It can be shared between this component and
034 * some other component, preferably passing it as {@link ChannelDictionary}
035 * (the read-only view) to the second component. Alternatively, the other
036 * component can use a {@link ChannelTracker} to track the mappings using
037 * events.
038 *
039 * @param <K> the key type
040 * @param <C> the channel type
041 * @param <A> the type of the associated data
042 */
043public class ChannelManager<K, C extends Channel, A>
044        implements ChannelDictionary<K, C, A> {
045
046    private final Map<K, Value<C, A>> entries = new ConcurrentHashMap<>();
047    private final Function<K, C> supplier;
048
049    /**
050     * Instantiates a new channel manager.
051     *
052     * @param supplier the supplier that creates new channels
053     */
054    public ChannelManager(Function<K, C> supplier) {
055        this.supplier = supplier;
056    }
057
058    /**
059     * Instantiates a new channel manager without a default supplier.
060     */
061    public ChannelManager() {
062        this(k -> null);
063    }
064
065    /**
066     * Return all keys.
067     *
068     * @return the keys.
069     */
070    @Override
071    public Set<K> keys() {
072        return entries.keySet();
073    }
074
075    /**
076     * Return all known values.
077     *
078     * @return the collection
079     */
080    @Override
081    public Collection<Value<C, A>> values() {
082        return entries.values();
083    }
084
085    /**
086     * Returns the channel and associates data registered for the key
087     * or an empty optional if no mapping exists.
088     * 
089     * @param key the key
090     * @return the result
091     */
092    public Optional<Value<C, A>> value(K key) {
093        return Optional.ofNullable(entries.get(key));
094    }
095
096    /**
097     * Store the given data.
098     *
099     * @param key the key
100     * @param channel the channel
101     * @param associated the associated
102     * @return the channel manager
103     */
104    public ChannelManager<K, C, A> put(K key, C channel, A associated) {
105        entries.put(key, new Value<>(channel, associated));
106        return this;
107    }
108
109    /**
110     * Store the given data.
111     *
112     * @param key the key
113     * @param channel the channel
114     * @return the channel manager
115     */
116    public ChannelManager<K, C, A> put(K key, C channel) {
117        put(key, channel, null);
118        return this;
119    }
120
121    /**
122     * Creates a new channel without adding it to the channel manager.
123     * After fully initializing the channel, it should be added to the 
124     * manager using {@link #put(K, C)}.
125     *
126     * @param key the key
127     * @return the c
128     */
129    public C createChannel(K key) {
130        return supplier.apply(key);
131    }
132
133    /**
134     * Returns the {@link Channel} for the given name, creating it using 
135     * the supplier passed to the constructor if it doesn't exist yet.
136     *
137     * @param key the key
138     * @return the channel
139     */
140    public C channelGet(K key) {
141        return computeIfAbsent(key, supplier);
142    }
143
144    /**
145     * Returns the {@link Channel} for the given name, creating it using
146     * the given supplier if it doesn't exist yet. 
147     *
148     * @param key the key
149     * @param supplier the supplier
150     * @return the channel
151     */
152    @SuppressWarnings({ "PMD.AssignmentInOperand",
153        "PMD.DataflowAnomalyAnalysis" })
154    public C computeIfAbsent(K key, Function<K, C> supplier) {
155        return entries.computeIfAbsent(key,
156            k -> new Value<>(supplier.apply(k), null)).channel();
157    }
158
159    /**
160     * Associate the entry for the channel with the given data. The entry
161     * for the channel must already exist.
162     *
163     * @param key the key
164     * @param data the data
165     * @return the channel manager
166     */
167    public ChannelManager<K, C, A> associate(K key, A data) {
168        Optional.ofNullable(entries.computeIfPresent(key,
169            (k, existing) -> new Value<>(existing.channel(), data)));
170        return this;
171    }
172
173    /**
174     * Removes the channel with the given name.
175     *
176     * @param name the name
177     */
178    public void remove(String name) {
179        entries.remove(name);
180    }
181}