package mightypork.utils.control.bus; import java.util.Collection; import java.util.HashSet; import mightypork.utils.control.bus.clients.DelegatingClient; import mightypork.utils.control.bus.clients.ToggleableClient; import mightypork.utils.logging.Log; /** * Message channel, module of {@link EventBus} * * @author MightyPork * @param message type * @param client (subscriber) type */ final public class EventChannel, CLIENT> { private Class clientClass; private Class messageClass; private boolean logging = false; /** * Create a channel * * @param messageClass event class * @param clientClass client class */ public EventChannel(Class messageClass, Class clientClass) { if (messageClass == null || clientClass == null) throw new NullPointerException("Null Message or Client class."); this.clientClass = clientClass; this.messageClass = messageClass; } /** * Enable logging of non-warning debug messages. * * @param logging enable logging */ public void enableLogging(boolean logging) { this.logging = logging; } /** * Try to broadcast a message.
* If message is of wrong type, false is returned. * * @param message a message to be sent * @param clients collection of clients * @return true if message was accepted by this channel */ public boolean broadcast(Event message, Collection clients) { if (!canBroadcast(message)) return false; EVENT evt = messageClass.cast(message); doBroadcast(evt, clients, new HashSet()); return true; } /** * Send the message * * @param message sent message * @param clients subscribing clients * @param processed clients already processed */ private void doBroadcast(final EVENT message, final Collection clients, final Collection processed) { for (Object client : clients) { if (!isClientValid(client)) { continue; } if (processed.contains(client)) { Log.w(" Client already served: " + Log.str(client)); continue; } processed.add(client); // opt-out if (client instanceof ToggleableClient) { if (!((ToggleableClient) client).isListening()) { if (logging) Log.f3(" Client disabled: " + Log.str(client)); continue; } } sendTo(client, message); if (client instanceof DelegatingClient) { if (((DelegatingClient) client).doesDelegate()) { Collection children = ((DelegatingClient) client).getChildClients(); if (children != null && children.size() > 0) doBroadcast(message, children, processed); } else { if (logging) Log.f3(" Client not delegating: " + Log.str(client)); } } } } /** * Send a message to a client. * * @param client target client * @param message message to send */ @SuppressWarnings("unchecked") private void sendTo(Object client, EVENT message) { if (isClientOfType(client)) { if (logging) Log.f3(" Delivered " + Log.str(message) + " to " + Log.str(client)); ((Event) message).handleBy((CLIENT) client); } } /** * Check if the given message can be broadcasted by this * {@link EventChannel} * * @param message event object * @return can be broadcasted */ public boolean canBroadcast(Event message) { return message != null && messageClass.isInstance(message); } /** * Create an instance for given types * * @param messageClass event class * @param clientClass client class * @return the broadcaster */ public static , F_CLIENT> EventChannel create(Class messageClass, Class clientClass) { return new EventChannel(messageClass, clientClass); } /** * Check if client is of channel type * * @param client client * @return is of type */ private boolean isClientOfType(Object client) { return clientClass.isInstance(client); } /** * Check if the channel is compatible with given * * @param client client * @return is supported */ public boolean isClientValid(Object client) { return isClientOfType(client) || (client instanceof DelegatingClient); } @Override public int hashCode() { final int prime = 13; int result = 1; result = prime * result + ((clientClass == null) ? 0 : clientClass.hashCode()); result = prime * result + ((messageClass == null) ? 0 : messageClass.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof EventChannel)) return false; EventChannel other = (EventChannel) obj; if (clientClass == null) { if (other.clientClass != null) return false; } else if (!clientClass.equals(other.clientClass)) return false; if (messageClass == null) { if (other.messageClass != null) return false; } else if (!messageClass.equals(other.messageClass)) return false; return true; } @Override public String toString() { return "{ " + Log.str(messageClass) + " => " + Log.str(clientClass) + " }"; } }