SinglePhaseTransducer.java
0001 /*
0002  *  SinglePhaseTransducer.java - transducer class
0003  *
0004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
0005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0006  *
0007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0008  *  software, licenced under the GNU Library General Public License,
0009  *  Version 2, June 1991 (in the distribution as file licence.html,
0010  *  and also available at http://gate.ac.uk/gate/licence.html).
0011  *
0012  *  Hamish Cunningham, 24/07/98
0013  *
0014  *  $Id: SinglePhaseTransducer.java 13337 2011-01-11 19:45:53Z johann_p $
0015  */
0016 
0017 package gate.jape;
0018 
0019 import java.util.*;
0020 
0021 import org.apache.log4j.Logger;
0022 
0023 import gate.*;
0024 import gate.annotation.AnnotationSetImpl;
0025 import gate.creole.ExecutionException;
0026 import gate.creole.ExecutionInterruptedException;
0027 import gate.creole.ontology.Ontology;
0028 import gate.event.ProgressListener;
0029 import gate.fsm.*;
0030 import gate.util.*;
0031 import java.io.IOException;
0032 import java.util.concurrent.atomic.AtomicInteger;
0033 
0034 /**
0035  * Represents a complete CPSL grammar, with a phase name, options and
0036  * rule set (accessible by name and by sequence). Implements a transduce
0037  * method taking a Document as input. Constructs from String or File.
0038  */
0039 public class SinglePhaseTransducer extends Transducer implements JapeConstants,
0040                                                      java.io.Serializable {
0041 
0042   private static final long serialVersionUID = -2749474684496896114L;
0043 
0044   protected static final Logger log = Logger
0045           .getLogger(SinglePhaseTransducer.class);
0046 
0047   private static AtomicInteger actionClassNumber = new AtomicInteger();
0048 
0049   /*
0050    * A structure to pass information to/from the fireRule() method.
0051    * Since Java won't let us return multiple values, we stuff them into
0052    * a 'state' object that fireRule() can update.
0053    */
0054   protected static class SearchState {
0055     Node startNode;
0056 
0057     long startNodeOff;
0058 
0059     long oldStartNodeOff;
0060 
0061     SearchState(Node startNode, long startNodeOff, long oldStartNodeOff) {
0062       this.startNode = startNode;
0063       this.startNodeOff = startNodeOff;
0064       this.oldStartNodeOff = oldStartNodeOff;
0065     }
0066   }
0067   
0068   private static final class FSMMatcherResult{
0069     /**
0070      @param activeFSMInstances
0071      @param acceptingFSMInstances
0072      */
0073     public FSMMatcherResult(List<FSMInstance> activeFSMInstances,
0074             List<FSMInstance> acceptingFSMInstances) {
0075       this.activeFSMInstances = activeFSMInstances;
0076       this.acceptingFSMInstances = acceptingFSMInstances;
0077     }
0078     
0079     private List<FSMInstance> acceptingFSMInstances;
0080     private List<FSMInstance> activeFSMInstances;
0081     
0082   }
0083 
0084   /** Construction from name. */
0085   public SinglePhaseTransducer(String name) {
0086     this.name = name;
0087     rules = new PrioritisedRuleList();
0088     finishedAlready = false;
0089   // Construction from name
0090 
0091   /** Type of rule application (constants defined in JapeConstants). */
0092   protected int ruleApplicationStyle = BRILL_STYLE;
0093 
0094   /** Set the type of rule application (types defined in JapeConstants). */
0095   public void setRuleApplicationStyle(int style) {
0096     ruleApplicationStyle = style;
0097   }
0098 
0099   /**
0100    * The list of rules in this transducer. Ordered by priority and
0101    * addition sequence (which will be file position if they come from a
0102    * file).
0103    */
0104   protected PrioritisedRuleList rules;
0105 
0106   protected FSM fsm;
0107 
0108   /**
0109    * A list of FSM instances that haven't blocked yet, used during matching.
0110    */
0111   protected List<FSMInstance> activeFSMInstances;
0112   
0113   public FSM getFSM() {
0114     return fsm;
0115   }
0116 
0117   /** Add a rule. */
0118   public void addRule(Rule rule) {
0119     rules.add(rule);
0120   // addRule
0121 
0122   /** The values of any option settings given. */
0123   private Map<String, String> optionSettings = new HashMap<String, String>();
0124 
0125   /**
0126    * Add an option setting. If this option is set already, the new value
0127    * overwrites the previous one.
0128    */
0129   public void setOption(String name, String setting) {
0130     optionSettings.put(name, setting);
0131   // setOption
0132 
0133   /** Get the value for a particular option. */
0134   public String getOption(String name) {
0135     return optionSettings.get(name);
0136   // getOption
0137 
0138   /** Whether the finish method has been called or not. */
0139   protected boolean finishedAlready;
0140 
0141   /**
0142    * Finish: replace dynamic data structures with Java arrays; called
0143    * after parsing.
0144    */
0145   public void finish() {
0146     // both MPT and SPT have finish called on them by the parser...
0147     if(finishedAlreadyreturn;
0148     finishedAlready = true;
0149 
0150     // each rule has a RHS which has a string for java code
0151     // those strings need to be compiled now
0152     
0153     // TODO: (JP) if we have some binary JAPE grammars loaded and then we
0154     // compile additional action classes here, we can potentially
0155     // get duplicate class names.
0156     // Instead, we should modify and increment the classname until
0157     // we find one that is not already taken.
0158     Map actionClasses = new HashMap(rules.size());
0159     for(Iterator i = rules.iterator(); i.hasNext();) {
0160       Rule rule = (Rule)i.next();
0161       rule.finish();
0162       actionClasses.put(rule.getRHS().getActionClassName(), rule.getRHS()
0163               .getActionClassString());
0164     }
0165     try {
0166       gate.util.Javac.loadClasses(actionClasses);
0167     }
0168     catch(Exception e) {
0169       throw new GateRuntimeException(e);
0170     }
0171     compileEventBlocksActionClass();
0172 
0173     // build the finite state machine transition graph
0174     fsm = createFSM();
0175     // clear the old style data structures
0176     rules.clear();
0177     rules = null;
0178   // finish
0179 
0180   protected FSM createFSM() {
0181     return new FSM(this);
0182   }
0183 
0184   // dam: was
0185   // private void addAnnotationsByOffset(Map map, SortedSet keys, Set
0186   // annotations){
0187   private void addAnnotationsByOffset(/* Map map, */SimpleSortedSet keys,
0188           Set annotations) {
0189     Iterator annIter = annotations.iterator();
0190     while(annIter.hasNext()) {
0191       Annotation ann = (Annotation)annIter.next();
0192       // ignore empty annotations
0193       long offset = ann.getStartNode().getOffset().longValue();
0194       if(offset == ann.getEndNode().getOffset().longValue()) continue;
0195       // dam: was
0196       /*
0197        * // Long offset = ann.getStartNode().getOffset();
0198        *
0199        * List annsAtThisOffset = null; if(keys.add(offset)){
0200        * annsAtThisOffset = new LinkedList(); map.put(offset,
0201        * annsAtThisOffset); }else{ annsAtThisOffset =
0202        * (List)map.get(offset); } annsAtThisOffset.add(ann);
0203        */
0204       // dam: end
0205       keys.add(offset, ann);
0206     }
0207   }// private void addAnnotationsByOffset()
0208 
0209   /**
0210    * Transduce a document using the annotation set provided and the
0211    * current rule application style.
0212    */
0213   public void transduce(Document doc, AnnotationSet inputAS,
0214           AnnotationSet outputASthrows JapeException, ExecutionException {
0215     interrupted = false;
0216     log.debug("Start: " + name);
0217     fireProgressChanged(0);
0218 
0219     // the input annotations will be read from this map
0220     // maps offset to list of annotations
0221     SimpleSortedSet offsets = new SimpleSortedSet();
0222     SimpleSortedSet annotationsByOffset = offsets;
0223 
0224     // select only the annotations of types specified in the input list
0225     if(input.isEmpty()) {
0226       addAnnotationsByOffset(offsets, inputAS);
0227     }
0228     else {
0229       Iterator typesIter = input.iterator();
0230       AnnotationSet ofOneType = null;
0231       while(typesIter.hasNext()) {
0232         ofOneType = inputAS.get((String)typesIter.next());
0233         if(ofOneType != null) {
0234           addAnnotationsByOffset(offsets, ofOneType);
0235         }
0236       }
0237     }
0238 
0239     if(annotationsByOffset.isEmpty()) {
0240       fireProcessFinished();
0241       return;
0242     }
0243 
0244     annotationsByOffset.sort();
0245     // define data structures
0246     // FSM instances that haven't blocked yet
0247     if(activeFSMInstances == null) {
0248       activeFSMInstances = new LinkedList<FSMInstance>();
0249     }
0250     else {
0251       activeFSMInstances.clear();
0252     }
0253 
0254     // FSM instances that have reached a final state
0255     // This is a list and the contained objects are sorted by the length
0256     // of the document content covered by the matched annotations
0257     List<FSMInstance> acceptingFSMInstances = new LinkedList<FSMInstance>();
0258 
0259     // find the first node of the document
0260     Node startNode = ((Annotation)((List)annotationsByOffset.get(offsets
0261             .first())).get(0)).getStartNode();
0262 
0263     // used to calculate the percentage of processing done
0264     long lastNodeOff = doc.getContent().size().longValue();
0265 
0266     // the offset of the node where the matching currently starts
0267     // the value -1 marks no more annotations to parse
0268     long startNodeOff = startNode.getOffset().longValue();
0269 
0270     // The structure that fireRule() will update
0271     SearchState state = new SearchState(startNode, startNodeOff, 0);
0272 
0273     // the big while for the actual parsing
0274     while(state.startNodeOff != -1) {
0275       // while there are more annotations to parse
0276       // create initial active FSM instance starting parsing from new
0277       // startNode
0278       // currentFSM = FSMInstance.getNewInstance(
0279       FSMInstance firstCurrentFSM = new FSMInstance(fsm, 
0280               fsm.getInitialState(),// fresh start
0281               state.startNode,// the matching starts form the current startNode
0282               state.startNode,// current position in AG is the start position
0283               new java.util.HashMap<String, AnnotationSet>(),// no bindings yet!
0284               doc);
0285 
0286       // at this point ActiveFSMInstances should always be empty!
0287       activeFSMInstances.clear();
0288       acceptingFSMInstances.clear();
0289       activeFSMInstances.add(firstCurrentFSM);
0290 
0291       // far each active FSM Instance, try to advance
0292       // while(!finished){
0293       activeFSMWhile: while(!activeFSMInstances.isEmpty()) {
0294         if(interrupted)
0295           throw new ExecutionInterruptedException("The execution of the \""
0296                   + getName()
0297                   "\" Jape transducer has been abruptly interrupted!");
0298 
0299         // take the first active FSM instance
0300         FSMInstance currentFSM = (FSMInstance)activeFSMInstances.remove(0);
0301         // process the current FSM instance
0302 //        if(currentFSM.getFSMPosition().isFinal()) {
0303 //          // the current FSM is in a final state
0304 //          acceptingFSMInstances.add((FSMInstance)currentFSM.clone());
0305 //          // if we're only looking for the shortest stop here
0306 //          if(ruleApplicationStyle == FIRST_STYLE) break;
0307 //        }
0308 
0309         FSMMatcherResult result = attemptAdvance(currentFSM, offsets,
0310                 annotationsByOffset, doc, inputAS);
0311         if(result != null){
0312           if(result.acceptingFSMInstances != null && 
0313                   !result.acceptingFSMInstances.isEmpty()) {
0314             acceptingFSMInstances.addAll(result.acceptingFSMInstances);
0315             if(ruleApplicationStyle == FIRST_STYLE ||
0316                ruleApplicationStyle == ONCE_STYLEbreak activeFSMWhile;
0317           }
0318           
0319           if(result.activeFSMInstances != null && 
0320                   !result.activeFSMInstances.isEmpty()) {
0321             activeFSMInstances.addAll(result.activeFSMInstances);
0322           }
0323         }
0324       }
0325       boolean keepGoing = fireRule(acceptingFSMInstances, state, lastNodeOff,
0326               offsets, inputAS, outputAS, doc, annotationsByOffset);
0327       if(!keepGoingbreak;
0328       if(((DefaultActionContext)actionContext).isPhaseEnded()) {
0329         ((DefaultActionContext)actionContext).setPhaseEnded(false);
0330         System.out.println("DEBUG: Ending phase prematurely");
0331         break;
0332       }
0333     }// while(state.startNodeOff != -1)
0334     // fsmRunnerPool.shutdown();
0335 
0336     fireProcessFinished();
0337     if (Benchmark.isBenchmarkingEnabled()) {
0338       for (RuleTime r : fsm.getRuleTimes()) {
0339         String ruleName = r.getRuleName();
0340         long timeSpentOnRule = r.getTimeSpent();
0341         r.setTimeSpent(0)// Reset time to zero for next document
0342         Benchmark.checkPointWithDuration(timeSpentOnRule, Benchmark.createBenchmarkId("rule__" + ruleName, this.getBenchmarkId()), this, this.benchmarkFeatures);
0343       }
0344     }
0345   // transduce
0346   
0347   
0348 
0349   /**
0350    * Try to advance the activeFSMInstances.
0351    
0352    @return a list of newly created FSMInstances
0353    */
0354   @SuppressWarnings("unchecked")
0355   private FSMMatcherResult attemptAdvance(FSMInstance currentInstance,
0356           SimpleSortedSet offsets, SimpleSortedSet annotationsByOffset,
0357           Document document, AnnotationSet inputAS) {
0358     long startTime = 0;
0359     if (Benchmark.isBenchmarkingEnabled()) {
0360       startTime = Benchmark.startPoint();
0361     }
0362     List<FSMInstance> newActiveInstances = null;
0363     List<FSMInstance> newAcceptingInstances = null;
0364 
0365     // Attempt advancing the current instance.
0366     // While doing that, generate new active FSM instances and 
0367     // new accepting FSM instances, as required
0368 
0369     // create a clone to be used for creating new states
0370     // the actual current instance cannot be used itself, as it may change
0371     FSMInstance currentClone = (FSMInstance)currentInstance.clone();
0372     
0373     // process the current FSM instance
0374     if(currentInstance.getFSMPosition().isFinal()) {
0375       // the current FSM is in a final state
0376       if(newAcceptingInstances == null){
0377         newAcceptingInstances = new ArrayList<FSMInstance>();
0378       }
0379 //        newAcceptingInstances.add((FSMInstance)currentInstance.clone());
0380       newAcceptingInstances.add((FSMInstance)currentClone);
0381       // if we're only looking for the shortest stop here
0382       if(ruleApplicationStyle == FIRST_STYLE ||
0383          ruleApplicationStyle == ONCE_STYLE ) {
0384         if (Benchmark.isBenchmarkingEnabled()) {
0385           updateRuleTime(currentInstance, startTime);
0386         }
0387         return new FSMMatcherResult(newActiveInstances, newAcceptingInstances);
0388       }
0389     }
0390 
0391     // get all the annotations that start where the current FSM
0392     // finishes
0393     SimpleSortedSet offsetsTailSet = offsets.tailSet(currentInstance
0394             .getAGPosition().getOffset().longValue());
0395     long theFirst = offsetsTailSet.first();
0396     List<Annotation> paths = (theFirst >= ?
0397             (List)annotationsByOffset.get(theFirstnull;
0398 
0399     if(paths != null && !paths.isEmpty()) {
0400       // get the transitions for the current state of the FSM
0401       State currentState = currentClone.getFSMPosition();
0402       Iterator transitionsIter = currentState.getTransitions().iterator();
0403       
0404       // A flag used to indicate when advancing the current instance requires 
0405       // the creation of a clone (i.e. when there are more than 1 ways to advance).
0406       boolean newInstanceRequired = false;
0407   
0408       // for each transition, keep the set of annotations starting at
0409       // current node (the "paths") that match each constraint of the
0410       // transition.
0411       transitionsWhile: while(transitionsIter.hasNext()) {
0412         Transition currentTransition = (Transition)transitionsIter.next();
0413   
0414         // There will only be multiple constraints if this transition is
0415         // over
0416         // a written constraint that has the "and" operator (comma) and
0417         // the
0418         // parts referr to different annotation types. For example -
0419         // {A,B} would result in 2 constraints in the array, while
0420         // {A.foo=="val", A.bar=="val"} would only be a single
0421         // constraint.
0422         Constraint[] currentConstraints = currentTransition.getConstraints()
0423                 .getConstraints();
0424   
0425         boolean hasPositiveConstraint = false;
0426         List<Annotation>[] matchesByConstraint = new List[currentConstraints.length];
0427         for(int i = 0; i < matchesByConstraint.length; i++)
0428           matchesByConstraint[inull;
0429         // Map<Constraint, Collection<Annotation>> matchingMap =
0430         // new LinkedHashMap<Constraint, Collection<Annotation>>();
0431   
0432         // check all negated constraints first. If any annotation
0433         // matches any
0434         // negated constraint, then the transition fails.
0435         for(int i = 0; i < currentConstraints.length; i++) {
0436           // for(Constraint c : currentConstraints) {
0437           Constraint c = currentConstraints[i];
0438           if(!c.isNegated()) {
0439             hasPositiveConstraint = true;
0440             continue;
0441           }
0442           List<Annotation> matchList = c.matches(paths, ontology, inputAS);
0443           if(!matchList.isEmpty()) continue transitionsWhile;
0444         }
0445   
0446         // Now check all non-negated constraints. At least one
0447         // annotation must
0448         // match each constraint.
0449         if(hasPositiveConstraint) {
0450           for(int i = 0; i < currentConstraints.length; i++) {
0451             // for(Constraint c : currentConstraints) {
0452             Constraint c = currentConstraints[i];
0453             if(c.isNegated()) continue;
0454             List<Annotation> matchList = c.matches(paths, ontology, inputAS);
0455             // if no annotations matched, then the transition fails.
0456             if(matchList.isEmpty()) {
0457               continue transitionsWhile;
0458             }
0459             else {
0460               // matchingMap.put(c, matchList);
0461               matchesByConstraint[i= matchList;
0462             }
0463           }
0464         // end if hasPositiveConstraint
0465         else {
0466           // There are no non-negated constraints. Since the negated
0467           // constraints
0468           // did not fail, this means that all of the current
0469           // annotations
0470           // are potentially valid. Add the whole set to the
0471           // matchingMap.
0472           // Use the first negated constraint for the debug trace since
0473           // any will do.
0474           // matchingMap.put(currentConstraints[0], paths);
0475           matchesByConstraint[0= paths;
0476         }
0477   
0478         // We have a match if every positive constraint is met by at
0479         // least one annot.
0480         // Given the sets Sx of the annotations that match constraint x,
0481         // compute all tuples (A1, A2, ..., An) where Ax comes from the
0482         // set Sx and n is the number of constraints
0483         List<List<Annotation>> matchLists = new ArrayList<List<Annotation>>();
0484         for(int i = 0; i < currentConstraints.length; i++) {
0485           // for(Map.Entry<Constraint,Collection<Annotation>> entry :
0486           // matchingMap.entrySet()) {
0487           // seeing the constraint is useful when debugging
0488           @SuppressWarnings("unused")
0489           Constraint c = currentConstraints[i];
0490           // Constraint c = entry.getKey();
0491           List<Annotation> matchList = matchesByConstraint[i];
0492           // Collection<Annotation> matchList = entry.getValue();
0493           if(matchList != null) {
0494             matchLists.add(matchList);
0495           }
0496           // if (matchList instanceof List)
0497           // matchLists.add((List<Annotation>)matchList);
0498           // else
0499           // matchLists.add(new ArrayList<Annotation>(matchList));
0500         }
0501         
0502         List<List<Annotation>> combinations = combine(matchLists, matchLists
0503                 .size()new LinkedList());
0504         // Create a new FSM for every tuple of annot
0505         
0506         for(List<Annotation> tuple : combinations) {
0507           // Find longest annotation and use that to mark the start of
0508           // the
0509           // new FSM
0510           Annotation matchingAnnot = getRightMostAnnotation(tuple);
0511   
0512           // we have a match.
0513           FSMInstance newFSMI;
0514           // create a new FSMInstance, advance it over
0515           // the current
0516           // annotation take care of the bindings and add it to
0517           // ActiveFSM
0518           if(newInstanceRequired){
0519             // we need to create a clone
0520             newFSMI = (FSMInstance)currentClone.clone();
0521             //set the old AG state
0522             //set the old FSM position
0523           }else{
0524             // we're advancing the current instance
0525             newFSMI = currentInstance;
0526             // next time, we'll have to create a new one
0527             newInstanceRequired = true;
0528             //save the old FSM position
0529           }
0530           newFSMI.setAGPosition(matchingAnnot.getEndNode());
0531           newFSMI.setFSMPosition(currentTransition.getTarget());
0532     
0533           // bindings
0534           java.util.Map binds = newFSMI.getBindings();
0535           java.util.Iterator labelsIter = currentTransition.getBindings()
0536                   .iterator();
0537           String oneLabel;
0538           AnnotationSet boundAnnots, newSet;
0539           while(labelsIter.hasNext()) {
0540             oneLabel = (String)labelsIter.next();
0541             boundAnnots = (AnnotationSet)binds.get(oneLabel);
0542             if(boundAnnots != null)
0543               newSet = new AnnotationSetImpl(boundAnnots);
0544             else newSet = new AnnotationSetImpl(document);
0545   
0546             for(Annotation annot : tuple) {
0547               newSet.add(annot);
0548             }
0549   
0550             binds.put(oneLabel, newSet);
0551           }// while(labelsIter.hasNext())
0552           if(newActiveInstances == null) {
0553             newActiveInstances = new ArrayList<FSMInstance>();
0554           }
0555           newActiveInstances.add(newFSMI);
0556         // iter over matching combinations
0557       }// while(transitionsIter.hasNext())
0558     }
0559     if (Benchmark.isBenchmarkingEnabled()) {
0560       updateRuleTime(currentInstance, startTime);
0561     }
0562     return new FSMMatcherResult(newActiveInstances, newAcceptingInstances);
0563   }
0564 
0565   /**
0566    * Increment the time spent by the rule associated with the FSM
0567    @param currentInstance  The FSMInstance which has been running since startTime
0568    @param startTime    The time that the FSMInstance started running
0569    */
0570   private void updateRuleTime(FSMInstance currentInstance, long startTime) {
0571     int index = currentInstance.getFSMPosition().getIndexInRuleList();
0572     currentInstance.getSupportGraph().getRuleTimes().get(index).addTime(Benchmark.startPoint() - startTime);
0573   }
0574 
0575   /**
0576    * Return the annotation with the right-most end node
0577    @param annots
0578    @return
0579    */
0580   protected Annotation getRightMostAnnotation(Collection<Annotation> annots) {
0581     long maxOffset = -1;
0582     Annotation retVal = null;
0583     for(Annotation annot : annots) {
0584       Long curOffset = annot.getEndNode().getOffset();
0585       if(curOffset > maxOffset) {
0586         maxOffset = curOffset;
0587         retVal = annot;
0588       }
0589     }
0590 
0591     return retVal;
0592   }
0593 
0594   /**
0595    * Computes all tuples (x1, x2, ..., xn) resulting from the linear
0596    * combination of the elements of n lists, where x1 comes from the 1st
0597    * list, x2 comes from the second, etc. This method works recursively.
0598    * The first call should have those parameters:
0599    *
0600    @param sourceLists an array of n lists whose elements will be
0601    *          combined
0602    @param maxTupleSize the number of elements per tuple
0603    @param incompleteTuple an empty list
0604    */
0605 
0606   private static List<List<Annotation>> combine(List<List<Annotation>> sourceLists,
0607           int maxTupleSize, List<Annotation> incompleteTuple) {
0608 
0609     List<List<Annotation>> newTupleList = new LinkedList<List<Annotation>>();
0610 
0611     if(incompleteTuple.size() == maxTupleSize) {
0612       newTupleList.add(incompleteTuple);
0613     }
0614     else {
0615       List<Annotation> currentSourceList = sourceLists.get(incompleteTuple.size());
0616       // use for loop instead of ListIterator to increase speed
0617       // (critical here)
0618       for(int i = 0; i < currentSourceList.size(); i++) {
0619         List<Annotation> augmentedTuple = (List<Annotation>)((LinkedList<Annotation>)incompleteTuple).clone();
0620         augmentedTuple.add(currentSourceList.get(i));
0621         newTupleList.addAll(combine(sourceLists, maxTupleSize, augmentedTuple));
0622       }
0623     }
0624 
0625     return newTupleList;
0626   }
0627 
0628   /**
0629    * Fire the rule that matched.
0630    *
0631    @return true if processing should keep going, false otherwise.
0632    */
0633 
0634   protected boolean fireRule(List<FSMInstance> acceptingFSMInstances,
0635           SearchState state, long lastNodeOff, SimpleSortedSet offsets,
0636           AnnotationSet inputAS, AnnotationSet outputAS, Document doc,
0637           SimpleSortedSet annotationsByOffsetthrows JapeException,
0638           ExecutionException {
0639 
0640     Node startNode = state.startNode;
0641     long startNodeOff = state.startNodeOff;
0642     long oldStartNodeOff = state.oldStartNodeOff;
0643 
0644     // FIRE THE RULE
0645     long lastAGPosition = -1;
0646     if(acceptingFSMInstances.isEmpty()) {
0647       // no rule to fire, advance to the next input offset
0648       lastAGPosition = startNodeOff + 1;
0649     }
0650     else if(ruleApplicationStyle == BRILL_STYLE
0651             || ruleApplicationStyle == ALL_STYLE) {
0652       // fire the rules corresponding to all accepting FSM instances
0653       Iterator<FSMInstance> accFSMIter = acceptingFSMInstances.iterator();
0654       FSMInstance currentAcceptor;
0655       RightHandSide currentRHS;
0656       lastAGPosition = startNode.getOffset().longValue();
0657 
0658       while(accFSMIter.hasNext()) {
0659         currentAcceptor = accFSMIter.next();
0660         currentRHS = currentAcceptor.getFSMPosition().getAction();
0661 
0662         currentRHS.transduce(doc, currentAcceptor.getBindings(), inputAS,
0663                 outputAS, ontology, actionContext);
0664 
0665         if(ruleApplicationStyle == BRILL_STYLE) {
0666           // find the maximal next position
0667           long currentAGPosition = currentAcceptor.getAGPosition().getOffset()
0668                   .longValue();
0669           if(currentAGPosition > lastAGPosition)
0670             lastAGPosition = currentAGPosition;
0671         }
0672       }
0673       if(ruleApplicationStyle == ALL_STYLE) {
0674         // simply advance to next offset
0675         lastAGPosition = lastAGPosition + 1;
0676       }
0677 
0678     }
0679     else if(ruleApplicationStyle == APPELT_STYLE
0680             || ruleApplicationStyle == FIRST_STYLE
0681             || ruleApplicationStyle == ONCE_STYLE) {
0682 
0683       // AcceptingFSMInstances is an ordered structure:
0684       // just execute the longest (last) rule
0685       Collections.sort(acceptingFSMInstances, Collections.reverseOrder());
0686       Iterator<FSMInstance> accFSMIter = acceptingFSMInstances.iterator();
0687       FSMInstance currentAcceptor = accFSMIter.next();
0688       if(isDebugMode()) {
0689         // see if we have any conflicts
0690         Iterator<FSMInstance> accIter = acceptingFSMInstances.iterator();
0691         FSMInstance anAcceptor;
0692         List<FSMInstance> conflicts = new ArrayList<FSMInstance>();
0693         while(accIter.hasNext()) {
0694           anAcceptor = accIter.next();
0695           if(anAcceptor.equals(currentAcceptor)) {
0696             conflicts.add(anAcceptor);
0697           }
0698           else {
0699             break;
0700           }
0701         }
0702         if(conflicts.size() 1) {
0703           log.info("Conflicts found during matching:"
0704                   "\n================================");
0705           accIter = conflicts.iterator();
0706           int i = 0;
0707           while(accIter.hasNext()) {
0708             if (log.isInfoEnabled())
0709               log.info(i++ + ") " + accIter.next().toString());
0710           }
0711         }
0712       }
0713       RightHandSide currentRHS = currentAcceptor.getFSMPosition().getAction();
0714 
0715       currentRHS.transduce(doc, currentAcceptor.getBindings(), inputAS,
0716               outputAS, ontology, actionContext);
0717 
0718       // if in matchGroup mode check other possible patterns in this
0719       // span
0720       if(isMatchGroupMode()) {
0721         // log.debug("Jape grammar in MULTI application style.");
0722         // ~bp: check for other matching fsm instances with same length,
0723         // priority and rule index : if such execute them also.
0724         String currentAcceptorString = null;
0725         multiModeWhile: while(accFSMIter.hasNext()) {
0726           FSMInstance rivalAcceptor = accFSMIter.next();
0727           // get rivals that match the same document segment
0728           // makes use of the semantic difference between the compareTo
0729           // and equals methods on FSMInstance
0730           if(rivalAcceptor.compareTo(currentAcceptor== 0) {
0731             // gets the rivals that are NOT COMPLETELY IDENTICAL with
0732             // the current acceptor.
0733             if(!rivalAcceptor.equals(currentAcceptor)) {
0734               //depends on the debug option in the transducer
0735               if(isDebugMode()) {
0736                 if(currentAcceptorString == null) {
0737                   // first rival
0738                   currentAcceptorString = currentAcceptor.toString();
0739                   if (log.isInfoEnabled()) {
0740                     log.info("~Jape Grammar Transducer : "
0741                             "\nConcurrent Patterns by length,priority and index (all transduced):");
0742                     log.info(currentAcceptorString);
0743                     log.info("bindings : " + currentAcceptor.getBindings());
0744                     log.info("Rivals Follow: ");
0745                   }
0746                 }
0747                 if (log.isInfoEnabled()) {
0748                   log.info(rivalAcceptor);
0749                   log.info("bindings : " + rivalAcceptor.getBindings());
0750                 }
0751               }// DEBUG
0752               currentRHS = rivalAcceptor.getFSMPosition().getAction();
0753 
0754               currentRHS.transduce(doc, rivalAcceptor.getBindings(), inputAS,
0755                       outputAS, ontology, actionContext);
0756 
0757             // equal rival
0758           }
0759           else {
0760             // if rival is not equal this means that there are no
0761             // further
0762             // equal rivals (since the list is sorted)
0763             break multiModeWhile;
0764           }
0765         // while there are fsm instances
0766       // matchGroupMode
0767 
0768       // if in ONCE mode stop after first match
0769       if(ruleApplicationStyle == ONCE_STYLE) {
0770         state.startNodeOff = startNodeOff;
0771         return false;
0772       }
0773 
0774       // advance in AG
0775       lastAGPosition = currentAcceptor.getAGPosition().getOffset().longValue();
0776     }
0777     else throw new RuntimeException("Unknown rule application style!");
0778 
0779     // advance on input
0780     SimpleSortedSet offsetsTailSet = offsets.tailSet(lastAGPosition);
0781     long theFirst = offsetsTailSet.first();
0782     if(theFirst < 0) {
0783       // no more input, phew! :)
0784       startNodeOff = -1;
0785       fireProcessFinished();
0786     else {
0787       long nextKey = theFirst;
0788       startNode = ((Annotation)((List)annotationsByOffset.get(nextKey))
0789               .get(0))// nextKey
0790               getStartNode();
0791       startNodeOff = startNode.getOffset().longValue();
0792 
0793       // eliminate the possibility for infinite looping
0794       if(oldStartNodeOff == startNodeOff) {
0795         // we are about to step twice in the same place, ...skip ahead
0796         lastAGPosition = startNodeOff + 1;
0797         offsetsTailSet = offsets.tailSet(lastAGPosition);
0798         theFirst = offsetsTailSet.first();
0799         if(theFirst < 0) {
0800           // no more input, phew! :)
0801           startNodeOff = -1;
0802           fireProcessFinished();
0803         }
0804         else {
0805           nextKey = theFirst;
0806           startNode = ((Annotation)((List)annotationsByOffset.get(theFirst))
0807                   .get(0)).getStartNode();
0808           startNodeOff = startNode.getOffset().longValue();
0809         }
0810       }// if(oldStartNodeOff == startNodeOff)
0811       // fire the progress event
0812       if(startNodeOff - oldStartNodeOff > 256) {
0813         if(isInterrupted())
0814           throw new ExecutionInterruptedException("The execution of the \""
0815                   + getName()
0816                   "\" Jape transducer has been abruptly interrupted!");
0817 
0818         fireProgressChanged((int)(100 * startNodeOff / lastNodeOff));
0819         oldStartNodeOff = startNodeOff;
0820       }
0821     }
0822     // by Shafirin Andrey start (according to Vladimir Karasev)
0823     // if(gate.Gate.isEnableJapeDebug()) {
0824     // if (null != phaseController) {
0825     // phaseController.TraceTransit(rulesTrace);
0826     // }
0827     // }
0828     // by Shafirin Andrey end
0829 
0830     state.oldStartNodeOff = oldStartNodeOff;
0831     state.startNodeOff = startNodeOff;
0832     state.startNode = startNode;
0833     return true;
0834   // fireRule
0835 
0836   /** Clean up (delete action class files, for e.g.). */
0837   public void cleanUp() {
0838     // for(DListIterator i = rules.begin(); ! i.atEnd(); i.advance())
0839     // ((Rule) i.get()).cleanUp();
0840   // cleanUp
0841 
0842   /** A string representation of this object. */
0843   public String toString() {
0844     return toString("");
0845   // toString()
0846 
0847   /** A string representation of this object. */
0848   public String toString(String pad) {
0849     String newline = Strings.getNl();
0850     String newPad = Strings.addPadding(pad, INDENT_PADDING);
0851 
0852     StringBuffer buf = new StringBuffer(pad + "SPT: name(" + name
0853             "); ruleApplicationStyle(");
0854 
0855     switch(ruleApplicationStyle) {
0856       case APPELT_STYLE:
0857         buf.append("APPELT_STYLE); ");
0858         break;
0859       case BRILL_STYLE:
0860         buf.append("BRILL_STYLE); ");
0861         break;
0862       default:
0863         break;
0864     }
0865 
0866     buf.append("rules(" + newline);
0867     if(rules != null) {
0868       Iterator rulesIterator = rules.iterator();
0869         while(rulesIterator.hasNext())
0870           buf.append(((Rule)rulesIterator.next()).toString(newPad" ");
0871     }
0872     buf.append(newline + pad + ")." + newline);
0873 
0874     return buf.toString();
0875   // toString(pad)
0876 
0877   // needed by fsm
0878   public PrioritisedRuleList getRules() {
0879     return rules;
0880   }
0881 
0882   /**
0883    * Adds a new type of input annotations used by this transducer. If
0884    * the list of input types is empty this transducer will parse all the
0885    * annotations in the document otherwise the types not found in the
0886    * input list will be completely ignored! To be used with caution!
0887    */
0888   public void addInput(String ident) {
0889     input.add(ident);
0890   }
0891 
0892   /**
0893    * Checks if this Phase has the annotation type as input. This is the
0894    * case if either no input annotation types were specified, in which case
0895    * all annotation types will be used, or if the annotation type was
0896    * specified.
0897    *
0898    @param ident the type of an annotation to be checked
0899    @return true if the annotation type will be used in this phase
0900    */
0901   public boolean hasInput(String ident) {
0902     return input.isEmpty() || input.contains(ident);
0903   }
0904 
0905   /**
0906    * Check if there is a restriction on the input annotation types
0907    * for this SPT, i.e. if there were annotation types specified for
0908    * the "Input:" declaration of this phase.
0909    *
0910    @return true if only certain annotation types are considered in this
0911    *   phase, false if all are considered.
0912    */
0913   public boolean isInputRestricted() {
0914     return !input.isEmpty();
0915   }
0916 
0917   public synchronized void removeProgressListener(ProgressListener l) {
0918     if(progressListeners != null && progressListeners.contains(l)) {
0919       Vector v = (Vector)progressListeners.clone();
0920       v.removeElement(l);
0921       progressListeners = v;
0922     }
0923   }
0924 
0925   public synchronized void addProgressListener(ProgressListener l) {
0926     Vector v = progressListeners == null
0927             new Vector(2)
0928             (Vector)progressListeners.clone();
0929     if(!v.contains(l)) {
0930       v.addElement(l);
0931       progressListeners = v;
0932     }
0933   }
0934 
0935   /**
0936    * Defines the types of input annotations that this transducer reads.
0937    * If this set is empty the transducer will read all the annotations
0938    * otherwise it will only "see" the annotations of types found in this
0939    * list ignoring all other types of annotations.
0940    */
0941   // by Shafirin Andrey start (modifier changed to public)
0942   public java.util.Set input = new java.util.HashSet();
0943 
0944   // java.util.Set input = new java.util.HashSet();
0945   // by Shafirin Andrey end
0946   private transient Vector progressListeners;
0947 
0948   protected void fireProgressChanged(int e) {
0949     if(progressListeners != null) {
0950       Vector listeners = progressListeners;
0951       int count = listeners.size();
0952       for(int i = 0; i < count; i++) {
0953         ((ProgressListener)listeners.elementAt(i)).progressChanged(e);
0954       }
0955     }
0956   }
0957 
0958   protected void fireProcessFinished() {
0959     if(progressListeners != null) {
0960       Vector listeners = progressListeners;
0961       int count = listeners.size();
0962       for(int i = 0; i < count; i++) {
0963         ((ProgressListener)listeners.elementAt(i)).processFinished();
0964       }
0965     }
0966   }
0967 
0968   public int getRuleApplicationStyle() {
0969     return ruleApplicationStyle;
0970   }
0971 
0972   private transient SourceInfo sourceInfo = null;
0973   private String controllerStartedEventBlock = "";
0974   private String controllerFinishedEventBlock = "";
0975   private String controllerAbortedEventBlock = "";
0976   private String javaImportsBlock = "";
0977   private Object controllerEventBlocksActionClass = null;
0978   private String controllerEventBlocksActionClassName;
0979   private static final String nl = Strings.getNl();
0980   private static final String controllerEventBlocksActionClassSourceTemplate =
0981     "package japeactionclasses;"+nl+
0982     "import java.io.*;"+nl+
0983     "import java.util.*;"+nl+
0984     "import gate.*;"+nl+
0985     "import gate.jape.*;"+nl+
0986     "import gate.creole.ontology.*;"+nl+
0987     "import gate.annotation.*;"+nl+
0988     "import gate.util.*;"+nl+
0989     "%%javaimports%%"+nl+nl+
0990     "public class %%classname%% implements ControllerEventBlocksAction {"+nl+
0991     "  private Ontology ontology;"+nl+
0992     "  public void setOntology(Ontology o) { ontology = o; }"+nl+
0993     "  public Ontology getOntology() { return ontology; }"+nl+
0994     "  private ActionContext ctx;"+nl+
0995     "  public void setActionContext(ActionContext ac) { ctx = ac; }"+nl+
0996     "  public ActionContext getActionContext() { return ctx; }"+nl+
0997     "  private Controller controller;"+nl+
0998     "  public void setController(Controller c) { controller = c; }"+nl+
0999     "  public Controller getController() { return controller; }"+nl+
1000     "  private Corpus corpus;"+nl+
1001     "  public void setCorpus(Corpus c) { corpus = c; }"+nl+
1002     "  public Corpus getCorpus() { return corpus; }"+nl+
1003     "  private Throwable throwable;"+nl+
1004     "  public void setThrowable(Throwable t) { throwable = t; }"+nl+
1005     "  public Throwable getThrowable() { return throwable; }"+nl+
1006     "  public void controllerExecutionStarted() {"+nl+
1007     "    %%started%%"+nl+
1008     "  }"+nl+
1009     "  public void controllerExecutionFinished() {"+nl+
1010     "    %%finished%%"+nl+
1011     "  }"+nl+
1012     "  public void controllerExecutionAborted() {"+nl+
1013     "    %%aborted%%"+nl+
1014     "  }"+nl+
1015     "}"+nl+
1016     ""+nl;
1017   
1018   public void setControllerEventBlocks(
1019     String started,
1020     String finished,
1021     String aborted,
1022     String javaimports) {
1023     controllerStartedEventBlock = started;
1024     controllerFinishedEventBlock = finished;
1025     controllerAbortedEventBlock = aborted;
1026     javaImportsBlock = javaimports;
1027   }
1028   public String generateControllerEventBlocksCode(
1029     String started,
1030     String finished,
1031     String aborted,
1032     String javaimports) {
1033     String sourceCode = null;
1034     // if any of the three blocks is not null, set the corpusBlockActionClassSource
1035     // to the final source code of the class
1036     if(started != null || finished != null || aborted != null) {
1037             
1038       sourceCode =
1039         controllerEventBlocksActionClassSourceTemplate;
1040 
1041       boolean neednewclassname = true;
1042       String  ceb_classname = "";
1043       while(neednewclassname) {
1044         ceb_classname =  "ControllerEventBlocksActionClass" +
1045           actionClassNumber.getAndIncrement();
1046         controllerEventBlocksActionClassName = "japeactionclasses." + ceb_classname;
1047         try {
1048           Gate.getClassLoader().loadClass(controllerEventBlocksActionClassName);
1049           neednewclassname = true;
1050         catch (ClassNotFoundException e) {
1051           neednewclassname = false;
1052         }
1053       }
1054       sourceCode =
1055         sourceCode.replace("%%classname%%",ceb_classname);
1056       
1057       sourceInfo = new SourceInfo(controllerEventBlocksActionClassName,name,"controllerEvents");
1058       
1059       sourceCode =
1060         sourceCode.replace("%%javaimports%%",
1061           javaimports != null ? javaimports : "// no 'Imports:' block for more imports defined");
1062       
1063       int index = sourceCode.indexOf("%%started%%");
1064       String previousCode = sourceCode.substring(0, index).trim();
1065       sourceCode =
1066         sourceCode.replace("%%started%%",
1067           started != null ? sourceInfo.addBlock(previousCode, started"// no code defined");
1068       
1069       index = sourceCode.indexOf("%%finished%%");
1070       previousCode = sourceCode.substring(0, index).trim();
1071       sourceCode =
1072         sourceCode.replace("%%finished%%",
1073           finished != null ? sourceInfo.addBlock(previousCode, finished"// no code defined");
1074       
1075       index = sourceCode.indexOf("%%aborted%%");
1076       previousCode = sourceCode.substring(0, index).trim();
1077       sourceCode =
1078         sourceCode.replace("%%aborted%%",
1079           aborted != null ? sourceInfo.addBlock(previousCode, aborted"// no code defined");
1080     }
1081     return sourceCode;
1082   }
1083 
1084   @Override
1085   public void runControllerExecutionStartedBlock(
1086     ActionContext ac, Controller c, Ontology othrows ExecutionException {
1087     if(controllerEventBlocksActionClass != null) {
1088       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1089         setController(c);
1090       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1091         setOntology(o);
1092       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1093         setActionContext(ac);
1094       if(instanceof CorpusController) {
1095         Corpus corpus = ((CorpusController)c).getCorpus();
1096          ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1097           setCorpus(corpus);
1098       else {
1099         ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1100           setCorpus(null);
1101       }
1102       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1103         setThrowable(null);
1104       
1105       try {
1106         ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1107           controllerExecutionStarted();
1108       }
1109       catch (Throwable e) {
1110       // if the action class throws an exception, re-throw it with a
1111       // full description of the problem, inc. stack trace and the RHS
1112       // action class code
1113       if (sourceInfo != nullsourceInfo.enhanceTheThrowable(e);
1114       
1115       if(instanceof Error) {
1116         throw (Error)e;
1117       }   
1118       if(instanceof RuntimeException) {
1119         throw (RuntimeException)e;
1120       }
1121       
1122       // shouldn't happen...
1123       throw new ExecutionException(
1124           "Couldn't run controller started action", e);
1125       }
1126     }
1127   }
1128 
1129   @Override
1130   public void runControllerExecutionFinishedBlock(
1131     ActionContext ac, Controller c, Ontology othrows ExecutionException {
1132     if(controllerEventBlocksActionClass != null) {
1133       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1134         setController(c);
1135       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1136         setOntology(o);
1137       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1138         setActionContext(ac);
1139       if(instanceof CorpusController) {
1140         Corpus corpus = ((CorpusController)c).getCorpus();
1141          ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1142           setCorpus(corpus);
1143       else {
1144         ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1145           setCorpus(null);
1146       }
1147       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1148         setThrowable(null);
1149       
1150       try {
1151         ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1152           controllerExecutionFinished();
1153       }
1154       catch (Throwable e) {
1155       // if the action class throws an exception, re-throw it with a
1156       // full description of the problem, inc. stack trace and the RHS
1157       // action class code
1158       if (sourceInfo != nullsourceInfo.enhanceTheThrowable(e);
1159       
1160       if(instanceof Error) {
1161         throw (Error)e;
1162       }   
1163       if(instanceof RuntimeException) {
1164         throw (RuntimeException)e;
1165       }
1166       
1167       // shouldn't happen...
1168       throw new ExecutionException(
1169           "Couldn't run controller finished action", e);
1170       }
1171     }
1172   }
1173 
1174   @Override
1175   public void runControllerExecutionAbortedBlock(
1176     ActionContext ac, Controller c, Throwable t, Ontology othrows ExecutionException {
1177     if(controllerEventBlocksActionClass != null) {
1178       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1179         setController(c);
1180       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1181         setOntology(o);
1182       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1183         setActionContext(ac);
1184       if(instanceof CorpusController) {
1185         Corpus corpus = ((CorpusController)c).getCorpus();
1186          ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1187           setCorpus(corpus);
1188       else {
1189         ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1190           setCorpus(null);
1191       }
1192       ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1193         setThrowable(t);
1194       
1195       try {
1196         ((ControllerEventBlocksActioncontrollerEventBlocksActionClass).
1197           controllerExecutionAborted();
1198       }
1199       catch (Throwable e) {
1200       // if the action class throws an exception, re-throw it with a
1201       // full description of the problem, inc. stack trace and the RHS
1202       // action class code
1203       if (sourceInfo != nullsourceInfo.enhanceTheThrowable(e);
1204       
1205       if(instanceof Error) {
1206         throw (Error)e;
1207       }   
1208       if(instanceof RuntimeException) {
1209         throw (RuntimeException)e;
1210       }
1211       
1212       // shouldn't happen...
1213       throw new ExecutionException(
1214           "Couldn't run controller aborted action", e);
1215       }
1216     }
1217   }
1218 
1219   private void writeObject(java.io.ObjectOutputStream out)
1220   throws IOException{
1221     Object save = controllerEventBlocksActionClass;
1222     controllerEventBlocksActionClass = null;
1223     out.defaultWriteObject();
1224     controllerEventBlocksActionClass = save;
1225   }
1226 
1227   private void readObject(java.io.ObjectInputStream in)
1228   throws IOException, ClassNotFoundException{
1229     in.defaultReadObject();
1230     compileEventBlocksActionClass();
1231   }
1232 
1233   private void compileEventBlocksActionClass() {
1234     String sourceCode = generateControllerEventBlocksCode(
1235       controllerStartedEventBlock, controllerFinishedEventBlock,
1236       controllerAbortedEventBlock, javaImportsBlock);
1237     if(sourceCode != null) {
1238       Map<String,String> actionClasses = new HashMap<String,String>(1);
1239       actionClasses.put(controllerEventBlocksActionClassName,
1240               sourceCode);
1241       try {
1242           gate.util.Javac.loadClasses(actionClasses);
1243           controllerEventBlocksActionClass =
1244             Gate.getClassLoader().
1245               loadClass(controllerEventBlocksActionClassName).newInstance();
1246         }catch(Exception e1){
1247           throw new GateRuntimeException (e1);
1248         }
1249     }
1250   }
1251 
1252   /**
1253    * This returns any compiled controller event blocks action class that
1254    * may exist at the time of calling or null. This is mainly needed for
1255    * alternate implementations of JAPE that are based on the core JAPE
1256    * classes and want to support controller event blocks too.
1257    *
1258    @return an object that represents the compiled event blocks or null
1259    */
1260   public ControllerEventBlocksAction getControllerEventBlocksActionClass() {
1261     return (ControllerEventBlocksAction)controllerEventBlocksActionClass;
1262   }
1263   
1264   /*
1265    * private void writeObject(ObjectOutputStream oos) throws IOException {
1266    * Out.prln("writing spt"); oos.defaultWriteObject();
1267    * Out.prln("finished writing spt"); } // writeObject
1268    */
1269 
1270 // class SinglePhaseTransducer
1271 
1272 /*
1273  * class SimpleSortedSet {
1274  *
1275  * static final int INCREMENT = 1023; int[] theArray = new
1276  * int[INCREMENT]; Object[] theObject = new Object[INCREMENT]; int
1277  * tsindex = 0; int size = 0; public static int avesize = 0; public
1278  * static int maxsize = 0; public static int avecount = 0; public
1279  * SimpleSortedSet() { avecount++; java.util.Arrays.fill(theArray,
1280  * Integer.MAX_VALUE); }
1281  *
1282  * public Object get(int elValue) { int index =
1283  * java.util.Arrays.binarySearch(theArray, elValue); if (index >=0)
1284  * return theObject[index]; return null; }
1285  *
1286  * public boolean add(int elValue, Object o) { int index =
1287  * java.util.Arrays.binarySearch(theArray, elValue); if (index >=0) {
1288  * ((ArrayList)theObject[index]).add(o); return false; } if (size ==
1289  * theArray.length) { int[] temp = new int[theArray.length + INCREMENT];
1290  * Object[] tempO = new Object[theArray.length + INCREMENT];
1291  * System.arraycopy(theArray, 0, temp, 0, theArray.length);
1292  * System.arraycopy(theObject, 0, tempO, 0, theArray.length);
1293  * java.util.Arrays.fill(temp, theArray.length, temp.length ,
1294  * Integer.MAX_VALUE); theArray = temp; theObject = tempO; } index =
1295  * ~index; System.arraycopy(theArray, index, theArray, index+1, size -
1296  * index ); System.arraycopy(theObject, index, theObject, index+1, size -
1297  * index ); theArray[index] = elValue; theObject[index] = new
1298  * ArrayList(); ((ArrayList)theObject[index]).add(o); size++; return
1299  * true; } public int first() { if (tsindex >= size) return -1; return
1300  * theArray[tsindex]; }
1301  *
1302  * public Object getFirst() { if (tsindex >= size) return null; return
1303  * theObject[tsindex]; }
1304  *
1305  * public SimpleSortedSet tailSet(int elValue) { if (tsindex <
1306  * theArray.length && elValue != theArray[tsindex]) { if (tsindex<(size-1) &&
1307  * elValue > theArray[tsindex] && elValue <= theArray[tsindex+1]) {
1308  * tsindex++; return this; } int index =
1309  * java.util.Arrays.binarySearch(theArray, elValue); if (index < 0)
1310  * index = ~index; tsindex = index; } return this; }
1311  *
1312  * public boolean isEmpty() { return size ==0; } };
1313  */