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    @Override
066    public Set<K> keys() {
067        return entries.keySet();
068    }
069
070    /**
071     * Return all known values.
072     *
073     * @return the collection
074     */
075    @Override
076    public Collection<Value<C, A>> values() {
077        return entries.values();
078    }
079
080    /**
081     * Returns the channel and associates data registered for the key
082     * or an empty optional if no mapping exists.
083     * 
084     * @param key the key
085     * @return the result
086     */
087    public Optional<Value<C, A>> value(K key) {
088        return Optional.ofNullable(entries.get(key));
089    }
090
091    /**
092     * Store the given data.
093     *
094     * @param key the key
095     * @param channel the channel
096     * @param associated the associated
097     * @return the channel manager
098     */
099    public ChannelManager<K, C, A> put(K key, C channel, A associated) {
100        entries.put(key, new Value<>(channel, associated));
101        return this;
102    }
103
104    /**
105     * Store the given data.
106     *
107     * @param key the key
108     * @param channel the channel
109     * @return the channel manager
110     */
111    public ChannelManager<K, C, A> put(K key, C channel) {
112        put(key, channel, null);
113        return this;
114    }
115
116    /**
117     * Returns the {@link Channel} for the given name, creating it using 
118     * the supplier passed to the constructor if it doesn't exist yet.
119     *
120     * @param key the key
121     * @return the channel
122     */
123    public C channelGet(K key) {
124        return computeIfAbsent(key, supplier);
125    }
126
127    /**
128     * Returns the {@link Channel} for the given name, creating it using
129     * the given supplier if it doesn't exist yet. 
130     *
131     * @param key the key
132     * @param supplier the supplier
133     * @return the channel
134     */
135    @SuppressWarnings({ "PMD.AssignmentInOperand",
136        "PMD.DataflowAnomalyAnalysis" })
137    public C computeIfAbsent(K key, Function<K, C> supplier) {
138        return entries.computeIfAbsent(key,
139            k -> new Value<>(supplier.apply(k), null)).channel();
140    }
141
142    /**
143     * Associate the entry for the channel with the given data. The entry
144     * for the channel must already exist.
145     *
146     * @param key the key
147     * @param data the data
148     * @return the channel manager
149     */
150    public ChannelManager<K, C, A> associate(K key, A data) {
151        Optional.ofNullable(entries.computeIfPresent(key,
152            (k, existing) -> new Value<>(existing.channel(), data)));
153        return this;
154    }
155
156    /**
157     * Removes the channel with the given name.
158     *
159     * @param name the name
160     */
161    public void remove(String name) {
162        entries.remove(name);
163    }
164}