001 package com.hammurapi.eventbus;
002
003 import java.lang.reflect.InvocationHandler;
004 import java.lang.reflect.Method;
005 import java.lang.reflect.Proxy;
006
007 import com.hammurapi.extract.CommutativeAnd;
008 import com.hammurapi.extract.Predicate;
009 import com.hammurapi.extract.True;
010
011 public final class ReflectiveEventHandler<E, C, H extends EventBus.Handle<E,Integer,C>, S extends EventStore<E,Integer,C,H,S>> implements EventHandler<E, Integer, C, H, S> {
012 private final C instance;
013 private final int offset;
014 private final Method mthd;
015 private final Class<E>[] pt;
016 private final Handler ha;
017 private Class<E> eventType;
018 private Class<?> contextType;
019 private Predicate<E,C> predicate;
020
021 ReflectiveEventHandler(
022 C instance,
023 int offset,
024 Method mthd,
025 Class<E>[] pt,
026 Handler ha,
027 Class<E> eventType,
028 Predicate<E,C>... predicates) {
029
030 this.instance = instance;
031 this.offset = offset;
032 this.mthd = mthd;
033 this.pt = pt;
034 this.ha = ha;
035 this.eventType = eventType;
036 if (offset>0) {
037 contextType=mthd.getParameterTypes()[0];
038 if (!contextType.isInterface()) {
039 throw new IllegalArgumentException("Event dispatch context parameter type should be interface: "+mthd);
040 }
041 }
042
043 if (predicates.length==0) {
044 predicate = True.getInstance();
045 } else if (predicates.length==1) {
046 predicate = predicates[0];
047 } else {
048 predicate = new CommutativeAnd<E,C>(0, null, predicates).normalize();
049 }
050 }
051
052 public Method getMethod() {
053 return mthd;
054 }
055
056 public C getInstance() {
057 return instance;
058 }
059
060 @Override
061 public boolean consumes() {
062 return ha.consumes();
063 }
064
065 @Override
066 public int getCardinality() {
067 return pt.length;
068 }
069
070 @Override
071 public C getContext() {
072 return instance;
073 }
074
075 @Override
076 public Integer getPriority() {
077 return ha.priority();
078 }
079
080 @SuppressWarnings("unchecked")
081 @Override
082 public void post(final EventDispatchContext<E, Integer, C, H, S> context, E... events) {
083 int expectedEventsLength = pt.length;
084 if (events.length!=expectedEventsLength) {
085 throw new EventDispatchException("Expected "+expectedEventsLength+" input events for method "+mthd+", got "+events.length);
086 }
087 Object[] args = new Object[pt.length+offset];
088 if (offset>0) {
089 if (contextType.isInstance(context)) {
090 args[0] = context;
091 } else {
092 // Dynamic proxy for event dispatch context.
093 InvocationHandler ih = new InvocationHandler() {
094
095 @Override
096 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
097 return method.invoke(context, args);
098 }
099 };
100
101 Class<?>[] contextInterfaces = context.getClass().getInterfaces();
102 Class<?>[] proxyInterfaces = new Class[contextInterfaces.length+1];
103 System.arraycopy(contextInterfaces, 0, proxyInterfaces, 0, contextInterfaces.length);
104 proxyInterfaces[contextInterfaces.length] = contextType;
105 args[0] = Proxy.newProxyInstance(contextType.getClassLoader(), proxyInterfaces, ih);
106 }
107 }
108 for (int i=0; i<events.length; ++i) {
109 args[i+offset] = events[i];
110 }
111 try {
112 Object toPost = mthd.invoke(instance, args);
113 if (eventType.isInstance(toPost)) {
114 context.post((E) toPost);
115 }
116 } catch (Exception e) {
117 throw new EventDispatchException("Failed to invoke event handler method "+mthd+": "+e, e);
118 }
119 }
120
121 @Override
122 public void reset() {
123 if (instance instanceof Resettable) {
124 ((Resettable) instance).reset();
125 }
126 }
127
128 @Override
129 public String toString() {
130 return "Reflective Handler(cardinality = "+getCardinality()+", method = "+mthd+", instance = "+instance+")";
131 }
132
133 @Override
134 public boolean isOneOff() {
135 return ha.oneOff();
136 }
137
138 @Override
139 public com.hammurapi.eventbus.EventHandlerBase.Mode getMode() {
140 return ha.mode();
141 }
142
143 @Override
144 public Predicate<E, C> getPredicate() {
145 return predicate;
146 }
147 }