package edu.uw.zookeeper.common; import static com.google.common.base.Preconditions.checkNotNull; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.logging.log4j.Logger; import net.engio.mbassy.common.IConcurrentSet; import net.engio.mbassy.common.StrongConcurrentSet; import net.engio.mbassy.common.WeakConcurrentSet; import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.base.Optional; public abstract class Automatons { public static <Q extends Enum<Q> & Function<I, Optional<Automaton.Transition<Q>>>, I> SimpleAutomaton<Q,I> createSimple(Class<Q> cls) { return SimpleAutomaton.create(cls.getEnumConstants()[0]); } public static <Q extends Function<I, Optional<Automaton.Transition<Q>>>, I> SimpleAutomaton<Q,I> createSimple( Q initialState) { return SimpleAutomaton.create(initialState); } public static <Q,I,T extends Automaton<Q,I>> LoggingAutomaton<Q,I,T> createLogging( Logger logger, T automaton) { return LoggingAutomaton.create(logger, automaton); } public static <Q,I,T extends Automaton<Q,I>> SynchronizedAutomaton<Q,I,T> createSynchronized( T automaton) { return SynchronizedAutomaton.create(automaton); } public static <Q,I,T extends Automaton<Q,I>> SimpleEventfulAutomaton<Q,I,T> createEventful( T automaton) { return SimpleEventfulAutomaton.createWeak(automaton); } public static <Q,I,T extends EventfulAutomaton<Q,I>> SynchronizedEventfulAutomaton<Q,I,T> createSynchronizedEventful( T automaton) { return SynchronizedEventfulAutomaton.create(automaton); } /** * Not thread-safe */ public static final class SimpleAutomaton<Q extends Function<I, Optional<Automaton.Transition<Q>>>, I> implements Automaton<Q,I> { public static <Q extends Function<I, Optional<Automaton.Transition<Q>>>, I> SimpleAutomaton<Q,I> create( Q initialState) { return new SimpleAutomaton<Q,I>(checkNotNull(initialState)); } private Q state; protected SimpleAutomaton(Q initialState) { this.state = initialState; } @Override public Q state() { return state; } @Override public Optional<Automaton.Transition<Q>> apply(I input) { Optional<Automaton.Transition<Q>> output = state.apply(input); if (output.isPresent()) { Q nextState = output.get().to(); if (! state.equals(nextState)) { state = checkNotNull(nextState); } } return output; } @Override public String toString() { return MoreObjects.toStringHelper(Automaton.class).addValue(state).toString(); } } public static abstract class ForwardingAutomaton<Q,I,T extends Automaton<Q,? super I>> implements Automaton<Q,I> { protected ForwardingAutomaton() {} @Override public Q state() { return delegate().state(); } @Override public Optional<Automaton.Transition<Q>> apply(I input) { return delegate().apply(input); } @Override public String toString() { return delegate().toString(); } protected abstract T delegate(); } public static class LoggingAutomaton<Q,I,T extends Automaton<Q,I>> extends ForwardingAutomaton<Q,I,T> { public static <Q,I,T extends Automaton<Q,I>> LoggingAutomaton<Q,I,T> create( Logger logger, T automaton) { return new LoggingAutomaton<Q,I,T>(checkNotNull(logger), checkNotNull(automaton)); } protected final Logger logger; protected final T automaton; protected LoggingAutomaton(Logger logger, T automaton) { this.logger = logger; this.automaton = automaton; } @Override public Optional<Automaton.Transition<Q>> apply(I input) { Optional<Automaton.Transition<Q>> output = super.apply(input); if (output.isPresent()) { log(output.get()); } return output; } protected void log(Automaton.Transition<Q> transition) { logger.debug("{} ({})", transition, this); } @Override protected T delegate() { return automaton; } } public static class SynchronizedAutomaton<Q,I,T extends Automaton<Q,I>> extends ForwardingAutomaton<Q,I,T> { public static <Q,I,T extends Automaton<Q,I>> SynchronizedAutomaton<Q,I,T> create( T automaton) { return new SynchronizedAutomaton<Q,I,T>(checkNotNull(automaton), new ReentrantReadWriteLock()); } protected final ReadWriteLock lock; protected final T automaton; protected SynchronizedAutomaton( T automaton, ReadWriteLock lock) { this.automaton = automaton; this.lock = lock; } public ReadWriteLock lock() { return lock; } @Override public Q state() { lock.readLock().lock(); try { return super.state(); } finally { lock.readLock().unlock(); } } @Override public Optional<Automaton.Transition<Q>> apply(I input) { lock.writeLock().lock(); try { return super.apply(input); } finally { lock.writeLock().unlock(); } } @Override public String toString() { lock.readLock().lock(); try { return super.toString(); } finally { lock.readLock().unlock(); } } @Override protected T delegate() { return automaton; } } public static interface AutomatonListener<Q> { void handleAutomatonTransition(Automaton.Transition<Q> transition); } public static interface EventfulAutomaton<Q,I> extends Automaton<Q,I>, Eventful<AutomatonListener<Q>> {} public static final class SimpleEventfulAutomaton<Q,I,T extends Automaton<Q,I>> extends ForwardingAutomaton<Q,I,T> implements EventfulAutomaton<Q,I> { public static <Q,I,T extends Automaton<Q,I>> SimpleEventfulAutomaton<Q,I,T> createWeak( T automaton) { return new SimpleEventfulAutomaton<Q,I,T>( new WeakConcurrentSet<AutomatonListener<Q>>(), checkNotNull(automaton)); } public static <Q,I,T extends Automaton<Q,I>> SimpleEventfulAutomaton<Q,I,T> createStrong( T automaton) { return new SimpleEventfulAutomaton<Q,I,T>( new StrongConcurrentSet<AutomatonListener<Q>>(), checkNotNull(automaton)); } private final IConcurrentSet<AutomatonListener<Q>> listeners; private final T automaton; protected SimpleEventfulAutomaton( IConcurrentSet<AutomatonListener<Q>> listeners, T automaton) { this.listeners = listeners; this.automaton = automaton; } /** * Not atomic */ @Override public Optional<Automaton.Transition<Q>> apply(I input) { // even if automaton is synchronized // if this function is called concurrently we can end up // with events delivered out of order Optional<Automaton.Transition<Q>> output = super.apply(input); if (output.isPresent()) { Automaton.Transition<Q> transition = output.get(); for (AutomatonListener<Q> listener: listeners) { listener.handleAutomatonTransition(transition); } } return output; } @Override public void subscribe(AutomatonListener<Q> listener) { listeners.add(listener); } @Override public boolean unsubscribe(AutomatonListener<Q> listener) { return listeners.remove(listener); } @Override protected T delegate() { return automaton; } } public static final class SynchronizedEventfulAutomaton<Q,I,T extends EventfulAutomaton<Q,I>> extends SynchronizedAutomaton<Q,I,T> implements EventfulAutomaton<Q,I> { public static <Q,I,T extends EventfulAutomaton<Q,I>> SynchronizedEventfulAutomaton<Q,I,T> create( T automaton) { return new SynchronizedEventfulAutomaton<Q,I,T>(checkNotNull(automaton), new ReentrantReadWriteLock()); } protected SynchronizedEventfulAutomaton( T automaton, ReadWriteLock lock) { super(automaton, lock); } @Override public void subscribe(AutomatonListener<Q> listener) { delegate().subscribe(listener); } @Override public boolean unsubscribe(AutomatonListener<Q> listener) { return delegate().unsubscribe(listener); } } }