All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gate.jape.SinglePhaseTransducer Maven / Gradle / Ivy

Go to download

ANNIE is a general purpose information extraction system that provides the building blocks of many other GATE applications.

The newest version!
/*
 *  SinglePhaseTransducer.java - transducer class
 *
 *  Copyright (c) 1995-2012, The University of Sheffield. See the file
 *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
 *
 *  This file is part of GATE (see http://gate.ac.uk/), and is free
 *  software, licenced under the GNU Library General Public License,
 *  Version 2, June 1991 (in the distribution as file licence.html,
 *  and also available at http://gate.ac.uk/gate/licence.html).
 *
 *  Hamish Cunningham, 24/07/98
 *
 *  $Id: SinglePhaseTransducer.java 17916 2014-05-05 10:07:28Z markagreenwood $
 */

package gate.jape;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import gate.Annotation;
import gate.AnnotationSet;
import gate.Controller;
import gate.Corpus;
import gate.CorpusController;
import gate.Document;
import gate.Gate;
import gate.Node;
import gate.annotation.AnnotationSetImpl;
import gate.creole.ExecutionException;
import gate.creole.ExecutionInterruptedException;
import gate.creole.ontology.Ontology;
import gate.event.ProgressListener;
import gate.fsm.FSM;
import gate.fsm.FSMInstance;
import gate.fsm.RuleTime;
import gate.fsm.State;
import gate.fsm.Transition;
import gate.util.Benchmark;
import gate.util.GateClassLoader;
import gate.util.GateRuntimeException;
import gate.util.SimpleSortedSet;
import gate.util.Strings;

/**
 * Represents a complete CPSL grammar, with a phase name, options and
 * rule set (accessible by name and by sequence). Implements a transduce
 * method taking a Document as input. Constructs from String or File.
 */
public class SinglePhaseTransducer extends Transducer implements JapeConstants,
                                                     Serializable {

  private static final long serialVersionUID = -2749474684496896114L;

  protected static final Logger log = LoggerFactory
          .getLogger(SinglePhaseTransducer.class);

  private static AtomicInteger actionClassNumber = new AtomicInteger();

  /*
   * A structure to pass information to/from the fireRule() method.
   * Since Java won't let us return multiple values, we stuff them into
   * a 'state' object that fireRule() can update.
   */
  protected static class SearchState {
    Node startNode;

    long startNodeOff;

    long oldStartNodeOff;

    SearchState(Node startNode, long startNodeOff, long oldStartNodeOff) {
      this.startNode = startNode;
      this.startNodeOff = startNodeOff;
      this.oldStartNodeOff = oldStartNodeOff;
    }
  }
  
  private static final class FSMMatcherResult{
    /**
     * @param activeFSMInstances
     * @param acceptingFSMInstances
     */
    public FSMMatcherResult(List activeFSMInstances,
            List acceptingFSMInstances) {
      this.activeFSMInstances = activeFSMInstances;
      this.acceptingFSMInstances = acceptingFSMInstances;
    }
    
    private List acceptingFSMInstances;
    private List activeFSMInstances;
    
  }

  /** Construction from name. */
  public SinglePhaseTransducer(String name) {
    this.name = name;
    rules = new PrioritisedRuleList();
    finishedAlready = false;
  } // Construction from name

  /** Type of rule application (constants defined in JapeConstants). */
  protected int ruleApplicationStyle = BRILL_STYLE;

  /** Set the type of rule application (types defined in JapeConstants). */
  public void setRuleApplicationStyle(int style) {
    ruleApplicationStyle = style;
  }

  /**
   * The list of rules in this transducer. Ordered by priority and
   * addition sequence (which will be file position if they come from a
   * file).
   */
  protected PrioritisedRuleList rules;

  protected FSM fsm;

  /**
   * A list of FSM instances that haven't blocked yet, used during matching.
   */
  protected List activeFSMInstances;
  
  public FSM getFSM() {
    return fsm;
  }

  /** Add a rule. */
  public void addRule(Rule rule) {
    rules.add(rule);
  } // addRule

  /** The values of any option settings given. */
  private Map optionSettings = new HashMap();

  /**
   * Add an option setting. If this option is set already, the new value
   * overwrites the previous one.
   */
  public void setOption(String name, String setting) {
    optionSettings.put(name, setting);
  } // setOption

  /** Get the value for a particular option. */
  public String getOption(String name) {
    return optionSettings.get(name);
  } // getOption

  /** Whether the finish method has been called or not. */
  protected boolean finishedAlready;

  /**
   * Finish: replace dynamic data structures with Java arrays; called
   * after parsing.
   */
  @Override
  public void finish(GateClassLoader classLoader) {
    // both MPT and SPT have finish called on them by the parser...
    if(finishedAlready) return;
    finishedAlready = true;

    // each rule has a RHS which has a string for java code
    // those strings need to be compiled now
    
    // TODO: (JP) if we have some binary JAPE grammars loaded and then we
    // compile additional action classes here, we can potentially
    // get duplicate class names.
    // Instead, we should modify and increment the classname until
    // we find one that is not already taken.
    Map actionClasses = new HashMap(rules.size());
    for(Iterator i = rules.iterator(); i.hasNext();) {
      Rule rule = i.next();
      rule.finish(classLoader);
      actionClasses.put(rule.getRHS().getActionClassName(), rule.getRHS()
              .getActionClassString());
    }
    try {
      gate.util.Javac.loadClasses(actionClasses, classLoader);
    }
    catch(Exception e) {
      throw new GateRuntimeException(e);
    }
    compileEventBlocksActionClass(classLoader);

    // build the finite state machine transition graph
    fsm = createFSM();
    // clear the old style data structures
    rules.clear();
    rules = null;
  } // finish

  protected FSM createFSM() {
    return new FSM(this);
  }

  // dam: was
  // private void addAnnotationsByOffset(Map map, SortedSet keys, Set
  // annotations){
  private void addAnnotationsByOffset(/* Map map, */SimpleSortedSet keys,
          Set annotations) {
    Iterator annIter = annotations.iterator();
    while(annIter.hasNext()) {
      Annotation ann = annIter.next();
      // ignore empty annotations
      long offset = ann.getStartNode().getOffset().longValue();
      if(offset == ann.getEndNode().getOffset().longValue()) continue;
      // dam: was
      /*
       * // Long offset = ann.getStartNode().getOffset();
       *
       * List annsAtThisOffset = null; if(keys.add(offset)){
       * annsAtThisOffset = new LinkedList(); map.put(offset,
       * annsAtThisOffset); }else{ annsAtThisOffset =
       * (List)map.get(offset); } annsAtThisOffset.add(ann);
       */
      // dam: end
      keys.add(offset, ann);
    }
  }// private void addAnnotationsByOffset()

  /**
   * Transduce a document using the annotation set provided and the
   * current rule application style.
   */
  @Override
  public void transduce(Document doc, AnnotationSet inputAS,
          AnnotationSet outputAS) throws JapeException, ExecutionException {
    interrupted = false;
    log.debug("Start: " + name);
    fireProgressChanged(0);

    // the input annotations will be read from this map
    // maps offset to list of annotations
    SimpleSortedSet offsets = new SimpleSortedSet();
    SimpleSortedSet annotationsByOffset = offsets;

    // select only the annotations of types specified in the input list
    if(input.isEmpty()) {
      addAnnotationsByOffset(offsets, inputAS);
    }
    else {
      Iterator typesIter = input.iterator();
      AnnotationSet ofOneType = null;
      while(typesIter.hasNext()) {
        ofOneType = inputAS.get(typesIter.next());
        if(ofOneType != null) {
          addAnnotationsByOffset(offsets, ofOneType);
        }
      }
    }

    if(annotationsByOffset.isEmpty()) {
      fireProcessFinished();
      return;
    }

    annotationsByOffset.sort();
    // define data structures
    // FSM instances that haven't blocked yet
    if(activeFSMInstances == null) {
      activeFSMInstances = new LinkedList();
    }
    else {
      activeFSMInstances.clear();
    }

    // FSM instances that have reached a final state
    // This is a list and the contained objects are sorted by the length
    // of the document content covered by the matched annotations
    List acceptingFSMInstances = new LinkedList();

    // find the first node of the document
    @SuppressWarnings("unchecked")
    Node startNode = ((List)annotationsByOffset.get(offsets
            .first())).get(0).getStartNode();

    // used to calculate the percentage of processing done
    long lastNodeOff = doc.getContent().size().longValue();

    // the offset of the node where the matching currently starts
    // the value -1 marks no more annotations to parse
    long startNodeOff = startNode.getOffset().longValue();

    // The structure that fireRule() will update
    SearchState state = new SearchState(startNode, startNodeOff, 0);

    // the big while for the actual parsing
    while(state.startNodeOff != -1) {
      // while there are more annotations to parse
      // create initial active FSM instance starting parsing from new
      // startNode
      // currentFSM = FSMInstance.getNewInstance(
      FSMInstance firstCurrentFSM = new FSMInstance(fsm, 
              fsm.getInitialState(),// fresh start
              state.startNode,// the matching starts form the current startNode
              state.startNode,// current position in AG is the start position
              new java.util.HashMap(),// no bindings yet!
              doc);

      // at this point ActiveFSMInstances should always be empty!
      activeFSMInstances.clear();
      acceptingFSMInstances.clear();
      activeFSMInstances.add(firstCurrentFSM);

      // far each active FSM Instance, try to advance
      // while(!finished){
      activeFSMWhile: while(!activeFSMInstances.isEmpty()) {
        if(interrupted)
          throw new ExecutionInterruptedException("The execution of the \""
                  + getName()
                  + "\" Jape transducer has been abruptly interrupted!");

        // take the first active FSM instance
        FSMInstance currentFSM = activeFSMInstances.remove(0);
        // process the current FSM instance
//        if(currentFSM.getFSMPosition().isFinal()) {
//          // the current FSM is in a final state
//          acceptingFSMInstances.add((FSMInstance)currentFSM.clone());
//          // if we're only looking for the shortest stop here
//          if(ruleApplicationStyle == FIRST_STYLE) break;
//        }

        FSMMatcherResult result = attemptAdvance(currentFSM, offsets,
                annotationsByOffset, doc, inputAS);
        if(result != null){
          if(result.acceptingFSMInstances != null && 
                  !result.acceptingFSMInstances.isEmpty()) {
            acceptingFSMInstances.addAll(result.acceptingFSMInstances);
            if(ruleApplicationStyle == FIRST_STYLE ||
               ruleApplicationStyle == ONCE_STYLE) break activeFSMWhile;
          }
          
          if(result.activeFSMInstances != null && 
                  !result.activeFSMInstances.isEmpty()) {
            activeFSMInstances.addAll(result.activeFSMInstances);
          }
        }
      }
      boolean keepGoing = fireRule(acceptingFSMInstances, state, lastNodeOff,
              offsets, inputAS, outputAS, doc, annotationsByOffset);
      if(!keepGoing) break;
      if(((DefaultActionContext)actionContext).isPhaseEnded()) {
        ((DefaultActionContext)actionContext).setPhaseEnded(false);
        log.debug("Ending phase prematurely");
        break;
      }
    }// while(state.startNodeOff != -1)
    // fsmRunnerPool.shutdown();

    fireProcessFinished();
    if (Benchmark.isBenchmarkingEnabled()) {
      for (RuleTime r : fsm.getRuleTimes()) {
        String ruleName = r.getRuleName();
        long timeSpentOnRule = r.getTimeSpent();
        r.setTimeSpent(0); // Reset time to zero for next document
        Benchmark.checkPointWithDuration(timeSpentOnRule, Benchmark.createBenchmarkId("rule__" + ruleName, this.getBenchmarkId()), this, this.benchmarkFeatures);
      }
    }
  } // transduce
  
  

  /**
   * Try to advance the activeFSMInstances.
   * 
   * @return a list of newly created FSMInstances
   */
  @SuppressWarnings("unchecked")
  private FSMMatcherResult attemptAdvance(FSMInstance currentInstance,
          SimpleSortedSet offsets, SimpleSortedSet annotationsByOffset,
          Document document, AnnotationSet inputAS) {
    long startTime = 0;
    if (Benchmark.isBenchmarkingEnabled()) {
      startTime = Benchmark.startPoint();
    }
    List newActiveInstances = null;
    List newAcceptingInstances = null;

    // Attempt advancing the current instance.
    // While doing that, generate new active FSM instances and 
    // new accepting FSM instances, as required

    // create a clone to be used for creating new states
    // the actual current instance cannot be used itself, as it may change
    FSMInstance currentClone = (FSMInstance)currentInstance.clone();
    
    // process the current FSM instance
    if(currentInstance.getFSMPosition().isFinal()) {
      // the current FSM is in a final state
      if(newAcceptingInstances == null){
        newAcceptingInstances = new ArrayList();
      }
//        newAcceptingInstances.add((FSMInstance)currentInstance.clone());
      newAcceptingInstances.add(currentClone);
      // if we're only looking for the shortest stop here
      if(ruleApplicationStyle == FIRST_STYLE ||
         ruleApplicationStyle == ONCE_STYLE ) {
        if (Benchmark.isBenchmarkingEnabled()) {
          updateRuleTime(currentInstance, startTime);
        }
        return new FSMMatcherResult(newActiveInstances, newAcceptingInstances);
      }
    }

    // get all the annotations that start where the current FSM
    // finishes
    SimpleSortedSet offsetsTailSet = offsets.tailSet(currentInstance
            .getAGPosition().getOffset().longValue());
    long theFirst = offsetsTailSet.first();
    List paths = (theFirst >= 0 ) ?
            (List)annotationsByOffset.get(theFirst) : null;

    if(paths != null && !paths.isEmpty()) {
      // get the transitions for the current state of the FSM
      State currentState = currentClone.getFSMPosition();
      Iterator transitionsIter = currentState.getTransitions().iterator();
      
      // A flag used to indicate when advancing the current instance requires 
      // the creation of a clone (i.e. when there are more than 1 ways to advance).
      boolean newInstanceRequired = false;
  
      // for each transition, keep the set of annotations starting at
      // current node (the "paths") that match each constraint of the
      // transition.
      transitionsWhile: while(transitionsIter.hasNext()) {
        Transition currentTransition = transitionsIter.next();
  
        // There will only be multiple constraints if this transition is
        // over
        // a written constraint that has the "and" operator (comma) and
        // the
        // parts referr to different annotation types. For example -
        // {A,B} would result in 2 constraints in the array, while
        // {A.foo=="val", A.bar=="val"} would only be a single
        // constraint.
        Constraint[] currentConstraints = currentTransition.getConstraints()
                .getConstraints();
  
        boolean hasPositiveConstraint = false;
        @SuppressWarnings("rawtypes")
        List[] matchesByConstraint = new List[currentConstraints.length];
        for(int i = 0; i < matchesByConstraint.length; i++)
          matchesByConstraint[i] = null;
        // Map> matchingMap =
        // new LinkedHashMap>();
  
        // check all negated constraints first. If any annotation
        // matches any
        // negated constraint, then the transition fails.
        for(int i = 0; i < currentConstraints.length; i++) {
          // for(Constraint c : currentConstraints) {
          Constraint c = currentConstraints[i];
          if(!c.isNegated()) {
            hasPositiveConstraint = true;
            continue;
          }
          List matchList = c.matches(paths, ontology, inputAS);
          if(!matchList.isEmpty()) continue transitionsWhile;
        }
  
        // Now check all non-negated constraints. At least one
        // annotation must
        // match each constraint.
        if(hasPositiveConstraint) {
          for(int i = 0; i < currentConstraints.length; i++) {
            // for(Constraint c : currentConstraints) {
            Constraint c = currentConstraints[i];
            if(c.isNegated()) continue;
            List matchList = c.matches(paths, ontology, inputAS);
            // if no annotations matched, then the transition fails.
            if(matchList.isEmpty()) {
              continue transitionsWhile;
            }
            else {
              // matchingMap.put(c, matchList);
              matchesByConstraint[i] = matchList;
            }
          }
        } // end if hasPositiveConstraint
        else {
          // There are no non-negated constraints. Since the negated
          // constraints
          // did not fail, this means that all of the current
          // annotations
          // are potentially valid. Add the whole set to the
          // matchingMap.
          // Use the first negated constraint for the debug trace since
          // any will do.
          // matchingMap.put(currentConstraints[0], paths);
          matchesByConstraint[0] = paths;
        }
  
        // We have a match if every positive constraint is met by at
        // least one annot.
        // Given the sets Sx of the annotations that match constraint x,
        // compute all tuples (A1, A2, ..., An) where Ax comes from the
        // set Sx and n is the number of constraints
        List> matchLists = new ArrayList>();
        for(int i = 0; i < currentConstraints.length; i++) {
          // for(Map.Entry> entry :
          // matchingMap.entrySet()) {
          // seeing the constraint is useful when debugging
          @SuppressWarnings("unused")
          Constraint c = currentConstraints[i];
          // Constraint c = entry.getKey();
          List matchList = matchesByConstraint[i];
          // Collection matchList = entry.getValue();
          if(matchList != null) {
            matchLists.add(matchList);
          }
          // if (matchList instanceof List)
          // matchLists.add((List)matchList);
          // else
          // matchLists.add(new ArrayList(matchList));
        }
        
        List> combinations = combine(matchLists, matchLists
                .size(), new LinkedList());
        // Create a new FSM for every tuple of annot
        
        for(List tuple : combinations) {
          // Find longest annotation and use that to mark the start of
          // the
          // new FSM
          Annotation matchingAnnot = getRightMostAnnotation(tuple);
  
          // we have a match.
          FSMInstance newFSMI;
          // create a new FSMInstance, advance it over
          // the current
          // annotation take care of the bindings and add it to
          // ActiveFSM
          if(newInstanceRequired){
            // we need to create a clone
            newFSMI = (FSMInstance)currentClone.clone();
            //set the old AG state
            //set the old FSM position
          }else{
            // we're advancing the current instance
            newFSMI = currentInstance;
            // next time, we'll have to create a new one
            newInstanceRequired = true;
            //save the old FSM position
          }
          newFSMI.setAGPosition(matchingAnnot.getEndNode());
          newFSMI.setFSMPosition(currentTransition.getTarget());
    
          // bindings
          Map binds = newFSMI.getBindings();
          Iterator labelsIter = currentTransition.getBindings().iterator();
          String oneLabel;
          AnnotationSet boundAnnots, newSet;
          while(labelsIter.hasNext()) {
            oneLabel = labelsIter.next();
            boundAnnots = binds.get(oneLabel);
            if(boundAnnots != null)
              newSet = new AnnotationSetImpl(boundAnnots);
            else newSet = new AnnotationSetImpl(document);
  
            for(Annotation annot : tuple) {
              newSet.add(annot);
            }
  
            binds.put(oneLabel, newSet);
          }// while(labelsIter.hasNext())
          if(newActiveInstances == null) {
            newActiveInstances = new ArrayList();
          }
          newActiveInstances.add(newFSMI);
        } // iter over matching combinations
      }// while(transitionsIter.hasNext())
    }
    if (Benchmark.isBenchmarkingEnabled()) {
      updateRuleTime(currentInstance, startTime);
    }
    return new FSMMatcherResult(newActiveInstances, newAcceptingInstances);
  }

  /**
   * Increment the time spent by the rule associated with the FSM
   * @param currentInstance  The FSMInstance which has been running since startTime
   * @param startTime    The time that the FSMInstance started running
   */
  private void updateRuleTime(FSMInstance currentInstance, long startTime) {
    int index = currentInstance.getFSMPosition().getIndexInRuleList();
    currentInstance.getSupportGraph().getRuleTimes().get(index).addTime(Benchmark.startPoint() - startTime);
  }

  /**
   * Return the annotation with the right-most end node
   */
  protected Annotation getRightMostAnnotation(Collection annots) {
    long maxOffset = -1;
    Annotation retVal = null;
    for(Annotation annot : annots) {
      Long curOffset = annot.getEndNode().getOffset();
      if(curOffset > maxOffset) {
        maxOffset = curOffset;
        retVal = annot;
      }
    }

    return retVal;
  }

  /**
   * Computes all tuples (x1, x2, ..., xn) resulting from the linear
   * combination of the elements of n lists, where x1 comes from the 1st
   * list, x2 comes from the second, etc. This method works recursively.
   * The first call should have those parameters:
   *
   * @param sourceLists an array of n lists whose elements will be
   *          combined
   * @param maxTupleSize the number of elements per tuple
   * @param incompleteTuple an empty list
   */

  private static List> combine(List> sourceLists,
          int maxTupleSize, List incompleteTuple) {

    List> newTupleList = new LinkedList>();

    if(incompleteTuple.size() == maxTupleSize) {
      newTupleList.add(incompleteTuple);
    }
    else {
      List currentSourceList = sourceLists.get(incompleteTuple.size());
      // use for loop instead of ListIterator to increase speed
      // (critical here)
      for(int i = 0; i < currentSourceList.size(); i++) {
        List augmentedTuple = new LinkedList(incompleteTuple);
        augmentedTuple.add(currentSourceList.get(i));
        newTupleList.addAll(combine(sourceLists, maxTupleSize, augmentedTuple));
      }
    }

    return newTupleList;
  }

  /**
   * Fire the rule that matched.
   *
   * @return true if processing should keep going, false otherwise.
   */

  @SuppressWarnings("unchecked")
  protected boolean fireRule(List acceptingFSMInstances,
          SearchState state, long lastNodeOff, SimpleSortedSet offsets,
          AnnotationSet inputAS, AnnotationSet outputAS, Document doc,
          SimpleSortedSet annotationsByOffset) throws JapeException,
          ExecutionException {

    Node startNode = state.startNode;
    long startNodeOff = state.startNodeOff;
    long oldStartNodeOff = state.oldStartNodeOff;

    // FIRE THE RULE
    long lastAGPosition = -1;
    if(acceptingFSMInstances.isEmpty()) {
      // no rule to fire, advance to the next input offset
      lastAGPosition = startNodeOff + 1;
    }
    else if(ruleApplicationStyle == BRILL_STYLE
            || ruleApplicationStyle == ALL_STYLE) {
      // fire the rules corresponding to all accepting FSM instances
      Iterator accFSMIter = acceptingFSMInstances.iterator();
      FSMInstance currentAcceptor;
      RightHandSide currentRHS;
      lastAGPosition = startNode.getOffset().longValue();

      while(accFSMIter.hasNext()) {
        currentAcceptor = accFSMIter.next();
        currentRHS = currentAcceptor.getFSMPosition().getAction();

        currentRHS.transduce(doc, currentAcceptor.getBindings(), inputAS,
                outputAS, ontology, actionContext);

        if(ruleApplicationStyle == BRILL_STYLE) {
          // find the maximal next position
          long currentAGPosition = currentAcceptor.getAGPosition().getOffset()
                  .longValue();
          if(currentAGPosition > lastAGPosition)
            lastAGPosition = currentAGPosition;
        }
      }
      if(ruleApplicationStyle == ALL_STYLE) {
        // simply advance to next offset
        lastAGPosition = lastAGPosition + 1;
      }

    }
    else if(ruleApplicationStyle == APPELT_STYLE
            || ruleApplicationStyle == FIRST_STYLE
            || ruleApplicationStyle == ONCE_STYLE) {

      // AcceptingFSMInstances is an ordered structure:
      // just execute the longest (last) rule
      Collections.sort(acceptingFSMInstances, Collections.reverseOrder());
      Iterator accFSMIter = acceptingFSMInstances.iterator();
      FSMInstance currentAcceptor = accFSMIter.next();
      if(isDebugMode()) {
        // see if we have any conflicts
        Iterator accIter = acceptingFSMInstances.iterator();
        FSMInstance anAcceptor;
        List conflicts = new ArrayList();
        while(accIter.hasNext()) {
          anAcceptor = accIter.next();
          if(anAcceptor.equals(currentAcceptor)) {
            conflicts.add(anAcceptor);
          }
          else {
            break;
          }
        }
        if(conflicts.size() > 1) {
          log.info("Conflicts found during matching:"
                  + "\n================================");
          accIter = conflicts.iterator();
          int i = 0;
          while(accIter.hasNext()) {
            if (log.isInfoEnabled())
              log.info(i++ + ") " + accIter.next().toString());
          }
        }
      }
      RightHandSide currentRHS = currentAcceptor.getFSMPosition().getAction();

      currentRHS.transduce(doc, currentAcceptor.getBindings(), inputAS,
              outputAS, ontology, actionContext);

      // if in matchGroup mode check other possible patterns in this
      // span
      if(isMatchGroupMode()) {
        // log.debug("Jape grammar in MULTI application style.");
        // ~bp: check for other matching fsm instances with same length,
        // priority and rule index : if such execute them also.
        String currentAcceptorString = null;
        multiModeWhile: while(accFSMIter.hasNext()) {
          FSMInstance rivalAcceptor = accFSMIter.next();
          // get rivals that match the same document segment
          // makes use of the semantic difference between the compareTo
          // and equals methods on FSMInstance
          if(rivalAcceptor.compareTo(currentAcceptor) == 0) {
            // gets the rivals that are NOT COMPLETELY IDENTICAL with
            // the current acceptor.
            if(!rivalAcceptor.equals(currentAcceptor)) {
              //depends on the debug option in the transducer
              if(isDebugMode()) {
                if(currentAcceptorString == null) {
                  // first rival
                  currentAcceptorString = currentAcceptor.toString();
                  if (log.isInfoEnabled()) {
                    log.info("~Jape Grammar Transducer : "
                            + "\nConcurrent Patterns by length,priority and index (all transduced):");
                    log.info(currentAcceptorString);
                    log.info("bindings : " + currentAcceptor.getBindings());
                    log.info("Rivals Follow: ");
                  }
                }
                if (log.isInfoEnabled()) {
                  log.info(rivalAcceptor.toString());
                  log.info("bindings : " + rivalAcceptor.getBindings());
                }
              }// DEBUG
              currentRHS = rivalAcceptor.getFSMPosition().getAction();

              currentRHS.transduce(doc, rivalAcceptor.getBindings(), inputAS,
                      outputAS, ontology, actionContext);

            } // equal rival
          }
          else {
            // if rival is not equal this means that there are no
            // further
            // equal rivals (since the list is sorted)
            break multiModeWhile;
          }
        } // while there are fsm instances
      } // matchGroupMode

      // if in ONCE mode stop after first match
      if(ruleApplicationStyle == ONCE_STYLE) {
        state.startNodeOff = startNodeOff;
        return false;
      }

      // advance in AG
      lastAGPosition = currentAcceptor.getAGPosition().getOffset().longValue();
    }
    else throw new RuntimeException("Unknown rule application style!");

    // advance on input
    SimpleSortedSet offsetsTailSet = offsets.tailSet(lastAGPosition);
    long theFirst = offsetsTailSet.first();
    if(theFirst < 0) {
      // no more input, phew! :)
      startNodeOff = -1;
      fireProcessFinished();
    } else {
      long nextKey = theFirst;
      startNode = ((List)annotationsByOffset.get(nextKey))
              .get(0). // nextKey
              getStartNode();
      startNodeOff = startNode.getOffset().longValue();

      // eliminate the possibility for infinite looping
      if(oldStartNodeOff == startNodeOff) {
        // we are about to step twice in the same place, ...skip ahead
        lastAGPosition = startNodeOff + 1;
        offsetsTailSet = offsets.tailSet(lastAGPosition);
        theFirst = offsetsTailSet.first();
        if(theFirst < 0) {
          // no more input, phew! :)
          startNodeOff = -1;
          fireProcessFinished();
        }
        else {
          nextKey = theFirst;
          startNode = ((List)annotationsByOffset.get(theFirst))
                  .get(0).getStartNode();
          startNodeOff = startNode.getOffset().longValue();
        }
      }// if(oldStartNodeOff == startNodeOff)
      // fire the progress event
      if(startNodeOff - oldStartNodeOff > 256) {
        if(isInterrupted())
          throw new ExecutionInterruptedException("The execution of the \""
                  + getName()
                  + "\" Jape transducer has been abruptly interrupted!");

        fireProgressChanged((int)(100 * startNodeOff / lastNodeOff));
        oldStartNodeOff = startNodeOff;
      }
    }
    // by Shafirin Andrey start (according to Vladimir Karasev)
    // if(gate.Gate.isEnableJapeDebug()) {
    // if (null != phaseController) {
    // phaseController.TraceTransit(rulesTrace);
    // }
    // }
    // by Shafirin Andrey end

    state.oldStartNodeOff = oldStartNodeOff;
    state.startNodeOff = startNodeOff;
    state.startNode = startNode;
    return true;
  } // fireRule

  /** Clean up (delete action class files, for e.g.). */
  @Override
  public void cleanUp() {
    // for(DListIterator i = rules.begin(); ! i.atEnd(); i.advance())
    // ((Rule) i.get()).cleanUp();
  } // cleanUp

  /** A string representation of this object. */
  @Override
  public String toString() {
    return toString("");
  } // toString()

  /** A string representation of this object. */
  @Override
  public String toString(String pad) {
    String newline = Strings.getNl();
    String newPad = Strings.addPadding(pad, INDENT_PADDING);

    StringBuffer buf = new StringBuffer(pad + "SPT: name(" + name
            + "); ruleApplicationStyle(");

    switch(ruleApplicationStyle) {
      case APPELT_STYLE:
        buf.append("APPELT_STYLE); ");
        break;
      case BRILL_STYLE:
        buf.append("BRILL_STYLE); ");
        break;
      default:
        break;
    }

    buf.append("rules(" + newline);
    if(rules != null) {
      Iterator rulesIterator = rules.iterator();
        while(rulesIterator.hasNext())
          buf.append(rulesIterator.next().toString(newPad) + " ");
    }
    buf.append(newline + pad + ")." + newline);

    return buf.toString();
  } // toString(pad)

  // needed by fsm
  public PrioritisedRuleList getRules() {
    return rules;
  }

  /**
   * Adds a new type of input annotations used by this transducer. If
   * the list of input types is empty this transducer will parse all the
   * annotations in the document otherwise the types not found in the
   * input list will be completely ignored! To be used with caution!
   */
  public void addInput(String ident) {
    input.add(ident);
  }

  /**
   * Checks if this Phase has the annotation type as input. This is the
   * case if either no input annotation types were specified, in which case
   * all annotation types will be used, or if the annotation type was
   * specified.
   *
   * @param ident the type of an annotation to be checked
   * @return true if the annotation type will be used in this phase
   */
  public boolean hasInput(String ident) {
    return input.isEmpty() || input.contains(ident);
  }

  /**
   * Check if there is a restriction on the input annotation types
   * for this SPT, i.e. if there were annotation types specified for
   * the "Input:" declaration of this phase.
   *
   * @return true if only certain annotation types are considered in this
   *   phase, false if all are considered.
   */
  public boolean isInputRestricted() {
    return !input.isEmpty();
  }

  @Override
  public synchronized void removeProgressListener(ProgressListener l) {
    if(progressListeners != null && progressListeners.contains(l)) {
      @SuppressWarnings("unchecked")
      Vector v = (Vector)progressListeners.clone();
      v.removeElement(l);
      progressListeners = v;
    }
  }

  @Override
  public synchronized void addProgressListener(ProgressListener l) {
    @SuppressWarnings("unchecked")
    Vector v = progressListeners == null
            ? new Vector(2)
            : (Vector)progressListeners.clone();
    if(!v.contains(l)) {
      v.addElement(l);
      progressListeners = v;
    }
  }

  /**
   * Defines the types of input annotations that this transducer reads.
   * If this set is empty the transducer will read all the annotations
   * otherwise it will only "see" the annotations of types found in this
   * list ignoring all other types of annotations.
   */
  // by Shafirin Andrey start (modifier changed to public)
  public Set input = new HashSet();

  // java.util.Set input = new java.util.HashSet();
  // by Shafirin Andrey end
  private transient Vector progressListeners;

  @Override
  protected void fireProgressChanged(int e) {
    if(progressListeners != null) {
      Vector listeners = progressListeners;
      int count = listeners.size();
      for(int i = 0; i < count; i++) {
        listeners.elementAt(i).progressChanged(e);
      }
    }
  }

  @Override
  protected void fireProcessFinished() {
    if(progressListeners != null) {
      Vector listeners = progressListeners;
      int count = listeners.size();
      for(int i = 0; i < count; i++) {
        listeners.elementAt(i).processFinished();
      }
    }
  }

  public int getRuleApplicationStyle() {
    return ruleApplicationStyle;
  }

  private transient SourceInfo sourceInfo = null;
  private String controllerStartedEventBlock = "";
  private String controllerFinishedEventBlock = "";
  private String controllerAbortedEventBlock = "";
  private String javaImportsBlock = "";
  private Object controllerEventBlocksActionClass = null;
  private String controllerEventBlocksActionClassName;
  private static final String nl = Strings.getNl();
  private static final String controllerEventBlocksActionClassSourceTemplate =
    "package %%packagename%%;"+nl+
    "import java.io.*;"+nl+
    "import java.util.*;"+nl+
    "import gate.*;"+nl+
    "import gate.jape.*;"+nl+
    "import gate.creole.ontology.*;"+nl+
    "import gate.annotation.*;"+nl+
    "import gate.util.*;"+nl+
    "%%javaimports%%"+nl+nl+
    "public class %%classname%% implements ControllerEventBlocksAction {"+nl+
    "  private Ontology ontology;"+nl+
    "  public void setOntology(Ontology o) { ontology = o; }"+nl+
    "  public Ontology getOntology() { return ontology; }"+nl+
    "  private ActionContext ctx;"+nl+
    "  public void setActionContext(ActionContext ac) { ctx = ac; }"+nl+
    "  public ActionContext getActionContext() { return ctx; }"+nl+
    "  private Controller controller;"+nl+
    "  public void setController(Controller c) { controller = c; }"+nl+
    "  public Controller getController() { return controller; }"+nl+
    "  private Corpus corpus;"+nl+
    "  public void setCorpus(Corpus c) { corpus = c; }"+nl+
    "  public Corpus getCorpus() { return corpus; }"+nl+
    "  private Throwable throwable;"+nl+
    "  public void setThrowable(Throwable t) { throwable = t; }"+nl+
    "  public Throwable getThrowable() { return throwable; }"+nl+
    "  public void controllerExecutionStarted() {"+nl+
    "    %%started%%"+nl+
    "  }"+nl+
    "  public void controllerExecutionFinished() {"+nl+
    "    %%finished%%"+nl+
    "  }"+nl+
    "  public void controllerExecutionAborted() {"+nl+
    "    %%aborted%%"+nl+
    "  }"+nl+
    "}"+nl+
    ""+nl;
  
  public void setControllerEventBlocks(
    String started,
    String finished,
    String aborted,
    String javaimports) {
    controllerStartedEventBlock = started;
    controllerFinishedEventBlock = finished;
    controllerAbortedEventBlock = aborted;
    javaImportsBlock = javaimports;
  }
  
  public String generateControllerEventBlocksCode(String packageName, String className) {
    String sourceCode = null;
    // if any of the three blocks is not null, set the corpusBlockActionClassSource
    // to the final source code of the class
    if(controllerStartedEventBlock != null || controllerFinishedEventBlock != null || controllerAbortedEventBlock != null) {
            
      sourceCode =
        controllerEventBlocksActionClassSourceTemplate;

      // if this method is called with a classname, use that (this happens 
      // when we are called from gate.jape.plus.SPTBuilder.buildSPT
      // If we get null or the empty string as a classname, generate our own
      String  ceb_classname = className;
      if(className == null || className.isEmpty()) {
        boolean neednewclassname = true;
        while(neednewclassname) {
          ceb_classname =  "ControllerEventBlocksActionClass" +
            actionClassNumber.getAndIncrement();
          controllerEventBlocksActionClassName = packageName + "." + ceb_classname;
          try {
            Gate.getClassLoader().loadClass(controllerEventBlocksActionClassName);
            neednewclassname = true;
          } catch (ClassNotFoundException e) {
            neednewclassname = false;
          }
        }
      } 
      sourceCode = sourceCode
        .replace("%%classname%%",ceb_classname)
        .replace("%%packagename%%", packageName);
        
      
      sourceInfo = new SourceInfo(controllerEventBlocksActionClassName,name,"controllerEvents");
      
      sourceCode =
        sourceCode.replace("%%javaimports%%",
          javaImportsBlock != null ? javaImportsBlock : "// no 'Imports:' block for more imports defined");
      
      int index = sourceCode.indexOf("%%started%%");
      String previousCode = sourceCode.substring(0, index).trim();
      sourceCode =
        sourceCode.replace("%%started%%",
          controllerStartedEventBlock != null ? sourceInfo.addBlock(previousCode, controllerStartedEventBlock) : "// no code defined");
      
      index = sourceCode.indexOf("%%finished%%");
      previousCode = sourceCode.substring(0, index).trim();
      sourceCode =
        sourceCode.replace("%%finished%%",
          controllerFinishedEventBlock != null ? sourceInfo.addBlock(previousCode, controllerFinishedEventBlock) : "// no code defined");
      
      index = sourceCode.indexOf("%%aborted%%");
      previousCode = sourceCode.substring(0, index).trim();
      sourceCode =
        sourceCode.replace("%%aborted%%",
          controllerAbortedEventBlock != null ? sourceInfo.addBlock(previousCode, controllerAbortedEventBlock) : "// no code defined");
    }
    return sourceCode;
  }

  @Override
  public void runControllerExecutionStartedBlock(
    ActionContext ac, Controller c, Ontology o) throws ExecutionException {
    if(controllerEventBlocksActionClass != null) {
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setController(c);
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setOntology(o);
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setActionContext(ac);
      if(c instanceof CorpusController) {
        Corpus corpus = ((CorpusController)c).getCorpus();
         ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
          setCorpus(corpus);
      } else {
        ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
          setCorpus(null);
      }
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setThrowable(null);
      
      try {
        ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
          controllerExecutionStarted();
      }
      catch (Throwable e) {
      // if the action class throws an exception, re-throw it with a
      // full description of the problem, inc. stack trace and the RHS
      // action class code
      if (sourceInfo != null) sourceInfo.enhanceTheThrowable(e);
      
      if(e instanceof Error) {
        throw (Error)e;
      }   
      if(e instanceof RuntimeException) {
        throw (RuntimeException)e;
      }
      
      // shouldn't happen...
      throw new ExecutionException(
          "Couldn't run controller started action", e);
      }
    }
  }

  @Override
  public void runControllerExecutionFinishedBlock(
    ActionContext ac, Controller c, Ontology o) throws ExecutionException {
    if(controllerEventBlocksActionClass != null) {
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setController(c);
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setOntology(o);
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setActionContext(ac);
      if(c instanceof CorpusController) {
        Corpus corpus = ((CorpusController)c).getCorpus();
         ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
          setCorpus(corpus);
      } else {
        ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
          setCorpus(null);
      }
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setThrowable(null);
      
      try {
        ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
          controllerExecutionFinished();
      }
      catch (Throwable e) {
      // if the action class throws an exception, re-throw it with a
      // full description of the problem, inc. stack trace and the RHS
      // action class code
      if (sourceInfo != null) sourceInfo.enhanceTheThrowable(e);
      
      if(e instanceof Error) {
        throw (Error)e;
      }   
      if(e instanceof RuntimeException) {
        throw (RuntimeException)e;
      }
      
      // shouldn't happen...
      throw new ExecutionException(
          "Couldn't run controller finished action", e);
      }
    }
  }

  @Override
  public void runControllerExecutionAbortedBlock(
    ActionContext ac, Controller c, Throwable t, Ontology o) throws ExecutionException {
    if(controllerEventBlocksActionClass != null) {
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setController(c);
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setOntology(o);
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setActionContext(ac);
      if(c instanceof CorpusController) {
        Corpus corpus = ((CorpusController)c).getCorpus();
         ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
          setCorpus(corpus);
      } else {
        ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
          setCorpus(null);
      }
      ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
        setThrowable(t);
      
      try {
        ((ControllerEventBlocksAction) controllerEventBlocksActionClass).
          controllerExecutionAborted();
      }
      catch (Throwable e) {
      // if the action class throws an exception, re-throw it with a
      // full description of the problem, inc. stack trace and the RHS
      // action class code
      if (sourceInfo != null) sourceInfo.enhanceTheThrowable(e);
      
      if(e instanceof Error) {
        throw (Error)e;
      }   
      if(e instanceof RuntimeException) {
        throw (RuntimeException)e;
      }
      
      // shouldn't happen...
      throw new ExecutionException(
          "Couldn't run controller aborted action", e);
      }
    }
  }

  private void writeObject(java.io.ObjectOutputStream out)
  throws IOException{
    Object save = controllerEventBlocksActionClass;
    controllerEventBlocksActionClass = null;
    out.defaultWriteObject();
    controllerEventBlocksActionClass = save;
  }

  private void readObject(java.io.ObjectInputStream in)
  throws IOException, ClassNotFoundException{
    in.defaultReadObject();
    compileEventBlocksActionClass(Gate.getClassLoader());
  }

  private void compileEventBlocksActionClass(GateClassLoader classloader) {
    String sourceCode = generateControllerEventBlocksCode("japeactionclasses","");
    if(sourceCode != null) {
			Map actionClasses = new HashMap(1);
			actionClasses.put(controllerEventBlocksActionClassName,
			        sourceCode);
      try {
			    gate.util.Javac.loadClasses(actionClasses, classloader);
          controllerEventBlocksActionClass =
            classloader.
              loadClass(controllerEventBlocksActionClassName).newInstance();
		    }catch(Exception e1){
			    throw new GateRuntimeException (e1);
	  	  }
    }
  }

  /**
   * This returns any compiled controller event blocks action class that
   * may exist at the time of calling or null. This is mainly needed for
   * alternate implementations of JAPE that are based on the core JAPE
   * classes and want to support controller event blocks too.
   *
   * @return an object that represents the compiled event blocks or null
   */
  public ControllerEventBlocksAction getControllerEventBlocksActionClass() {
    return (ControllerEventBlocksAction)controllerEventBlocksActionClass;
  }
  
  /*
   * private void writeObject(ObjectOutputStream oos) throws IOException {
   * Out.prln("writing spt"); oos.defaultWriteObject();
   * Out.prln("finished writing spt"); } // writeObject
   */

} // class SinglePhaseTransducer

/*
 * class SimpleSortedSet {
 *
 * static final int INCREMENT = 1023; int[] theArray = new
 * int[INCREMENT]; Object[] theObject = new Object[INCREMENT]; int
 * tsindex = 0; int size = 0; public static int avesize = 0; public
 * static int maxsize = 0; public static int avecount = 0; public
 * SimpleSortedSet() { avecount++; java.util.Arrays.fill(theArray,
 * Integer.MAX_VALUE); }
 *
 * public Object get(int elValue) { int index =
 * java.util.Arrays.binarySearch(theArray, elValue); if (index >=0)
 * return theObject[index]; return null; }
 *
 * public boolean add(int elValue, Object o) { int index =
 * java.util.Arrays.binarySearch(theArray, elValue); if (index >=0) {
 * ((ArrayList)theObject[index]).add(o); return false; } if (size ==
 * theArray.length) { int[] temp = new int[theArray.length + INCREMENT];
 * Object[] tempO = new Object[theArray.length + INCREMENT];
 * System.arraycopy(theArray, 0, temp, 0, theArray.length);
 * System.arraycopy(theObject, 0, tempO, 0, theArray.length);
 * java.util.Arrays.fill(temp, theArray.length, temp.length ,
 * Integer.MAX_VALUE); theArray = temp; theObject = tempO; } index =
 * ~index; System.arraycopy(theArray, index, theArray, index+1, size -
 * index ); System.arraycopy(theObject, index, theObject, index+1, size -
 * index ); theArray[index] = elValue; theObject[index] = new
 * ArrayList(); ((ArrayList)theObject[index]).add(o); size++; return
 * true; } public int first() { if (tsindex >= size) return -1; return
 * theArray[tsindex]; }
 *
 * public Object getFirst() { if (tsindex >= size) return null; return
 * theObject[tsindex]; }
 *
 * public SimpleSortedSet tailSet(int elValue) { if (tsindex <
 * theArray.length && elValue != theArray[tsindex]) { if (tsindex<(size-1) &&
 * elValue > theArray[tsindex] && elValue <= theArray[tsindex+1]) {
 * tsindex++; return this; } int index =
 * java.util.Arrays.binarySearch(theArray, elValue); if (index < 0)
 * index = ~index; tsindex = index; } return this; }
 *
 * public boolean isEmpty() { return size ==0; } };
 */




© 2015 - 2024 Weber Informatics LLC | Privacy Policy