/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.utils.observer.update; import com.jcwhatever.nucleus.collections.observer.agent.AgentHashMap; import com.jcwhatever.nucleus.collections.observer.agent.AgentMap; import com.jcwhatever.nucleus.utils.PreCon; import java.util.ArrayList; import java.util.List; /** * Used for types that need to keep multiple update agents for multiple update contexts. * * <p>{@link NamedUpdateAgents} uses a lazy initialization approach to * prevent using resources if they are not needed. Invoking the {@link #getAgent} * method always returns a result. If the agent is not present, a new one is created.</p> * * <p>Use {@link #hasAgent} to determine if the agent has been created yet.</p> * * <p>Use {@link #update} to send updates to subscribers of a named agent without causing * the unnecessary object initialization that would happen if the update were to be done * by invoking {@link #getAgent} first.</p> */ public class NamedUpdateAgents { volatile AgentMap<String, UpdateAgent<?>> _agents; private volatile String _recentName; private volatile UpdateAgent<?> _recent; /** * Get the number of instantiated agents. */ public int size() { if (_agents == null) return 0; return _agents.size(); } /** * Determine if there are any initialized agents. */ public boolean isEmpty() { return size() == 0; } /** * Determine if the tracker contains an agent by a certain name. * * @param name The name of the agent. */ public boolean hasAgent(String name) { PreCon.notNull(name); if (_agents == null) return false; if (name.equals(_recentName)) { if (_recent.isDisposed()) { _recentName = null; _recent = null; } else { return true; } } UpdateAgent<?> agent = _agents.get(name); if (agent != null) { _recentName = name; _recent = agent; } return agent != null; } /** * Get an existing agent or create a new one. * * @param name The name of the agent. * * @param <T> The agent value type. */ public <T> UpdateAgent<T> getAgent(String name) { PreCon.notNull(name); if (name.equals(_recentName)) { if (_recent.isDisposed()) { _recentName = null; _recent = null; } else { @SuppressWarnings("unchecked") UpdateAgent<T> recent = (UpdateAgent<T>) _recent; return recent; } } UpdateAgent<T> agent = null; if (_agents == null) { _agents = new AgentHashMap<>(5); } else { @SuppressWarnings("unchecked") UpdateAgent<T> get = (UpdateAgent<T>)_agents.get(name); agent = get; } if (agent == null) { agent = new UpdateAgent<>(3); _agents.set(name, agent); } return agent; } /** * Update an agents subscribers. * * @param name The name of the agent. * @param argument The update argument. * * @param <T> The argument type. */ public <T> void update(String name, T argument) { PreCon.notNull(name); if (!hasAgent(name)) return; @SuppressWarnings("unchecked") UpdateAgent<T> agent = (UpdateAgent<T>)_recent; agent.update(argument); } /** * Dispose all agents. * * <p>The {@link NamedUpdateAgents} instance can still be used.</p> */ public void disposeAgents() { if (_agents == null) return; List<UpdateAgent<?>> agents; //noinspection SynchronizeOnNonFinalField synchronized (_agents) { agents = new ArrayList<>(_agents.values()); } for (UpdateAgent<?> agent : agents) { agent.dispose(); } _recentName = null; _recent = null; _agents.dispose(); _agents = null; } }