Skip to content

Instantly share code, notes, and snippets.

@yawn
Created October 10, 2011 11:42
Show Gist options
  • Save yawn/1275110 to your computer and use it in GitHub Desktop.
Save yawn/1275110 to your computer and use it in GitHub Desktop.
FSM.java
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.NoSuchElementException;
import static java.lang.String.format;
/**
* @author Joern Barthel <jb@kreuzwerker.de>
*/
public class FSM<E extends Enum, S extends Enum> {
public class Transition<S extends Enum> implements Supplier<Boolean> {
private final S to;
private final Supplier<Boolean> guard;
public Transition(S to) {
this(to, Suppliers.ofInstance(Boolean.TRUE));
}
public <T> Transition(S to, Supplier<Boolean> guard) {
this.to = to;
this.guard = guard;
}
public Boolean get() {
return guard.get();
}
public S getTo() {
return to;
}
}
private S state;
private final Cache<E, Multimap<S, Transition<S>>> transitions = CacheBuilder.newBuilder()
.build(CacheLoader.from(new Supplier<Multimap<S, Transition<S>>>() {
public Multimap<S, Transition<S>> get() {
return ArrayListMultimap.create();
}
}));
public FSM(S initialState) {
this.state = initialState;
}
public void add(E on, S from, S to) {
transitions.getUnchecked(on).put(from, new Transition<S>(to));
}
public void add(E on, S from, S to, Supplier<Boolean> guard) {
transitions.getUnchecked(on).put(from, new Transition<S>(to, guard));
}
public void add(E on, S from, Transition<S> to) {
transitions.getUnchecked(on).put(from, to);
}
public S event(E e) {
Collection<Transition<S>> targets = transitions.getUnchecked(e).get(state);
try {
this.state = Iterables.find(targets, new Predicate<Supplier<Boolean>>() {
public boolean apply(@Nullable Supplier<Boolean> input) {
return input.get();
}
}).getTo();
} catch (NoSuchElementException ex) {
throw new IllegalStateException(format("Cannot transition via '%s' from '%s'", e, state));
}
return state;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment