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

org.fxmisc.wellbehaved.event.StatefulEventHandlerTemplate Maven / Gradle / Ivy

The newest version!
package org.fxmisc.wellbehaved.event;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.function.Supplier;

import javafx.event.Event;
import javafx.event.EventType;

public final class StatefulEventHandlerTemplate implements EventHandlerTemplate {

    @FunctionalInterface
    public interface StateTransition {
        S transition(T target, S state, E event);
    }

    /**
     * An instance of this interface is expected to consume the event
     * if it successfully handled the event. If the event was not handled by an
     * instance of this interface, the event should be left unconsumed and the
     * returned state should be unchanged.
     */
    @FunctionalInterface
    public interface StateTransitioningHandler {
        S handle(T target, S state, E event);

        default  StateTransitioningHandler orElse(
                StateTransitioningHandler nextHandler) {
            return (u, s, f) -> {
                S newState = StateTransitioningHandler.this.handle(u, s, f);
                if(f.isConsumed()) {
                    return newState;
                } else {
                    return nextHandler.handle(u, s, f);
                }
            };
        }

        default  StateTransitioningHandler onlyWhen(BiPredicate condition) {
            return (u, s, e) -> {
                return condition.test(u, s)
                        ? StateTransitioningHandler.this.handle(u, s, e)
                        : s;
            };
        }

        default StatefulEventHandlerTemplate initialStateSupplier(Supplier initialStateSupplier) {
            return new StatefulEventHandlerTemplate<>(this, initialStateSupplier);
        }
    }

    public static abstract class Builder {

        private static  Builder empty() {
            return new Builder() {
                @Override
                 List> getHandlers(
                        int additionalCapacity) {
                    return new ArrayList<>(additionalCapacity);
                }
            };
        }

        // private constructor to prevent subclassing by the user
        private Builder() {}

        public  On on(EventPattern eventMatcher) {
            return new On<>(this, eventMatcher);
        }

        public  On on(EventType eventType) {
            return on(EventPattern.eventTypePattern(eventType));
        }

        public  Builder addHandler(
                StateTransitioningHandler handler) {
            return new CompositeBuilder<>(this, handler);
        }

        public StateTransitioningHandler createHandler() {
            return (t, s, e) -> {
                S newState = s;
                for(StateTransitioningHandler handler: getHandlers()) {
                    newState = handler.handle(t, newState, e);
                    if(e.isConsumed()) {
                        break;
                    }
                }
                return newState;
            };
        }

        public StatefulEventHandlerTemplate initialStateSupplier(Supplier initialStateSupplier) {
            return createHandler().initialStateSupplier(initialStateSupplier);
        }

        List> getHandlers() {
            return getHandlers(0);
        }

        abstract  List> getHandlers(int additionalCapacity);
    }

    private static class CompositeBuilder extends Builder {
        private final Builder previousBuilder;
        private final StateTransitioningHandler handler;

        private CompositeBuilder(
                Builder previousBuilder,
                StateTransitioningHandler handler) {
            this.previousBuilder = previousBuilder;
            this.handler = handler;
        }

        @Override
         List> getHandlers(
                int additionalCapacity) {
            List> handlers = previousBuilder.getHandlers(additionalCapacity + 1);
            handlers.add(handler);
            return handlers;
        }
    }

    public static class On {
        private final Builder previousBuilder;
        private final EventPattern eventMatcher;

        private On(
                Builder previousBuilder,
                EventPattern eventMatcher) {
            this.previousBuilder = previousBuilder;
            this.eventMatcher = eventMatcher;
        }

        public On where(Predicate condition) {
            return new On<>(previousBuilder, eventMatcher.and(condition));
        }

        public  When when(Predicate condition) {
            return when((u, s) -> condition.test(u));
        }

        public  When when(BiPredicate condition) {
            return new When<>(previousBuilder, eventMatcher, condition);
        }

        public  Builder act(BiConsumer action) {
            return act((u, s, e) -> { action.accept(u, e); return s; });
        }

        public  Builder act(StateTransition action) {
            return previousBuilder.addHandler((u, s, e) -> {
                Optional optF = eventMatcher.match(e);
                if(optF.isPresent()) {
                    F f = optF.get();
                    S newState = action.transition(u, s, f);
                    f.consume();
                    return newState;
                } else {
                    return s;
                }
            });
        }
    }

    public static class When {
        private final Builder previousBuilder;
        private final EventPattern eventMatcher;
        private final BiPredicate condition;

        private When(
                Builder previousBuilder,
                EventPattern eventMatcher,
                BiPredicate condition) {
            this.previousBuilder = previousBuilder;
            this.eventMatcher = eventMatcher;
            this.condition = condition;
        }

        public  Builder act(BiConsumer action) {
            return act((u, s, e) -> { action.accept(u, e); return s; });
        }

        public  Builder act(StateTransition action) {
            return previousBuilder.addHandler((u, s, e) -> {
                Optional optF = eventMatcher.match(e);
                if(optF.isPresent() && condition.test(u, s)) {
                    F f = optF.get();
                    S newState = action.transition(u, s, f);
                    f.consume();
                    return newState;
                } else {
                    return s;
                }
            });
        }
    }

    public static  On on(
            EventPattern eventMatcher) {
        return Builder.empty().on(eventMatcher);
    }

    public static  On on(
            EventType eventType) {
        return Builder.empty().on(eventType);
    }

    public static  Builder
    startWith(StateTransitioningHandler handler) {
        return Builder.empty().addHandler(handler);
    }


    private final Supplier initialStateSupplier;
    private final StateTransitioningHandler handler;

    StatefulEventHandlerTemplate(
            StateTransitioningHandler handler,
            Supplier initialStateSupplier) {
        this.initialStateSupplier = initialStateSupplier;
        this.handler = handler;
    }

    @Override
    public BiConsumer getHandler() {
        return new BiConsumer() {
            private S state = initialStateSupplier.get();

            @Override
            public void accept(T t, E e) {
                state = handler.handle(t, state, e);
            }
        };
    }

    public  StatefulEventHandlerTemplate onlyWhen(
            BiPredicate condition) {
        return handler.onlyWhen(condition).initialStateSupplier(initialStateSupplier);
    }

    public  StatefulEventHandlerTemplate addHandler(
            StateTransitioningHandler nextHandler) {
        return handler.orElse(nextHandler).initialStateSupplier(initialStateSupplier);
    }
}