gate.jape.SinglePhaseTransducer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of annie Show documentation
Show all versions of annie Show documentation
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