![JAR search and dependency download from the Maven repository](/logo.png)
com.github.dakusui.jcunit.fsm.StateRouter Maven / Gradle / Ivy
package com.github.dakusui.jcunit.fsm;
import com.github.dakusui.jcunit.core.Checks;
import com.github.dakusui.jcunit.core.Utils;
import java.util.*;
public abstract class StateRouter {
private final FSM fsm;
private final List> destinations;
private final Map, ScenarioSequence> routes;
public StateRouter(FSM fsm, List> destinations) {
Checks.checknotnull(fsm);
Checks.checknotnull(destinations);
this.destinations = Collections.unmodifiableList(Utils.singleton(destinations));
this.routes = new LinkedHashMap, ScenarioSequence>();
for (State each : destinations) {
if (each.equals(fsm.initialState())) {
//noinspection unchecked
this.routes.put(each, (ScenarioSequence) ScenarioSequence.EMPTY);
} else {
this.routes.put(each, null);
}
}
this.fsm = fsm;
traverse(fsm.initialState(), new LinkedList>(), new LinkedHashSet>());
List> unreachableDestinations = new ArrayList>(this.destinations.size());
for (State each : this.destinations) {
if (this.routes.get(each) == null) {
unreachableDestinations.add(each);
}
}
Checks.checktest(
unreachableDestinations.size() == 0,
"The states '%s' can't be reached from the initial state of the given FSM.",
unreachableDestinations,
this.fsm.initialState()
);
}
public ScenarioSequence routeTo(State state) {
Checks.checkcond(this.destinations.contains(state));
return this.routes.get(state);
}
private void traverse(State state, List> path, Set> visited) {
for (Transition each : possibleTransitionsFrom(state)) {
State next = next(state, each);
if (next == State.VOID)
return;
if (visited.contains(next))
continue;
visited.add(next);
List> pathToNext = new LinkedList>(path);
pathToNext.add(each);
if (this.destinations.contains(next)) {
this.routes.put(next, buildStoryFromTransitions(pathToNext));
}
traverse(next, pathToNext, visited);
}
}
private ScenarioSequence buildStoryFromTransitions(final List> pathToNext) {
return new ScenarioSequence.Base() {
@Override
public int size() {
return pathToNext.size();
}
@Override
public State state(int i) {
Checks.checkcond(i >= 0 && i < size());
State ret = StateRouter.this.fsm.initialState();
for (int c = 0; c < i; c++) {
next(ret, new Transition(action(i), args(i)));
}
return ret;
}
@Override
public Action action(int i) {
return pathToNext.get(i).action;
}
@Override
public Object arg(int i, int j) {
return this.args(i).values()[j];
}
@Override
public boolean hasArg(int i, int j) {
Checks.checkcond(j >= 0);
return args(i).size() > j;
}
@Override
public Args args(int i) {
return pathToNext.get(i).args;
}
@Override
public String toString() {
return Utils.toString(this);
}
};
}
private State next(State state, Transition t) {
return state.expectation(t.action, t.args).state;
}
protected abstract List> possibleTransitionsFrom(State state);
public static class Transition {
public final Action action;
public final Args args;
public Transition(Action action, Args args) {
this.action = action;
this.args = args;
}
@Override
public int hashCode() {
return this.action.hashCode();
}
@Override
public boolean equals(Object anotherObject) {
if (!(anotherObject instanceof Transition))
return false;
//noinspection unchecked
Transition another = (Transition) anotherObject;
return this.action.equals(another.action) && Arrays.deepEquals(this.args.values(), another.args.values());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy