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    }