FSMInstance.java
001 /*
002  *  FSMInstance.java
003  *
004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Valentin Tablan, 05/May/2000
013  *
014  *  $Id: FSMInstance.java 12370 2010-03-17 09:01:13Z ian_roberts $
015  */
016 package gate.fsm;
017 
018 import java.io.Serializable;
019 import java.util.*;
020 
021 import gate.*;
022 import gate.jape.RightHandSide;
023 import gate.util.Err;
024 import gate.util.InvalidOffsetException;
025 
026 /**
027   * The objects of this class represent instances of working Finite State
028   * Machine during parsing a gate document (annotation set).
029   * In order to completely define the state a FSM is in one needs to store
030   * information regarding:
031   * -the position in the FSM transition graph
032   * -the position in the annotation graph
033   * -the set of bindings that occured up to the current state.
034   *  note that a set of bindings is an object of type Map that maps names
035   * (java.lang.String) to bags of annotations (gate.AnnotationSet)
036   */
037 public class FSMInstance implements Comparable, Cloneable, Serializable {
038 
039   /** Debug flag */
040   private static final boolean DEBUG = false;
041 
042   /** Creates a new FSMInstance object.
043     @param supportGraph the transition graph of the FSM
044     @param FSMPosition the state this instance will be in
045     @param startNode the node in the AnnotationSet where this FSM instance
046     * started the matching
047     @param AGPosition the node in the AnnotationSet up to which this FSM Instance
048     * advanced during the matching.
049     @param bindings a HashMap that maps from labels (objects of type String)
050     * to sets of annotations (objects of type AnnotationSet). This map stores
051     * all the bindings that took place during the matching process.
052     * This FSMInstance started the matching on an AnnotationSet from "startNode"
053     * and advanced to "AGPosition"; during this process it traversed the path in
054     * the transition graph "supportGraph" from the initial state to
055     * "FSMPosition" and made the bindings stored in "bindings".
056     */
057   public FSMInstance(FSM supportGraph, State FSMPosition,
058                      Node startNode, Node AGPosition,
059                      HashMap<String, AnnotationSet> bindings,
060                      Document document) {
061 
062     this.supportGraph = supportGraph;
063     this.FSMPosition = FSMPosition;
064     this.startNode = startNode;
065     this.AGPosition = AGPosition;
066     this.bindings = bindings;
067     this.document = document;
068     length = AGPosition.getOffset().longValue() -
069              startNode.getOffset().longValue();
070     fileIndex = FSMPosition.getFileIndex();
071     priority = FSMPosition.getPriority();
072   }
073 
074   /** Returns the FSM transition graph that backs this FSM instance
075     @return an FSM object
076     */
077   public FSM getSupportGraph(){ return supportGraph; }
078 
079   /** Returns the position in the support graph for this FSM instance
080     @return an object of type State
081     */
082   public State getFSMPosition(){ return FSMPosition; }
083 
084   /** Sets the position in the support transition graph for this FSM instance
085     * Convenience method for when the state is not known at construction time.
086     */
087   public void setFSMPosition(State newFSMPos) {
088     FSMPosition = newFSMPos;
089     fileIndex = FSMPosition.getFileIndex();
090     priority = FSMPosition.getPriority();
091   }
092 
093   /** Returns the index in the Jape definition file of the rule that caused
094     * the generation of the FSM state this instance is in.
095     * This value is correct if and only if this FSM instance is in a final
096     * state of the FSM transition graph.
097     @return an int value.
098     */
099   public int getFileIndex(){ return fileIndex; }
100 
101   /** Returns the node in the AnnotationSet from which this FSM instance
102     * started the matching process.
103     @return a gate.Node object
104     */
105   public Node getStartAGPosition(){ return startNode; }
106 
107   /** Returns the node up to which this FSM instance advanced in the
108     * Annotation graph during the matching process.
109     @return a gate.Node object
110     */
111   public Node getAGPosition(){ return AGPosition; }
112 
113   /** Sets the current position in the AnnotationSet.
114     * Convenience method for cases when this value is not known at construction
115     * time.
116     @param node a position in the AnnotationSet
117     */
118   public void setAGPosition(Node node){
119     AGPosition = node;
120     length = AGPosition.getOffset().longValue() -
121              startNode.getOffset().longValue();
122   }
123 
124   /** Gets the map representing the bindings that took place during the matching
125     * process this FSM instance performed.
126     @return a HashMap object
127     */
128   public HashMap<String, AnnotationSet> getBindings() { return bindings; }
129 
130   /** Returns the length of the parsed region in the document under scrutiny.
131     * More precisely this is the distnace between the Node in the annotation
132     * graph where the matching started and the current position.
133     @return a long value
134     */
135   public long getLength() { return length; }
136 
137   /** Overrides the hashCode method from Object so this obejcts can be stored in
138     * hash maps and hash sets.
139     */
140   public int hashCode() {
141     return (int)length ^ priority ^ fileIndex ^ bindings.hashCode() ^
142            FSMPosition.getAction().hashCode();
143   }
144 
145   public boolean equals(Object other){
146     if(other instanceof FSMInstance){
147       FSMInstance otherFSM = (FSMInstance)other;
148       boolean result = length == otherFSM.length &&
149              priority == otherFSM.priority &&
150              fileIndex == otherFSM.fileIndex &&
151              bindings.equals(otherFSM.bindings&&
152              FSMPosition.getAction().equals(otherFSM.FSMPosition.getAction());
153       return result;
154     }else{
155       throw new ClassCastException(other.getClass().toString());
156     }
157   }
158 
159   /** Returns a clone of this object.
160     * The cloning is done bitwise except for the bindings that are cloned by
161     * themselves
162     @return an Object value that is actually a FSMInstance object
163     */
164   public Object clone() {
165     //do a classic clone except for bindings which need to be cloned themselves
166     try {
167       FSMInstance clone = (FSMInstance)super.clone();
168       clone.bindings = (HashMap)bindings.clone();
169       return clone;
170     catch (CloneNotSupportedException cnse) {
171       cnse.printStackTrace(Err.getPrintWriter());
172       return null;
173     }
174   }
175 
176   /*
177   public Object clone() {
178   //do a classic clone except for bindings which need to be cloned themselves
179   //Out.println("Clone!");
180     FSMInstance clone = FSMInstance.getNewInstance(this.supportGraph,
181                                                    this.FSMPosition,
182                                                    this.startNode,
183                                                    this.AGPosition,
184                                                    null);
185     clone.bindings = (HashMap)(bindings.clone());
186     return (FSMInstance)clone;
187   }
188   */
189 
190   /** Implementation of the compareTo method required by the Comparable
191     * interface. The comparison is based on the size of the matched region and
192     * the index in the definition file of the rule associated to this FSM
193     * instance (which needs to be in a final state)
194     * The order imposed by this method is the priority needed in case of a
195     * multiple match.
196     */
197   public int compareTo(Object obj) {
198     if (obj instanceof FSMInstance) {
199       if(obj == thisreturn 0;
200       FSMInstance other = (FSMInstance)obj;
201       if(length < other.getLength()) return -1;
202       else if(length > other.getLength()) return 1;
203       //equal length
204       else if(priority < other.priorityreturn -1;
205       else if(priority > other.priorityreturn 1;
206       //equal priority
207       else return other.fileIndex - fileIndex;
208     else throw new ClassCastException(
209                     "Attempt to compare a FSMInstance object to an object " +
210                     "of type " + obj.getClass()+"!");
211   }
212 
213   /** Returns a textual representation of this FSM instance.
214     */
215   public String toString() {
216     String res = "";
217     RightHandSide rhs = getFSMPosition().getAction();
218     if(rhs != null){
219       res += rhs.getPhaseName() "." + rhs.getRuleName() ": \"";
220       try{
221         res += document.getContent().getContent(
222                         getStartAGPosition().getOffset(),
223                         getAGPosition().getOffset()).toString() "\"";
224       }catch(InvalidOffsetException ioe){
225         ioe.printStackTrace(Err.getPrintWriter());
226       }
227 
228       Iterator<String> labelIter = bindings.keySet().iterator();
229       res += "\n{";
230       while(labelIter.hasNext()){
231         String label = (String)labelIter.next();
232         Collection<Annotation> annots = bindings.get(label);
233         res += "\n" + label + ": ";
234         Iterator<Annotation> annIter = annots.iterator();
235         while(annIter.hasNext()){
236           Annotation ann  = annIter.next();
237           res += ann.getType() "(\"";
238           try{
239             res += document.getContent().
240                             getContent(ann.getStartNode().getOffset(),
241                                        ann.getEndNode().getOffset()).toString();
242           }catch(InvalidOffsetException ioe){
243             ioe.printStackTrace(Err.getPrintWriter());
244           }
245           res += "\") ";
246         }
247       }
248       res += "\n}";
249     }else{
250       res +=  "FSM position :" + FSMPosition.getIndex() +
251               "\nFirst matched ANN at:" + startNode.getId() +
252               "\nLast matched ANN at :" + AGPosition.getId() +
253               "\nPriority :" + priority +
254               "\nFile index :" + fileIndex +
255               "\nBindings     :" + bindings;
256     }
257     return res;
258   }
259 
260   /** The FSM for which this FSMInstance is an instance of. */
261   private FSM supportGraph;
262 
263   /** The current state of this FSMInstance */
264   private State FSMPosition;
265 
266   /** The place (Node) in the AnnotationSet where the matching started*/
267   private Node AGPosition, startNode;
268 
269   /** A map from java.lang.String to gate.AnnotationSet describing all the
270     * bindings that took place during matching.
271     * needs to be HashMap instead of simply Map in order to cloneable
272     */
273   private HashMap<String, AnnotationSet> bindings;
274 
275   /** The size of the matched region in the Annotation Set*/
276   private long length = 0;
277 
278   /**
279     * The index in the definition file of the rule from which the AGPosition
280     * state was generated.
281     */
282   private int fileIndex;
283 
284 
285   private Document document;
286   /**
287     * The priority in the definition file of the rule from which the AGPosition
288     * state was generated.
289     */
290   private int priority;
291 
292   /** Static method that provides new FSM instances. This method handles some
293     * basic object pooling in order to reuse the FSMInstance objects.
294     * This is considered to be a good idea because during jape transducing
295     * a large number of FSMIntances are needed for short periods.
296     */
297   public static FSMInstance getNewInstance(FSM supportGraph, State FSMPosition,
298                                            Node startNode, Node AGPosition,
299                                            HashMap<String, AnnotationSet> bindings,
300                                            Document doc) {
301     FSMInstance res;
302     if(myInstances.isEmpty()) res = new FSMInstance(supportGraph, FSMPosition,
303                                                     startNode, AGPosition,
304                                                     bindings, doc);
305     else {
306       res = (FSMInstance)myInstances.removeFirst();
307       res.supportGraph = supportGraph;
308       res.FSMPosition = FSMPosition;
309       res.startNode = startNode;
310       res.AGPosition = AGPosition;
311       res.bindings = bindings;
312     }
313     return res;
314   }
315 
316   /** Static method used to return a FSMInstance that is not needed anymore
317     */
318   public static void returnInstance(FSMInstance ins) {
319     myInstances.addFirst(ins);
320   }
321 
322   /** Release all the FSMInstances that are not currently in use */
323   public static void clearInstances() {
324     myInstances = new LinkedList();
325   }
326 
327   /** The list of existing instances of type FSMInstance */
328   private static LinkedList myInstances;
329 
330   /** The offset in the input List where the last matched annotation was*/
331   static{
332     myInstances = new LinkedList();
333   }
334 
335 // FSMInstance