1 | package com.hammurapi.eventbus; |
2 | |
3 | import java.lang.reflect.InvocationHandler; |
4 | import java.lang.reflect.Method; |
5 | import java.lang.reflect.Proxy; |
6 | |
7 | import com.hammurapi.extract.CommutativeAnd; |
8 | import com.hammurapi.extract.Predicate; |
9 | import com.hammurapi.extract.True; |
10 | |
11 | 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> { |
12 | private final C instance; |
13 | private final int offset; |
14 | private final Method mthd; |
15 | private final Class<E>[] pt; |
16 | private final Handler ha; |
17 | private Class<E> eventType; |
18 | private Class<?> contextType; |
19 | private Predicate<E,C> predicate; |
20 | |
21 | ReflectiveEventHandler( |
22 | C instance, |
23 | int offset, |
24 | Method mthd, |
25 | Class<E>[] pt, |
26 | Handler ha, |
27 | Class<E> eventType, |
28 | Predicate<E,C>... predicates) { |
29 | |
30 | this.instance = instance; |
31 | this.offset = offset; |
32 | this.mthd = mthd; |
33 | this.pt = pt; |
34 | this.ha = ha; |
35 | this.eventType = eventType; |
36 | if (offset>0) { |
37 | contextType=mthd.getParameterTypes()[0]; |
38 | if (!contextType.isInterface()) { |
39 | throw new IllegalArgumentException("Event dispatch context parameter type should be interface: "+mthd); |
40 | } |
41 | } |
42 | |
43 | if (predicates.length==0) { |
44 | predicate = True.getInstance(); |
45 | } else if (predicates.length==1) { |
46 | predicate = predicates[0]; |
47 | } else { |
48 | predicate = new CommutativeAnd<E,C>(0, null, predicates).normalize(); |
49 | } |
50 | } |
51 | |
52 | public Method getMethod() { |
53 | return mthd; |
54 | } |
55 | |
56 | public C getInstance() { |
57 | return instance; |
58 | } |
59 | |
60 | @Override |
61 | public boolean consumes() { |
62 | return ha.consumes(); |
63 | } |
64 | |
65 | @Override |
66 | public int getCardinality() { |
67 | return pt.length; |
68 | } |
69 | |
70 | @Override |
71 | public C getContext() { |
72 | return instance; |
73 | } |
74 | |
75 | @Override |
76 | public Integer getPriority() { |
77 | return ha.priority(); |
78 | } |
79 | |
80 | @SuppressWarnings("unchecked") |
81 | @Override |
82 | public void post(final EventDispatchContext<E, Integer, C, H, S> context, E... events) { |
83 | int expectedEventsLength = pt.length; |
84 | if (events.length!=expectedEventsLength) { |
85 | throw new EventDispatchException("Expected "+expectedEventsLength+" input events for method "+mthd+", got "+events.length); |
86 | } |
87 | Object[] args = new Object[pt.length+offset]; |
88 | if (offset>0) { |
89 | if (contextType.isInstance(context)) { |
90 | args[0] = context; |
91 | } else { |
92 | // Dynamic proxy for event dispatch context. |
93 | InvocationHandler ih = new InvocationHandler() { |
94 | |
95 | @Override |
96 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
97 | return method.invoke(context, args); |
98 | } |
99 | }; |
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 | } |