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