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

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