package esl.cuenet.source.selection.impl;
import esl.cuenet.source.selection.Matcher;
import esl.cuenet.source.selection.NFAT;
import esl.cuenet.source.selection.State;
import esl.cuenet.source.selection.Transition;
import java.util.Collections;
import java.util.HashSet;
public class NFATImpl implements NFAT {
private State[] states = null;
private HashSet<State> start = new HashSet<State>();
private HashSet<State> finals = new HashSet<State>();
public static final char EPSILON = '~';
private char currentItem;
public NFATImpl(State[] states) {
this.states = states;
}
@Override
public State[] startStates() {
State[] states = new State[start.size()];
start.toArray(states);
return states;
}
@Override
public State[] getFinalStates() {
State[] states = new State[start.size()];
finals.toArray(states);
return states;
}
@Override
public boolean isFinalState(State state) {
return finals.contains(state);
}
public void markAsStartState(State state) {
start.add(state);
}
public void markAsFinalState(State state) {
finals.add(state);
}
private void current(char c) {
currentItem = c;
}
@Override
public boolean match(String _string) {
HashSet<State> currentStates = new HashSet<State>();
Collections.addAll(currentStates, startStates());
HashSet<State> nextStates = new HashSet<State>();
currentStates = epsilonAll(currentStates);
for (char character : _string.toCharArray()) {
HashSet<State> temp = new HashSet<State>();
for (State current: currentStates) temp.add(current);
for (State current: temp) {
nextStates.addAll(step(current, character));
currentStates.remove(current);
currentStates.addAll(nextStates);
}
epsilonAll(currentStates);
nextStates = new HashSet<State>();
}
for (State state: currentStates) {
if (isFinalState(state)) return true;
for (State e: epsilonClosure(state))
if (isFinalState(e)) return true;
}
return false;
}
private HashSet<State> epsilonAll(HashSet<State> currentStates) {
HashSet<State> nextStates = new HashSet<State>();
boolean allEpsilons = true;
for (State current: currentStates) {
for (Transition transition: current.getTransitions()) {
if (transition.isEpsilonTransition())
nextStates.add(transition.outState());
else
allEpsilons = false;
}
if ( !allEpsilons ) nextStates.add(current);
}
return nextStates;
}
private HashSet<State> epsilonClosure(State state) {
HashSet<State> epsilonNexts = new HashSet<State>();
for (Transition transition: state.getTransitions()) {
if (transition.isEpsilonTransition()) epsilonNexts.add(transition.outState());
}
return epsilonNexts;
}
private HashSet<State> step(State current, char character) {
HashSet<State> nextStates = new HashSet<State>();
for (Transition transition: current.getTransitions()) {
current(character);
if (transition.isEpsilonTransition()) continue;
if (transition.matcher().match())
nextStates.add(transition.outState());
}
return nextStates;
}
public static State[] constructStates(String[] stateNames) {
State[] states = new State[stateNames.length];
for (int i=0; i<stateNames.length; i++) states[i] = new StateImpl(stateNames[i], null);
return states;
}
public Transition addTransition(State start, State end, char matcher) {
TransitionImpl transition;
if (matcher == NFATImpl.EPSILON)
transition = new TransitionImpl(end);
else
transition = new TransitionImpl(end, new StringMatcher(matcher));
((StateImpl) start).transitions.add(transition);
return transition;
}
public static class StateImpl implements State {
private final HashSet<Transition> transitions = new HashSet<Transition>();
private final String name;
public StateImpl(String name, Transition[] transitions) {
if (transitions != null) Collections.addAll(this.transitions, transitions);
this.name = name;
}
@Override
public Transition[] getTransitions() {
Transition[] ts= new Transition[transitions.size()];
transitions.toArray(ts);
return ts;
}
@Override
public String name() {
return name;
}
}
public static class TransitionImpl implements Transition {
private final State oState;
private final Matcher matcher;
private final boolean isEpsilon;
public TransitionImpl(State oState) {
this.oState = oState;
this.matcher = null;
this.isEpsilon = true;
}
public TransitionImpl(State oState, Matcher matcher) {
this.oState = oState;
this.matcher = matcher;
this.isEpsilon = false;
}
@Override
public State outState() {
return oState;
}
@Override
public Matcher matcher() {
return matcher;
}
@Override
public boolean isEpsilonTransition() {
return isEpsilon;
}
}
public class StringMatcher implements Matcher {
private final char character;
public StringMatcher(char c) {
this.character = c;
}
@Override
public boolean match() {
return this.character == currentItem;
}
}
}