| FSM |

    1   /*
    2    *  FSM.java
    3    *
    4    *  Copyright (c) 1998-2001, The University of Sheffield.
    5    *
    6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
    7    *  software, licenced under the GNU Library General Public License,
    8    *  Version 2, June 1991 (in the distribution as file licence.html,
    9    *  and also available at http://gate.ac.uk/gate/licence.html).
    10   *
    11   *  Valentin Tablan, 29/Mar/2000
    12   *
    13   *  $Id: FSM.java,v 1.21 2001/11/01 15:49:08 valyt Exp $
    14   */
    15
    16  package gate.fsm;
    17
    18  import java.util.*;
    19
    20  import gate.jape.*;
    21  import gate.util.*;
    22
    23  /**
    24    * This class implements a standard Finite State Machine.
    25    * It is used for both deterministic and non-deterministic machines.
    26    */
    27  public class FSM implements JapeConstants {
    28
    29    /** Debug flag */
    30    private static final boolean DEBUG = false;
    31
    32    /**
    33      * Builds a standalone FSM starting from a single phase transducer.
    34      * @param spt the single phase transducer to be used for building this FSM.
    35      */
    36    public FSM(SinglePhaseTransducer spt){
    37      initialState = new State();
    38      transducerName = spt.getName();
    39      Iterator rulesEnum = spt.getRules().iterator();
    40      Rule currentRule;
    41
    42      while(rulesEnum.hasNext()){
    43        currentRule = (Rule) rulesEnum.next();
    44        FSM ruleFSM = new FSM(currentRule);
    45        initialState.addTransition(new Transition(null,
    46                                                  ruleFSM.getInitialState()));
    47      }
    48
    49      eliminateVoidTransitions();
    50  //Out.prln("Transducer " + spt.getName() + " converted to " + allStates.size() + " states");
    51
    52    }
    53
    54    /**
    55      * Builds a FSM starting from a rule. This FSM is actually a part of a larger
    56      * one (usually the one that is built based on the single phase transducer
    57      * that contains the rule).
    58      * built by this constructor.
    59      * @param rule the rule to be used for the building process.
    60      */
    61    public FSM(Rule rule) {
    62
    63      initialState = new State();
    64      LeftHandSide lhs = rule.getLHS();
    65      PatternElement[][] constraints =
    66                         lhs.getConstraintGroup().getPatternElementDisjunction();
    67      // the rectangular array constraints is a disjunction of sequences of
    68      // constraints = [[PE]:[PE]...[PE] ||
    69      //                [PE]:[PE]...[PE] ||
    70      //                ...
    71      //                [PE]:[PE]...[PE] ]
    72
    73      //The current and the next state for the current ROW.
    74      State currentRowState, nextRowState;
    75      State finalState = new State();
    76      PatternElement currentPattern;
    77
    78      for(int i = 0; i < constraints.length; i++){
    79        // for each row we have to create a sequence of states that will accept
    80        // the sequence of annotations described by the restrictions on that row.
    81        // The final state of such a sequence will always be a final state which
    82        // will have associated the right hand side of the rule used for this
    83        // constructor.
    84
    85        // For each row we will start from the initial state.
    86        currentRowState = initialState;
    87        for(int j=0; j < constraints[i].length; j++) {
    88
    89          // parse the sequence of constraints:
    90          // For each basic pattern element add a new state and link it to the
    91          // currentRowState.
    92          // The case of kleene operators has to be considered!
    93          currentPattern = constraints[i][j];
    94          State insulator = new State();
    95          currentRowState.addTransition(new Transition(null,insulator));
    96          currentRowState = insulator;
    97          if(currentPattern instanceof BasicPatternElement) {
    98            //the easy case
    99            nextRowState = new State();
    100           currentRowState.addTransition(
    101             new Transition((BasicPatternElement)currentPattern, nextRowState));
    102           currentRowState = nextRowState;
    103         } else if(currentPattern instanceof ComplexPatternElement) {
    104
    105           // the current pattern is a complex pattern element
    106           // ..it will probaly be converted into a sequence of states itself.
    107           currentRowState =  convertComplexPE(
    108                               currentRowState,
    109                               (ComplexPatternElement)currentPattern,
    110                               new LinkedList());
    111         } else {
    112           // we got an unknown kind of pattern
    113           throw new RuntimeException("Strange looking pattern: " +
    114                                      currentPattern);
    115         }
    116
    117       } // for j
    118
    119       //link the end of the current row to the final state using
    120       //an empty transition.
    121       currentRowState.addTransition(new Transition(null,finalState));
    122       finalState.setAction(rule.getRHS());
    123       finalState.setFileIndex(rule.getPosition());
    124       finalState.setPriority(rule.getPriority());
    125     } // for i
    126   }
    127
    128   /**
    129     * Gets the initial state of this FSM
    130     * @return an object of type gate.fsm.State representing the initial state.
    131     */
    132   public State getInitialState() {
    133     return initialState;
    134   } // getInitialState
    135
    136   /**
    137     * Receives a state to start from and a complex pattern element.
    138     * Parses the complex pattern element and creates all the necessary states
    139     * and transitions for accepting annotations described by the given PE.
    140     * @param state the state to start from
    141     * @param cpe the pattern to be recognized
    142     * @param label the bindings name for all the annotation accepted along
    143     * the way this is actually a listy of Strings. It is necessary to use
    144     * a list becuase of the reccursive definition of ComplexPatternElement.
    145     * @return the final state reached after accepting a sequence of annotations
    146     * as described in the pattern
    147     */
    148   private State convertComplexPE(State startState,
    149                                 ComplexPatternElement cpe, LinkedList labels){
    150     //create a copy
    151     LinkedList newBindings = (LinkedList)labels.clone();
    152     String localLabel = cpe.getBindingName ();
    153
    154     if(localLabel != null)newBindings.add(localLabel);
    155
    156     PatternElement[][] constraints =
    157                        cpe.getConstraintGroup().getPatternElementDisjunction();
    158
    159     // the rectangular array constraints is a disjunction of sequences of
    160     // constraints = [[PE]:[PE]...[PE] ||
    161     //                [PE]:[PE]...[PE] ||
    162     //                ...
    163     //                [PE]:[PE]...[PE] ]
    164
    165     //The current and the next state for the current ROW.
    166     State currentRowState, nextRowState, endState = new State();
    167     PatternElement currentPattern;
    168
    169     for(int i = 0; i < constraints.length; i++) {
    170       // for each row we have to create a sequence of states that will accept
    171       // the sequence of annotations described by the restrictions on that row.
    172       // The final state of such a sequence will always be a finale state which
    173       // will have associated the right hand side of the rule used for this
    174       // constructor.
    175
    176       //For each row we will start from the initial state.
    177       currentRowState = startState;
    178       for(int j=0; j < (constraints[i]).length; j++) {
    179
    180         //parse the sequence of constraints:
    181         //For each basic pattern element add a new state and link it to the
    182         //currentRowState.
    183         //The case of kleene operators has to be considered!
    184         State insulator = new State();
    185         currentRowState.addTransition(new Transition(null,insulator));
    186         currentRowState = insulator;
    187         currentPattern = constraints[i][j];
    188         if(currentPattern instanceof BasicPatternElement) {
    189
    190           //the easy case
    191           nextRowState = new State();
    192           currentRowState.addTransition(
    193             new Transition((BasicPatternElement)currentPattern,
    194                             nextRowState,newBindings));
    195           currentRowState = nextRowState;
    196         } else if(currentPattern instanceof ComplexPatternElement) {
    197
    198           // the current pattern is a complex pattern element
    199           // ..it will probaly be converted into a sequence of states itself.
    200           currentRowState =  convertComplexPE(
    201                               currentRowState,
    202                               (ComplexPatternElement)currentPattern,
    203                               newBindings);
    204         } else {
    205
    206           //we got an unknown kind of pattern
    207           throw new RuntimeException("Strange looking pattern:"+currentPattern);
    208         }
    209
    210       } // for j
    211         // link the end of the current row to the general end state using
    212         // an empty transition.
    213         currentRowState.addTransition(new Transition(null,endState));
    214     } // for i
    215
    216     // let's take care of the kleene operator
    217     int kleeneOp = cpe.getKleeneOp();
    218     switch (kleeneOp){
    219       case NO_KLEENE_OP:{
    220         break;
    221       }
    222       case KLEENE_QUERY:{
    223         //allow to skip everything via a null transition
    224         startState.addTransition(new Transition(null,endState));
    225         break;
    226       }
    227       case KLEENE_PLUS:{
    228
    229         // allow to return to startState
    230         endState.addTransition(new Transition(null,startState));
    231         break;
    232       }
    233       case KLEENE_STAR:{
    234
    235         // allow to skip everything via a null transition
    236         startState.addTransition(new Transition(null,endState));
    237
    238         // allow to return to startState
    239         endState.addTransition(new Transition(null,startState));
    240         break;
    241       }
    242       default:{
    243         throw new RuntimeException("Unknown Kleene operator"+kleeneOp);
    244       }
    245     } // switch (cpe.getKleeneOp())
    246     return endState;
    247   } // convertComplexPE
    248
    249   /**
    250     * Converts this FSM from a non-deterministic to a deterministic one by
    251     * eliminating all the unrestricted transitions.
    252     */
    253   public void eliminateVoidTransitions() {
    254
    255     dStates.clear(); //kalina: replaced from new HashSet()
    256     LinkedList unmarkedDStates = new LinkedList();
    257     AbstractSet currentDState = new HashSet();
    258     //kalina: prefer clear coz faster than init()
    259     newStates.clear();
    260
    261     currentDState.add(initialState);
    262     currentDState = lambdaClosure(currentDState);
    263     dStates.add(currentDState);
    264     unmarkedDStates.add(currentDState);
    265
    266     // create a new state that will take the place the set of states
    267     // in currentDState
    268     initialState = new State();
    269     newStates.put(currentDState, initialState);
    270
    271     // find out if the new state is a final one
    272     Iterator innerStatesIter = currentDState.iterator();
    273     RightHandSide action = null;
    274
    275     while(innerStatesIter.hasNext()){
    276       State currentInnerState = (State)innerStatesIter.next();
    277       if(currentInnerState.isFinal()){
    278         action = (RightHandSide)currentInnerState.getAction();
    279         initialState.setAction(action);
    280         initialState.setFileIndex(currentInnerState.getFileIndex());
    281         initialState.setPriority(currentInnerState.getPriority());
    282         break;
    283       }
    284     }
    285
    286     while(!unmarkedDStates.isEmpty()) {
    287       currentDState = (AbstractSet)unmarkedDStates.removeFirst();
    288       Iterator insideStatesIter = currentDState.iterator();
    289
    290       while(insideStatesIter.hasNext()) {
    291         State innerState = (State)insideStatesIter.next();
    292         Iterator transIter = innerState.getTransitions().iterator();
    293
    294         while(transIter.hasNext()) {
    295           Transition currentTrans = (Transition)transIter.next();
    296
    297           if(currentTrans.getConstraints() !=null) {
    298             State target = currentTrans.getTarget();
    299             AbstractSet newDState = new HashSet();
    300             newDState.add(target);
    301             newDState = lambdaClosure(newDState);
    302
    303             if(!dStates.contains(newDState)) {
    304               dStates.add(newDState);
    305               unmarkedDStates.add(newDState);
    306               State newState = new State();
    307               newStates.put(newDState, newState);
    308
    309               //find out if the new state is a final one
    310               innerStatesIter = newDState.iterator();
    311               while(innerStatesIter.hasNext()) {
    312                 State currentInnerState = (State)innerStatesIter.next();
    313
    314                 if(currentInnerState.isFinal()) {
    315                   newState.setAction(
    316                           (RightHandSide)currentInnerState.getAction());
    317                   newState.setFileIndex(currentInnerState.getFileIndex());
    318                   newState.setPriority(currentInnerState.getPriority());
    319                   break;
    320                 }
    321               }
    322             }// if(!dStates.contains(newDState))
    323
    324             State currentState = (State)newStates.get(currentDState);
    325             State newState = (State)newStates.get(newDState);
    326             currentState.addTransition(new Transition(
    327                                         currentTrans.getConstraints(),
    328                                         newState,
    329                                         currentTrans.getBindings()));
    330           }// if(currentTrans.getConstraints() !=null)
    331
    332         }// while(transIter.hasNext())
    333
    334       }// while(insideStatesIter.hasNext())
    335
    336     }// while(!unmarkedDstates.isEmpty())
    337
    338     /*
    339     //find final states
    340     Iterator allDStatesIter = dStates.iterator();
    341     while(allDStatesIter.hasNext()){
    342       currentDState = (AbstractSet) allDStatesIter.next();
    343       Iterator innerStatesIter = currentDState.iterator();
    344       while(innerStatesIter.hasNext()){
    345         State currentInnerState = (State) innerStatesIter.next();
    346         if(currentInnerState.isFinal()){
    347           State newState = (State)newStates.get(currentDState);
    348
    349           newState.setAction(currentInnerState.getAction());
    350           break;
    351         }
    352       }
    353
    354     }
    355     */
    356     allStates = newStates.values();
    357   }//eliminateVoidTransitions
    358
    359   /*
    360     * Computes the lambda-closure (aka epsilon closure) of the given set of
    361     * states, that is the set of states that are accessible from any of the
    362     * states in the given set using only unrestricted transitions.
    363     * @return a set containing all the states accessible from this state via
    364     * transitions that bear no restrictions.
    365     */
    366   private AbstractSet lambdaClosure(AbstractSet s) {
    367     // the stack/queue used by the algorithm
    368     LinkedList list = new LinkedList(s);
    369
    370     // the set to be returned
    371     AbstractSet lambdaClosure = new HashSet(s);
    372     State top;
    373     Iterator transIter;
    374     Transition currentTransition;
    375     State currentState;
    376     while(!list.isEmpty()){
    377       top = (State)list.removeFirst();
    378       transIter = top.getTransitions().iterator();
    379
    380       while(transIter.hasNext()){
    381         currentTransition = (Transition)transIter.next();
    382
    383         if(currentTransition.getConstraints() == null){
    384           currentState = currentTransition.getTarget();
    385           if(!lambdaClosure.contains(currentState)){
    386             lambdaClosure.add(currentState);
    387             list.addFirst(currentState);
    388           }// if(!lambdaClosure.contains(currentState))
    389
    390         }// if(currentTransition.getConstraints() == null)
    391
    392       }
    393     }
    394     return lambdaClosure;
    395   } // lambdaClosure
    396
    397   /**
    398     * Returns a GML (Graph Modelling Language) representation of the transition
    399     * graph of this FSM.
    400     */
    401   public String getGML() {
    402
    403     String res = "graph[ \ndirected 1\n";
    404 ///    String nodes = "", edges = "";
    405     StringBuffer nodes = new StringBuffer(gate.Gate.STRINGBUFFER_SIZE),
    406                  edges = new StringBuffer(gate.Gate.STRINGBUFFER_SIZE);
    407
    408     Iterator stateIter = allStates.iterator();
    409     while (stateIter.hasNext()){
    410       State currentState = (State)stateIter.next();
    411       int stateIndex = currentState.getIndex();
    412 /*      nodes += "node[ id " + stateIndex +
    413                " label \"" + stateIndex;
    414 */
    415         nodes.append("node[ id ");
    416         nodes.append(stateIndex);
    417         nodes.append(" label \"");
    418         nodes.append(stateIndex);
    419
    420              if(currentState.isFinal()){
    421 /*              nodes += ",F";
    422               nodes += "\\n" + currentState.getAction().shortDesc();
    423 */
    424               nodes.append(",F\\n" + currentState.getAction().shortDesc());
    425              }
    426 ///             nodes +=  "\"  ]\n";
    427              nodes.append("\"  ]\n");
    428 ///      edges += currentState.getEdgesGML();
    429       edges.append(currentState.getEdgesGML());
    430     }
    431     res += nodes.toString() + edges.toString() + "]\n";
    432     return res;
    433   } // getGML
    434
    435   /**
    436     * Returns a textual description of this FSM.
    437     */
    438   public String toString(){
    439     String res = "Starting from:" + initialState.getIndex() + "\n";
    440     Iterator stateIter = allStates.iterator();
    441     while (stateIter.hasNext()){
    442       res += "\n\n" + stateIter.next();
    443     }
    444     return res;
    445   } // toString
    446
    447   /**
    448     * The initial state of this FSM.
    449     */
    450   private State initialState;
    451
    452   /**
    453     * The set of states for this FSM
    454     */
    455   private transient Collection allStates =  new HashSet();
    456
    457   //kalina: added this member here to minimise HashMap allocation
    458   private transient Map newStates = new HashMap();
    459   private transient Set dStates = new HashSet();
    460
    461   private String transducerName;
    462
    463 } // FSM
    464

| FSM |