com.github.dakusui.jcunit.fsm.StateRouter Maven / Gradle / Ivy
package com.github.dakusui.jcunit.fsm;
import com.github.dakusui.jcunit.core.utils.Utils;
import java.util.*;
import static com.github.dakusui.jcunit.core.utils.Checks.*;
public interface StateRouter {
ScenarioSequence routeTo(State state);
class Base implements StateRouter {
private final Map, List>> adjacents;
private final Map, FSM.Edge> links;
private final State initialState;
public Base(FSM fsm) {
this.adjacents = buildAdjacents(fsm);
this.links = buildLinks(fsm);
this.initialState = fsm.initialState();
}
@Override
public ScenarioSequence routeTo(State state) {
if (this.initialState.equals(state))
return ScenarioSequence.Empty.getInstance();
List> route = Utils.newList();
checktest(
route(this.initialState, state, route, Utils.>newSet()),
"The state '%s' can't be reached from the initial state of the given FSM.",
state,
this.initialState
);
return buildScenarioSequenceFromTransitions(route);
}
private ScenarioSequence buildScenarioSequenceFromTransitions(final List> route) {
return new ScenarioSequence.Base() {
@Override
public int size() {
return route.size();
}
@Override
public State state(int i) {
checkcond(i >= 0 && i < size());
State ret = StateRouter.Base.this.initialState;
for (int c = 0; c < i; c++) {
ret = next(ret, new FSM.Edge(action(c), args(c)));
}
return ret;
}
@Override
public Action action(int i) {
return route.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) {
checkcond(j >= 0);
return args(i).size() > j;
}
@Override
public Args args(int i) {
return route.get(i).args;
}
@Override
public String toString() {
return PrivateUtils.toString(this);
}
};
}
boolean route(State cur, State to, List> route, Set> visited) {
if (!this.adjacents.containsKey(cur))
return false;
for (State each : checknotnull(this.adjacents.get(cur))) {
route.add(this.links.get(new StatePair(cur, each)));
if (each.equals(to)) {
return true;
} else {
if (!visited.contains(each)) {
visited.add(each);
if (route(each, to, route, visited)) {
return true;
}
}
}
if (!route.isEmpty()) {
route.remove(route.size() - 1);
}
}
return false;
}
Map, List>> buildAdjacents(FSM fsm) {
// from -> tos
final Map, List>> ret = Utils.newMap();
for (State eachFromState : fsm.states()) {
for (Action eachAction : fsm.actions()) {
for (Args eachArgs : FSMUtils.possibleArgsList(eachAction)) {
State eachToState = eachFromState.expectation(eachAction, eachArgs).state;
if (State.Void.getInstance().equals(eachToState))
continue;
List> toStates = getToStates(ret, eachFromState);
if (!toStates.contains(eachToState))
toStates.add(eachToState);
}
}
}
return ret;
}
private List> getToStates(Map, List>> ret, State eachFromState) {
if (!ret.containsKey(eachFromState))
ret.put(eachFromState, Utils.>newList());
return ret.get(eachFromState);
}
Map, FSM.Edge> buildLinks(FSM fsm) {
final Map, FSM.Edge> edges = Utils.newMap();
for (State fromState : fsm.states()) {
for (Action eachAction : fsm.actions()) {
for (Args eachArgs : FSMUtils.possibleArgsList(eachAction)) {
State toState = fromState.expectation(eachAction, eachArgs).state;
if (State.Void.getInstance().equals(toState))
continue;
StatePair link = new StatePair(fromState, toState);
if (edges.containsKey(link))
continue;
edges.put(link, new FSM.Edge(eachAction, eachArgs));
}
}
}
return edges;
}
static class StatePair {
State from;
State to;
StatePair(State from, State to) {
this.from = checknotnull(from);
this.to = checknotnull(to);
}
public int hashCode() {
return this.from.hashCode();
}
public boolean equals(Object anotherObject) {
if (!(anotherObject instanceof StatePair))
return false;
StatePair another = (StatePair) anotherObject;
return this.from.equals(another.from) && this.to.equals(another.to);
}
}
private static State next(State state, FSM.Edge t) {
return state.expectation(t.action, t.args).state;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy