package it.halfone.regex;
import it.halfone.hava.container.Tuple;
import it.halfone.hava.search.SearchKnot;
import it.halfone.parser.Parser;
import it.halfone.parser.Step;
import it.halfone.parser.event.ChangeDepthEvent;
import it.halfone.parser.event.CheckEvent;
import it.halfone.parser.event.Event;
import it.halfone.parser.event.LateralStepEvent;
import it.halfone.parser.event.LikenessEvent;
import it.halfone.parser.event.MarkEvent;
import it.halfone.parser.token.AlphabethicToken;
import it.halfone.parser.token.AnyToken;
import it.halfone.parser.token.CharToken;
import it.halfone.parser.token.DigitToken;
import it.halfone.parser.token.EmptyToken;
import it.halfone.parser.token.Token;
import it.halfone.parser.token.WhitespaceToken;
import it.halfone.parser.token.WordToken;
import it.halfone.regex.symbol.Symbol;
import it.halfone.regex.symbol.Symbol.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Regex - 22/set/2011
*
* Classe Immutabile
*
* Una Regex serve per il riconoscimento di stringhe. Intuitivamente una Regex � un grafo, i cui nodi sono Simboli, con la proprieta' che contiene almeno un
* simbolo di tipo 'START' e almeno un simbolo di tipo 'END'.
*
*
* @author Andrea La Rosa
*/
public class Regex {
/*
* NOTE DI IMPLEMENTAZIONE
*
* 1) symbolList[i] � di tipo CLOSED_SELF_START se e solo se symbolList[i+1] � tipo CLOSED_SELF_END
* 2) se symbolList[i] � di tipo POP e symbolList[j] � di tipo CLOSED_SELF_START allora ogni cammino che porta da j ad i deve
* passare per almeno un simbolo che non � un meta-simbolo
* 3) se symbolList[i] � di tipo PUSH e symbolList[j] � di tipo CLOSED_SELF_END allora ogni cammino che porta da j ad i deve
* passare per almeno un simbolo che non � un meta-simbolo
*/
private int maxMarkIndex = 0;
private List<Symbol> symbolList;
private Map<Integer, List<Integer>> connections;
private boolean isSelf = false;
/**
* Default constructor
*/
private Regex() {
symbolList = new ArrayList<Symbol>();
connections = new HashMap<Integer, List<Integer>>();
}
/**
* @param symbols
* Aggiunge alla lista dei simboli della Regex su cui � invocato il metodo la lista dei simboli passati per argomento.
*/
private void addSymbol(Symbol... simbols) {
symbolList.addAll(Arrays.asList(simbols));
}
/**
* @param first
* @param second
*/
private void addConnection(int first, int second) {
if (connections.get(first) == null) {
connections.put(first, new ArrayList<Integer>());
}
connections.get(first).add(second);
}
/**
*
*/
private void reIndexMarks() {
int index = 1;
Map<Integer, Integer> indexMapping = new HashMap<Integer, Integer>();
for (Symbol symbol : symbolList) {
if (symbol.getMarkIndex() > 0) {
indexMapping.put(symbol.getMarkIndex(), index);
symbol = symbol.setMark(index, symbol.getMarkName());
index++;
} else if (symbol.getMarkIndex() < 0) {
symbol = symbol.setMark(indexMapping.get(-symbol.getMarkIndex()), symbol.getMarkName());
}
}
maxMarkIndex = index - 1;
}
/**
* @param likeness
* @return
*/
public Regex boost(int likeness) {
Regex retVal = new Regex();
retVal.isSelf = this.isSelf;
retVal.addSymbol(new Symbol("", Type.START));
retVal.addSymbol(this.symbolList.toArray(new Symbol[] {}));
for (Integer key : this.connections.keySet()) {
for (Integer successor : this.connections.get(key)) {
retVal.addConnection(key + 1, successor + 1);
}
}
retVal.addSymbol(new Symbol("", Type.END));
retVal.addConnection(0, 1);
retVal.addConnection(retVal.symbolList.size() - 2, retVal.symbolList.size() - 1);
retVal.symbolList.set(1, this.symbolList.get(0).setLikeness(likeness));
retVal.reIndexMarks();
return retVal;
}
/**
* @param literal
* la stringa da riconoscere.
*
* @return Una Regex capace di riconoscere la stringa literal, e nessun'altra stringa.
*/
public static Regex literal(String literal) {
Regex retVal = new Regex();
retVal.addSymbol(new Symbol("", Type.START));
for (int i = 0; i < literal.length(); i++) {
retVal.addSymbol(new Symbol(literal.charAt(i) + "", Type.CHAR));
retVal.addConnection(i, i + 1);
}
retVal.addSymbol(new Symbol("", Type.END));
retVal.addConnection(literal.length(), literal.length() + 1);
return retVal;
}
/**
* @param t
* @return
*/
private static Regex classRegex(Type t) {
Regex retVal = new Regex();
retVal.addSymbol(new Symbol("", Type.START));
retVal.addSymbol(new Symbol("", t));
retVal.addSymbol(new Symbol("", Type.END));
retVal.addConnection(0, 1);
retVal.addConnection(1, 2);
return retVal;
}
/**
* @return
*/
public static Regex any() {
return classRegex(Type.ANY);
}
/**
* @return
*/
public static Regex digit() {
return classRegex(Type.DIGIT);
}
/**
* @return
*/
public static Regex nonDigit() {
return classRegex(Type.NONDIGIT);
}
/**
* @return
*/
public static Regex word() {
return classRegex(Type.WORD);
}
/**
* @return
*/
public static Regex nonWord() {
return classRegex(Type.NONWORD);
}
/**
* @return
*/
public static Regex whitespace() {
return classRegex(Type.WHITESPACE);
}
/**
* @return
*/
public static Regex nonWhitespace() {
return classRegex(Type.NONWHITESPACE);
}
/**
* @return
*/
public static Regex alphabetic() {
return classRegex(Type.ALPHA);
}
/**
* @return
*/
public static Regex nonAlphabetic() {
return classRegex(Type.NONALPHA);
}
/**
* @param other
* @return
*/
public Regex then(String other) {
return this.then(Regex.literal(other));
}
/**
* @param other
* @return
*/
public Regex then(Regex other) {
Regex retVal = new Regex();
retVal.isSelf = this.isSelf || other.isSelf;
retVal.addSymbol(new Symbol("", Type.START));
retVal.addSymbol(this.symbolList.toArray(new Symbol[] {}));
retVal.addSymbol(other.symbolList.toArray(new Symbol[] {}));
retVal.addSymbol(new Symbol("", Type.END));
for (Integer key : this.connections.keySet()) {
for (Integer successor : this.connections.get(key)) {
retVal.addConnection(key + 1, successor + 1);
}
}
for (Integer key : other.connections.keySet()) {
for (Integer successor : other.connections.get(key)) {
retVal.addConnection(key + 1 + this.symbolList.size(), successor + 1 + this.symbolList.size());
}
}
retVal.addConnection(0, 1);
retVal.addConnection(this.symbolList.size(), this.symbolList.size() + 1);
retVal.addConnection(retVal.symbolList.size() - 2, retVal.symbolList.size() - 1);
retVal.reIndexMarks();
return retVal;
}
/**
* @param regexes
* @return
*/
public static Regex or(Regex... regexes) {
Regex retVal = regexes[0];
for (int i = 1; i < regexes.length; i++) {
retVal = retVal.or(regexes[i]);
}
return retVal;
}
/**
* @param other
* @return
*/
public Regex or(String other) {
return this.or(Regex.literal(other));
}
/**
* @param other
* @return
*/
public Regex or(Regex other) {
Regex retVal = new Regex();
retVal.isSelf = this.isSelf || other.isSelf;
retVal.addSymbol(new Symbol("", Type.START));
retVal.addSymbol(this.symbolList.toArray(new Symbol[] {}));
retVal.addSymbol(other.symbolList.toArray(new Symbol[] {}));
retVal.addSymbol(new Symbol("", Type.END));
for (Integer key : this.connections.keySet()) {
for (Integer successor : this.connections.get(key)) {
retVal.addConnection(key + 1, successor + 1);
}
}
for (Integer key : other.connections.keySet()) {
for (Integer successor : other.connections.get(key)) {
retVal.addConnection(key + 1 + this.symbolList.size(), successor + 1 + this.symbolList.size());
}
}
retVal.addConnection(0, 1);
retVal.addConnection(0, 1 + this.symbolList.size());
retVal.addConnection(this.symbolList.size(), retVal.symbolList.size() - 1);
retVal.addConnection(retVal.symbolList.size() - 2, retVal.symbolList.size() - 1);
retVal.reIndexMarks();
return retVal;
}
/**
* @param minTimes
* @param maxTimes
* @return
*/
public Regex repeat(int minTimes, int maxTimes) {
Regex retVal = new Regex();
retVal.isSelf = this.isSelf;
retVal.addSymbol(new Symbol("", Type.START));
for (int i = 0; i < maxTimes; i++) {
retVal.addSymbol(this.symbolList.toArray(new Symbol[] {}));
for (Integer key : this.connections.keySet()) {
for (Integer successor : this.connections.get(key)) {
retVal.addConnection(key + 1 + (i * this.symbolList.size()), successor + 1 + (i * this.symbolList.size()));
}
}
retVal.addConnection(i * this.symbolList.size(), 1 + (i * this.symbolList.size()));
}
retVal.addSymbol(new Symbol("", Type.END));
retVal.addConnection(retVal.symbolList.size() - 2, retVal.symbolList.size() - 1);
for (int i = minTimes; i < maxTimes; i++) {
retVal.addConnection(i * this.symbolList.size(), retVal.symbolList.size() - 1);
}
retVal.reIndexMarks();
return retVal;
}
/**
* @param times
* @return
*/
public Regex repeat(int times) {
return this.repeat(times, times);
}
/**
* @param minTimes
* @return
*/
public Regex atLeast(int minTimes) {
Regex retVal = new Regex();
retVal.isSelf = this.isSelf;
retVal.addSymbol(new Symbol("", Type.START));
for (int i = 0; i < minTimes; i++) {
retVal.addSymbol(this.symbolList.toArray(new Symbol[] {}));
for (Integer key : this.connections.keySet()) {
for (Integer successor : this.connections.get(key)) {
retVal.addConnection(key + 1 + (i * this.symbolList.size()), successor + 1 + (i * this.symbolList.size()));
}
}
retVal.addConnection(i * this.symbolList.size(), 1 + (i * this.symbolList.size()));
}
retVal.addSymbol(new Symbol("", Type.END));
retVal.addConnection(retVal.symbolList.size() - 2, retVal.symbolList.size() - 1);
retVal.addConnection(retVal.symbolList.size() - 2, ((minTimes - 1) * this.symbolList.size()) + 1);
retVal.reIndexMarks();
return retVal;
}
/**
* @return
*/
public Regex atLeastOnce() {
return atLeast(1);
}
/**
* @return
*/
public Regex optional() {
Regex retVal = new Regex();
retVal.isSelf = this.isSelf;
retVal.addSymbol(new Symbol("", Type.START));
retVal.addSymbol(this.symbolList.toArray(new Symbol[] {}));
for (Integer key : this.connections.keySet()) {
for (Integer successor : this.connections.get(key)) {
retVal.addConnection(key + 1, successor + 1);
}
}
retVal.addSymbol(new Symbol("", Type.END));
retVal.addConnection(0, 1);
retVal.addConnection(0, retVal.symbolList.size() - 1);
retVal.addConnection(retVal.symbolList.size() - 2, retVal.symbolList.size() - 1);
retVal.reIndexMarks();
return retVal;
}
/**
* @return
*/
public Regex star() {
Regex retVal = new Regex();
retVal.isSelf = this.isSelf;
retVal.addSymbol(new Symbol("", Type.START));
retVal.addSymbol(this.symbolList.toArray(new Symbol[] {}));
for (Integer key : this.connections.keySet()) {
for (Integer successor : this.connections.get(key)) {
retVal.addConnection(key + 1, successor + 1);
}
}
retVal.addSymbol(new Symbol("", Type.END));
retVal.addConnection(0, 1);
retVal.addConnection(0, retVal.symbolList.size() - 1);
retVal.addConnection(retVal.symbolList.size() - 2, retVal.symbolList.size() - 1);
retVal.addConnection(retVal.symbolList.size() - 2, 1);
retVal.reIndexMarks();
return retVal;
}
/**
* @return
*/
public Regex mark(String markName) {
Regex retVal = new Regex();
retVal.isSelf = this.isSelf;
retVal.addSymbol(new Symbol("", Type.START));
retVal.addSymbol(this.symbolList.toArray(new Symbol[] {}));
retVal.addSymbol(new Symbol("", Type.END));
for (Integer key : this.connections.keySet()) {
for (Integer successor : this.connections.get(key)) {
retVal.addConnection(key + 1, successor + 1);
}
}
retVal.addConnection(0, 1);
retVal.addConnection(retVal.symbolList.size() - 2, retVal.symbolList.size() - 1);
retVal.symbolList.set(1, this.symbolList.get(0).setMark(maxMarkIndex + 1, markName));
retVal.symbolList.set(retVal.symbolList.size() - 2, this.symbolList.get(this.symbolList.size() - 1).setMark(-maxMarkIndex - 1, markName));
retVal.reIndexMarks();
return retVal;
}
/**
* @return
*/
public static Regex self() {
Regex retVal = new Regex();
retVal.isSelf = true;
retVal.addSymbol(new Symbol("", Type.OPEN_SELF_START));
retVal.addSymbol(new Symbol("", Type.OPEN_SELF_END));
return retVal;
}
/**
* @return
*/
public Regex close() {
if (isSelf == false) {
return this;
}
Regex retVal = new Regex();
retVal.addSymbol(new Symbol("", Type.START));
retVal.addSymbol(new Symbol("", Type.CHECK_IN));
retVal.addSymbol(this.symbolList.toArray(new Symbol[] {}));
retVal.addSymbol(new Symbol("", Type.CHECK_OUT));
retVal.addSymbol(new Symbol("", Type.END));
for (Integer key : this.connections.keySet()) {
for (Integer successor : this.connections.get(key)) {
retVal.addConnection(key + 2, successor + 2);
}
}
for (int i = 0; i < retVal.symbolList.size(); i++) {
Symbol symbol = retVal.symbolList.get(i);
if (symbol.getType() == Type.OPEN_SELF_START) {
retVal.symbolList.set(i, new Symbol("", Type.CLOSED_SELF_START));
retVal.addConnection(i, 2);
} else if (symbol.getType() == Type.OPEN_SELF_END) {
retVal.symbolList.set(i, new Symbol("", Type.CLOSED_SELF_END));
retVal.addConnection(retVal.symbolList.size() - 3, i);
}
}
retVal.symbolList.set(2, new Symbol("", Type.PUSH));
retVal.symbolList.set(retVal.symbolList.size() - 3, new Symbol("", Type.POP));
retVal.addConnection(0, 1);
retVal.addConnection(1, 2);
retVal.addConnection(this.symbolList.size() + 1, this.symbolList.size() + 2);
retVal.addConnection(this.symbolList.size() + 2, this.symbolList.size() + 3);
retVal.reIndexMarks();
return retVal;
}
/**
* L'operazione fondamentale che associa ad una Regex chiusa un Parser capace di identificare le stringhe che rispettano la Regex.
*
* @return un Parser capace di riconoscere le stringhe che rispettano la Regex compilata.
*/
public Parser compile() {
/*
* NOTE DI IMPLEMENTAZIONE
*
* La strategia scelta consiste nella creazione della mappa che associa ad ogni Token la lista degli Step uscenti, per poi
* produrre il Parser mediante tale mappa.
*/
Map<Token, List<Step>> stepMap = new HashMap<Token, List<Step>>();
List<Symbol> symbolList = new ArrayList<Symbol>(this.symbolList);
List<Step> boundary = new ArrayList<Step>();
Set<Token> explored = new HashSet<Token>();
List<Step> successorList = new ArrayList<Step>();
/*
* La prima parte dell'algoritmo consiste nella mera traduzione dei simboli che compongono la Regex nei
* corrispondenti Token. Ciascun simbolo viene quindi valutato: se non e' un meta-simbolo, ovvero se la sua
* funzione e' il riconoscimento di uno o piu' caratteri, allora questo viene 'tradotto' in un Token 'significativo'.
* Viceversa, ogni meta-simbolo viene per adesso tradotto in un CharToken, come se il Simbolo fosse destinato al
* riconoscimento di un carattere.
* Particolare menzione va fatta per il primo e l'ultimo simbolo, che sono sicuramente meta-simboli di tipo Start ed End.
* Questi vengono tradotti direttamente in EmptyToken, ossia Token che non riconoscono alcun carattere.
*/
Token[] tokens = new Token[symbolList.size()];
for (int i = 0; i < symbolList.size(); i++) {
if (i == 0 || i == symbolList.size() - 1) {
tokens[i] = new EmptyToken();
} else {
switch (symbolList.get(i).getType()) {
case ANY:
tokens[i] = new AnyToken();
break;
case WORD:
tokens[i] = new WordToken(true);
break;
case NONWORD:
tokens[i] = new WordToken(false);
break;
case DIGIT:
tokens[i] = new DigitToken(true);
break;
case NONDIGIT:
tokens[i] = new DigitToken(false);
break;
case WHITESPACE:
tokens[i] = new WhitespaceToken(true);
break;
case NONWHITESPACE:
tokens[i] = new WhitespaceToken(false);
break;
case ALPHA:
tokens[i] = new AlphabethicToken(true);
break;
case NONALPHA:
tokens[i] = new AlphabethicToken(false);
break;
default:
tokens[i] = new CharToken(symbolList.get(i).getRepresentation().charAt(0));
break;
}
}
}
Map<Token, List<Integer>> connectionsMap = new HashMap<Token, List<Integer>>();
for (int key : connections.keySet()) {
connectionsMap.put(tokens[key], connections.get(key));
}
/*
* La seconda parte dell'algoritmo consiste nell'associare un identificativo univoco a ciascuna coppia di simboli CLOSED_SELF, e
* memorizzare le associazioni in un'apposita mappa.
* Supponiamo, ad esempio, che in una Regex le coppie CLOSED_SELF siano la (3,4), la (7,8) e la (12,13) (ovvero, symbolList[3] � di
* tipo CLOSED_SELF_START e symbolList[4] � di tipo CLOSED_SELF_END, e similmente con le altre coppie di numeri). In questo caso
* associeremmo alla prima coppia l'ID "1", alla seconda l'ID "2" e alla terza l'ID "3", per poi salvare in una mappa le seguenti
* associazioni:
* ID: 1 ---- > 3
* ID: -1 ---- > 4
* ID: 2 ---- > 7
* ID: -2 ---- > 8
* ID: 3 ---- > 12
* ID: -3 ---- > 13
* Notare che i CLOSED_SELF_START sono sempre associati ad ID positivi e i CLOSED_SELF_END sono associati a ID negativi.
*/
Map<Integer, String> closedSelfIdMap = new HashMap<Integer, String>();
int index = 1;
for (int i = 0; i < symbolList.size(); i++) {
if (symbolList.get(i).getType() == Type.CLOSED_SELF_START) {
closedSelfIdMap.put(i, index + "");
index *= -1;
closedSelfIdMap.put(i + 1, index + "");
index *= -1;
index++;
}
}
/*
* La terza parte dell'algoritmo consiste nella ricerca dei cosiddetti INFINIPUSH e INFINIPOP. Un INIFINIPUSH � un simbolo di
* tipo PUSH con la particolarita' che esiste un cammino composto di soli meta-simboli che parte e finisce da lui.
* L'algoritmo cicla su tutti i simboli, ma si sofferma solo su quelli di tipo PUSH e POP (gli altri non interessano).
* Non appena trova un candidato (supponiamo un PUSH), inizia un processo diviso in due fasi.
* Nella prima si verifica che il candidato sia effettivamente un INFINIPUSH, percorrendo tutti i possibili cammini che
* partono da lui e che passano per meta-simboli.
* Nella seconda invece si prendono in considerazione i cammini trovati nella fase precedente, al fine di memorizzarli in una
* struttura apposita. E' in questa seconda fase che viene utilizzata la mappa costruita nella prima parte dell'algoritmo.
* Infatti, posto che un qualsiasi cammino composto da meta-simboli che parta da un nodo PUSH/POP per ritornarvi debba necessariamente
* passare per un e un solo nodo CLOSED_SELF_START/END (di piu': il passo che arriva al nodo CLOSED_SELF e' sempre il penultimo, e con
* l'ultimo si torna al PUSH/POP di partenza), si tratta di vedere a quante coppie di tipo CLOSED_SELF e' collegato il nodo in esame.
* Supponiamo ad esempio che il simbolo i, di tipo PUSH, sia anche un INFINIPUSH, e che in totale le coppie CLOSED_SELF siano:
* (3,4), (8,9), (11,12). Se il simbolo i � INFINIPUSH allora questo � collegato ad almeno una delle 3 coppie; supponiamo che sia
* collegato alla (3,4) e alla (11,12), i cui ID (memorizzati nella prima parte dell'algoritmo) sono rispettivamente "1" e "3".
* Nella seconda parte dell'algoritmo allora memorizzeremo l'associazione fra i e la coppia di id "1" e "3". L'associazione e'
* memorizzata mediante una stringa, che in questo caso sarebbe "(1|3)*".
*/
Map<Integer, String> infiniLateralStackMap = new HashMap<Integer, String>();
List<SearchKnot<Tuple<Token, Integer>>> discoveryBoundaryList = new ArrayList<SearchKnot<Tuple<Token, Integer>>>();
List<Token> discoveryExploredList = new ArrayList<Token>();
List<SearchKnot<Tuple<Token, Integer>>> discoveryFinalList = new ArrayList<SearchKnot<Tuple<Token, Integer>>>();
List<SearchKnot<Tuple<Token, Integer>>> newDiscoveryBoundaryList = new ArrayList<SearchKnot<Tuple<Token, Integer>>>();
for (int i = 0; i < symbolList.size(); i++) {
Symbol actualSymbol = symbolList.get(i);
if (actualSymbol.getType() == Type.PUSH || actualSymbol.getType() == Type.POP) {
SearchKnot<Tuple<Token, Integer>> root = new SearchKnot<Tuple<Token, Integer>>(new Tuple<Token, Integer>(tokens[i], i), null);
newDiscoveryBoundaryList.clear();
discoveryFinalList.clear();
discoveryBoundaryList.clear();
discoveryBoundaryList.add(root);
discoveryExploredList.clear();
while (discoveryBoundaryList.isEmpty() == false) {
while (discoveryBoundaryList.isEmpty() == false) {
SearchKnot<Tuple<Token, Integer>> actual = discoveryBoundaryList.remove(0);
discoveryExploredList.add(actual.getValue().getFirst());
for (int succ : connectionsMap.get(actual.getValue().getFirst())) {
Symbol succSymbol = symbolList.get(succ);
if (succSymbol == actualSymbol) {
discoveryFinalList.add(new SearchKnot<Tuple<Token, Integer>>(new Tuple<Token, Integer>(tokens[succ], succ), actual));
} else if (Symbol.isMetaSymbol(succSymbol.getType()) && succ < symbolList.size() - 1) {
if (discoveryExploredList.contains(tokens[succ]) == false) {
newDiscoveryBoundaryList.add(new SearchKnot<Tuple<Token, Integer>>(new Tuple<Token, Integer>(tokens[succ], succ), actual));
}
}
}
}
discoveryBoundaryList.clear();
discoveryBoundaryList.addAll(newDiscoveryBoundaryList);
newDiscoveryBoundaryList.clear();
}
if (discoveryFinalList.isEmpty() == false) {
List<String> selfIdList = new ArrayList<String>();
for (SearchKnot<Tuple<Token, Integer>> discoveryFinal : discoveryFinalList) {
while (discoveryFinal.getParent() != null) {
Symbol symbol = symbolList.get(discoveryFinal.getValue().getSecond());
if (symbol.getType() == Type.CLOSED_SELF_START || symbol.getType() == Type.CLOSED_SELF_END) {
selfIdList.add(closedSelfIdMap.get(discoveryFinal.getValue().getSecond()));
}
discoveryFinal = discoveryFinal.getParent();
}
}
StringBuilder sb = new StringBuilder();
sb.append("(" + selfIdList.get(0));
for (int j = 1; j < selfIdList.size(); j++) {
sb.append("|" + selfIdList.get(j));
}
sb.append(")*");
symbolList.set(i, new Symbol("", actualSymbol.getType() == Type.PUSH ? Type.INFINI_PUSH : Type.INFINI_POP));
String prefix = actualSymbol.getType() == Type.PUSH ? "" : "-";
infiniLateralStackMap.put(i, prefix + sb.toString());
} else {
infiniLateralStackMap.put(i, "");
}
}
}
for (int i = 0; i < symbolList.size(); i++) {
Symbol actualSymbol = symbolList.get(i);
if (i == 0 || (Symbol.isMetaSymbol(actualSymbol.getType()) == false)) {
boundary.clear();
boundary.add(new Step(tokens[i], new ArrayList<Event>()));
explored.clear();
successorList.clear();
while (boundary.isEmpty() == false) {
Step actual = boundary.remove(0);
explored.add(actual.getDestination());
for (int succ : connectionsMap.get(actual.getDestination())) {
Symbol succSymbol = symbolList.get(succ);
List<Event> eventList = new ArrayList<Event>(actual.getEventList());
if (succSymbol.getLikeness() != 0) {
eventList.add(new LikenessEvent(succSymbol.getLikeness()));
}
if (succSymbol.getMarkIndex() != 0) {
eventList.add(new MarkEvent(succSymbol.getMarkIndex() > 0, Math.abs(succSymbol.getMarkIndex()), succSymbol.getMarkName()));
}
if (succSymbol.getType() == Type.CHECK_IN || succSymbol.getType() == Type.CHECK_OUT) {
eventList.add(new CheckEvent(succSymbol.getType() == Type.CHECK_IN));
}
if (succSymbol.getType() == Type.CLOSED_SELF_START || succSymbol.getType() == Type.CLOSED_SELF_END) {
eventList.add(new LateralStepEvent(closedSelfIdMap.get(succ)));
}
if (Symbol.isStackRelated(succSymbol.getType())) {
String minDepth = "";
String maxDepth = "";
switch (succSymbol.getType()) {
case PUSH:
minDepth = "1";
maxDepth = "1";
break;
case POP:
minDepth = "-1";
maxDepth = "-1";
break;
case INFINI_PUSH:
minDepth = "1";
maxDepth = "+I";
eventList.add(new LateralStepEvent(infiniLateralStackMap.get(succ)));
break;
case INFINI_POP:
minDepth = "-I";
maxDepth = "-1";
eventList.add(new LateralStepEvent(infiniLateralStackMap.get(succ)));
break;
}
eventList.add(new ChangeDepthEvent(minDepth, maxDepth));
}
if ((Symbol.isMetaSymbol(succSymbol.getType()) == false) || succ == symbolList.size() - 1) {
successorList.add(new Step(tokens[succ], eventList));
} else {
Step newStep = new Step(tokens[succ], eventList);
if (explored.contains(tokens[succ]) == false) {
boundary.add(newStep);
}
}
}
}
stepMap.put(tokens[i], new ArrayList<Step>(successorList));
}
}
return new Parser(stepMap, tokens[0], tokens[symbolList.size() - 1]);
}
/**
* @throws IllegalArgumentException
*/
public void validateOrThrow() throws IllegalArgumentException {
for (int j = 0; j < symbolList.size() - 1; j++) {
if (connections.get(j) == null) {
throw new IllegalArgumentException();
}
}
}
}