Package it.halfone.regex

Source Code of it.halfone.regex.Regex

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();
      }
    }
  }
}
TOP

Related Classes of it.halfone.regex.Regex

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.