package org.limewire.listener;
import org.limewire.listener.EventListenerList.EventListenerListContext;
import org.limewire.logging.Log;
/**
* A Multicaster that caches the last event it handles or broadcasts.
*<p>
* The cached event is used for two purposes:
* <pre>
* 1. New listeners who are added will have their handleEvent(E event)
* method called with the cached event
*
* 2. When broadcast(E event) and handleEvent(E event) are called
* on the CacheingEventMulticaster, the event is only broadcast
* if it is not equal to the cached event. Event classes should override equals()
* for this to provide any meaningful implementation
* </pre>
*/
public class CachingEventMulticasterImpl<E> implements CachingEventMulticaster<E> {
private final EventMulticaster<E> multicaster;
private final BroadcastPolicy broadcastPolicy;
private final EventListenerListContext listenerContext;
private final Object LOCK = new Object();
private volatile E cachedEvent;
public CachingEventMulticasterImpl() {
this(BroadcastPolicy.ALWAYS, new EventMulticasterImpl<E>());
}
public CachingEventMulticasterImpl(Log log) {
this(BroadcastPolicy.ALWAYS, log);
}
public CachingEventMulticasterImpl(BroadcastPolicy broadcastPolicy) {
this(broadcastPolicy, new EventMulticasterImpl<E>());
}
public CachingEventMulticasterImpl(BroadcastPolicy broadcastPolicy, Log log) {
this(broadcastPolicy, new EventMulticasterImpl<E>(log));
}
/**
* @param broadcastPolicy the {@link BroadcastPolicy} to use when broadcasting events.
* @param multicaster the {@link EventMulticaster} this delegates to when broadcasting events.
* @param listenerContext the {@link EventListenerListContext} to use when broadcasting events directly.
*/
public CachingEventMulticasterImpl(BroadcastPolicy broadcastPolicy, EventMulticaster<E> multicaster) {
this.broadcastPolicy = broadcastPolicy;
this.multicaster = multicaster;
this.listenerContext = multicaster.getListenerContext();
}
/**
* Adds a listener and calls its handleEvent() method with the
* most recent Event, if any.
*/
@Override
public void addListener(EventListener<E> eEventListener) {
E copy = cachedEvent;
if(copy != null) {
// An alternate way to do this would be to add some kind of notifyListener(EventListener, Event)
// method/interface, similar to EventListenerList#notifyListener, that would internally
// use the context. This would remove the need to pass a context to this class,
// but would require a more difficult interface be implemented by multicasters.
// Overall it's probably the better option to do it via notifyListener, because that would
// also allow the multicaster to control how the event is broadcast, but harder to fit
// into the existing multicaster impls.
EventListenerList.dispatch(eEventListener, copy, listenerContext);
}
multicaster.addListener(eEventListener);
}
@Override
public boolean removeListener(EventListener<E> eEventListener) {
return multicaster.removeListener(eEventListener);
}
@Override
public void handleEvent(E event) {
broadcast(event);
}
@Override
public void broadcast(E event) {
boolean broadcast = false;
synchronized(LOCK) {
if(cachedEvent == null ||
broadcastPolicy == BroadcastPolicy.ALWAYS ||
!cachedEvent.equals(event)) {
cachedEvent = event;
broadcast = true;
}
}
if(broadcast) {
multicaster.broadcast(event);
}
}
@Override
public E getLastEvent() {
return cachedEvent;
}
@Override
public EventListenerListContext getListenerContext() {
return listenerContext;
}
}