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    @Override
093    public Optional<Value<C, A>> value(K key) {
094        return Optional.ofNullable(entries.get(key));
095    }
096
097    /**
098     * Store the given data.
099     *
100     * @param key the key
101     * @param channel the channel
102     * @param associated the associated
103     * @return the channel manager
104     */
105    public ChannelManager<K, C, A> put(K key, C channel, A associated) {
106        entries.put(key, new Value<>(channel, associated));
107        return this;
108    }
109
110    /**
111     * Store the given data.
112     *
113     * @param key the key
114     * @param channel the channel
115     * @return the channel manager
116     */
117    public ChannelManager<K, C, A> put(K key, C channel) {
118        put(key, channel, null);
119        return this;
120    }
121
122    /**
123     * Creates a new channel without adding it to the channel manager.
124     * After fully initializing the channel, it should be added to the 
125     * manager using {@link #put(K, C)}.
126     *
127     * @param key the key
128     * @return the c
129     */
130    public C createChannel(K key) {
131        return supplier.apply(key);
132    }
133
134    /**
135     * Returns the {@link Channel} for the given name, creating it using 
136     * the supplier passed to the constructor if it doesn't exist yet.
137     *
138     * @param key the key
139     * @return the channel
140     */
141    public C channelGet(K key) {
142        return computeIfAbsent(key, supplier);
143    }
144
145    /**
146     * Returns the {@link Channel} for the given name, creating it using
147     * the given supplier if it doesn't exist yet. 
148     *
149     * @param key the key
150     * @param supplier the supplier
151     * @return the channel
152     */
153    public C computeIfAbsent(K key, Function<K, C> supplier) {
154        return entries.computeIfAbsent(key,
155            k -> new Value<>(supplier.apply(k), null)).channel();
156    }
157
158    /**
159     * Associate the entry for the channel with the given data. The entry
160     * for the channel must already exist.
161     *
162     * @param key the key
163     * @param data the data
164     * @return the channel manager
165     */
166    public ChannelManager<K, C, A> associate(K key, A data) {
167        Optional.ofNullable(entries.computeIfPresent(key,
168            (k, existing) -> new Value<>(existing.channel(), data)));
169        return this;
170    }
171
172    /**
173     * Removes the channel with the given name.
174     *
175     * @param name the name
176     */
177    public void remove(K name) {
178        entries.remove(name);
179    }
180}