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