Rogue: Savage Rats, a retro-themed dungeon crawler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
rogue-savage-rats/src/mightypork/gamecore/control/bus/EventChannel.java

215 lines
5.1 KiB

package mightypork.gamecore.control.bus;
import java.util.Collection;
import java.util.HashSet;
import mightypork.gamecore.control.bus.clients.DelegatingClient;
import mightypork.gamecore.control.bus.clients.ToggleableClient;
import mightypork.gamecore.control.bus.events.Event;
import mightypork.gamecore.control.bus.events.types.SingleReceiverEvent;
import mightypork.utils.logging.Log;
/**
* Event delivery channel, module of {@link EventBus}
*
* @author MightyPork
* @param <EVENT> event type
* @param <CLIENT> client (subscriber) type
*/
final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
private final Class<CLIENT> clientClass;
private final Class<EVENT> eventClass;
/**
* Create a channel
*
* @param eventClass event class
* @param clientClass client class
*/
public EventChannel(Class<EVENT> eventClass, Class<CLIENT> clientClass) {
if (eventClass == null || clientClass == null) {
throw new NullPointerException("Null Event or Client class.");
}
this.clientClass = clientClass;
this.eventClass = eventClass;
}
/**
* Try to broadcast a event.<br>
* If event is of wrong type, <code>false</code> is returned.
*
* @param event a event to be sent
* @param clients collection of clients
* @return true if event was sent
*/
public boolean broadcast(Event<?> event, Collection<Object> clients)
{
if (!canBroadcast(event)) return false;
return doBroadcast(eventClass.cast(event), clients, new HashSet<>());
}
/**
* Send the event
*
* @param event sent event
* @param clients subscribing clients
* @param processed clients already processed
* @return success
*/
private boolean doBroadcast(final EVENT event, final Collection<Object> clients, final Collection<Object> processed)
{
boolean sent = false;
final boolean singular = event.getClass().isAnnotationPresent(SingleReceiverEvent.class);
for (final Object client : clients) {
// exclude obvious non-clients
if (!isClientValid(client)) {
continue;
}
// avoid executing more times
if (processed.contains(client)) {
Log.w("<bus> Client already served: " + Log.str(client));
continue;
}
processed.add(client);
// opt-out
if (client instanceof ToggleableClient) {
if (!((ToggleableClient) client).isListening()) continue;
}
sent |= sendTo(client, event);
// singular event ain't no whore, handled once only.
if (sent && singular) return true;
// pass on to delegated clients
if (client instanceof DelegatingClient) {
if (((DelegatingClient) client).doesDelegate()) {
final Collection<Object> children = ((DelegatingClient) client).getChildClients();
if (children != null && children.size() > 0) {
sent |= doBroadcast(event, children, processed);
}
}
}
}
return sent;
}
/**
* Send an event to a client.
*
* @param client target client
* @param event event to send
* @return success
*/
@SuppressWarnings("unchecked")
private boolean sendTo(Object client, EVENT event)
{
if (isClientOfType(client)) {
((Event<CLIENT>) event).handleBy((CLIENT) client);
return true;
}
return false;
}
/**
* Check if the given event can be broadcasted by this {@link EventChannel}
*
* @param event event object
* @return can be broadcasted
*/
public boolean canBroadcast(Event<?> event)
{
return event != null && eventClass.isInstance(event);
}
/**
* Create an instance for given types
*
* @param eventClass event class
* @param clientClass client class
* @return the broadcaster
*/
public static <F_EVENT extends Event<F_CLIENT>, F_CLIENT> EventChannel<F_EVENT, F_CLIENT> create(Class<F_EVENT> eventClass, Class<F_CLIENT> clientClass)
{
return new EventChannel<>(eventClass, 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 + ((eventClass == null) ? 0 : eventClass.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;
final EventChannel<?, ?> other = (EventChannel<?, ?>) obj;
if (clientClass == null) {
if (other.clientClass != null) return false;
} else if (!clientClass.equals(other.clientClass)) return false;
if (eventClass == null) {
if (other.eventClass != null) return false;
} else if (!eventClass.equals(other.eventClass)) return false;
return true;
}
@Override
public String toString()
{
return "{ " + Log.str(eventClass) + " => " + Log.str(clientClass) + " }";
}
}