    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    }