package org.bukkit.plugin.messaging;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Standard implementation to {@link Messenger}
*/
public class StandardMessenger implements Messenger
{
private final Map<String, Set<PluginMessageListenerRegistration>> incomingByChannel = new HashMap<String, Set<PluginMessageListenerRegistration>>();
private final Map<Plugin, Set<PluginMessageListenerRegistration>> incomingByPlugin = new HashMap<Plugin, Set<PluginMessageListenerRegistration>>();
private final Map<String, Set<Plugin>> outgoingByChannel = new HashMap<String, Set<Plugin>>();
private final Map<Plugin, Set<String>> outgoingByPlugin = new HashMap<Plugin, Set<String>>();
private final Object incomingLock = new Object();
private final Object outgoingLock = new Object();
private void addToOutgoing(Plugin plugin, String channel)
{
synchronized(outgoingLock)
{
Set<Plugin> plugins = outgoingByChannel.get(channel);
Set<String> channels = outgoingByPlugin.get(plugin);
if(plugins == null)
{
plugins = new HashSet<Plugin>();
outgoingByChannel.put(channel, plugins);
}
if(channels == null)
{
channels = new HashSet<String>();
outgoingByPlugin.put(plugin, channels);
}
plugins.add(plugin);
channels.add(channel);
}
}
private void removeFromOutgoing(Plugin plugin, String channel)
{
synchronized(outgoingLock)
{
Set<Plugin> plugins = outgoingByChannel.get(channel);
Set<String> channels = outgoingByPlugin.get(plugin);
if(plugins != null)
{
plugins.remove(plugin);
if(plugins.isEmpty())
{
outgoingByChannel.remove(channel);
}
}
if(channels != null)
{
channels.remove(channel);
if(channels.isEmpty())
{
outgoingByChannel.remove(channel);
}
}
}
}
private void removeFromOutgoing(Plugin plugin)
{
synchronized(outgoingLock)
{
Set<String> channels = outgoingByPlugin.get(plugin);
if(channels != null)
{
String[] toRemove = channels.toArray(new String[0]);
outgoingByPlugin.remove(plugin);
for(String channel : toRemove)
{
removeFromOutgoing(plugin, channel);
}
}
}
}
private void addToIncoming(PluginMessageListenerRegistration registration)
{
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByChannel.get(registration.getChannel());
if(registrations == null)
{
registrations = new HashSet<PluginMessageListenerRegistration>();
incomingByChannel.put(registration.getChannel(), registrations);
}
else
{
if(registrations.contains(registration))
{
throw new IllegalArgumentException("This registration already exists");
}
}
registrations.add(registration);
registrations = incomingByPlugin.get(registration.getPlugin());
if(registrations == null)
{
registrations = new HashSet<PluginMessageListenerRegistration>();
incomingByPlugin.put(registration.getPlugin(), registrations);
}
else
{
if(registrations.contains(registration))
{
throw new IllegalArgumentException("This registration already exists");
}
}
registrations.add(registration);
}
}
private void removeFromIncoming(PluginMessageListenerRegistration registration)
{
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByChannel.get(registration.getChannel());
if(registrations != null)
{
registrations.remove(registration);
if(registrations.isEmpty())
{
incomingByChannel.remove(registration.getChannel());
}
}
registrations = incomingByPlugin.get(registration.getPlugin());
if(registrations != null)
{
registrations.remove(registration);
if(registrations.isEmpty())
{
incomingByPlugin.remove(registration.getPlugin());
}
}
}
}
private void removeFromIncoming(Plugin plugin, String channel)
{
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin);
if(registrations != null)
{
PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]);
for(PluginMessageListenerRegistration registration : toRemove)
{
if(registration.getChannel().equals(channel))
{
removeFromIncoming(registration);
}
}
}
}
}
private void removeFromIncoming(Plugin plugin)
{
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin);
if(registrations != null)
{
PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]);
incomingByPlugin.remove(plugin);
for(PluginMessageListenerRegistration registration : toRemove)
{
removeFromIncoming(registration);
}
}
}
}
public boolean isReservedChannel(String channel)
{
validateChannel(channel);
return channel.equals("REGISTER") || channel.equals("UNREGISTER");
}
public void registerOutgoingPluginChannel(Plugin plugin, String channel)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
validateChannel(channel);
if(isReservedChannel(channel))
{
throw new ReservedChannelException(channel);
}
addToOutgoing(plugin, channel);
}
public void unregisterOutgoingPluginChannel(Plugin plugin, String channel)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
validateChannel(channel);
removeFromOutgoing(plugin, channel);
}
public void unregisterOutgoingPluginChannel(Plugin plugin)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
removeFromOutgoing(plugin);
}
public PluginMessageListenerRegistration registerIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
validateChannel(channel);
if(isReservedChannel(channel))
{
throw new ReservedChannelException(channel);
}
if(listener == null)
{
throw new IllegalArgumentException("Listener cannot be null");
}
PluginMessageListenerRegistration result = new PluginMessageListenerRegistration(this, plugin, channel, listener);
addToIncoming(result);
return result;
}
public void unregisterIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
if(listener == null)
{
throw new IllegalArgumentException("Listener cannot be null");
}
validateChannel(channel);
removeFromIncoming(new PluginMessageListenerRegistration(this, plugin, channel, listener));
}
public void unregisterIncomingPluginChannel(Plugin plugin, String channel)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
validateChannel(channel);
removeFromIncoming(plugin, channel);
}
public void unregisterIncomingPluginChannel(Plugin plugin)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
removeFromIncoming(plugin);
}
public Set<String> getOutgoingChannels()
{
synchronized(outgoingLock)
{
Set<String> keys = outgoingByChannel.keySet();
return ImmutableSet.copyOf(keys);
}
}
public Set<String> getOutgoingChannels(Plugin plugin)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
synchronized(outgoingLock)
{
Set<String> channels = outgoingByPlugin.get(plugin);
if(channels != null)
{
return ImmutableSet.copyOf(channels);
}
else
{
return ImmutableSet.of();
}
}
}
public Set<String> getIncomingChannels()
{
synchronized(incomingLock)
{
Set<String> keys = incomingByChannel.keySet();
return ImmutableSet.copyOf(keys);
}
}
public Set<String> getIncomingChannels(Plugin plugin)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin);
if(registrations != null)
{
Builder<String> builder = ImmutableSet.builder();
for(PluginMessageListenerRegistration registration : registrations)
{
builder.add(registration.getChannel());
}
return builder.build();
}
else
{
return ImmutableSet.of();
}
}
}
public Set<PluginMessageListenerRegistration> getIncomingChannelRegistrations(Plugin plugin)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin);
if(registrations != null)
{
return ImmutableSet.copyOf(registrations);
}
else
{
return ImmutableSet.of();
}
}
}
public Set<PluginMessageListenerRegistration> getIncomingChannelRegistrations(String channel)
{
validateChannel(channel);
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByChannel.get(channel);
if(registrations != null)
{
return ImmutableSet.copyOf(registrations);
}
else
{
return ImmutableSet.of();
}
}
}
public Set<PluginMessageListenerRegistration> getIncomingChannelRegistrations(Plugin plugin, String channel)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
validateChannel(channel);
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin);
if(registrations != null)
{
Builder<PluginMessageListenerRegistration> builder = ImmutableSet.builder();
for(PluginMessageListenerRegistration registration : registrations)
{
if(registration.getChannel().equals(channel))
{
builder.add(registration);
}
}
return builder.build();
}
else
{
return ImmutableSet.of();
}
}
}
public boolean isRegistrationValid(PluginMessageListenerRegistration registration)
{
if(registration == null)
{
throw new IllegalArgumentException("Registration cannot be null");
}
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(registration.getPlugin());
if(registrations != null)
{
return registrations.contains(registration);
}
return false;
}
}
public boolean isIncomingChannelRegistered(Plugin plugin, String channel)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
validateChannel(channel);
synchronized(incomingLock)
{
Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin);
if(registrations != null)
{
for(PluginMessageListenerRegistration registration : registrations)
{
if(registration.getChannel().equals(channel))
{
return true;
}
}
}
return false;
}
}
public boolean isOutgoingChannelRegistered(Plugin plugin, String channel)
{
if(plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null");
}
validateChannel(channel);
synchronized(outgoingLock)
{
Set<String> channels = outgoingByPlugin.get(plugin);
if(channels != null)
{
return channels.contains(channel);
}
return false;
}
}
public void dispatchIncomingMessage(Player source, String channel, byte[] message)
{
if(source == null)
{
throw new IllegalArgumentException("Player source cannot be null");
}
if(message == null)
{
throw new IllegalArgumentException("Message cannot be null");
}
validateChannel(channel);
Set<PluginMessageListenerRegistration> registrations = getIncomingChannelRegistrations(channel);
for(PluginMessageListenerRegistration registration : registrations)
{
// Spigot Start
try
{
registration.getListener().onPluginMessageReceived(channel, source, message);
} catch(Throwable t)
{
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Could not pass incoming plugin message to " + registration.getPlugin(), t);
}
// Spigot End
}
}
/**
* Validates a Plugin Channel name.
*
* @param channel Channel name to validate.
*/
public static void validateChannel(String channel)
{
if(channel == null)
{
throw new IllegalArgumentException("Channel cannot be null");
}
if(channel.length() > Messenger.MAX_CHANNEL_SIZE)
{
throw new ChannelNameTooLongException(channel);
}
}
/**
* Validates the input of a Plugin Message, ensuring the arguments are all
* valid.
*
* @param messenger Messenger to use for validation.
* @param source Source plugin of the Message.
* @param channel Plugin Channel to send the message by.
* @param message Raw message payload to send.
* @throws IllegalArgumentException Thrown if the source plugin is
* disabled.
* @throws IllegalArgumentException Thrown if source, channel or message
* is null.
* @throws MessageTooLargeException Thrown if the message is too big.
* @throws ChannelNameTooLongException Thrown if the channel name is too
* long.
* @throws ChannelNotRegisteredException Thrown if the channel is not
* registered for this plugin.
*/
public static void validatePluginMessage(Messenger messenger, Plugin source, String channel, byte[] message)
{
if(messenger == null)
{
throw new IllegalArgumentException("Messenger cannot be null");
}
if(source == null)
{
throw new IllegalArgumentException("Plugin source cannot be null");
}
if(!source.isEnabled())
{
throw new IllegalArgumentException("Plugin must be enabled to send messages");
}
if(message == null)
{
throw new IllegalArgumentException("Message cannot be null");
}
if(!messenger.isOutgoingChannelRegistered(source, channel))
{
throw new ChannelNotRegisteredException(channel);
}
if(message.length > Messenger.MAX_MESSAGE_SIZE)
{
throw new MessageTooLargeException(message);
}
validateChannel(channel);
}
}