All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rx.core.EventTarget Maven / Gradle / Ivy

package org.rx.core;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.rx.beans.FlagsEnum;
import org.rx.beans.NEnum;

import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;

import static org.rx.core.Contract.*;

public interface EventTarget> {
    @Slf4j
    class Delegate, TArgs extends EventArgs> implements BiConsumer {
        @Getter
        private final List> invocationList = new CopyOnWriteArrayList<>();

        @SuppressWarnings(NonWarning)
        @Override
        public void accept(TSender target, TArgs args) {
            require(target, args);

            FlagsEnum flags = target.eventFlags();
            if (flags.has(EventTarget.EventFlags.ThreadSafe)) {
                synchronized (target) {
                    innerRaise(target, args, flags);
                }
                return;
            }
            innerRaise(target, args, flags);
        }

        private void innerRaise(TSender target, TArgs args, FlagsEnum flags) {
            for (BiConsumer biConsumer : invocationList) {
                try {
                    biConsumer.accept(target, args);
                } catch (Exception e) {
                    if (!flags.has(EventTarget.EventFlags.Quietly)) {
                        throw e;
                    }
                    log.warn("innerRaise", e);
                }
            }
        }
    }

    @RequiredArgsConstructor
    enum EventFlags implements NEnum {
        None(0),
        ThreadSafe(1),
        Quietly(1 << 1),
        DynamicAttach(1 << 2);

        @Getter
        private final int value;
    }

    default FlagsEnum eventFlags() {
        return EventFlags.None.flags();
    }

    default  void attachEvent(String eventName, BiConsumer event) {
        attachEvent(eventName, event, true);
    }

    @SuppressWarnings(NonWarning)
    @SneakyThrows
    default  void attachEvent(String eventName, BiConsumer event, boolean combine) {
        require(eventName);

        Field field = Reflects.getFields(this.getClass()).firstOrDefault(p -> p.getName().equals(eventName));
        if (field == null) {
            if (!eventFlags().has(EventFlags.DynamicAttach)) {
                throw new InvalidOperationException(String.format("Event %s not defined", eventName));
            }
            EventListener.getInstance().attach(this, eventName, event, combine);
            return;
        }
        field.set(this, combine ? combine((BiConsumer) field.get(this), event) : event);
    }

    @SuppressWarnings(NonWarning)
    @SneakyThrows
    default  void detachEvent(String eventName, BiConsumer event) {
        require(eventName);

        Field field = Reflects.getFields(this.getClass()).firstOrDefault(p -> p.getName().equals(eventName));
        if (field == null) {
            if (!eventFlags().has(EventFlags.DynamicAttach)) {
                throw new InvalidOperationException(String.format("Event %s not defined", eventName));
            }
            EventListener.getInstance().detach(this, eventName, event);
            return;
        }
        field.set(this, remove((BiConsumer) field.get(this), event));
    }

    @SuppressWarnings(NonWarning)
    @SneakyThrows
    default  void raiseEvent(String eventName, TArgs args) {
        require(eventName);

        Field field = Reflects.getFields(this.getClass()).firstOrDefault(p -> p.getName().equals(eventName));
        if (field == null) {
            if (!eventFlags().has(EventFlags.DynamicAttach)) {
                throw new InvalidOperationException(String.format("Event %s not defined", eventName));
            }
            EventListener.getInstance().raise(this, eventName, args);
            return;
        }
        raiseEvent((BiConsumer) field.get(this), args);
    }

    @SuppressWarnings(NonWarning)
    default  void raiseEvent(BiConsumer event, TArgs args) {
        require(args);

        if (event == null) {
            return;
        }
        event.accept((TSender) this, args);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy