package ameba.event;
import ameba.exception.AmebaException;
import java.lang.reflect.Method;
/**
* <p>Abstract EventBus class.</p>
*
* @author icode
*/
public interface EventBus<Event extends ameba.event.Event> {
/**
* <p>createMix.</p>
*
* @param <Event> event
* @return a {@link ameba.event.EventBus} object.
*/
static <Event extends ameba.event.Event> EventBus<Event> createMix() {
return new Mixed<>();
}
/**
* <p>create.</p>
*
* @return a {@link ameba.event.EventBus} object.
*/
static EventBus create() {
return new BasicEventBus();
}
/**
* <p>subscribe.</p>
*
* @param event a {@link java.lang.Class} object.
* @param listener a {@link ameba.event.Listener} object.
*/
<E extends Event> void subscribe(Class<E> event, Listener<E> listener);
/**
* subscribe event by {@link ameba.event.Subscribe} annotation
* <pre>
* class SubEevent {
* public SubEevent(){
* EventBus.subscribe(this);
* }
* {@literal @Subscribe({ Container.ReloadEvent.class })}
* private void doSome(MyEvent e){
* ....
* }
* }
* class SubEevent2 {
* {@literal @Subscribe({ Container.ReloadEvent.class })}
* private void doSome(){
* ....
* }
* {@literal @Subscribe({ Container.ReloadEvent.class })}
* private void doSome(ReloadEvent e){
* ....
* }
* }
* class SubEevent2 {
* {@literal @Subscribe({ Container.ReloadEvent.class })}
* private void doSome(){
* ....
* }
* }
* </pre>
*
* @param obj class or instance
*/
@SuppressWarnings("unchecked")
default void subscribe(Object obj) {
if (obj == null) {
return;
}
Class objClass;
if (obj instanceof Class) {
objClass = (Class) obj;
try {
obj = objClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new AmebaException("subscribe event error, "
+ objClass.getName() + " must be have a public void arguments constructor", e);
}
} else {
objClass = obj.getClass();
}
final Object finalObj = obj;
for (Method method : objClass.getDeclaredMethods()) {
Subscribe subscribe = method.getAnnotation(Subscribe.class);
if (subscribe != null && !method.isBridge()) {
Class[] argsClass = method.getParameterTypes();
final Class<Event>[] needEvent = new Class[argsClass.length];
Class<Event>[] events = (Class<Event>[]) subscribe.value();
for (int i = 0; i < argsClass.length; i++) {
if (ameba.event.Event.class.isAssignableFrom(argsClass[i])) {
needEvent[i] = argsClass[i];
}
}
if (subscribe.value().length == 0) {
events = needEvent;
}
method.setAccessible(true);
for (final Class<Event> event : events) {
if (event == null) continue;
Listener<Event> listener = ev -> {
Object[] args = new Object[needEvent.length];
try {
for (int i = 0; i < needEvent.length; i++) {
if (needEvent[i] != null && needEvent[i].isAssignableFrom(event)) {
args[i] = ev;
}
}
method.invoke(finalObj, args);
} catch (IllegalAccessException e) {
throw new AmebaException("handle event error, " + method.getName()
+ " method must be not have arguments or extends from Event argument", e);
} catch (Exception e) {
throw new AmebaException("handle " + method.getName() + " event error. ", e);
}
};
subscribe(event, subscribe.async() ? new AsyncListener<Event>() {
@Override
public void onReceive(Event event) {
listener.onReceive(event);
}
} : listener);
}
}
}
}
/**
* <p>unsubscribe.</p>
*
* @param event a {@link java.lang.Class} object.
* @param listener a {@link ameba.event.Listener} object.
*/
<E extends Event> void unsubscribe(Class<E> event, final Listener<E> listener);
/**
* <p>unsubscribe.</p>
*
* @param event a {@link java.lang.Class} object.
*/
<E extends Event> void unsubscribe(Class<E> event);
/**
* <p>publish.</p>
*
* @param event a {@link ameba.event.Event} object.
*/
<E extends Event> void publish(E event);
class Mixed<Event extends ameba.event.Event> extends BasicEventBus<Event> {
private final AsyncEventBus<Event> asyncEventBus;
Mixed() {
asyncEventBus = AsyncEventBus.create();
}
@Override
public <E extends Event> void subscribe(Class<E> event, final Listener<E> listener) {
if (listener instanceof AsyncListener) {
asyncEventBus.subscribe(event, (AsyncListener<E>) listener);
} else {
super.subscribe(event, listener);
}
}
@Override
public <E extends Event> void unsubscribe(Class<E> event, final Listener<E> listener) {
if (listener instanceof AsyncListener) {
asyncEventBus.unsubscribe(event, listener);
} else {
super.unsubscribe(event, listener);
}
}
@Override
public <E extends Event> void unsubscribe(Class<E> event) {
asyncEventBus.unsubscribe(event);
super.unsubscribe(event);
}
@Override
public <E extends Event> void publish(E event) {
if (event == null) return;
asyncEventBus.publish(event);
super.publish(event);
}
}
}