|
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 |
|