From 43d39989bd4276c0dca210d34b5268b48f64ec41 Mon Sep 17 00:00:00 2001 From: fxzjshm Date: Mon, 8 Feb 2016 16:08:31 +0800 Subject: [PATCH] Fix #7: Add AsmEventBus but hasn't been tested. --- .gitignore | 4 +- android/.project | 34 -- .../blackandwhiteforest/AndroidLauncher.java | 5 +- .../event/BAWFEventBus.java | 91 ----- core/.project | 18 - .../BlackAndWhiteForest.java | 4 +- .../api/BAWFEventReceiver.java | 12 - .../blackandwhiteforest/util/BAWFSovery.java | 37 ++ desktop/.project | 25 -- .../desktop/DesktopLauncher.java | 6 +- .../blackandwhiteforest/util/BAWFConfig.java | 69 ---- ios/.project | 19 - .../blackandwhiteforest/IOSLauncher.java | 4 + .../blackandwhiteforest/util/BAWFConfig.java | 62 --- .../event/BAWFAutoEventBus.java | 45 +++ .../event/BAWFEventBus.java | 7 +- .../blackandwhiteforest/util/BAWFConfig.java | 0 .../net/hakugyokurou/aeb/AsyncEventBus.java | 105 ++++++ .../src/net/hakugyokurou/aeb/EventBus.java | 333 ++++++++++++++++ .../net/hakugyokurou/aeb/EventDispatcher.java | 356 ++++++++++++++++++ .../net/hakugyokurou/aeb/EventInvoker.java | 61 +++ .../net/hakugyokurou/aeb/PriorEventBus.java | 77 ++++ .../aeb/auxiliary/IDeadEventHandler.java | 10 + .../ISubscriberExceptionHandler.java | 10 + .../aeb/exception/AEBRegisterException.java | 14 + .../aeb/generator/AsmInvokerGenerator.java | 336 +++++++++++++++++ .../aeb/generator/InvokerGenerator.java | 10 + .../generator/ReflectionInvokerGenerator.java | 18 + .../quickstart/AnnotatedPriorityJudge.java | 45 +++ .../quickstart/AnnotatedSubscriberFinder.java | 56 +++ .../quickstart/DiscardDeadEventHandler.java | 25 ++ .../aeb/quickstart/EventSubscriber.java | 17 + .../LoggingSubscriberExceptionHandler.java | 22 ++ .../aeb/strategy/EnumDispatchStrategy.java | 6 + .../aeb/strategy/EnumHierarchyStrategy.java | 6 + .../aeb/strategy/EnumInvokerGenerator.java | 10 + .../aeb/strategy/IPriorityStrategy.java | 22 ++ .../aeb/strategy/ISubscriberStrategy.java | 46 +++ .../aeb/util/ArrayCOWArrayList.java | 75 ++++ .../aeb/util/EventBusBuilder.java | 240 ++++++++++++ .../aeb/util/SectionalLinkedList.java | 223 +++++++++++ 41 files changed, 2225 insertions(+), 340 deletions(-) delete mode 100644 android/.project delete mode 100644 android/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java delete mode 100644 core/.project delete mode 100644 core/src/com/entermoor/blackandwhiteforest/api/BAWFEventReceiver.java create mode 100644 core/src/com/entermoor/blackandwhiteforest/util/BAWFSovery.java delete mode 100644 desktop/.project delete mode 100644 desktop/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java delete mode 100644 ios/.project delete mode 100644 ios/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java create mode 100644 native-platform/src/com/entermoor/blackandwhiteforest/event/BAWFAutoEventBus.java rename {desktop => native-platform}/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java (91%) rename {android => native-platform}/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java (100%) create mode 100644 native-platform/src/net/hakugyokurou/aeb/AsyncEventBus.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/EventBus.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/EventDispatcher.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/EventInvoker.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/PriorEventBus.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/auxiliary/IDeadEventHandler.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/auxiliary/ISubscriberExceptionHandler.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/exception/AEBRegisterException.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/generator/AsmInvokerGenerator.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/generator/InvokerGenerator.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/generator/ReflectionInvokerGenerator.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/quickstart/AnnotatedPriorityJudge.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/quickstart/AnnotatedSubscriberFinder.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/quickstart/DiscardDeadEventHandler.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/quickstart/EventSubscriber.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/quickstart/LoggingSubscriberExceptionHandler.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/strategy/EnumDispatchStrategy.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/strategy/EnumHierarchyStrategy.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/strategy/EnumInvokerGenerator.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/strategy/IPriorityStrategy.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/strategy/ISubscriberStrategy.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/util/ArrayCOWArrayList.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/util/EventBusBuilder.java create mode 100644 native-platform/src/net/hakugyokurou/aeb/util/SectionalLinkedList.java diff --git a/.gitignore b/.gitignore index 5cb4300..3ce7b96 100644 --- a/.gitignore +++ b/.gitignore @@ -74,4 +74,6 @@ SourceHanSansCN-Normal.ttf BAWFConfig.properties org.eclipse.jdt.core.prefs org.springsource.ide.eclipse.gradle.core.import.prefs -org.springsource.ide.eclipse.gradle.core.prefs \ No newline at end of file +org.springsource.ide.eclipse.gradle.core.prefs +.settings/ +.project \ No newline at end of file diff --git a/android/.project b/android/.project deleted file mode 100644 index 7aa6d68..0000000 --- a/android/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - BlackAndWhiteForest-android - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - org.springsource.ide.eclipse.gradle.core.nature - org.eclipse.jdt.core.javanature - com.android.ide.eclipse.adt.AndroidNature - - diff --git a/android/src/com/entermoor/blackandwhiteforest/AndroidLauncher.java b/android/src/com/entermoor/blackandwhiteforest/AndroidLauncher.java index 2afd5f1..61138bb 100644 --- a/android/src/com/entermoor/blackandwhiteforest/AndroidLauncher.java +++ b/android/src/com/entermoor/blackandwhiteforest/AndroidLauncher.java @@ -2,6 +2,7 @@ import android.os.Bundle; import dalvik.system.DexClassLoader; +import net.hakugyokurou.aeb.strategy.EnumInvokerGenerator; import java.io.File; import java.io.FileInputStream; @@ -13,7 +14,7 @@ import com.badlogic.gdx.files.FileHandle; import com.entermoor.blackandwhiteforest.BlackAndWhiteForest; import com.entermoor.blackandwhiteforest.api.IBAWFPlugin; -import com.entermoor.blackandwhiteforest.event.BAWFEventBus; +import com.entermoor.blackandwhiteforest.event.BAWFAutoEventBus; import com.entermoor.blackandwhiteforest.util.BAWFConfig; import com.entermoor.blackandwhiteforest.util.BAWFCrashHandler; @@ -26,7 +27,7 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AndroidApplicationConfiguration config = new AndroidApplicationConfiguration(); initialize(BlackAndWhiteForest.INSTANSE, config); - BlackAndWhiteForest.eventBus = new BAWFEventBus(); + BlackAndWhiteForest.eventBus = new BAWFAutoEventBus(EnumInvokerGenerator.REFLECT); BlackAndWhiteForest.config = new BAWFConfig(); BlackAndWhiteForest.toInitList.add(new IBAWFPlugin() { diff --git a/android/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java b/android/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java deleted file mode 100644 index e855637..0000000 --- a/android/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.entermoor.blackandwhiteforest.event; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import com.entermoor.blackandwhiteforest.api.BAWFEventReceiver; -import com.entermoor.blackandwhiteforest.api.IBAWFEventReceiver; - -/** - * @author fxzjshm - */ -public class BAWFEventBus implements IBAWFEventBus { - - public List receiverList = new ArrayList(); - public List receiverMethodList = new ArrayList(); - public List receiverInstanceList = new ArrayList(); - - public boolean register(Object receiver) { - boolean isSuccessful = true; - Method[] methods = receiver.getClass().getDeclaredMethods(); - for (int i = 0; i < methods.length; i++) { - - Annotation[] annotations = methods[i].getAnnotations(); - for (int j = 0; j < annotations.length; j++) { - if (annotations[j].annotationType().getName().equals(BAWFEventReceiver.class.getName())) { - isSuccessful = isSuccessful && receiverMethodList.add(methods[i]) - && receiverInstanceList.add(receiver); - break; - } else if (j == annotations.length) { - isSuccessful = false; - } - } - } - if (receiver instanceof IBAWFEventReceiver) { - if (isSuccessful) { - return receiverList.add((IBAWFEventReceiver) receiver); - } else { - return false; - } - } - return isSuccessful; - } - - public boolean post(BAWFEvent event) { - for (Iterator iterator = receiverList.iterator(); iterator.hasNext();) { - iterator.next().receiveEvent(event); - } - for (Iterator iterator = receiverMethodList.iterator(); iterator.hasNext();) { - Method currentMethod = iterator.next(); - Class[] parClassType = currentMethod.getParameterTypes(); - if (parClassType.length == 1 && parClassType[0].getName().equals(event.getClass().getName())) { - try { - currentMethod.invoke(receiverInstanceList.get(receiverMethodList.indexOf(currentMethod)), event); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - } - return true; - } - - public boolean unregister(Object receiver) { - boolean isSuccessful = true; - Method[] methods = receiver.getClass().getDeclaredMethods(); - for (int i = 0; i < methods.length; i++) { - - Annotation[] annotations = methods[i].getAnnotations(); - for (int j = 0; j < annotations.length; j++) { - if (annotations[j].annotationType().getName().equals(BAWFEventReceiver.class.getName())) { - isSuccessful = isSuccessful && receiverMethodList.remove(methods[i]) - && receiverInstanceList.remove(receiver); - break; - } else if (j == annotations.length) { - isSuccessful = false; - } - } - } - if (receiver instanceof IBAWFEventReceiver) { - if (isSuccessful) { - return receiverList.remove((IBAWFEventReceiver) receiver); - } else { - return false; - } - } - return isSuccessful; - } -} diff --git a/core/.project b/core/.project deleted file mode 100644 index d6d056b..0000000 --- a/core/.project +++ /dev/null @@ -1,18 +0,0 @@ - - - BlackAndWhiteForest-core - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.springsource.ide.eclipse.gradle.core.nature - org.eclipse.jdt.core.javanature - - diff --git a/core/src/com/entermoor/blackandwhiteforest/BlackAndWhiteForest.java b/core/src/com/entermoor/blackandwhiteforest/BlackAndWhiteForest.java index f54c5e2..561f445 100644 --- a/core/src/com/entermoor/blackandwhiteforest/BlackAndWhiteForest.java +++ b/core/src/com/entermoor/blackandwhiteforest/BlackAndWhiteForest.java @@ -71,9 +71,9 @@ public class BlackAndWhiteForest extends Game { public static final BlackAndWhiteForest INSTANSE = new BlackAndWhiteForest(); - /** Will be null in Html5 and iOS. */ + /** Will be null in GWT. */ public static IBAWFEventBus eventBus; - /** Will be null in Html5. */ + /** Will be null in GWT. */ public static IBAWFConfig config; public static List toInitList = new ArrayList(); diff --git a/core/src/com/entermoor/blackandwhiteforest/api/BAWFEventReceiver.java b/core/src/com/entermoor/blackandwhiteforest/api/BAWFEventReceiver.java deleted file mode 100644 index acb7df3..0000000 --- a/core/src/com/entermoor/blackandwhiteforest/api/BAWFEventReceiver.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.entermoor.blackandwhiteforest.api; - -import java.lang.annotation.Target; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface BAWFEventReceiver { - -} diff --git a/core/src/com/entermoor/blackandwhiteforest/util/BAWFSovery.java b/core/src/com/entermoor/blackandwhiteforest/util/BAWFSovery.java new file mode 100644 index 0000000..c131b98 --- /dev/null +++ b/core/src/com/entermoor/blackandwhiteforest/util/BAWFSovery.java @@ -0,0 +1,37 @@ +package com.entermoor.blackandwhiteforest.util; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.XmlReader.Element; + +import magory.svg.Sovery; + +public class BAWFSovery extends Sovery{ + + @Override + public void newImage(String name, Element el, float xxx, float yyy, float width, float height, float rr) { + // TODO Auto-generated method stub + + } + + @Override + public void newRect(String name, Element el, float xxx, float yyy, float width, float height, float rr) { + // TODO Auto-generated method stub + + } + + @Override + public void newText(String text, Element el, float xxx, float yyy, float width, float height, float rr, + Color color) { + // TODO Auto-generated method stub + + } + + @Override + public void newPath(Array path, Element el, String title) { + // TODO Auto-generated method stub + + } + +} diff --git a/desktop/.project b/desktop/.project deleted file mode 100644 index fe62019..0000000 --- a/desktop/.project +++ /dev/null @@ -1,25 +0,0 @@ - - - BlackAndWhiteForest-desktop - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.springsource.ide.eclipse.gradle.core.nature - org.eclipse.jdt.core.javanature - - - - assets - 2 - PARENT-1-PROJECT_LOC/android/assets - - - diff --git a/desktop/src/com/entermoor/blackandwhiteforest/desktop/DesktopLauncher.java b/desktop/src/com/entermoor/blackandwhiteforest/desktop/DesktopLauncher.java index 8a38e43..79e71e7 100644 --- a/desktop/src/com/entermoor/blackandwhiteforest/desktop/DesktopLauncher.java +++ b/desktop/src/com/entermoor/blackandwhiteforest/desktop/DesktopLauncher.java @@ -13,15 +13,17 @@ import com.badlogic.gdx.files.FileHandle; import com.entermoor.blackandwhiteforest.BlackAndWhiteForest; import com.entermoor.blackandwhiteforest.api.IBAWFPlugin; -import com.entermoor.blackandwhiteforest.event.BAWFEventBus; +import com.entermoor.blackandwhiteforest.event.BAWFAutoEventBus; import com.entermoor.blackandwhiteforest.util.BAWFConfig; import com.entermoor.blackandwhiteforest.util.BAWFCrashHandler; +import net.hakugyokurou.aeb.strategy.EnumInvokerGenerator; + public class DesktopLauncher { public static void main (String[] arg) { LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); - BlackAndWhiteForest.eventBus=new BAWFEventBus(); + BlackAndWhiteForest.eventBus = new BAWFAutoEventBus(EnumInvokerGenerator.ASM); BlackAndWhiteForest.config = new BAWFConfig(); BlackAndWhiteForest.toInitList.add(new IBAWFPlugin() { diff --git a/desktop/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java b/desktop/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java deleted file mode 100644 index 258c4d2..0000000 --- a/desktop/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.entermoor.blackandwhiteforest.util; - -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Properties; - -import com.badlogic.gdx.files.FileHandle; -import com.entermoor.blackandwhiteforest.BlackAndWhiteForest; -import com.entermoor.blackandwhiteforest.api.IBAWFPlugin; - -/** - * Key list: - * ContactInfo - * */ -public class BAWFConfig implements IBAWFConfig{ - - public static Properties config = new Properties(); - - static { - BlackAndWhiteForest.toInitList.add(new IBAWFPlugin() { - - @Override - public void init() { - try { - FileHandle configFile = BlackAndWhiteForest.getSavePath("BAWFConfig.properties"); - if (!configFile.exists()) - configFile.file().createNewFile(); - config.load(configFile.read()); - } catch (Exception e) { - BAWFCrashHandler.handleCrash(e); - } - } - }); - } - - public String get(String key, String defaultValue) { - String val = config.getProperty(key); - if (val == null) { - config.setProperty(key, defaultValue); - return defaultValue; - } else { - return val; - } - } - - public String get(String key) { - return config.getProperty(key, null); - } - - public void set(String key, String value){ - config.setProperty(key, value); - try { - config.store(new FileOutputStream(BlackAndWhiteForest.getSavePath("BAWFConfig.properties").file()), "The properties of BlackAndWhiteForest"); - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Override - public void setProperty(String key, String value) { - config.setProperty(key, value); - } - -} \ No newline at end of file diff --git a/ios/.project b/ios/.project deleted file mode 100644 index 2e5a80d..0000000 --- a/ios/.project +++ /dev/null @@ -1,19 +0,0 @@ - - - BlackAndWhiteForest-ios - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.springsource.ide.eclipse.gradle.core.nature - org.eclipse.jdt.core.javanature - org.robovm.eclipse.RoboVMNature - - diff --git a/ios/src/com/entermoor/blackandwhiteforest/IOSLauncher.java b/ios/src/com/entermoor/blackandwhiteforest/IOSLauncher.java index 6aaf8e9..f10f654 100644 --- a/ios/src/com/entermoor/blackandwhiteforest/IOSLauncher.java +++ b/ios/src/com/entermoor/blackandwhiteforest/IOSLauncher.java @@ -6,13 +6,17 @@ import com.badlogic.gdx.backends.iosrobovm.IOSApplication; import com.badlogic.gdx.backends.iosrobovm.IOSApplicationConfiguration; import com.entermoor.blackandwhiteforest.BlackAndWhiteForest; +import com.entermoor.blackandwhiteforest.event.BAWFAutoEventBus; import com.entermoor.blackandwhiteforest.util.BAWFConfig; +import net.hakugyokurou.aeb.strategy.EnumInvokerGenerator; + public class IOSLauncher extends IOSApplication.Delegate { @Override protected IOSApplication createApplication() { IOSApplicationConfiguration config = new IOSApplicationConfiguration(); BlackAndWhiteForest.config = new BAWFConfig(); + BlackAndWhiteForest.eventBus = new BAWFAutoEventBus(EnumInvokerGenerator.REFLECT); return new IOSApplication(BlackAndWhiteForest.INSTANSE, config); } diff --git a/ios/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java b/ios/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java deleted file mode 100644 index d1768c2..0000000 --- a/ios/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.entermoor.blackandwhiteforest.util; - -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Properties; - -import com.badlogic.gdx.files.FileHandle; -import com.entermoor.blackandwhiteforest.BlackAndWhiteForest; - -/** - * Key list: - * ContactInfo - * */ -public class BAWFConfig implements IBAWFConfig{ - - public static Properties config = new Properties(); - - static { - try { - FileHandle configFile = BlackAndWhiteForest.getSavePath("BAWFConfig.properties"); - if (!configFile.exists()) - configFile.file().createNewFile(); - config.load(configFile.read()); - } catch (Exception e) { - BAWFCrashHandler.handleCrash(e); - } - } - - public String get(String key, String defaultValue) { - String val = config.getProperty(key); - if (val == null) { - config.setProperty(key, defaultValue); - return defaultValue; - } else { - return val; - } - } - - public String get(String key) { - return config.getProperty(key, null); - } - - public void set(String key, String value){ - config.setProperty(key, value); - try { - config.store(new FileOutputStream(BlackAndWhiteForest.getSavePath("BAWFConfig.properties").file()), "The properties of BlackAndWhiteForest"); - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Override - public void setProperty(String key, String value) { - config.setProperty(key, value); - } - -} \ No newline at end of file diff --git a/native-platform/src/com/entermoor/blackandwhiteforest/event/BAWFAutoEventBus.java b/native-platform/src/com/entermoor/blackandwhiteforest/event/BAWFAutoEventBus.java new file mode 100644 index 0000000..74a5511 --- /dev/null +++ b/native-platform/src/com/entermoor/blackandwhiteforest/event/BAWFAutoEventBus.java @@ -0,0 +1,45 @@ +package com.entermoor.blackandwhiteforest.event; + +import java.lang.reflect.Method; + +import com.entermoor.blackandwhiteforest.util.BAWFCrashHandler; + +import net.hakugyokurou.aeb.EventBus; +import net.hakugyokurou.aeb.auxiliary.ISubscriberExceptionHandler; +import net.hakugyokurou.aeb.strategy.EnumInvokerGenerator; + +public class BAWFAutoEventBus implements IBAWFEventBus { + + public EventBus originEventBus; + + public BAWFAutoEventBus(EnumInvokerGenerator type) { + originEventBus=new EventBus("BAWFEventBus", type); + originEventBus.setSubscriberExceptionHandler(new ISubscriberExceptionHandler() { + + @Override + public void handleSubscriberException(EventBus eventBus, Object handler, Method subscriber, Object event, + Throwable e) { + BAWFCrashHandler.handleCrash(e); + } + }); + } + + @Override + public boolean register(Object receiver) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean post(BAWFEvent event) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean unregister(Object receiver) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/desktop/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java b/native-platform/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java similarity index 91% rename from desktop/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java rename to native-platform/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java index e855637..e4660fe 100644 --- a/desktop/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java +++ b/native-platform/src/com/entermoor/blackandwhiteforest/event/BAWFEventBus.java @@ -6,9 +6,10 @@ import java.util.Iterator; import java.util.List; -import com.entermoor.blackandwhiteforest.api.BAWFEventReceiver; import com.entermoor.blackandwhiteforest.api.IBAWFEventReceiver; +import net.hakugyokurou.aeb.quickstart.EventSubscriber; + /** * @author fxzjshm */ @@ -25,7 +26,7 @@ public boolean register(Object receiver) { Annotation[] annotations = methods[i].getAnnotations(); for (int j = 0; j < annotations.length; j++) { - if (annotations[j].annotationType().getName().equals(BAWFEventReceiver.class.getName())) { + if (annotations[j].annotationType().getName().equals(EventSubscriber.class.getName())) { isSuccessful = isSuccessful && receiverMethodList.add(methods[i]) && receiverInstanceList.add(receiver); break; @@ -70,7 +71,7 @@ public boolean unregister(Object receiver) { Annotation[] annotations = methods[i].getAnnotations(); for (int j = 0; j < annotations.length; j++) { - if (annotations[j].annotationType().getName().equals(BAWFEventReceiver.class.getName())) { + if (annotations[j].annotationType().getName().equals(EventSubscriber.class.getName())) { isSuccessful = isSuccessful && receiverMethodList.remove(methods[i]) && receiverInstanceList.remove(receiver); break; diff --git a/android/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java b/native-platform/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java similarity index 100% rename from android/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java rename to native-platform/src/com/entermoor/blackandwhiteforest/util/BAWFConfig.java diff --git a/native-platform/src/net/hakugyokurou/aeb/AsyncEventBus.java b/native-platform/src/net/hakugyokurou/aeb/AsyncEventBus.java new file mode 100644 index 0000000..3157bfc --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/AsyncEventBus.java @@ -0,0 +1,105 @@ +package net.hakugyokurou.aeb; + +import java.lang.reflect.Method; +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import net.hakugyokurou.aeb.auxiliary.IDeadEventHandler; +import net.hakugyokurou.aeb.auxiliary.ISubscriberExceptionHandler; +import net.hakugyokurou.aeb.strategy.EnumHierarchyStrategy; +import net.hakugyokurou.aeb.strategy.ISubscriberStrategy; + +public class AsyncEventBus extends EventBus{ + + protected final Executor executor; + protected final EventBus bus; + + public AsyncEventBus(Executor executor, EventBus wrappedEB) { + this(null,executor,wrappedEB); + } + + public AsyncEventBus(String name, Executor executor, EventBus wrappedEB) { + super(name); + this.executor = executor; + this.bus = wrappedEB; + if(executor==null) + throw new NullPointerException("Executor can't be null."); + if(wrappedEB==null) + throw new NullPointerException("Wrapped event bus can't be null."); + if(bus instanceof AsyncEventBus) + throw new IllegalArgumentException("AsyncEventBuses can't wrap each other."); + } + + @Override + public synchronized void setDeadEventHandler( + IDeadEventHandler deadEventHandler) { + bus.setDeadEventHandler(deadEventHandler); + } + + @Override + public synchronized void setSubscriberExceptionHandler( + ISubscriberExceptionHandler exceptionHandler) { + bus.setSubscriberExceptionHandler(exceptionHandler); + } + + @Override + public synchronized void setLogger(Logger logger) { + bus.setLogger(logger); + } + + @Override + public EnumHierarchyStrategy getHierarchyStrategy() { + return bus.getHierarchyStrategy(); + } + + @Override + public ISubscriberStrategy getSubscriberStrategy() { + return bus.getSubscriberStrategy(); + } + + @Override + public synchronized IDeadEventHandler getDeadEventHandler() { + return bus.getDeadEventHandler(); + } + + @Override + public synchronized ISubscriberExceptionHandler getSubscriberExceptionHandler() { + return bus.getSubscriberExceptionHandler(); + } + + @Override + public synchronized Logger getLogger() { + return bus.getLogger(); + } + + @Override + public void register(Object handler) { + bus.register(handler); + } + + @Override + public void unregister(Object handler) { + bus.unregister(handler); + } + + @Override + public void post(Object event) { + executor.execute(new Runnable() { + private Object event; + public Runnable setEvent(Object event) { + this.event = event; + return this; + } + public void run() { + AsyncEventBus.this.bus.post(event); + } + }.setEvent(event)); + } + + @Override + protected void addReceiver(Class event, Object handler, + Method subscriber, EventInvoker invoker) { + bus.addReceiver(event, handler, subscriber, invoker); + } + +} diff --git a/native-platform/src/net/hakugyokurou/aeb/EventBus.java b/native-platform/src/net/hakugyokurou/aeb/EventBus.java new file mode 100644 index 0000000..aa4aa9a --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/EventBus.java @@ -0,0 +1,333 @@ +package net.hakugyokurou.aeb; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.logging.Logger; + +import net.hakugyokurou.aeb.auxiliary.IDeadEventHandler; +import net.hakugyokurou.aeb.auxiliary.ISubscriberExceptionHandler; +import net.hakugyokurou.aeb.exception.AEBRegisterException; +import net.hakugyokurou.aeb.generator.InvokerGenerator; +import net.hakugyokurou.aeb.generator.AsmInvokerGenerator; +import net.hakugyokurou.aeb.generator.ReflectionInvokerGenerator; +import net.hakugyokurou.aeb.quickstart.AnnotatedSubscriberFinder; +import net.hakugyokurou.aeb.quickstart.EventSubscriber; +import net.hakugyokurou.aeb.quickstart.LoggingSubscriberExceptionHandler; +import net.hakugyokurou.aeb.quickstart.DiscardDeadEventHandler; +import net.hakugyokurou.aeb.strategy.EnumHierarchyStrategy; +import net.hakugyokurou.aeb.strategy.EnumInvokerGenerator; +import net.hakugyokurou.aeb.strategy.ISubscriberStrategy; + +/** + * EventBus can dispatch events to event listeners which have registered. + * + *

How to use

+ *

(1).Create a instance of event bus.
+ * (2).Register subscriber.
+ * (3).Post events.

+ * + *

How to create

+ *

You can just create the event bus by its constructor, or build it by {@link net.hakugyokurou.aeb.util.EventBusBuilder}.
+ * There are 6 constructors in EventBus:
+ * (No param): An auto-generating name and default settings.
+ * String: Use a custom name and default settings.
+ * String, EnumInvokerGenerator: Use a custom name, custom invoker generator and default other settings.
+ * String, ISubscriberStrategy: Use a custom name, custom subscriber strategy and default other settings.
+ * String, EnumHierarchyStrategy: Use a custom name, custom hierarchy strategy and default other settings.
+ * String, EnumHierarchyStrategy, ISubscriberStrategy, EnumInvokerGenerator: + * Use a custom name and custom settings.

+ * To get more information about settings (strategies), check the see also.

+ * + *

How to register subscriber

+ *

AsmEventBus allows user customize the strategy of subscriber. It has provided a preset strategy: + * {@link net.hakugyokurou.aeb.quickstart.AnnotatedSubscriberFinder} as a default subscriber strategy. + * It will find the methods which have a special annotation. If you want to use it, put a + * {@link net.hakugyokurou.aeb.quickstart.EventSubscriber} annotation in your subscriber method. + * The method must be public, non-abstract, no return and has (and only has) one non-primitive parameter. + * The parameter will be considered acceptable event. When post events, if the type of a event is equal to the + * type of acceptable event, the event will be sent to this method (However, even if they are not equal, if the type of + * acceptable event is the super class of type of event, it will also be sent).

+ *

To register a subscriber, uses:
+ * eventBus.register(subscriber);
+ * The subscriber can be a instance of subscriber class, and it can also be the subscriber class, since event bus allows + * static subscriber method.
+ * You can use eventbus.unregister to remove subscriber.

+ * + *

How to post event

+ *

You can use eventbus.post(event) to post event. This event bus is sync event bus, whose subscriber will be + * invoke in poster's thread. If you want the subscribers run in another thread, use {@link AsyncEventBus}.

+ * + * @author szszss + * + * @see ISubscriberStrategy + * @see EnumHierarchyStrategy + * @see EnumInvokerGenerator + * @see IDeadEventHandler + * @see ISubscriberExceptionHandler + */ +public class EventBus { + + private static final AtomicInteger ID_GENERATOR = new AtomicInteger(0); + protected final transient int id; + protected final String name; + protected final EnumHierarchyStrategy hierarchyStrategy; + protected final ISubscriberStrategy subscriberStrategy; + protected final boolean baseOnInstance; + + protected IDeadEventHandler deadEventHandler; + protected ISubscriberExceptionHandler exceptionHandler; + protected Logger logger; + + protected static Map eventInvokerCache = Collections.synchronizedMap(new WeakHashMap(64)); + + protected final InvokerGenerator invokerGenerator; + protected Map, EventDispatcher> eventMappingInvoker = new WeakHashMap, EventDispatcher>(); + protected ReadWriteLock eventMappingInvokerLock = new ReentrantReadWriteLock(); + + protected Map handlerMappingMethods = Collections.synchronizedMap(new WeakHashMap()); + + + public EventBus() { + this(null); + } + + public EventBus(String name) { + this(name, getDefaultSubscriberStrategy()); + } + + public EventBus(String name, EnumInvokerGenerator invokerGenerator) { + this(name, EnumHierarchyStrategy.EXTENDED_FIRST, getDefaultSubscriberStrategy(), EnumInvokerGenerator.getDefault()); + } + + public EventBus(String name, ISubscriberStrategy subscriberStrategy) { + this(name, EnumHierarchyStrategy.EXTENDED_FIRST, subscriberStrategy, EnumInvokerGenerator.getDefault()); + } + + public EventBus(String name, EnumHierarchyStrategy hierarchyStrategy) { + this(name, hierarchyStrategy, getDefaultSubscriberStrategy(), EnumInvokerGenerator.getDefault()); + } + + public EventBus(String name, EnumHierarchyStrategy hierarchyStrategy, + ISubscriberStrategy subscriberStrategy, EnumInvokerGenerator invokerGenerator) { + this.id = ID_GENERATOR.getAndIncrement(); + this.name = name==null?getDefaultName():name; + this.hierarchyStrategy = hierarchyStrategy; + this.subscriberStrategy = subscriberStrategy; + this.baseOnInstance = subscriberStrategy.isDependOnInstance(); + switch (invokerGenerator) { + case ASM: + this.invokerGenerator = new AsmInvokerGenerator(); + break; + default: + this.invokerGenerator = new ReflectionInvokerGenerator(); + break; + } + } + + protected synchronized static String getDefaultName() { + return "EventBus"+(System.nanoTime()%10000L); + } + + public static ISubscriberStrategy getDefaultSubscriberStrategy() { + return AnnotatedSubscriberFinder.SINGLETON; + } + + public static IDeadEventHandler getDefaultDeadEventHandler() { + return DiscardDeadEventHandler.SINGLETON; + } + + public static ISubscriberExceptionHandler getDefaultSubscriberExceptionHandler() { + return LoggingSubscriberExceptionHandler.SINGLETON; + } + + public static Logger getDefaultLogger(EventBus eventBus) { + return Logger.getLogger(eventBus.getClass().getName()+"."+eventBus.getName()); + } + + public synchronized void setDeadEventHandler(IDeadEventHandler deadEventHandler) { + this.deadEventHandler = deadEventHandler; + } + + public synchronized void setSubscriberExceptionHandler(ISubscriberExceptionHandler exceptionHandler) { + this.exceptionHandler = exceptionHandler; + } + + public synchronized void setLogger(Logger logger) { + this.logger = logger; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public EnumHierarchyStrategy getHierarchyStrategy() { + return hierarchyStrategy; + } + + public ISubscriberStrategy getSubscriberStrategy() { + return subscriberStrategy; + } + + public synchronized IDeadEventHandler getDeadEventHandler() { + if(deadEventHandler==null) + deadEventHandler = getDefaultDeadEventHandler(); + return deadEventHandler; + } + + public synchronized ISubscriberExceptionHandler getSubscriberExceptionHandler() { + if(exceptionHandler==null) + exceptionHandler = getDefaultSubscriberExceptionHandler(); + return exceptionHandler; + } + + public synchronized Logger getLogger() { + if(logger==null) + logger = getDefaultLogger(this); + return logger; + } + + public void register(Object handler) { + Class klass = handler instanceof Class ? (Class)handler : handler.getClass(); + Method[] methods = subscriberStrategy.findSubscribers(handler); + //EventInvoker[] invokers = new EventInvoker[methods.length]; + //int index=0; + boolean hasError = false; //TODO:What should we do if there are wrong things? + for(Method method : methods) { + Class event = method.getParameterTypes()[0]; + EventInvoker invoker = eventInvokerCache.get(method); + if(invoker==null) + { + try { + invoker = invokerGenerator.generateInvoker(klass, method, event); + eventInvokerCache.put(method, invoker); + //invokers[index++] = invoker; + } catch (AEBRegisterException e) { + e.printStackTrace(); + hasError = true; + continue; + } + } + addReceiver(event, handler, method, invoker); + } + handlerMappingMethods.put(handler, methods); + } + + protected void addReceiver(Class event, Object handler, Method subscriber, EventInvoker invoker) { + EventDispatcher dispatcher; + eventMappingInvokerLock.readLock().lock(); + try { + dispatcher = eventMappingInvoker.get(event); + } + finally { + eventMappingInvokerLock.readLock().unlock(); + } + if(dispatcher==null) + { + dispatcher = hierarchyStrategy==EnumHierarchyStrategy.EXTENDED_FIRST? + new EventDispatcher.SPEventDispatcherEF(this, event):new EventDispatcher.SPEventDispatcherSF(this, event); + eventMappingInvokerLock.writeLock().lock(); + try { + dealHierarchy(dispatcher); + eventMappingInvoker.put(event, dispatcher); + } + finally { + eventMappingInvokerLock.writeLock().unlock(); + } + } + dispatcher.addReceiver(handler, invoker); + } + + protected final void dealHierarchy(EventDispatcher dispatcher) { + for(EventDispatcher o2 : eventMappingInvoker.values()) + { + if(o2.isSuper(dispatcher)) + { + if(dispatcher.getParent()==null || o2.isSuper(dispatcher.getParent())) + { + dispatcher.setParent(o2); + } + } + else if(dispatcher.isSuper(o2)) + { + if(o2.getParent()==null || dispatcher.isSuper(o2.getParent())) + { + o2.setParent(dispatcher); + } + } + + } + } + + protected final void repairHierarchy(EventDispatcher dispatcher) { + eventMappingInvokerLock.writeLock().lock(); + try { + dealHierarchy(dispatcher); + } + finally { + eventMappingInvokerLock.writeLock().unlock(); + } + } + + public void unregister(Object handler) { + Class klass = handler instanceof Class ? (Class)handler : handler.getClass(); + Method[] methods = handlerMappingMethods.get(handler); + if(methods==null) + { + //TODO:DO SOMETHINGS + return; + } + boolean hasError = false; + for(Method method : methods) { + Class event = method.getParameterTypes()[0]; + EventInvoker invoker = eventInvokerCache.get(method); + if(invoker==null) + { + continue; + } + EventDispatcher dispatcher; + eventMappingInvokerLock.readLock().lock(); + try { + dispatcher = eventMappingInvoker.get(event); + } + finally { + eventMappingInvokerLock.readLock().unlock(); + } + if(dispatcher==null) + { + continue; + } + dispatcher.removeReceiver(handler, invoker); + } + //TODO:This method is a big sucker, fix it on some day. + } + + public void post(Object event) { + EventDispatcher dispatcher; + eventMappingInvokerLock.readLock().lock(); + try { + dispatcher = eventMappingInvoker.get(event.getClass()); + } + finally { + eventMappingInvokerLock.readLock().unlock(); + } + boolean dead = true; + if(dispatcher!=null) + { + dead = !dispatcher.post(event); + } + if(dead) + { + getDeadEventHandler().handleDeadEvent(this, event); + } + } +} diff --git a/native-platform/src/net/hakugyokurou/aeb/EventDispatcher.java b/native-platform/src/net/hakugyokurou/aeb/EventDispatcher.java new file mode 100644 index 0000000..1beb49a --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/EventDispatcher.java @@ -0,0 +1,356 @@ +package net.hakugyokurou.aeb; + +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import net.hakugyokurou.aeb.util.ArrayCOWArrayList; + +abstract class EventDispatcher{ + + protected WeakReference parentDispatcher = new WeakReference(null); + protected boolean hasParent = false; + protected final WeakReference> eventType; + protected final EventBus bus; + + public EventDispatcher(EventBus bus, Class eventType) { + this.bus = bus; + this.eventType = new WeakReference>(eventType); + } + + /** + * Return that if left-value's event is the super class of right-value's event.
+ * @param o2 the right-value + * @return + */ + public final boolean isSuper(EventDispatcher o2) { + return this.eventType.get().isAssignableFrom(o2.eventType.get()); + } + + public final EventDispatcher getParent() { + return parentDispatcher.get(); + } + + public final void setParent(EventDispatcher dispatcher) { + hasParent = true; + parentDispatcher = new WeakReference(dispatcher); + } + + public abstract boolean post(Object event); + + protected final boolean postParent(Object event) { + if(hasParent) + { + EventDispatcher parent = parentDispatcher.get(); + if(parent!=null) + { + return parent.post(event); + } + else + { + hasParent = false; + bus.repairHierarchy(this); + return postParent(event); + } + } + return false; + } + + abstract void addReceiver(Object receiver, EventInvoker invoker); + + abstract void addReceiver(Object receiver, EventInvoker invoker, int priority); + + abstract void removeReceiver(Object receiver, EventInvoker invoker); + + protected static class Entry { + + public final Object receiver; + public final EventInvoker invoker; + + public Entry(Object receiver, EventInvoker invoker) { + this.receiver = receiver; + this.invoker = invoker; + } + + @Override + public boolean equals(Object object) { + if(!(object instanceof Entry)) + return false; + Entry entry = (Entry)object; + return entry.receiver==this.receiver && entry.invoker==this.invoker; + } + + @Override + public int hashCode() { + return receiver.hashCode() + invoker.hashCode(); + } + } + + static abstract class SinglePriorityEventDispatcher extends EventDispatcher { + + protected List entries = new CopyOnWriteArrayList(); + + public SinglePriorityEventDispatcher(EventBus bus, Class eventType) { + super(bus, eventType); + } + + void addReceiver(Object receiver, EventInvoker invoker) { + entries.add(new Entry(receiver,invoker)); + } + + void addReceiver(Object receiver, EventInvoker invoker, int priority) { + throw new UnsupportedOperationException(); + } + + void removeReceiver(Object receiver, EventInvoker invoker) { + Entry entry = new Entry(receiver,invoker); + entries.remove(entry); + } + + protected final void invokeEveryone(Object event) { + Iterator iterator = entries.iterator(); + for(;iterator.hasNext();) + { + Entry entry = iterator.next(); + try { + entry.invoker.invoke(entry.receiver, event); + } catch (Throwable e) { + bus.getSubscriberExceptionHandler().handleSubscriberException(bus, entry.receiver, entry.invoker.getSubscriber(), event, e); + } + } + } + } + + static class SPEventDispatcherSF extends SinglePriorityEventDispatcher { + public SPEventDispatcherSF(EventBus bus, Class eventType) { + super(bus, eventType); + } + @Override + public boolean post(Object event) { + boolean parentPosted = postParent(event); + if(entries.isEmpty()) + return parentPosted||false; + invokeEveryone(event); + return true; + } + } + + static class SPEventDispatcherEF extends SinglePriorityEventDispatcher { + public SPEventDispatcherEF(EventBus bus, Class eventType) { + super(bus, eventType); + } + @Override + public boolean post(Object event) { + if(entries.isEmpty()) + return postParent(event)||false; + invokeEveryone(event); + postParent(event); + return true; + } + } + + static abstract class MultiPrioritiesEventDispatcher extends EventDispatcher { + + protected final ArrayCOWArrayList cowals; + + public MultiPrioritiesEventDispatcher(EventBus bus, int priorities, Class eventType) { + super(bus, eventType); + this.cowals = new ArrayCOWArrayList(priorities); + } + + void addReceiver(Object receiver, EventInvoker invoker) { + throw new UnsupportedOperationException(); + } + + void addReceiver(Object receiver, EventInvoker invoker, int priority) { + cowals.add(new Entry(receiver,invoker),priority); + } + + void removeReceiver(Object receiver, EventInvoker invoker) { + Entry entry = new Entry(receiver,invoker); + cowals.remove(entry); + } + + protected final void invokeSomeone(Entry entry, Object event) { + try { + entry.invoker.invoke(entry.receiver, event); + } catch (Throwable e) { + bus.getSubscriberExceptionHandler().handleSubscriberException(bus, entry.receiver, entry.invoker.getSubscriber(), event, e); + } + } + } + + static class MPEventDispatcherPFSF extends MultiPrioritiesEventDispatcher { + //Priority first, super first. + public MPEventDispatcherPFSF(EventBus bus, int priorities, Class eventType) { + super(bus, priorities, eventType); + } + + @Override + public boolean post(Object event) { + boolean parentPosted = postParent(event); + if(cowals.isEmpty()) + return parentPosted||false; + int i = cowals.getSections(); + Iterator iterator; + for(--i;i>=0;i--) + { + iterator = cowals.getIterator(i); + for(;iterator.hasNext();) + { + Entry entry = iterator.next(); + invokeSomeone(entry,event); + } + } + return true; + } + } + + static class MPEventDispatcherHFSF extends MultiPrioritiesEventDispatcher { + //Hierarchy first, super first. + public MPEventDispatcherHFSF(EventBus bus, int priorities, Class eventType) { + super(bus, priorities, eventType); + } + + @Override + public boolean post(Object event) { + if(cowals.isEmpty()) + return postParent(event)||false; + int i = cowals.getSections(); + Iterator iterator; + for(--i;i>=0;i--) + { + callParent(event, i); + iterator = cowals.getIterator(i); + for(;iterator.hasNext();) + { + Entry entry = iterator.next(); + invokeSomeone(entry,event); + } + } + return true; + } + + + protected boolean callParent(Object event, int priority) { + if(hasParent) + { + MPEventDispatcherHFSF parent = (MPEventDispatcherHFSF)parentDispatcher.get(); + if(parent!=null) + { + return parent.callParent(event, priority); + } + else + { + hasParent = false; + bus.repairHierarchy(this); + return callParent(event, priority); + } + } + if(cowals.isEmpty(priority)) + return false; + else + { + Iterator iterator = cowals.getIterator(priority); + for(;iterator.hasNext();) + { + Entry entry = iterator.next(); + invokeSomeone(entry,event); + } + return true; + } + } + } + + static class MPEventDispatcherPFEF extends MultiPrioritiesEventDispatcher { + //Priority first, extended first. + public MPEventDispatcherPFEF(EventBus bus, int priorities, Class eventType) { + super(bus, priorities, eventType); + } + + @Override + public boolean post(Object event) { + if(cowals.isEmpty()) + return postParent(event)||false; + int i = cowals.getSections(); + Iterator iterator; + for(--i;i>=0;i--) + { + iterator = cowals.getIterator(i); + for(;iterator.hasNext();) + { + Entry entry = iterator.next(); + invokeSomeone(entry,event); + } + } + postParent(event); + return true; + } + } + + static class MPEventDispatcherHFEF extends MultiPrioritiesEventDispatcher { + //Hierarchy first, extended first. + public MPEventDispatcherHFEF(EventBus bus, int priorities, Class eventType) { + super(bus, priorities, eventType); + } + + @Override + public boolean post(Object event) { + if(cowals.isEmpty()) + return postParent(event)||false; + int i = cowals.getSections(); + Iterator iterator; + for(--i;i>=0;i--) + { + iterator = cowals.getIterator(i); + for(;iterator.hasNext();) + { + Entry entry = iterator.next(); + invokeSomeone(entry, event); + } + callParent(event, i); + } + return true; + } + + + protected boolean callParent(Object event, int priority) { + if(hasParent) + { + MPEventDispatcherHFEF parent = (MPEventDispatcherHFEF)parentDispatcher.get(); + if(parent!=null) + { + if(!cowals.isEmpty(priority)) + { + Iterator iterator = cowals.getIterator(priority); + for(;iterator.hasNext();) + { + Entry entry = iterator.next(); + invokeSomeone(entry, event); + } + } + return (!cowals.isEmpty(priority))||parent.callParent(event, priority); + } + else + { + hasParent = false; + bus.repairHierarchy(this); + return callParent(event, priority); + } + } + if(cowals.isEmpty(priority)) + return false; + else + { + Iterator iterator = cowals.getIterator(priority); + for(;iterator.hasNext();) + { + Entry entry = iterator.next(); + invokeSomeone(entry, event); + } + return true; + } + } + } +} diff --git a/native-platform/src/net/hakugyokurou/aeb/EventInvoker.java b/native-platform/src/net/hakugyokurou/aeb/EventInvoker.java new file mode 100644 index 0000000..372d200 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/EventInvoker.java @@ -0,0 +1,61 @@ +package net.hakugyokurou.aeb; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.Lock; + +public abstract class EventInvoker { + + protected final Method subscriber; + + public EventInvoker(Method subscriber) { + this.subscriber = subscriber; + } + + public Method getSubscriber() { + return subscriber; + } + + public abstract void invoke(Object receiver, Object event) throws Throwable; + + public static class AsyncReflectedEventInvoker extends EventInvoker{ + + public AsyncReflectedEventInvoker(Method subscriber) { + super(subscriber); + subscriber.setAccessible(true); + } + @Override + public void invoke(Object receiver, Object event) throws Throwable{ + subscriber.invoke(receiver, event); + } + } + + public static class SyncReflectedEventInvoker extends EventInvoker{ + + public SyncReflectedEventInvoker(Method subscriber) { + super(subscriber); + subscriber.setAccessible(true); + } + @Override + public synchronized void invoke(Object receiver, Object event) throws Throwable{ + subscriber.invoke(receiver, event); + } + } + + /*public static class WWW extends EventInvoker { + + public WWW(Method subscriber) { + super(subscriber); + } + + @Override + public void invoke(Object receiver, Object event) { + ((EventBus)receiver).register(event); + } + + }*/ +} diff --git a/native-platform/src/net/hakugyokurou/aeb/PriorEventBus.java b/native-platform/src/net/hakugyokurou/aeb/PriorEventBus.java new file mode 100644 index 0000000..06b91e7 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/PriorEventBus.java @@ -0,0 +1,77 @@ +package net.hakugyokurou.aeb; + +import java.lang.reflect.Method; + +import net.hakugyokurou.aeb.strategy.EnumDispatchStrategy; +import net.hakugyokurou.aeb.strategy.EnumHierarchyStrategy; +import net.hakugyokurou.aeb.strategy.EnumInvokerGenerator; +import net.hakugyokurou.aeb.strategy.IPriorityStrategy; +import net.hakugyokurou.aeb.strategy.ISubscriberStrategy; + +//Prioritized +public class PriorEventBus extends EventBus{ + + private final int priorities; + private final EnumDispatchStrategy dispatchStrategy; + private final IPriorityStrategy priorityStrategy; + + public PriorEventBus(IPriorityStrategy priorityStrategy) { + this(null,priorityStrategy); + } + + public PriorEventBus(String name, IPriorityStrategy priorityStrategy) { + this(name, getDefaultSubscriberStrategy(), priorityStrategy); + } + + public PriorEventBus(String name, EnumHierarchyStrategy hierarchyStrategy, IPriorityStrategy priorityStrategy) { + this(name, hierarchyStrategy, getDefaultSubscriberStrategy(), EnumInvokerGenerator.getDefault(), priorityStrategy); + } + + public PriorEventBus(String name, ISubscriberStrategy subscriberStrategy, IPriorityStrategy priorityStrategy) { + this(name, EnumHierarchyStrategy.EXTENDED_FIRST, subscriberStrategy, EnumInvokerGenerator.getDefault(), priorityStrategy); + } + + public PriorEventBus(String name, EnumInvokerGenerator invokerGenerator, IPriorityStrategy priorityStrategy) { + this(name, EnumHierarchyStrategy.EXTENDED_FIRST, getDefaultSubscriberStrategy(), invokerGenerator, priorityStrategy); + } + + public PriorEventBus(String name, EnumHierarchyStrategy hierarchyStrategy, ISubscriberStrategy subscriberStrategy, + EnumInvokerGenerator invokerGenerator, IPriorityStrategy priorityStrategy) { + super(name, hierarchyStrategy, subscriberStrategy, invokerGenerator); + if(priorityStrategy==null) + throw new NullPointerException("PriorityStrategy can't be null."); + this.priorities = priorityStrategy.getPriorities(); + this.dispatchStrategy = priorityStrategy.getDispatchStrategy(); + this.priorityStrategy = priorityStrategy; + } + + @Override + protected void addReceiver(Class event, Object handler, Method subscriber, EventInvoker invoker) { + EventDispatcher dispatcher; + eventMappingInvokerLock.readLock().lock(); + try { + dispatcher = eventMappingInvoker.get(event); + } + finally { + eventMappingInvokerLock.readLock().unlock(); + } + if(dispatcher==null) + { + dispatcher = hierarchyStrategy==EnumHierarchyStrategy.EXTENDED_FIRST? + dispatchStrategy==EnumDispatchStrategy.PRIORITY_FIRST?new EventDispatcher.MPEventDispatcherPFEF(this, priorities, event): + new EventDispatcher.MPEventDispatcherHFEF(this, priorities, event): + dispatchStrategy==EnumDispatchStrategy.PRIORITY_FIRST?new EventDispatcher.MPEventDispatcherPFSF(this, priorities, event): + new EventDispatcher.MPEventDispatcherHFSF(this, priorities, event); + eventMappingInvokerLock.writeLock().lock(); + try { + dealHierarchy(dispatcher); + eventMappingInvoker.put(event, dispatcher); + } + finally { + eventMappingInvokerLock.writeLock().unlock(); + } + } + //int priority = priorityStrategy.judgePriority(subscriber); + dispatcher.addReceiver(handler, invoker, priorityStrategy.judgePriority(subscriber)); + } +} diff --git a/native-platform/src/net/hakugyokurou/aeb/auxiliary/IDeadEventHandler.java b/native-platform/src/net/hakugyokurou/aeb/auxiliary/IDeadEventHandler.java new file mode 100644 index 0000000..d9956ee --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/auxiliary/IDeadEventHandler.java @@ -0,0 +1,10 @@ +package net.hakugyokurou.aeb.auxiliary; + +import java.lang.reflect.Method; + +import net.hakugyokurou.aeb.EventBus; + +public interface IDeadEventHandler { + + public abstract void handleDeadEvent(EventBus eventBus, Object event); +} diff --git a/native-platform/src/net/hakugyokurou/aeb/auxiliary/ISubscriberExceptionHandler.java b/native-platform/src/net/hakugyokurou/aeb/auxiliary/ISubscriberExceptionHandler.java new file mode 100644 index 0000000..34099a1 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/auxiliary/ISubscriberExceptionHandler.java @@ -0,0 +1,10 @@ +package net.hakugyokurou.aeb.auxiliary; + +import java.lang.reflect.Method; + +import net.hakugyokurou.aeb.EventBus; + +public interface ISubscriberExceptionHandler { + + public abstract void handleSubscriberException(EventBus eventBus, Object handler, Method subscriber, Object event, Throwable e); +} diff --git a/native-platform/src/net/hakugyokurou/aeb/exception/AEBRegisterException.java b/native-platform/src/net/hakugyokurou/aeb/exception/AEBRegisterException.java new file mode 100644 index 0000000..ff25367 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/exception/AEBRegisterException.java @@ -0,0 +1,14 @@ +package net.hakugyokurou.aeb.exception; + +public class AEBRegisterException extends Exception { + + /** + * + */ + private static final long serialVersionUID = -4475116579696162608L; + + public AEBRegisterException(Throwable cause) { + super(cause); + } + +} diff --git a/native-platform/src/net/hakugyokurou/aeb/generator/AsmInvokerGenerator.java b/native-platform/src/net/hakugyokurou/aeb/generator/AsmInvokerGenerator.java new file mode 100644 index 0000000..ec08cb3 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/generator/AsmInvokerGenerator.java @@ -0,0 +1,336 @@ +package net.hakugyokurou.aeb.generator; + +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import net.hakugyokurou.aeb.EventInvoker; +import net.hakugyokurou.aeb.exception.AEBRegisterException; + +public class AsmInvokerGenerator implements InvokerGenerator { + + private final static int HEADER_MAGIC = 0xCAFEBABE; //Magic number + private final static short HEADER_VERSION = 49; //Java1.5 + private final static byte CONSTANT_TAG_UTF8 = 1; //UTF8_info + private final static byte CONSTANT_TAG_CLASS = 7; //Class_info + private final static byte CONSTANT_TAG_METHOD = 10; //Methodref_info + private final static byte CONSTANT_TAG_NAT = 12; //NameAndType_info + private final static byte[] CONSTANT_CLASSNAME_PARENT = EventInvoker.class.getName().replace('.', '/').getBytes(); + private final static byte[] CONSTANT_METHOD_INIT_NAME = "".getBytes(); + private final static byte[] CONSTANT_METHOD_INIT_DESC = "(Ljava/lang/reflect/Method;)V".getBytes(); + private final static byte[] CONSTANT_METHOD_INVOKE_NAME = "invoke".getBytes(); + private final static byte[] CONSTANT_METHOD_INVOKE_DESC = "(Ljava/lang/Object;Ljava/lang/Object;)V".getBytes(); + private final static byte[] CONSTANT_ATTR_LNT = "LineNumberTable".getBytes(); + private final static byte[] CONSTANT_ATTR_LVT = "LocalVariableTable".getBytes(); + private final static byte[] CONSTANT_ATTR_CODE = "Code".getBytes(); + private final static byte[] CONSTANT_ATTR_EXCEPTION = "Exceptions".getBytes(); + private final static byte[] CONSTANT_VAR_THIS = "this".getBytes(); + private final static byte[] CONSTANT_VAR_SUBSCRIBER = "subscriber".getBytes(); + private final static byte[] CONSTANT_VAR_METHODDESC = "Ljava/lang/reflect/Method;".getBytes(); + private final static byte[] CONSTANT_VAR_OBJECT = "Ljava/lang/Object;".getBytes(); + private final static byte[] CONSTANT_VAR_THROWABLE = "java/lang/Throwable".getBytes(); + private final static byte[] CONSTANT_VAR_RECEIVER = "receiver".getBytes(); + private final static byte[] CONSTANT_VAR_EVENT = "event".getBytes(); + + private final static byte[] HEADER; + private final static byte[] CONSTANTPOOL; + private final static byte[] ZLICH = { 0, 1, //Access: public + 0, 10, //Class name + 0, 19, //Parent name + 0, 0, //Interface amount + 0, 0, //Field amount + 0, 2 };//Method amount + private final static byte[] METHOD_INIT = { + 0, 1, //public + 0, 21, //Name:#21 + 0, 22, //Desc:#22 (Ljava/lang/reflect/Method;)V + 0, 1, //1 attr + 0, 28, //ATTR: Code + 0, 0, 0, 62, //Length: 62 bytes + 0, 2, //Max stack: 2 + 0, 2, //Max local var: 2 + 0, 0, 0, 6, //Code length: 6 bytes + 42, 43, -73, 0, 8, -79, //Code: invoke super(subscriber); + 0, 0, //No exception + 0, 2, //2 attrs + 0, 29, //ATTR: LocalVarTable + 0, 0, 0, 22, //Length: 22 bytes + 0, 2, //2 vars + 0, 0, + 0, 6, + 0, 9, + 0, 2, + 0, 0, + + 0, 0, + 0, 6, + 0, 11, + 0, 12, + 0, 1, + 0, 30, //ATTR: LineNumberTable + 0, 0, 0, 10, //Length: 10 bytes + 0, 2, // 2 lines + 0, 0, + 0, 27, + + 0, 5, + 0, 28 + }; + private final static byte[] METHOD_INVOKE_PART1 = { + 0, 1, //public + 0, 13, //Name:#13 invoke + 0, 14, //Desc:#14 (Ljava/lang/Object;Ljava/lang/Object;)V + 0, 2, //2 attrs + 0, 28, //ATTR: Code + 0, 0, 0, 78, //Length: 78 bytes + 0, 2, //Max stack: 2 + 0, 3, //Max local var: 3 + 0, 0, 0, 12 //Code length: 12 bytes + }; + private final static byte[] METHOD_INVOKE_PART2_NONSTATIC = { + 43, -64, 0, 18, 44, -64, 0, 20, -74, 0, 24, -79 //Code: cast and invoke reciver.subscriber(castedEvent); + }; + private final static byte[] METHOD_INVOKE_PART2_STATIC = { + 0, 0, 0, 0, 44, -64, 0, 20, -72, 0, 24, -79 //Code: cast and invoke ReciverClass.subscriber(castedEvent); + }; + private final static byte[] METHOD_INVOKE_PART3 = { + 0, 0, //No exception + 0, 2, //2 attrs + 0, 29, //ATTR: LocalVarTable + 0, 0, 0, 32, //Length: 32 bytes + 0, 3, //3 vars + 0, 0, + 0, 12, + 0, 9, + 0, 2, + 0, 0, + + 0, 0, + 0, 12, + 0, 25, + 0, 26, + 0, 1, + + 0, 0, + 0, 12, + 0, 27, + 0, 26, + 0, 2, + 0, 30, //ATTR: LineNumberTable + 0, 0, 0, 10, //Length: 10 bytes + 0, 2, // 2 lines + 0, 0, + 0, 7, + + 0, 11, + 0, 8, + 0, 31, //ATTR: Exceptions + 0, 0, + 0, 4, + 0, 1, + 0, 16, + 0, 0 + }; + + static { + ByteBuffer buffer = ByteBuffer.allocate(512); + { + buffer.putInt(HEADER_MAGIC).putShort((short) 0).putShort(HEADER_VERSION); + buffer.flip(); + buffer.get(HEADER = new byte[buffer.limit()]); + buffer.clear(); + } + { + //Constant Pool + /*#7 */buffer.put(CONSTANT_TAG_NAT).putShort((short) 21).putShort((short) 22); + /*#8 */buffer.put(CONSTANT_TAG_METHOD).putShort((short) 19).putShort((short) 7); + /*#9 */buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_VAR_THIS.length).put(CONSTANT_VAR_THIS); + /*#10*/buffer.put(CONSTANT_TAG_CLASS).putShort((short) 1); //Ref of this class + /*#11*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_VAR_SUBSCRIBER.length).put(CONSTANT_VAR_SUBSCRIBER); + /*#12*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_VAR_METHODDESC.length).put(CONSTANT_VAR_METHODDESC); + /*#13*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_METHOD_INVOKE_NAME.length).put(CONSTANT_METHOD_INVOKE_NAME); + /*#14*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_METHOD_INVOKE_DESC.length).put(CONSTANT_METHOD_INVOKE_DESC); + /*#15*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_VAR_THROWABLE.length).put(CONSTANT_VAR_THROWABLE); + /*#16*/buffer.put(CONSTANT_TAG_CLASS).putShort((short) 15); //Ref of Throwable + /*#17*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_CLASSNAME_PARENT.length).put(CONSTANT_CLASSNAME_PARENT); //Parent name + /*#18*/buffer.put(CONSTANT_TAG_CLASS).putShort((short) 3); //Ref of handler class + /*#19*/buffer.put(CONSTANT_TAG_CLASS).putShort((short) 17); //Ref of parent class + /*#20*/buffer.put(CONSTANT_TAG_CLASS).putShort((short) 4); //Ref of event class + /*#21*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_METHOD_INIT_NAME.length).put(CONSTANT_METHOD_INIT_NAME); + /*#22*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_METHOD_INIT_DESC.length).put(CONSTANT_METHOD_INIT_DESC); + /*#23*/buffer.put(CONSTANT_TAG_NAT).putShort((short) 5).putShort((short) 6); + /*#24*/buffer.put(CONSTANT_TAG_METHOD).putShort((short) 18).putShort((short) 23); + /*#25*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_VAR_RECEIVER.length).put(CONSTANT_VAR_RECEIVER); + /*#26*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_VAR_OBJECT.length).put(CONSTANT_VAR_OBJECT); + /*#27*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_VAR_EVENT.length).put(CONSTANT_VAR_EVENT); + /*#28*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_ATTR_CODE.length).put(CONSTANT_ATTR_CODE); + /*#29*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_ATTR_LVT.length).put(CONSTANT_ATTR_LVT); + /*#30*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_ATTR_LNT.length).put(CONSTANT_ATTR_LNT); + /*#31*/buffer.put(CONSTANT_TAG_UTF8).putShort((short)CONSTANT_ATTR_EXCEPTION.length).put(CONSTANT_ATTR_EXCEPTION); + buffer.flip(); + buffer.get(CONSTANTPOOL = new byte[buffer.limit()]); + } + buffer.clear(); + } + + public EventInvoker generateInvoker(Class handler, Method subscriber, + Class event) throws AEBRegisterException { + + String handlerName = handler.getName().replace('.', '/'); + String eventName = event.getName().replace('.', '/'); + String subscriberName = subscriber.getName(); + String subscriberDesc = "(L" + eventName + ";)V"; + String invokerName = handlerName+"_Invoker_"+subscriberName+"_"+Math.abs(subscriber.hashCode());; + String invokerDesc = "L" + invokerName + ";"; + + int variableLength = invokerName.length() + + invokerDesc.length() + handlerName.length() + eventName.length() + + subscriberName.length() + subscriberDesc.length() + 6 + 7*2; + int overallLength = HEADER.length + variableLength + CONSTANTPOOL.length + ZLICH.length + METHOD_INIT.length + + METHOD_INVOKE_PART1.length + METHOD_INVOKE_PART2_NONSTATIC.length + METHOD_INVOKE_PART3.length; + int pos = 0; + byte[] bytes = new byte[overallLength]; + //Header + System.arraycopy(HEADER, 0, bytes, pos, HEADER.length); + pos += HEADER.length; + //Constant Pool + ByteBuffer buffer = ByteBuffer.allocate(variableLength); //TODO:SHIT + buffer.putShort((short) 32); + /*#1 */buffer.put(CONSTANT_TAG_UTF8).putShort((short)invokerName.length()).put(invokerName.getBytes()); //Class name + /*#2 */buffer.put(CONSTANT_TAG_UTF8).putShort((short)invokerDesc.length()).put(invokerDesc.getBytes()); + /*#3 */buffer.put(CONSTANT_TAG_UTF8).putShort((short)handlerName.length()).put(handlerName.getBytes()); + /*#4 */buffer.put(CONSTANT_TAG_UTF8).putShort((short)eventName.length()).put(eventName.getBytes()); + /*#5 */buffer.put(CONSTANT_TAG_UTF8).putShort((short)subscriberName.length()).put(subscriberName.getBytes()); + /*#6 */buffer.put(CONSTANT_TAG_UTF8).putShort((short)subscriberDesc.length()).put(subscriberDesc.getBytes()); + buffer.flip(); + buffer.get(bytes, pos, buffer.limit()); + pos += buffer.limit(); + buffer.clear(); + System.arraycopy(CONSTANTPOOL, 0, bytes, pos, CONSTANTPOOL.length); + pos += CONSTANTPOOL.length; + //Zlich + System.arraycopy(ZLICH, 0, bytes, pos, ZLICH.length); + pos += ZLICH.length; + //Method: + System.arraycopy(METHOD_INIT, 0, bytes, pos, METHOD_INIT.length); + pos += METHOD_INIT.length; + //Method:invoke + System.arraycopy(METHOD_INVOKE_PART1, 0, bytes, pos, METHOD_INVOKE_PART1.length); + pos += METHOD_INVOKE_PART1.length; + if((subscriber.getModifiers() & Modifier.STATIC) > 0) + System.arraycopy(METHOD_INVOKE_PART2_STATIC, 0, bytes, pos, METHOD_INVOKE_PART2_STATIC.length); + else + System.arraycopy(METHOD_INVOKE_PART2_NONSTATIC, 0, bytes, pos, METHOD_INVOKE_PART2_NONSTATIC.length); + pos += METHOD_INVOKE_PART2_STATIC.length; //Nonstatic one has a same length than static one + System.arraycopy(METHOD_INVOKE_PART3, 0, bytes, pos, METHOD_INVOKE_PART3.length); + + Method define = null; + Object klass; + boolean unaccessible = false; + //XXX:In some case, this is NOT thread-safe. For example, an another thread changes the access of define while we are invoking... + try { + ClassLoader cl = handler.getClassLoader(); + define = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] {String.class, byte[].class, int.class, int.class} ); + unaccessible = !define.isAccessible(); + if(unaccessible) + define.setAccessible(true); + klass = ((Class)define.invoke(cl, invokerName.replace('/', '.'),bytes,0,bytes.length)) + .getConstructor(Method.class).newInstance(subscriber); + } catch (Exception e) { + throw new AEBRegisterException(e); + } finally { + if(unaccessible) + define.setAccessible(false); + } + return (EventInvoker)klass; + } +} + +/* + BACKUP: Old AsmInvokerGenerator which depends on ASM-lib + + public class AsmInvokerGenerator implements InvokerGenerator { + + protected static String CLASS_NAME_EventInvoker = EventInvoker.class.getName().replace('.', '/'); + //protected static String CLASS_NAME_Event = Event.class.getName().replace('.', '/'); + + protected static String CONST_PARAMS = "(Ljava/lang/Object;Ljava/lang/Object;)V"; + protected static String CONST_LV = "Ljava/lang/Object;"; + + public EventInvoker generateInvoker(Class handler, Method subscriber, Class event) throws AEBRegisterException { + String handlerName = handler.getName().replace('.', '/'); + String invokerName = handlerName+"_Invoker_"+subscriber.getName()+"_"+Math.abs(subscriber.hashCode()); + String invokerName2 = invokerName.replace('/', '.'); //Too silly...Someone makes it smart, please. + String eventName = event.getName().replace('.', '/'); + + ClassLoader cl = handler.getClassLoader(); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, invokerName, null, CLASS_NAME_EventInvoker, null); + { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "(Ljava/lang/reflect/Method;)V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(27, l0); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, CLASS_NAME_EventInvoker, "", "(Ljava/lang/reflect/Method;)V", false); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(28, l1); + mv.visitInsn(Opcodes.RETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", "L"+invokerName+";", null, l0, l2, 0); + mv.visitLocalVariable("subscriber", "Ljava/lang/reflect/Method;", null, l0, l2, 1); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "invoke", CONST_PARAMS, null, new String[] { "java/lang/Throwable" }); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(7, l0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, handlerName); + mv.visitVarInsn(Opcodes.ALOAD, 2); + mv.visitTypeInsn(Opcodes.CHECKCAST, eventName); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, handlerName, subscriber.getName(), "(L"+eventName+";)V", false); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(8, l1); + mv.visitInsn(Opcodes.RETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", "L"+invokerName+";", null, l0, l2, 0); + mv.visitLocalVariable("receiver", "Ljava/lang/Object;", null, l0, l2, 1); + mv.visitLocalVariable("event", CONST_LV, null, l0, l2, 2); + mv.visitMaxs(2, 3); + mv.visitEnd(); + } + cw.visitEnd(); + //Were these written by me? Fucking of course not. They were proudly generated by Bytecode Outline! + byte[] bytes = cw.toByteArray(); + Method define = null; + Object klass; + boolean unaccessible = false; + //XXX:In some case, this is NOT thread-safe. For example, an another thread changes the access of define while we are invoking... + try { + define = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] {String.class, byte[].class, int.class, int.class} ); + unaccessible = !define.isAccessible(); + if(unaccessible) + define.setAccessible(true); + klass = ((Class)define.invoke(cl, invokerName2,bytes,0,bytes.length)).getConstructor(Method.class).newInstance(subscriber); + } catch (Exception e) { + throw new AEBRegisterException(e); + } finally { + if(unaccessible) + define.setAccessible(false); + } + return (EventInvoker)klass; + } +} + */ diff --git a/native-platform/src/net/hakugyokurou/aeb/generator/InvokerGenerator.java b/native-platform/src/net/hakugyokurou/aeb/generator/InvokerGenerator.java new file mode 100644 index 0000000..48ffeb9 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/generator/InvokerGenerator.java @@ -0,0 +1,10 @@ +package net.hakugyokurou.aeb.generator; + +import java.lang.reflect.Method; + +import net.hakugyokurou.aeb.EventInvoker; +import net.hakugyokurou.aeb.exception.AEBRegisterException; + +public interface InvokerGenerator { + public abstract EventInvoker generateInvoker(Class handler, Method subscriber, Class event) throws AEBRegisterException; +} diff --git a/native-platform/src/net/hakugyokurou/aeb/generator/ReflectionInvokerGenerator.java b/native-platform/src/net/hakugyokurou/aeb/generator/ReflectionInvokerGenerator.java new file mode 100644 index 0000000..9851432 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/generator/ReflectionInvokerGenerator.java @@ -0,0 +1,18 @@ +package net.hakugyokurou.aeb.generator; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import net.hakugyokurou.aeb.EventInvoker; +import net.hakugyokurou.aeb.exception.AEBRegisterException; + +public class ReflectionInvokerGenerator implements InvokerGenerator { + + public EventInvoker generateInvoker(Class handler, + Method subscriber, Class event) throws AEBRegisterException { + if((subscriber.getModifiers() & Modifier.SYNCHRONIZED) > 0) + return new net.hakugyokurou.aeb.EventInvoker.SyncReflectedEventInvoker(subscriber); + else + return new net.hakugyokurou.aeb.EventInvoker.AsyncReflectedEventInvoker(subscriber); + } +} diff --git a/native-platform/src/net/hakugyokurou/aeb/quickstart/AnnotatedPriorityJudge.java b/native-platform/src/net/hakugyokurou/aeb/quickstart/AnnotatedPriorityJudge.java new file mode 100644 index 0000000..3f80721 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/quickstart/AnnotatedPriorityJudge.java @@ -0,0 +1,45 @@ +package net.hakugyokurou.aeb.quickstart; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +import net.hakugyokurou.aeb.strategy.EnumDispatchStrategy; +import net.hakugyokurou.aeb.strategy.IPriorityStrategy; + +public class AnnotatedPriorityJudge implements IPriorityStrategy{ + + /** + * The singleton of {@link AnnotatedPriorityJudge}. + */ + public static final AnnotatedPriorityJudge SINGLETON = new AnnotatedPriorityJudge(EventSubscriber.class); + + protected final Class annotation; + protected final EnumDispatchStrategy dispatchStrategy; + + public AnnotatedPriorityJudge(Class annotation) { + this(annotation, EnumDispatchStrategy.PRIORITY_FIRST); + } + + public AnnotatedPriorityJudge(Class annotation, EnumDispatchStrategy dispatchStrategy) { + this.annotation = annotation; + this.dispatchStrategy = dispatchStrategy; + } + + public int getPriorities() { + return EventSubscriber.PRIORITY_HIGHEST; + } + + public EnumDispatchStrategy getDispatchStrategy() { + return dispatchStrategy; + } + + public int judgePriority(Method subscriber) { + EventSubscriber eventSubscriber = subscriber.getAnnotation(annotation); + if(eventSubscriber!=null) + { + return eventSubscriber.priority(); + } + return EventSubscriber.PRIORITY_NORMAL; + } + +} diff --git a/native-platform/src/net/hakugyokurou/aeb/quickstart/AnnotatedSubscriberFinder.java b/native-platform/src/net/hakugyokurou/aeb/quickstart/AnnotatedSubscriberFinder.java new file mode 100644 index 0000000..410e0c5 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/quickstart/AnnotatedSubscriberFinder.java @@ -0,0 +1,56 @@ +package net.hakugyokurou.aeb.quickstart; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; + +import net.hakugyokurou.aeb.strategy.EnumDispatchStrategy; +import net.hakugyokurou.aeb.strategy.IPriorityStrategy; +import net.hakugyokurou.aeb.strategy.ISubscriberStrategy; + +/** + * AnnotatedSubscriberFinder is a quick-starting implement of ISubscriberStrategy. + * It will find the methods which have a given annotation from the handler. + * + * @author szszss + */ +public class AnnotatedSubscriberFinder implements ISubscriberStrategy { + + /** + * The singleton of {@link AnnotatedSubscriberFinder}. + */ + public static final AnnotatedSubscriberFinder SINGLETON = new AnnotatedSubscriberFinder(EventSubscriber.class); + + protected final Class annotation; + + public AnnotatedSubscriberFinder(Class annotation) { + this.annotation = annotation; + } + + /** + * {@link AnnotatedSubscriberFinder} doesn't depend on the instance. + */ + public boolean isDependOnInstance() { + return false; + } + + public Method[] findSubscribers(Object handler) { + Class klass; + if(handler instanceof Class) + klass = (Class)handler; + else + klass = handler.getClass(); //If the handler is an instance, get the class. + Method[] methods = klass.getMethods(); + ArrayList cache = new ArrayList(methods.length); + for(Method method : methods) + { + if(method.getAnnotation(annotation)!=null) + { + Class[] params = method.getParameterTypes(); + if(!method.isVarArgs() && params.length==1 && method.getReturnType().equals(void.class)) + cache.add(method); + } + } + return cache.toArray(new Method[cache.size()]); + } +} diff --git a/native-platform/src/net/hakugyokurou/aeb/quickstart/DiscardDeadEventHandler.java b/native-platform/src/net/hakugyokurou/aeb/quickstart/DiscardDeadEventHandler.java new file mode 100644 index 0000000..c00d087 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/quickstart/DiscardDeadEventHandler.java @@ -0,0 +1,25 @@ +package net.hakugyokurou.aeb.quickstart; + +import java.lang.reflect.Method; + +import net.hakugyokurou.aeb.EventBus; +import net.hakugyokurou.aeb.auxiliary.IDeadEventHandler; + +/** + * DiscardDeadEventHandler is a quick-starting implement of IDeadEventHandler. + * It will silently discard the dead events when it receives them. + * + * @author szszss + */ +public class DiscardDeadEventHandler implements IDeadEventHandler{ + + /** + * The singleton of {@link DiscardDeadEventHandler}. + */ + public static final DiscardDeadEventHandler SINGLETON = new DiscardDeadEventHandler(); + + public void handleDeadEvent(EventBus eventBus, Object event) { + //Do nothing, silence is gold. + } + +} diff --git a/native-platform/src/net/hakugyokurou/aeb/quickstart/EventSubscriber.java b/native-platform/src/net/hakugyokurou/aeb/quickstart/EventSubscriber.java new file mode 100644 index 0000000..861c544 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/quickstart/EventSubscriber.java @@ -0,0 +1,17 @@ +package net.hakugyokurou.aeb.quickstart; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface EventSubscriber { + public final static int PRIORITY_HIGHEST = 4; + public final static int PRIORITY_HIGH = 3; + public final static int PRIORITY_NORMAL = 2; + public final static int PRIORITY_LOW = 1; + public final static int PRIORITY_LOWEST = 0; + public int priority() default PRIORITY_NORMAL; +} diff --git a/native-platform/src/net/hakugyokurou/aeb/quickstart/LoggingSubscriberExceptionHandler.java b/native-platform/src/net/hakugyokurou/aeb/quickstart/LoggingSubscriberExceptionHandler.java new file mode 100644 index 0000000..b92f005 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/quickstart/LoggingSubscriberExceptionHandler.java @@ -0,0 +1,22 @@ +package net.hakugyokurou.aeb.quickstart; + +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.hakugyokurou.aeb.EventBus; +import net.hakugyokurou.aeb.auxiliary.ISubscriberExceptionHandler; + +public class LoggingSubscriberExceptionHandler implements ISubscriberExceptionHandler{ + + /** + * The singleton of {@link LoggingSubscriberExceptionHandler}. + */ + public static final LoggingSubscriberExceptionHandler SINGLETON = new LoggingSubscriberExceptionHandler(); + + public void handleSubscriberException(EventBus eventBus, Object handler, + Method subscriber, Object event, Throwable e) { + Logger logger = eventBus.getLogger(); + logger.log(Level.SEVERE, "Event handler:"+handler+" met an exception in "+subscriber.getName()+". Cause:"+e.getCause()); + } +} diff --git a/native-platform/src/net/hakugyokurou/aeb/strategy/EnumDispatchStrategy.java b/native-platform/src/net/hakugyokurou/aeb/strategy/EnumDispatchStrategy.java new file mode 100644 index 0000000..653ee43 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/strategy/EnumDispatchStrategy.java @@ -0,0 +1,6 @@ +package net.hakugyokurou.aeb.strategy; + +public enum EnumDispatchStrategy { + HIERARCHY_FIRST, + PRIORITY_FIRST; +} diff --git a/native-platform/src/net/hakugyokurou/aeb/strategy/EnumHierarchyStrategy.java b/native-platform/src/net/hakugyokurou/aeb/strategy/EnumHierarchyStrategy.java new file mode 100644 index 0000000..8b1361a --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/strategy/EnumHierarchyStrategy.java @@ -0,0 +1,6 @@ +package net.hakugyokurou.aeb.strategy; + +public enum EnumHierarchyStrategy { + SUPER_FIRST, + EXTENDED_FIRST; +} diff --git a/native-platform/src/net/hakugyokurou/aeb/strategy/EnumInvokerGenerator.java b/native-platform/src/net/hakugyokurou/aeb/strategy/EnumInvokerGenerator.java new file mode 100644 index 0000000..460a01a --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/strategy/EnumInvokerGenerator.java @@ -0,0 +1,10 @@ +package net.hakugyokurou.aeb.strategy; + +public enum EnumInvokerGenerator { + ASM, + REFLECT; + + public static EnumInvokerGenerator getDefault() { + return ASM; + } +} diff --git a/native-platform/src/net/hakugyokurou/aeb/strategy/IPriorityStrategy.java b/native-platform/src/net/hakugyokurou/aeb/strategy/IPriorityStrategy.java new file mode 100644 index 0000000..9d86e77 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/strategy/IPriorityStrategy.java @@ -0,0 +1,22 @@ +package net.hakugyokurou.aeb.strategy; + +import java.lang.reflect.Method; + +/** + * IPriorityStrategy is used by PriorEventBus. It is used to decide which priority subscriber belonged to, + * how many priorities dose the prior event bus have, and how the event dispatch.
+ * It has a default implement: AnnotatedPriorityJudger. + * + * @author szszss + * + * @see AnnotatedPriorityJudge + * @see EnumDispatchStrategy + */ +public interface IPriorityStrategy { + + public abstract int getPriorities(); + + public abstract EnumDispatchStrategy getDispatchStrategy(); + + public abstract int judgePriority(Method subscriber); +} diff --git a/native-platform/src/net/hakugyokurou/aeb/strategy/ISubscriberStrategy.java b/native-platform/src/net/hakugyokurou/aeb/strategy/ISubscriberStrategy.java new file mode 100644 index 0000000..ac2b616 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/strategy/ISubscriberStrategy.java @@ -0,0 +1,46 @@ +package net.hakugyokurou.aeb.strategy; + +import java.lang.reflect.Method; + +/** + * ISubscriberStrategy describes that how event bus finds subscribers from the instance or class.
+ * It has a default implement: AnnotatedSubscriberFinder. + * + * @author szszss + * + * @see AnnotatedSubscriberFinder + */ +public interface ISubscriberStrategy { + + /** + * ATTENTION: + * Although this method is unused so far, you should serious deal with it, because it may be put into service in some time.

+ * + * baseInInstance describes that whether the subscribers depend on class or instance of class. In other words, + * May the subscribers be different when they are from different instance of same class?

+ * + * If the subscribers are same so long as they are from same class, this method should return false, + * since they don't depend on instance. This is the behavior of most event bus system, such as Google Guava.
+ * However, if the subscribers may be different though they are from same class, this method should return true, + * because they depend on the difference of class' instance.

+ * + * For example:
+ * + * EventBus eventBus = ...
+ * Handler h1 = new Handler();
+ * Handler h2 = new Handler();
+ * eventBus.register(h1);
+ * eventBus.register(h2);
+ *
+ * If event bus get (or may get) different subscribers in two registering, then the subscribers depend on instance.

+ * + * Depending on instance can be used to implement some special function. + * + * @return false if every instance of handler have same subscribers so long as instances are from same class, + * true if the subscribers are base on handler's instance. + */ + public boolean isDependOnInstance(); + + public abstract Method[] findSubscribers(Object handler); + +} diff --git a/native-platform/src/net/hakugyokurou/aeb/util/ArrayCOWArrayList.java b/native-platform/src/net/hakugyokurou/aeb/util/ArrayCOWArrayList.java new file mode 100644 index 0000000..b90c359 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/util/ArrayCOWArrayList.java @@ -0,0 +1,75 @@ +package net.hakugyokurou.aeb.util; + +import java.util.Iterator; +import java.util.concurrent.CopyOnWriteArrayList; + +public class ArrayCOWArrayList { + + private final CopyOnWriteArrayList[] arrays; + private final int sections; + private int size = 0; + + /** + * Max section count. Now is 65535. + */ + public static final int MAX_SECTIONS; + + static { + MAX_SECTIONS = 65535; //Prevent compile-time inline. + } + + @SuppressWarnings("unchecked") + public ArrayCOWArrayList(int sections) { + if(sections<0 || sections>MAX_SECTIONS) + throw new IllegalArgumentException("Invaild sections:"+sections); + this.arrays = new CopyOnWriteArrayList[sections]; + for(int i=0;i(); + } + this.sections = sections; + } + + public void add(E value, int section) { + if(section<0 || section>=this.sections) + throw new IndexOutOfBoundsException("Section:"+section+" is out of <0-"+(sections-1)+">."); + arrays[section].add(value); + size++; + } + + public boolean remove(E value) { + for(CopyOnWriteArrayList cowal : arrays) + { + if(cowal.remove(value)) + { + size--; + return true; + } + } + return false; + } + + public Iterator getIterator(int section) { + return arrays[section].iterator(); + } + + public int getSections() { + return sections; + } + + public boolean isEmpty() { + return size==0; + } + + public int getSize() { + return size; + } + + public boolean isEmpty(int section) { + return arrays[section].isEmpty(); + } + + public int getSize(int section) { + return arrays[section].size(); + } +} diff --git a/native-platform/src/net/hakugyokurou/aeb/util/EventBusBuilder.java b/native-platform/src/net/hakugyokurou/aeb/util/EventBusBuilder.java new file mode 100644 index 0000000..7dbe2a4 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/util/EventBusBuilder.java @@ -0,0 +1,240 @@ +package net.hakugyokurou.aeb.util; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +import net.hakugyokurou.aeb.AsyncEventBus; +import net.hakugyokurou.aeb.EventBus; +import net.hakugyokurou.aeb.PriorEventBus; +import net.hakugyokurou.aeb.auxiliary.IDeadEventHandler; +import net.hakugyokurou.aeb.auxiliary.ISubscriberExceptionHandler; +import net.hakugyokurou.aeb.quickstart.AnnotatedPriorityJudge; +import net.hakugyokurou.aeb.quickstart.AnnotatedSubscriberFinder; +import net.hakugyokurou.aeb.quickstart.LoggingSubscriberExceptionHandler; +import net.hakugyokurou.aeb.quickstart.DiscardDeadEventHandler; +import net.hakugyokurou.aeb.strategy.EnumDispatchStrategy; +import net.hakugyokurou.aeb.strategy.EnumHierarchyStrategy; +import net.hakugyokurou.aeb.strategy.EnumInvokerGenerator; +import net.hakugyokurou.aeb.strategy.IPriorityStrategy; +import net.hakugyokurou.aeb.strategy.ISubscriberStrategy; + +/** + * EventBusBuilder is a tool which can create a EventBus in Builder Pattern.
+ *

Examples:

+ * + *

+ * //Build a normal event bus

+ * //Build a normal EB with default settings:
+ * EventBusBuilder builder = EventBusBuilder.createBuilder().get();

+ * //Build a normal EB with a custom name and hierarchy strategy:
+ * EventBusBuilder builder = EventBusBuilder.createBuilder(). + * buildEventBus.setName("PowerDaze").setHierarchyStrategy(EnumHierarchyStrategy.SUPER_FIRST).get();
+ *

+ *

+ * //Build a prioritized event bus with default settings:
+ * EventBusBuilder builder = EventBusBuilder.createBuilder().buildPriorEventBus().get();

+ *

+ *

+ * //Build an async event bus with auto-generated wrapped normal event bus and default settings:
+ * EventBusBuilder builder = EventBusBuilder.createBuilder().buildAsyncEventBus().get();

+ *

+ * @author szszss + */ +public class EventBusBuilder { + + protected static final int EVENT_BUS = 0; + protected static final int PRIOR_EVENT_BUS = 1; + protected static final int ASYNC_EVENT_BUS = 2; + + protected int type = EVENT_BUS; + protected String name; + protected Executor executor; + protected EventBus wrapped; + protected EnumDispatchStrategy dispatchStrategy = EnumDispatchStrategy.PRIORITY_FIRST; + protected EnumHierarchyStrategy hierarchyStrategy = EnumHierarchyStrategy.EXTENDED_FIRST; + protected EnumInvokerGenerator invokerGenerator = EnumInvokerGenerator.getDefault(); //ASM + protected IPriorityStrategy priorityStrategy = AnnotatedPriorityJudge.SINGLETON; + protected ISubscriberStrategy subscriberStrategy = AnnotatedSubscriberFinder.SINGLETON; + protected IDeadEventHandler deadEventHandler = DiscardDeadEventHandler.SINGLETON; + protected ISubscriberExceptionHandler exceptionHandler = LoggingSubscriberExceptionHandler.SINGLETON; + protected Logger logger; + + /** + * Create an instance of EventBusBuilder. + * @return An instance of EventBusBuilder + */ + public static EventBusBuilder createBuilder() { + return new EventBusBuilder(); + } + + /** + * Request for building a normal event bus. This is the default type. + * @return Builder + */ + public EventBusBuilder buildEventBus() { + type = EVENT_BUS; + return this; + } + + /** + * Request for building a prioritized event bus. + * @return Builder + */ + public EventBusBuilder buildPriorEventBus() { + type = EVENT_BUS; + return this; + } + + /** + * Request for building an async event bus. + * @return Builder + */ + public EventBusBuilder buildAsyncEventBus() { + type = EVENT_BUS; + return this; + } + + /** + * Set the name of event bus. This is nullable, since event bus will get a default name if this is null. + * @param name The name of event bus. Nullable + * @return Builder + */ + public EventBusBuilder setName(String name) { + this.name = name; + return this; + } + + /** + * Set an executor for async event bus. Although the executor is non-nullable in asyncEB, + * this param is nullable, because it will create a single thread executor as default executor. + * However, this is feature is just a fool-proofing. Users shouldn't depend on this feature. + * @param executor The executor of async event bus. Nullable + * @return Builder + */ + public EventBusBuilder setExecutor(Executor executor) { + this.executor = executor; + return this; + } + + /** + * Set a sync event bus for async event bus. Although the wrapped event bus is non-nullable in asyncEB, + * this param is nullable, because it will create a normal event bus with default settings as default wrappedEB. + * However, this is feature is just a fool-proofing. Users shouldn't depend on this feature. + * @param wrapped The wrapped sync event bus of async event bus. Nullable + * @return Builder + */ + public EventBusBuilder setWrappedEventBus(EventBus eb) { + this.wrapped = eb; + return this; + } + + /** + * Set the dispatch strategy for prioritized event bus. The default value is PRIORITY_FIRST. + * @param dispatchStrategy The dispatch strategy of prioritized event bus + * @return Builder + * @see EnumDispatchStrategy + */ + public EventBusBuilder setDispatchStrategy(EnumDispatchStrategy dispatchStrategy) { + this.dispatchStrategy = dispatchStrategy; + return this; + } + + /** + * Set the hierarchy strategy. The default value is EXTENDED_FIRST. + * @param hierarchyStrategy The hierarchy strategy of event bus + * @return Builder + * @see EnumHierarchyStrategy + */ + public EventBusBuilder setHierarchyStrategy(EnumHierarchyStrategy hierarchyStrategy) { + this.hierarchyStrategy = hierarchyStrategy; + return this; + } + + /** + * Set the priority strategy for prioritized event bus. The default value is the singleton of {@link AnnotatedPriorityJudge}. + * @param priorityStrategy The priority strategy of prioritized event bus + * @return Builder + * @see IPriorityStrategy + */ + public EventBusBuilder setPriorityStrategy(IPriorityStrategy priorityStrategy) { + this.priorityStrategy = priorityStrategy; + return this; + } + + /** + * Set the subscriber strategy for event bus. The default value is the singleton of {@link AnnotatedSubscriberFinder}. + * @param subscriberStrategy The subscriber strategy of event bus + * @return Builder + * @see ISubscriberStrategy + */ + public EventBusBuilder setSubscriberStrategy(ISubscriberStrategy subscriberStrategy) { + this.subscriberStrategy = subscriberStrategy; + return this; + } + + /** + * Set the dead event handler for event bus. The default value is the singleton of {@link DiscardDeadEventHandler}. + * @param deadEventHandler The dead event handler of event bus. + * @return Builder + * @see IDeadEventHandler + */ + public EventBusBuilder setDeadEventHandler(IDeadEventHandler deadEventHandler) { + this.deadEventHandler = deadEventHandler; + return this; + } + + /** + * Set the subscriber exception handler for event bus. The default value is the singleton of {@link LoggingSubscriberExceptionHandler}. + * @param subscriberExceptionHandler The subscriber exception handler of event bus. + * @return Builder + * @see ISubscriberExceptionHandler + */ + public EventBusBuilder setSubscriberExceptionHandler(ISubscriberExceptionHandler subscriberExceptionHandler) { + this.exceptionHandler = subscriberExceptionHandler; + return this; + } + + /** + * Set the logger for event bus. This is nullable. + * @param logger The logger of event bus. + * @return Builder + */ + public EventBusBuilder setLogger(Logger logger) { + this.logger = logger; + return this; + } + + /** + * Build the event bus with the settings. After build, the builder is still available. + * However, you may need to change the name of new event bus. + * @return An instance of event bus you requested + */ + public EventBus get() { + EventBus eventBus; + switch (type) { + case PRIOR_EVENT_BUS: + eventBus = new PriorEventBus(name, hierarchyStrategy, subscriberStrategy, EnumInvokerGenerator.getDefault(), priorityStrategy); + break; + case ASYNC_EVENT_BUS: + EventBus eb = wrapped; + Executor exe = executor; + if(eb==null) + eb = new EventBus(name + "$DEFAULT_EB", hierarchyStrategy, subscriberStrategy, EnumInvokerGenerator.getDefault()); + if(exe==null) + exe = Executors.newSingleThreadExecutor(); + eventBus = new AsyncEventBus(name, executor, eb); + break; + default: + eventBus = new EventBus(name, hierarchyStrategy, subscriberStrategy, EnumInvokerGenerator.getDefault()); + break; + } + eventBus.setDeadEventHandler(deadEventHandler); + eventBus.setSubscriberExceptionHandler(exceptionHandler); + if(logger!=null) + eventBus.setLogger(logger); + return eventBus; + } +} diff --git a/native-platform/src/net/hakugyokurou/aeb/util/SectionalLinkedList.java b/native-platform/src/net/hakugyokurou/aeb/util/SectionalLinkedList.java new file mode 100644 index 0000000..1c3a312 --- /dev/null +++ b/native-platform/src/net/hakugyokurou/aeb/util/SectionalLinkedList.java @@ -0,0 +1,223 @@ +package net.hakugyokurou.aeb.util; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.ConcurrentSkipListMap; + +/** + * @Deprecated Nothing but a fucking bug fag. There's no evidence that SectionalLinkedList is faster than ArrayCOWArrayList, + * and I'm sure that this is a shit with tons of defect. + */ +@Deprecated +class SectionalLinkedList implements Iterable{ + + private final Map> nodes = new HashMap>(); + private final Entry rootNodes[]; + private final int sections; + + private Entry headNode; + + public SectionalLinkedList(int sections) { + if(sections<=0) + throw new RuntimeException(); //TODO:An exception. + this.rootNodes = new Entry[sections]; + this.sections = sections; + } + + public Iterator iterator() { + return iterator(sections-1); + } + + public Iterator iterator(int start) { + synchronized (rootNodes) { + return new SLLIterator(sections, rootNodes, sections-1); + } + } + + public void add(E value, int section) { + if(section<0 || section>=this.sections) + throw new RuntimeException(); //TODO:An exception. + synchronized (rootNodes) { + Entry entry = new Entry(value, section); + nodes.put(value, entry); + Entry ingress = rootNodes[section]; + if(ingress!=null) + { + entry.prev = ingress; + entry.next = ingress.next; + if(ingress.next!=null) + ingress.next.prev = entry; + ingress.next = entry; + } + else + { + //There are two situations, No any node, or this (when sections is 5): + // I + //H I I + //222333 + //As you see, 0 and 1 haven't ingress. + //Sorry for my poor English... I don't know how to express... + if(headNode==null) //If no nodes. + { + headNode = entry; + for(int i=section+1;i entry = nodes.remove(value); + if(entry==null) + return false; + for(int i=0;i implements Iterator { + + private int sectionBreakpoint; + private Entry current; + private Entry next; + + public SLLIterator(int sections, Entry[] rootNodes, int start) { + next = (Entry)rootNodes[start]; + sectionBreakpoint = sections; + } + + public boolean hasNext() { + return next!=null; + } + + public E next() { + current = next; + next = next.prev; + return current.value; + } + + public void remove() { //Why fucking remove? + throw new UnsupportedOperationException(); + } + + public boolean isBreakpoint() { //Shit, I'm sure that there is a bug. 如果多个rootNode指向一个节点会怎么样? + if(current.section { + public Entry next; + public Entry prev; + public final E value; + public final int section; + + public Entry(E value, int section) { + this.value = value; + this.section = section; + } + + @Override + public boolean equals(Object object) { + if(!(object instanceof Entry)) + return false; + Entry entry = (Entry)object; + return entry.value==this.value&&entry.section==this.section; + } + + @Override + public int hashCode() { + return value.hashCode()*(section+1); + } + } + + +}