001 /*
002 * FSM.java
003 *
004 * Copyright (c) 1995-2010, The University of Sheffield. See the file
005 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006 *
007 * This file is part of GATE (see http://gate.ac.uk/), and is free
008 * software, licenced under the GNU Library General Public License,
009 * Version 2, June 1991 (in the distribution as file licence.html,
010 * and also available at http://gate.ac.uk/gate/licence.html).
011 *
012 * Valentin Tablan, 29/Mar/2000
013 *
014 * $Id: FSM.java 12006 2009-12-01 17:24:28Z thomas_heitz $
015 */
016
017 package gate.fsm;
018
019 import java.util.*;
020
021 import gate.jape.*;
022 import gate.util.Benchmark;
023 import gate.util.SimpleArraySet;
024 import static gate.jape.KleeneOperator.Type.*;
025
026 /**
027 * This class implements a standard Finite State Machine.
028 * It is used for both deterministic and non-deterministic machines.
029 */
030 public class FSM implements JapeConstants {
031
032 private ArrayList<RuleTime> ruleTimes = new ArrayList<RuleTime>();
033
034 public ArrayList<RuleTime> getRuleTimes() {
035 return ruleTimes;
036 }
037
038 private void decorateStates() {
039 HashMap<String,Integer> temporaryRuleNameToIndexMap = new HashMap<String,Integer>();
040 ruleTimes.add(new RuleTime(0,State.INITIAL_RULE));
041 ruleTimes.add(new RuleTime(0,State.UNKNOWN_RULE));
042 ruleTimes.add(new RuleTime(0,State.UNVISITED_RULE));
043 int ruleIndex = State.INITIAL_INDEX;
044 for (Transition t : this.getInitialState().getTransitions()) {
045 ruleIndex = t.getTarget().getRuleForState(temporaryRuleNameToIndexMap, ruleTimes);
046 assert (ruleIndex != State.UNVISITED_INDEX) && (ruleIndex != State.UNKNOWN_INDEX);
047 }
048 this.getInitialState().setIndexInRuleList(State.INITIAL_INDEX);
049 }
050
051
052 /** Debug flag */
053 private static final boolean DEBUG = false;
054
055 /**
056 * The constructor that all the other constructors should call.
057 */
058 protected FSM() {
059 initialState = new State();
060 }
061
062 /**
063 * Builds a standalone FSM starting from a single phase transducer.
064 * @param spt the single phase transducer to be used for building this FSM.
065 */
066 public FSM(SinglePhaseTransducer spt){
067 this();
068 addRules(spt.getRules());
069 if (Benchmark.isBenchmarkingEnabled()) {
070 this.decorateStates();
071 }
072 }
073
074 /**
075 * Do the work involved in creating an FSM from a PrioritisedRuleList.
076 */
077 protected void addRules(PrioritisedRuleList rules) {
078 Iterator rulesEnum = rules.iterator();
079
080 while(rulesEnum.hasNext()){
081 FSM ruleFSM = spawn((Rule) rulesEnum.next());
082
083 //added by Karter start -> JapeDebugger
084 ruleHash.putAll(ruleFSM.ruleHash);
085 //added by Karter end
086
087 initialState.addTransition(new Transition(null,
088 ruleFSM.getInitialState()));
089 }
090
091 eliminateVoidTransitions();
092 }
093
094 /**
095 * Builds a FSM starting from a rule. This FSM is actually a part of a larger
096 * one (usually the one that is built based on the single phase transducer
097 * that contains the rule).
098 * built by this constructor.
099 * @param rule the rule to be used for the building process.
100 */
101 public FSM(Rule rule) {
102 this();
103 setRule(rule);
104 }
105
106
107 /**
108 * Do the work involved in creating an FSM from a Rule.
109 */
110 protected void setRule(Rule rule) {
111
112 LeftHandSide lhs = rule.getLHS();
113
114 //added by Karter start -> JapeDebugger
115 LinkedList<String> ll = new LinkedList<String>();
116 String label = currentLHSBinding(lhs);
117 ll.add(label);
118 ruleHash.put(rule.getName(), label);
119 //added by Karter end
120
121
122 PatternElement[][] constraints =
123 lhs.getConstraintGroup().getPatternElementDisjunction();
124 // the rectangular array constraints is a disjunction of sequences of
125 // constraints = [[PE]:[PE]...[PE] ||
126 // [PE]:[PE]...[PE] ||
127 // ...
128 // [PE]:[PE]...[PE] ]
129
130 //The current and the next state for the current ROW.
131 State currentRowState, nextRowState;
132 State finalState = new State();
133 PatternElement currentPattern;
134
135 for(int i = 0; i < constraints.length; i++){
136 // for each row we have to create a sequence of states that will accept
137 // the sequence of annotations described by the restrictions on that row.
138 // The final state of such a sequence will always be a final state which
139 // will have associated the right hand side of the rule used for this
140 // constructor.
141
142 // For each row we will start from the initial state.
143 currentRowState = initialState;
144 for(int j=0; j < constraints[i].length; j++) {
145
146 // parse the sequence of constraints:
147 // For each basic pattern element add a new state and link it to the
148 // currentRowState.
149 // The case of kleene operators has to be considered!
150 currentPattern = constraints[i][j];
151 State insulator = new State();
152 currentRowState.addTransition(new Transition(null,insulator));
153 currentRowState = insulator;
154 if(currentPattern instanceof BasicPatternElement) {
155 //the easy case
156 nextRowState = new State();
157
158 //added by Karter start -> JapeDebugger
159 LinkedList<String> sll = new LinkedList<String>();
160 sll.add(currentBasicBinding( (BasicPatternElement) currentPattern));
161 currentRowState.addTransition(
162 new Transition( (BasicPatternElement) currentPattern,
163 nextRowState
164 /*added by Karter*/, sll));
165 //added by Karter end
166
167 currentRowState = nextRowState;
168 } else if(currentPattern instanceof ComplexPatternElement) {
169
170 // the current pattern is a complex pattern element
171 // ..it will probaly be converted into a sequence of states itself.
172
173 // -> JapeDebugger
174 currentRowState = convertComplexPE(
175 currentRowState,
176 (ComplexPatternElement) currentPattern,
177 /*changed by Karter "new LinkedList()"*/ll);
178
179 } else {
180 // we got an unknown kind of pattern
181 throw new RuntimeException("Strange looking pattern: " +
182 currentPattern);
183 }
184
185 } // for j
186
187 //link the end of the current row to the final state using
188 //an empty transition.
189 currentRowState.addTransition(new Transition(null,finalState));
190 finalState.setAction(rule.getRHS());
191 finalState.setFileIndex(rule.getPosition());
192 finalState.setPriority(rule.getPriority());
193 } // for i
194 }
195
196 /**
197 * Builds a FSM starting from a ComplexPatternElement. This FSM is usually
198 * part of a larger FSM based on the Rule that contains the
199 * ComplexPatternElement.
200 *
201 * @param cpe
202 * the ComplexPatternElement to be used for the building process.
203 */
204 protected FSM(ComplexPatternElement cpe) {
205 this();
206 finalState = convertComplexPE(initialState, cpe, new LinkedList<String>());
207 finalState.isFinal = true;
208 }
209
210 /**
211 * A factory method for new FSMs like this one, given a Rule object.
212 */
213 protected FSM spawn(Rule r) {
214 return new FSM(r);
215 }
216
217 /**
218 * A factory method for new FSMs like this one, given a ComplexPatternElement
219 * object.
220 */
221 protected FSM spawn(ComplexPatternElement currentPattern) {
222 return new FSM(currentPattern);
223 }
224
225 /**
226 * Gets the initial state of this FSM
227 * @return an object of type gate.fsm.State representing the initial state.
228 */
229 public State getInitialState() {
230 return initialState;
231 } // getInitialState
232
233 /**
234 * Receives a state to start from and a complex pattern element.
235 * Parses the complex pattern element and creates all the necessary states
236 * and transitions for accepting annotations described by the given PE.
237 * @param startState the state to start from
238 * @param cpe the pattern to be recognized
239 * @param labels the bindings name for all the annotation accepted along
240 * the way. This is actually a list of Strings. It is necessary to use
241 * a list because of the recursive definition of ComplexPatternElement.
242 * @return the final state reached after accepting a sequence of annotations
243 * as described in the pattern
244 */
245 private State convertComplexPE(State startState,
246 ComplexPatternElement cpe, LinkedList<String> labels){
247
248 State endState = generateStates(startState, cpe, labels);
249
250 // now take care of the kleene operator
251 KleeneOperator kleeneOp = cpe.getKleeneOp();
252 KleeneOperator.Type type = kleeneOp.getType();
253 if (type == OPTIONAL) {
254 //allow to skip everything via a null transition
255 startState.addTransition(new Transition(null,endState));
256 }
257 else if (type == PLUS) {
258 // allow to return to startState
259 endState.addTransition(new Transition(null,startState));
260 }
261 else if (type == STAR) {
262 // allow to skip everything via a null transition
263 startState.addTransition(new Transition(null,endState));
264
265 // allow to return to startState
266 endState.addTransition(new Transition(null,startState));
267 }
268 else if (type == RANGE) {
269 Integer min = kleeneOp.getMin();
270 Integer max = kleeneOp.getMax();
271
272 // keep a list of the start states for each possible optional sets so can make
273 // direct transitions from them to the final end state
274 List<State> startStateList = new ArrayList<State>();
275
276 if (min == null || min == 0) {
277 //if min is empty or 0, allow to skip everything via a null transition
278 startStateList.add(startState);
279 }
280 else if (min > 1) {
281 // add min-1 copies of the set of states for the CPE. It's -1 because
282 // one set was already added by the first generateStates call
283 int numCopies = min - 1;
284 for(int i = 1; i <= numCopies; i++) {
285 // the end state of the previous set always moves up to be the
286 // start state of the next set.
287 startState = endState;
288 endState = generateStates(startState, cpe, labels);
289 }
290 }
291
292 if (max == null) {
293 // if there is no defined max, allow to return to startState any
294 // number of times. Start state may be the original start or, if
295 // min > 1, then it's the start of the last set of states added.
296 // Example: A range with min 3 and max = unbounded will look like
297 // this:
298 // v------|
299 // start1...end1->start2...end2->start3...end3->...
300 //
301 endState.addTransition(new Transition(null,startState));
302 }
303 else if (max > min) {
304 // there are some optional state sets. Make a copy of the state
305 // set for each.
306 int numCopies = max-min;
307
308 //if min == 0 then reduce numCopies by one since we already added
309 //one set of states that are optional
310 if (min == 0)
311 numCopies--;
312
313 for(int i = 1; i <= numCopies; i++) {
314 startState = endState;
315 startStateList.add(startState);
316 endState = generateStates(startState, cpe, labels);
317 }
318 }
319
320 //each of the optional stages can transition directly to the final end
321 for(State state : startStateList) {
322 state.addTransition(new Transition(null,endState));
323 }
324
325 } //end if type == RANGE
326
327 return endState;
328 } // convertComplexPE
329
330 /**
331 * Receives a state to start from and a complex pattern element.
332 * Parses the complex pattern element and creates all the necessary states
333 * and transitions for traversing the annotations described by the given PE
334 * exactly once. Does not add any transitions for kleene operators.
335 * @param startState the state to start from
336 * @param cpe the pattern to be recognized
337 * @param labels the bindings name for all the annotation accepted along
338 * the way. This is actually a list of Strings. It is necessary to use
339 * a list because of the recursive definition of ComplexPatternElement.
340 * @return the final state reached after accepting a sequence of annotations
341 * as described in the pattern
342 */
343 private State generateStates(State startState, ComplexPatternElement cpe,
344 LinkedList<String> labels) {
345 //create a copy
346 LinkedList<String> newBindings = (LinkedList<String>)labels.clone();
347 String localLabel = cpe.getBindingName ();
348
349 if(localLabel != null)newBindings.add(localLabel);
350
351 ConstraintGroup constraintGroup = cpe.getConstraintGroup();
352 PatternElement[][] constraints =
353 constraintGroup.getPatternElementDisjunction();
354
355 // the rectangular array constraints is a disjunction of sequences of
356 // constraints = [[PE]:[PE]...[PE] ||
357 // [PE]:[PE]...[PE] ||
358 // ...
359 // [PE]:[PE]...[PE] ]
360
361 //The current and the next state for the current ROW.
362 State currentRowState, nextRowState, endState = new State();
363 PatternElement currentPattern;
364
365 for(int i = 0; i < constraints.length; i++) {
366 // for each row we have to create a sequence of states that will accept
367 // the sequence of annotations described by the restrictions on that row.
368 // The final state of such a sequence will always be a finale state which
369 // will have associated the right hand side of the rule used for this
370 // constructor.
371
372 //For each row we will start from the initial state.
373 currentRowState = startState;
374 for(int j=0; j < (constraints[i]).length; j++) {
375
376 //parse the sequence of constraints:
377 //For each basic pattern element add a new state and link it to the
378 //currentRowState.
379 //The case of kleene operators has to be considered!
380 State insulator = new State();
381 currentRowState.addTransition(new Transition(null,insulator));
382 currentRowState = insulator;
383 currentPattern = constraints[i][j];
384 if(currentPattern instanceof BasicPatternElement) {
385
386 //the easy case
387 nextRowState = new State();
388
389 //added by Karter start -> JapeDebugger
390 newBindings.add(currentBasicBinding( (BasicPatternElement)
391 currentPattern));
392 //added by Karter end
393
394
395 currentRowState.addTransition(
396 new Transition((BasicPatternElement)currentPattern,
397 nextRowState,newBindings));
398 currentRowState = nextRowState;
399 } else if(currentPattern instanceof ComplexPatternElement) {
400
401 // the current pattern is a complex pattern element
402 // ..it will probaly be converted into a sequence of states itself.
403 currentRowState = convertComplexPE(
404 currentRowState,
405 (ComplexPatternElement)currentPattern,
406 newBindings);
407 } else {
408
409 //we got an unknown kind of pattern
410 throw new RuntimeException("Strange looking pattern:"+currentPattern);
411 }
412
413 } // for j
414 // link the end of the current row to the general end state using
415 // an empty transition.
416 currentRowState.addTransition(new Transition(null,endState));
417 } // for i
418 return endState;
419 }
420
421 /**
422 * Converts this FSM from a non-deterministic to a deterministic one by
423 * eliminating all the unrestricted transitions.
424 */
425 public void eliminateVoidTransitions() {
426
427 dStates.clear(); //kalina: replaced from new HashSet()
428 LinkedList<AbstractSet<State>> unmarkedDStates = new LinkedList<AbstractSet<State>>();
429 AbstractSet<State> currentDState = new HashSet<State>();
430 //kalina: prefer clear coz faster than init()
431 newStates.clear();
432
433 currentDState.add(initialState);
434 currentDState = lambdaClosure(currentDState);
435 dStates.add(currentDState);
436 unmarkedDStates.add(currentDState);
437
438 // create a new state that will take the place the set of states
439 // in currentDState
440 initialState = new State();
441 newStates.put(currentDState, initialState);
442
443 // find out if the new state is a final one
444 Iterator innerStatesIter = currentDState.iterator();
445 RightHandSide action = null;
446
447 while(innerStatesIter.hasNext()){
448 State currentInnerState = (State)innerStatesIter.next();
449 if(currentInnerState.isFinal()){
450 action = (RightHandSide)currentInnerState.getAction();
451 initialState.setAction(action);
452 initialState.setFileIndex(currentInnerState.getFileIndex());
453 initialState.setPriority(currentInnerState.getPriority());
454 break;
455 }
456 }
457
458 while(!unmarkedDStates.isEmpty()) {
459 currentDState = unmarkedDStates.removeFirst();
460 Iterator insideStatesIter = currentDState.iterator();
461
462 while(insideStatesIter.hasNext()) {
463 State innerState = (State)insideStatesIter.next();
464 Iterator transIter = innerState.getTransitions().iterator();
465
466 while(transIter.hasNext()) {
467 Transition currentTrans = (Transition)transIter.next();
468
469 if(currentTrans.getConstraints() !=null) {
470 State target = currentTrans.getTarget();
471 AbstractSet<State> newDState = new HashSet<State>();
472 newDState.add(target);
473 newDState = lambdaClosure(newDState);
474
475 if(!dStates.contains(newDState)) {
476 dStates.add(newDState);
477 unmarkedDStates.add(newDState);
478 State newState = new State();
479 newStates.put(newDState, newState);
480
481 //find out if the new state is a final one
482 innerStatesIter = newDState.iterator();
483 while(innerStatesIter.hasNext()) {
484 State currentInnerState = (State)innerStatesIter.next();
485
486 if(currentInnerState.isFinal()) {
487 newState.setAction(
488 (RightHandSide)currentInnerState.getAction());
489 newState.setFileIndex(currentInnerState.getFileIndex());
490 newState.setPriority(currentInnerState.getPriority());
491 break;
492 }
493 }
494 }// if(!dStates.contains(newDState))
495
496 State currentState = (State)newStates.get(currentDState);
497 State newState = (State)newStates.get(newDState);
498 currentState.addTransition(new Transition(
499 currentTrans.getConstraints(),
500 newState,
501 currentTrans.getBindings()));
502 }// if(currentTrans.getConstraints() !=null)
503
504 }// while(transIter.hasNext())
505
506 }// while(insideStatesIter.hasNext())
507
508 }// while(!unmarkedDstates.isEmpty())
509
510 /*
511 //find final states
512 Iterator allDStatesIter = dStates.iterator();
513 while(allDStatesIter.hasNext()){
514 currentDState = (AbstractSet) allDStatesIter.next();
515 Iterator innerStatesIter = currentDState.iterator();
516 while(innerStatesIter.hasNext()){
517 State currentInnerState = (State) innerStatesIter.next();
518 if(currentInnerState.isFinal()){
519 State newState = (State)newStates.get(currentDState);
520
521 newState.setAction(currentInnerState.getAction());
522 break;
523 }
524 }
525
526 }
527 */
528 allStates = newStates.values();
529 }//eliminateVoidTransitions
530
531 /*
532 * Computes the lambda-closure (aka epsilon closure) of the given set of
533 * states, that is the set of states that are accessible from any of the
534 * states in the given set using only unrestricted transitions.
535 * @return a set containing all the states accessible from this state via
536 * transitions that bear no restrictions.
537 */
538 private AbstractSet<State> lambdaClosure(AbstractSet<State> s) {
539 // the stack/queue used by the algorithm
540 LinkedList<State> list = new LinkedList<State>(s);
541
542 // the set to be returned
543 AbstractSet<State> lambdaClosure = new HashSet<State>(s);
544 State top;
545 Iterator transIter;
546 Transition currentTransition;
547 State currentState;
548 while(!list.isEmpty()){
549 top = (State)list.removeFirst();
550 transIter = top.getTransitions().iterator();
551
552 while(transIter.hasNext()){
553 currentTransition = (Transition)transIter.next();
554
555 if(currentTransition.getConstraints() == null){
556 currentState = currentTransition.getTarget();
557 if(!lambdaClosure.contains(currentState)){
558 lambdaClosure.add(currentState);
559 list.addFirst(currentState);
560 }// if(!lambdaClosure.contains(currentState))
561
562 }// if(currentTransition.getConstraints() == null)
563
564 }
565 }
566 return lambdaClosure;
567 } // lambdaClosure
568
569
570 /**
571 * Two members used by forEachState().
572 */
573 protected State currentState;
574 protected Transition currentTransition;
575
576 /**
577 * Iterates over all the states in this FSM, setting currentState and
578 * currentTransition, then calling the given Runnable callback.
579 */
580 protected void forEachState (java.lang.Runnable r) {
581 Set<State> stackToProcess = new HashSet<State>();
582 Set<State> processed = new HashSet<State>();
583
584 stackToProcess.add(initialState);
585 while (!stackToProcess.isEmpty()) {
586 currentState = (State) stackToProcess.iterator().next();
587 stackToProcess.remove(currentState);
588 processed.add(currentState);
589
590 for(Transition t : currentState.getTransitions()) {
591 currentTransition = t;
592 State target = currentTransition.getTarget();
593 if (processed.contains(target) || stackToProcess.contains(target)) continue;
594 stackToProcess.add(target);
595
596 r.run();
597 }
598 }
599 }
600
601 /**
602 * @return a Map whose keys contain the states of this FSM, and whose values
603 * contain their corresponding transitions. This method actually walks
604 * the FSM, so it may be called before the FSM is finalized with
605 * compactTransitions().
606 */
607 public Map<State,SimpleArraySet<Transition>> getAllStates() {
608 /*
609 * This method can't use the allStates data member, since it's sometimes
610 * called before allStates is populated.
611 */
612
613 Map<State,SimpleArraySet<Transition>> statesToReturn = new HashMap<State,SimpleArraySet<Transition>>();
614 Set<State> stackToProcess = new HashSet<State>();
615 Set<State> processed = new HashSet<State>();
616
617 stackToProcess.add(initialState);
618 while (!stackToProcess.isEmpty()) {
619 currentState = (State) stackToProcess.iterator().next();
620 stackToProcess.remove(currentState);
621 processed.add(currentState);
622
623
624 SimpleArraySet<Transition> transitions = currentState.getTransitions();
625 statesToReturn.put(currentState, transitions);
626 for (Iterator<Transition> iter = transitions.iterator(); iter.hasNext();) {
627 currentTransition = iter.next();
628 State target = currentTransition.getTarget();
629 if (processed.contains(target) || stackToProcess.contains(target)) continue;
630 stackToProcess.add(target);
631 }
632 }
633
634 return statesToReturn;
635 }
636
637 /**
638 * Returns a representation of this FSM in the GraphViz graph-visualization
639 * language. We use the "digraph" (directed graph) format. Nodes are labeled
640 * by their numerical indexes. A node's shape is a diamond if it's the initial
641 * state, and round otherwise. A node is green if it's an initial state, red
642 * if it's a final state, and black otherwise. Final states are also marked
643 * with a double-line outline.
644 *
645 * @see <a href="http://www.graphviz.org/">the GraphViz web site </a> for
646 * software to translate the output of this method into pretty pictures.
647 *
648 * @param includeConstraints
649 * whether to include a stringified representation of each
650 * transition object as part of its label. The default is false.
651 * @return
652 */
653 public String asGraphViz(boolean includeConstraints) {
654 StringBuffer result = new StringBuffer();
655
656 result.append("digraph G {\n");
657
658 for(State currentState : getAllStates().keySet()) {
659 int stateIndex = currentState.getIndex();
660 Map<String,String> opts = new HashMap<String,String>();
661 opts.put("shape", currentState == initialState ? "diamond" : "circle");
662 opts.put("color", currentState == initialState ? "green" : currentState.isFinal() ? "red" : "black");
663 if (currentState.isFinal()) {
664 opts.put("peripheries", "2");
665 if (DEBUG) {
666 opts.put("shape", "rectangle");
667 opts.put("label", "" + stateIndex + "-" + currentState.getAction());
668 }
669 }
670
671 result.append(" " + stateIndex + " [" + encodeForGraphViz(opts) + "]" + ";\n");
672
673 for(Transition t : currentState.getTransitions()) {
674 String extraText = includeConstraints
675 ? " [label=\"" + t.toString(false) + "\"]"
676 : "";
677 result.append(" " + stateIndex + " -> " + t.getTarget().getIndex()
678 + extraText + ";\n");
679 }
680 }
681
682 result.append("}\n");
683
684 return result.toString();
685 }
686
687 /**
688 * Given a Map, encodes its keys and values as strings suitable for use as a
689 * GraphViz label. Embedded "\r\n" sequences are replaced by "\\l" to create
690 * line feeds, and embedded backslashes are escaped. The returned String takes
691 * the form "key1=value1, key2=value2, ...".
692 */
693 String encodeForGraphViz (Map<String,String> m) {
694 ArrayList<String> temp = new ArrayList<String>(m.size());
695 for(String k : m.keySet()) {
696 String v = m.get(k);
697 v = v.replaceAll("\r\n", "\\\\l");
698 v = v.replaceAll("\"", "\\\\\"");
699 temp.add(k + "=\"" + v + "\"");
700 }
701
702 StringBuffer toReturn = new StringBuffer();
703 for (int i = 0; i < temp.size(); i++)
704 {
705 if (i != 0) toReturn.append(",");
706 toReturn.append(temp.get(i));
707 }
708 return toReturn.toString();
709 }
710
711
712 /**
713 * Returns a GML (Graph Modelling Language) representation of the transition
714 * graph of this FSM.
715 */
716 public String getGML() {
717
718 String res = "graph[ \ndirected 1\n";
719
720 StringBuffer nodes = new StringBuffer(gate.Gate.STRINGBUFFER_SIZE),
721 edges = new StringBuffer(gate.Gate.STRINGBUFFER_SIZE);
722
723 Iterator stateIter = allStates.iterator();
724 while (stateIter.hasNext()){
725 State currentState = (State)stateIter.next();
726 int stateIndex = currentState.getIndex();
727 nodes.append("node[ id ");
728 nodes.append(stateIndex);
729 nodes.append(" label \"");
730 nodes.append(stateIndex);
731
732 if(currentState.isFinal()){
733 nodes.append(",F\\n" + currentState.getAction().shortDesc());
734 }
735 nodes.append("\" ]\n");
736 edges.append(currentState.getEdgesGML());
737 }
738 res += nodes.toString() + edges.toString() + "]\n";
739 return res;
740 } // getGML
741
742 /**
743 * Returns a textual description of this FSM.
744 */
745 public String toString(){
746 String res = "Starting from:" + initialState.getIndex() + "\n";
747 Iterator stateIter = allStates.iterator();
748 while (stateIter.hasNext()){
749 res += "\n\n" + stateIter.next();
750 }
751 return res;
752 } // toString
753
754 /**
755 * The initial state of this FSM.
756 */
757 private State initialState;
758
759 /**
760 * The final state of this FSM (usually only valid during construction).
761 */
762 protected State finalState;
763
764 /**
765 * The set of states for this FSM
766 */
767 private transient Collection allStates = new HashSet();
768
769 //kalina: added this member here to minimise HashMap allocation
770 private transient Map<AbstractSet,State> newStates = new HashMap<AbstractSet,State>();
771 private transient Set<AbstractSet> dStates = new HashSet<AbstractSet>();
772
773
774 //added by Karter start
775 private String currentBinding(ComplexPatternElement cpe, int indent) {
776 if (indent == 0)
777 bpeId = 0;
778 String ind = "";
779 for (int i = 0; i < indent; i++) {
780 ind += " ";
781 }
782 String binds = ind + "(\n";
783 PatternElement[][] pe = cpe.getConstraintGroup().
784 getPatternElementDisjunction();
785 for (int i = 0; i < pe.length; i++) {
786 PatternElement[] patternElements = pe[i];
787 for (int j = 0; j < patternElements.length; j++) {
788 PatternElement patternElement = patternElements[j];
789 if (patternElement instanceof ComplexPatternElement) {
790 ComplexPatternElement complexPatternElement = (ComplexPatternElement)
791 patternElement;
792 binds += currentBinding(complexPatternElement, indent + 1);
793
794 }
795 else {
796 binds += ind + " ";
797 binds += currentBasicBinding((BasicPatternElement) patternElement);
798 binds += "\n";
799 }
800 }
801 binds += ind + " |\n";
802 }
803 binds = binds.substring(0, binds.length() - 5);
804 binds += ")" + cpe.getKleeneOp().toString() + "\n";
805 if (indent == 0)
806 bpeId = 0;
807 return binds;
808 }
809
810 private String currentBasicBinding(BasicPatternElement bpe) {
811 StringBuilder sb = new StringBuilder("{");
812 Constraint[] cons = bpe.getConstraints();
813 for (int k = 0; k < cons.length; k++) {
814 sb.append(cons[k].getDisplayString(""));
815 if (k < cons.length - 1)
816 sb.append(",");
817 }
818 sb.append("}").append(" *").append(bpeId++).append("*");
819 return sb.toString();
820 }
821
822 private String currentLHSBinding(LeftHandSide lhs) {
823 String binds = "(\n";
824 PatternElement[][] pe = lhs.getConstraintGroup().
825 getPatternElementDisjunction();
826 for (int i = 0; i < pe.length; i++) {
827 PatternElement[] patternElements = pe[i];
828 for (int j = 0; j < patternElements.length; j++) {
829 PatternElement patternElement = patternElements[j];
830 if (patternElement instanceof ComplexPatternElement) {
831 ComplexPatternElement complexPatternElement = (ComplexPatternElement)
832 patternElement;
833 binds += currentBinding(complexPatternElement, 1);
834
835 }
836 else {
837 binds += " ";
838 binds += currentBasicBinding((BasicPatternElement) patternElement);
839 binds += "\n";
840 }
841 }
842 binds += " |\n";
843 }
844 binds = binds.substring(0, binds.length() - 5);
845 binds += ")\n";
846 bpeId = 0;
847 return binds;
848 }
849
850 int bpeId = 0;
851 public HashMap<String,String> ruleHash = new HashMap<String,String>();
852 //added by Karter end
853 } // FSM
|