Newer
Older
ultramine_bukkit / src / main / java / org / bukkit / plugin / messaging / StandardMessenger.java
@vlad20012 vlad20012 on 24 Feb 2017 14 KB initial
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);
	}
}