1   /*
2    *  FSMInstance.java
3    *
4    *  Copyright (c) 1998-2005, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Valentin Tablan, 05/May/2000
12   *
13   *  $Id: FSMInstance.java,v 1.25 2005/01/11 13:51:34 ian Exp $
14   */
15  package gate.fsm;
16  
17  import java.io.Serializable;
18  import java.util.*;
19  
20  import gate.*;
21  import gate.jape.RightHandSide;
22  import gate.util.Err;
23  import gate.util.InvalidOffsetException;
24  
25  /**
26    * The objects of this class represent instances of working Finite State
27    * Machine during parsing a gate document (annotation set).
28    * In order to completely define the state a FSM is in one needs to store
29    * information regarding:
30    * -the position in the FSM transition graph
31    * -the position in the annotation graph
32    * -the set of bindings that occured up to the current state.
33    *  note that a set of bindings is an object of type Map that maps names
34    * (java.lang.String) to bags of annotations (gate.AnnotationSet)
35    */
36  public class FSMInstance implements Comparable, Cloneable, Serializable {
37  
38    /** Debug flag */
39    private static final boolean DEBUG = false;
40  
41    /** Creates a new FSMInstance object.
42      * @param supportGraph the transition graph of the FSM
43      * @param FSMPosition the state this instance will be in
44      * @param startNode the node in the AnnotationSet where this FSM instance
45      * started the matching
46      * @param AGPosition the node in the AnnotationSet up to which this FSM Instance
47      * advanced during the matching.
48      * @param bindings a HashMap that maps from labels (objects of type String)
49      * to sets of annotations (objects of type AnnotationSet). This map stores
50      * all the bindings that took place during the matching process.
51      * This FSMInstance started the matching on an AnnotationSet from "startNode"
52      * and advanced to "AGPosition"; during this process it traversed the path in
53      * the transition graph "supportGraph" from the initial state to
54      * "FSMPosition" and made the bindings stored in "bindings".
55      */
56    public FSMInstance(FSM supportGraph, State FSMPosition,
57                       Node startNode, Node AGPosition, HashMap bindings,
58                       Document document) {
59  
60      this.supportGraph = supportGraph;
61      this.FSMPosition = FSMPosition;
62      this.startNode = startNode;
63      this.AGPosition = AGPosition;
64      this.bindings = bindings;
65      this.document = document;
66      length = AGPosition.getOffset().longValue() -
67               startNode.getOffset().longValue();
68      fileIndex = FSMPosition.getFileIndex();
69      priority = FSMPosition.getPriority();
70    }
71  
72    /** Returns the FSM transition graph that backs this FSM instance
73      * @return an FSM object
74      */
75    public FSM getSupportGraph(){ return supportGraph; }
76  
77    /** Returns the position in the support graph for this FSM instance
78      * @return an object of type State
79      */
80    public State getFSMPosition(){ return FSMPosition; }
81  
82    /** Sets the position in the support transition graph for this FSM instance
83      * Convenience method for when the state is not known at construction time.
84      */
85    public void setFSMPosition(State newFSMPos) {
86      FSMPosition = newFSMPos;
87      fileIndex = FSMPosition.getFileIndex();
88      priority = FSMPosition.getPriority();
89    }
90  
91    /** Returns the index in the Jape definition file of the rule that caused
92      * the generation of the FSM state this instance is in.
93      * This value is correct if and only if this FSM instance is in a final
94      * state of the FSM transition graph.
95      * @return an int value.
96      */
97    public int getFileIndex(){ return fileIndex; }
98  
99    /** Returns the node in the AnnotationSet from which this FSM instance
100     * started the matching process.
101     * @return a gate.Node object
102     */
103   public Node getStartAGPosition(){ return startNode; }
104 
105   /** Returns the node up to which this FSM instance advanced in the
106     * Annotation graph during the matching process.
107     * @return a gate.Node object
108     */
109   public Node getAGPosition(){ return AGPosition; }
110 
111   /** Sets the current position in the AnnotationSet.
112     * Convenience method for cases when this value is not known at construction
113     * time.
114     * @param node a position in the AnnotationSet
115     */
116   public void setAGPosition(Node node){
117     AGPosition = node;
118     length = AGPosition.getOffset().longValue() -
119              startNode.getOffset().longValue();
120   }
121 
122   /** Gets the map representing the bindings that took place during the matching
123     * process this FSM instance performed.
124     * @return a HashMap object
125     */
126   public HashMap getBindings() { return bindings; }
127 
128   /** Returns the length of the parsed region in the document under scrutiny.
129     * More precisely this is the distnace between the Node in the annotation
130     * graph where the matching started and the current position.
131     * @return a long value
132     */
133   public long getLength() { return length; }
134 
135   /** Overrides the hashCode method from Object so this obejcts can be stored in
136     * hash maps and hash sets.
137     */
138   public int hashCode() {
139     return (int)length ^ priority ^ fileIndex ^ bindings.hashCode() ^
140            FSMPosition.getAction().hashCode();
141   }
142 
143   public boolean equals(Object other){
144     if(other instanceof FSMInstance){
145       FSMInstance otherFSM = (FSMInstance)other;
146       boolean result = length == otherFSM.length &&
147              priority == otherFSM.priority &&
148              fileIndex == otherFSM.fileIndex &&
149              bindings.equals(otherFSM.bindings) &&
150              FSMPosition.getAction().equals(otherFSM.FSMPosition.getAction());
151       return result;
152     }else{
153       throw new ClassCastException(other.getClass().toString());
154     }
155   }
156 
157   /** Returns a clone of this object.
158     * The cloning is done bitwise except for the bindings that are cloned by
159     * themselves
160     * @return an Object value that is actually a FSMInstance object
161     */
162   public Object clone() {
163     //do a classic clone except for bindings which need to be cloned themselves
164     try {
165       FSMInstance clone = (FSMInstance)super.clone();
166       clone.bindings = (HashMap)bindings.clone();
167       return clone;
168     } catch (CloneNotSupportedException cnse) {
169       cnse.printStackTrace(Err.getPrintWriter());
170       return null;
171     }
172   }
173 
174   /*
175   public Object clone() {
176   //do a classic clone except for bindings which need to be cloned themselves
177   //Out.println("Clone!");
178     FSMInstance clone = FSMInstance.getNewInstance(this.supportGraph,
179                                                    this.FSMPosition,
180                                                    this.startNode,
181                                                    this.AGPosition,
182                                                    null);
183     clone.bindings = (HashMap)(bindings.clone());
184     return (FSMInstance)clone;
185   }
186   */
187 
188   /** Implementation of the compareTo method required by the Comparable
189     * interface. The comparison is based on the size of the matched region and
190     * the index in the definition file of the rule associated to this FSM
191     * instance (which needs to be in a final state)
192     * The order imposed by this method is the priority needed in case of a
193     * multiple match.
194     */
195   public int compareTo(Object obj) {
196     if (obj instanceof FSMInstance) {
197       if(obj == this) return 0;
198       FSMInstance other = (FSMInstance)obj;
199       if(length < other.getLength()) return -1;
200       else if(length > other.getLength()) return 1;
201       //equal length
202       else if(priority < other.priority) return -1;
203       else if(priority > other.priority) return 1;
204       //equal priority
205       else return other.fileIndex - fileIndex;
206     } else throw new ClassCastException(
207                     "Attempt to compare a FSMInstance object to an object " +
208                     "of type " + obj.getClass()+"!");
209   }
210 
211   /** Returns a textual representation of this FSM instance.
212     */
213   public String toString() {
214     String res = "";
215     RightHandSide rhs = getFSMPosition().getAction();
216     if(rhs != null){
217       res += rhs.getPhaseName() + "." + rhs.getRuleName() + ": \"";
218       try{
219         res += document.getContent().getContent(
220                         getStartAGPosition().getOffset(),
221                         getAGPosition().getOffset()).toString() + "\"";
222       }catch(InvalidOffsetException ioe){
223         ioe.printStackTrace(Err.getPrintWriter());
224       }
225 
226       Iterator labelIter = bindings.keySet().iterator();
227       res += "\n{";
228       while(labelIter.hasNext()){
229         String label = (String)labelIter.next();
230         Collection annots = (Collection)bindings.get(label);
231         res += "\n" + label + ": ";
232         Iterator annIter = annots.iterator();
233         while(annIter.hasNext()){
234           Annotation ann  = (Annotation)annIter.next();
235           res += ann.getType() + "(\"";
236           try{
237             res += document.getContent().
238                             getContent(ann.getStartNode().getOffset(),
239                                        ann.getEndNode().getOffset()).toString();
240           }catch(InvalidOffsetException ioe){
241             ioe.printStackTrace(Err.getPrintWriter());
242           }
243           res += "\") ";
244         }
245       }
246       res += "\n}";
247     }else{
248       res +=  "FSM position :" + FSMPosition.getIndex() +
249               "\nFirst matched ANN at:" + startNode.getId() +
250               "\nLast matched ANN at :" + AGPosition.getId() +
251               "\nPriority :" + priority +
252               "\nFile index :" + fileIndex +
253               "\nBindings     :" + bindings;
254     }
255     return res;
256   }
257 
258   /** The FSM for which this FSMInstance is an instance of. */
259   private FSM supportGraph;
260 
261   /** The current state of this FSMInstance */
262   private State FSMPosition;
263 
264   /** The place (Node) in the AnnotationSet where the matching started*/
265   private Node AGPosition, startNode;
266 
267   /** A map from java.lang.String to gate.AnnotationSet describing all the
268     * bindings that took place during matching.
269     * needs to be HashMap instead of simply Map in order to cloneable
270     */
271   private HashMap bindings;
272 
273   /** The size of the matched region in the Annotation Set*/
274   private long length = 0;
275 
276   /**
277     * The index in the definition file of the rule from which the AGPosition
278     * state was generated.
279     */
280   private int fileIndex;
281 
282 
283   private Document document;
284   /**
285     * The priority in the definition file of the rule from which the AGPosition
286     * state was generated.
287     */
288   private int priority;
289 
290   /** Static method that provides new FSM instances. This method handles some
291     * basic object pooling in order to reuse the FSMInstance objects.
292     * This is considered to be a good idea because during jape transducing
293     * a large number of FSMIntances are needed for short periods.
294     */
295   public static FSMInstance getNewInstance(FSM supportGraph, State FSMPosition,
296                                            Node startNode, Node AGPosition,
297                                            HashMap bindings, Document doc) {
298     FSMInstance res;
299     if(myInstances.isEmpty()) res = new FSMInstance(supportGraph, FSMPosition,
300                                                     startNode, AGPosition,
301                                                     bindings, doc);
302     else {
303       res = (FSMInstance)myInstances.removeFirst();
304       res.supportGraph = supportGraph;
305       res.FSMPosition = FSMPosition;
306       res.startNode = startNode;
307       res.AGPosition = AGPosition;
308       res.bindings = bindings;
309     }
310     return res;
311   }
312 
313   /** Static method used to return a FSMInstance that is not needed anymore
314     */
315   public static void returnInstance(FSMInstance ins) {
316     myInstances.addFirst(ins);
317   }
318 
319   /** Release all the FSMInstances that are not currently in use */
320   public static void clearInstances() {
321     myInstances = new LinkedList();
322   }
323 
324   /** The list of existing instances of type FSMInstance */
325   private static LinkedList myInstances;
326 
327   /** The offset in the input List where the last matched annotation was*/
328   static{
329     myInstances = new LinkedList();
330   }
331 
332 } // FSMInstance
333