/*
* FSMInstance.java
*
* Copyright (c) 1998-2001, The University of Sheffield.
*
* 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).
*
* Valentin Tablan, 05/May/2000
*
* Minor modifications made by Luc Plamondon, Universit� de Montr�al, 27/11/03:
* - migrated original file from gate.fsm to
* ca.umontreal.iro.rali.gate.fsm package.
*
* $Id$
*/
package ca.umontreal.iro.rali.gate.fsm;
import gate.*;
import gate.annotation.AnnotationSetImpl;
import gate.util.*;
import ca.umontreal.iro.rali.gate.jape.*;
import java.util.*;
import java.io.*;
/**
* The objects of this class represent instances of working Finite State
* Machine during parsing a gate document (annotation set).
* In order to completely define the state a FSM is in one needs to store
* information regarding:
* -the position in the FSM transition graph
* -the position in the annotation graph
* -the set of bindings that occured up to the current state.
* note that a set of bindings is an object of type Map that maps names
* (java.lang.String) to bags of annotations (gate.AnnotationSet)
*/
public class FSMInstance implements Comparable, Cloneable, Serializable {
/** Debug flag */
private static final boolean DEBUG = false;
/** Creates a new FSMInstance object.
* @param supportGraph the transition graph of the FSM
* @param FSMPosition the state this instance will be in
* @param startNode the node in the AnnotationSet where this FSM instance
* started the matching
* @param AGPosition the node in the AnnotationSet up to which this FSM Instance
* advanced during the matching.
* @param bindings a HashMap that maps from labels (objects of type String)
* to sets of annotations (objects of type AnnotationSet). This map stores
* all the bindings that took place during the matching process.
* This FSMInstance started the matching on an AnnotationSet from "startNode"
* and advanced to "AGPosition"; during this process it traversed the path in
* the transition graph "supportGraph" from the initial state to
* "FSMPosition" and made the bindings stored in "bindings".
*/
public FSMInstance(FSM supportGraph, State FSMPosition,
Node startNode, Node AGPosition, HashMap bindings,
Document document) {
this.supportGraph = supportGraph;
this.FSMPosition = FSMPosition;
this.startNode = startNode;
this.AGPosition = AGPosition;
this.bindings = bindings;
this.document = document;
length = AGPosition.getOffset().longValue() -
startNode.getOffset().longValue();
fileIndex = FSMPosition.getFileIndex();
priority = FSMPosition.getPriority();
}
/** Returns the FSM transition graph that backs this FSM instance
* @return an FSM object
*/
public FSM getSupportGraph(){ return supportGraph; }
/** Returns the position in the support graph for this FSM instance
* @return an object of type State
*/
public State getFSMPosition(){ return FSMPosition; }
/** Sets the position in the support transition graph for this FSM instance
* Convenience method for when the state is not known at construction time.
*/
public void setFSMPosition(State newFSMPos) {
FSMPosition = newFSMPos;
fileIndex = FSMPosition.getFileIndex();
priority = FSMPosition.getPriority();
}
/** Returns the index in the Jape definition file of the rule that caused
* the generation of the FSM state this instance is in.
* This value is correct if and only if this FSM instance is in a final
* state of the FSM transition graph.
* @return an int value.
*/
public int getFileIndex(){ return fileIndex; }
/** Returns the node in the AnnotationSet from which this FSM instance
* started the matching process.
* @return a gate.Node object
*/
public Node getStartAGPosition(){ return startNode; }
/** Returns the node up to which this FSM instance advanced in the
* Annotation graph during the matching process.
* @return a gate.Node object
*/
public Node getAGPosition(){ return AGPosition; }
/** Sets the current position in the AnnotationSet.
* Convenience method for cases when this value is not known at construction
* time.
* @param node a position in the AnnotationSet
*/
public void setAGPosition(Node node){
AGPosition = node;
length = AGPosition.getOffset().longValue() -
startNode.getOffset().longValue();
}
/** Gets the map representing the bindings that took place during the matching
* process this FSM instance performed.
* @return a HashMap object
*/
public HashMap getBindings() { return bindings; }
/** Returns the length of the parsed region in the document under scrutiny.
* More precisely this is the distnace between the Node in the annotation
* graph where the matching started and the current position.
* @return a long value
*/
public long getLength() { return length; }
/** Overrides the hashCode method from Object so this obejcts can be stored in
* hash maps and hash sets.
*/
public int hashCode() {
return (int)length ^ priority ^ fileIndex ^ bindings.hashCode() ^
FSMPosition.getAction().hashCode();
}
public boolean equals(Object other){
if(other instanceof FSMInstance){
FSMInstance otherFSM = (FSMInstance)other;
boolean result = length == otherFSM.length &&
priority == otherFSM.priority &&
fileIndex == otherFSM.fileIndex &&
bindings.equals(otherFSM.bindings) &&
FSMPosition.getAction().equals(otherFSM.FSMPosition.getAction());
return result;
}else{
throw new ClassCastException(other.getClass().toString());
}
}
/** Returns a clone of this object.
* The cloning is done bitwise except for the bindings that are cloned by
* themselves
* @return an Object value that is actually a FSMInstance object
*/
public Object clone() {
//do a classic clone except for bindings which need to be cloned themselves
try {
FSMInstance clone = (FSMInstance)super.clone();
clone.bindings = (HashMap)bindings.clone();
return clone;
} catch (CloneNotSupportedException cnse) {
cnse.printStackTrace(Err.getPrintWriter());
return null;
}
}
/*
public Object clone() {
//do a classic clone except for bindings which need to be cloned themselves
//Out.println("Clone!");
FSMInstance clone = FSMInstance.getNewInstance(this.supportGraph,
this.FSMPosition,
this.startNode,
this.AGPosition,
null);
clone.bindings = (HashMap)(bindings.clone());
return (FSMInstance)clone;
}
*/
/** Implementation of the compareTo method required by the Comparable
* interface. The comparison is based on the size of the matched region and
* the index in the definition file of the rule associated to this FSM
* instance (which needs to be in a final state)
* The order imposed by this method is the priority needed in case of a
* multiple match.
*/
public int compareTo(Object obj) {
if (obj instanceof FSMInstance) {
if(obj == this) return 0;
FSMInstance other = (FSMInstance)obj;
if(length < other.getLength()) return -1;
else if(length > other.getLength()) return 1;
//equal length
else if(priority < other.priority) return -1;
else if(priority > other.priority) return 1;
//equal priority
else return other.fileIndex - fileIndex;
} else throw new ClassCastException(
"Attempt to compare a FSMInstance object to an object " +
"of type " + obj.getClass()+"!");
}
/** Returns a textual representation of this FSM instance.
*/
public String toString() {
String res = "";
RightHandSide rhs = getFSMPosition().getAction();
if(rhs != null){
res += rhs.getPhaseName() + "." + rhs.getRuleName() + ": \"";
try{
res += document.getContent().getContent(
getStartAGPosition().getOffset(),
getAGPosition().getOffset()).toString() + "\"";
}catch(InvalidOffsetException ioe){
ioe.printStackTrace(Err.getPrintWriter());
}
Iterator labelIter = bindings.keySet().iterator();
res += "\n{";
while(labelIter.hasNext()){
String label = (String)labelIter.next();
Collection annots = (Collection)bindings.get(label);
res += "\n" + label + ": ";
Iterator annIter = annots.iterator();
while(annIter.hasNext()){
Annotation ann = (Annotation)annIter.next();
res += ann.getType() + "(\"";
try{
res += document.getContent().
getContent(ann.getStartNode().getOffset(),
ann.getEndNode().getOffset()).toString();
}catch(InvalidOffsetException ioe){
ioe.printStackTrace(Err.getPrintWriter());
}
res += "\") ";
}
}
res += "\n}";
}else{
res += "FSM position :" + FSMPosition.getIndex() +
"\nFirst matched ANN at:" + startNode.getId() +
"\nLast matched ANN at :" + AGPosition.getId() +
"\nPriority :" + priority +
"\nFile index :" + fileIndex +
"\nBindings :" + bindings;
}
return res;
}
/** The FSM for which this FSMInstance is an instance of. */
private FSM supportGraph;
/** The current state of this FSMInstance */
private State FSMPosition;
/** The place (Node) in the AnnotationSet where the matching started*/
private Node AGPosition, startNode;
/** A map from java.lang.String to gate.AnnotationSet describing all the
* bindings that took place during matching.
* needs to be HashMap instead of simply Map in order to cloneable
*/
private HashMap bindings;
/** The size of the matched region in the Annotation Set*/
private long length = 0;
/**
* The index in the definition file of the rule from which the AGPosition
* state was generated.
*/
private int fileIndex;
private Document document;
/**
* The priority in the definition file of the rule from which the AGPosition
* state was generated.
*/
private int priority;
/** Static method that provides new FSM instances. This method handles some
* basic object pooling in order to reuse the FSMInstance objects.
* This is considered to be a good idea because during jape transducing
* a large number of FSMIntances are needed for short periods.
*/
public static FSMInstance getNewInstance(FSM supportGraph, State FSMPosition,
Node startNode, Node AGPosition,
HashMap bindings, Document doc) {
FSMInstance res;
if(myInstances.isEmpty()) res = new FSMInstance(supportGraph, FSMPosition,
startNode, AGPosition,
bindings, doc);
else {
res = (FSMInstance)myInstances.removeFirst();
res.supportGraph = supportGraph;
res.FSMPosition = FSMPosition;
res.startNode = startNode;
res.AGPosition = AGPosition;
res.bindings = bindings;
}
return res;
}
/** Static method used to return a FSMInstance that is not needed anymore
*/
public static void returnInstance(FSMInstance ins) {
myInstances.addFirst(ins);
}
/** Release all the FSMInstances that are not currently in use */
public static void clearInstances() {
myInstances = new LinkedList();
}
/** The list of existing instances of type FSMInstance */
private static LinkedList myInstances;
/** The offset in the input List where the last matched annotation was*/
static{
myInstances = new LinkedList();
}
} // FSMInstance