| GateFormatXmlDocumentHandler |

    1   /*
    2    *  GateFormatXmlDocumentHandler.java
    3    *
    4    *  Copyright (c) 1998-2001, 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   *  Cristian URSU,  22 Nov 2000
    12   *
    13   *  $Id: GateFormatXmlDocumentHandler.java,v 1.20 2001/11/15 17:10:45 cursu Exp $
    14   */
    15
    16  package gate.xml;
    17
    18  import java.util.*;
    19  import java.lang.reflect.*;
    20
    21  import gate.corpora.*;
    22  import gate.annotation.*;
    23  import gate.util.*;
    24  import gate.*;
    25  import gate.event.*;
    26
    27
    28  import org.xml.sax.*;
    29  import org.xml.sax.helpers.*;
    30
    31
    32  /**
    33    * Implements the behaviour of the XML reader. This is the reader for
    34    * Gate Xml documents saved with DocumentImplementation.toXml() method.
    35    */
    36  public class GateFormatXmlDocumentHandler extends DefaultHandler{
    37    /** Debug flag */
    38    private static final boolean DEBUG = false;
    39
    40    /**
    41      */
    42    public GateFormatXmlDocumentHandler(gate.Document aDocument){
    43      // This string contains the plain text (the text without markup)
    44      tmpDocContent = new StringBuffer(aDocument.getContent().size().intValue());
    45
    46      // Colector is used later to transform all custom objects into annotation
    47      // objects
    48      colector = new LinkedList();
    49
    50      // The Gate document
    51      doc = aDocument;
    52      currentAnnotationSet = doc.getAnnotations();
    53    }//GateFormatXmlDocumentHandler
    54
    55    /**
    56      * This method is called when the SAX parser encounts the beginning of the
    57      * XML document.
    58      */
    59    public void startDocument() throws org.xml.sax.SAXException {
    60    }// startDocument
    61
    62    /**
    63      * This method is called when the SAX parser encounts the end of the
    64      * XML document.
    65      * Here we set the content of the gate Document to be the one generated
    66      * inside this class (tmpDocContent).
    67      * After that we use the colector to generate all the annotation reffering
    68      * this new gate document.
    69      */
    70    public void endDocument() throws org.xml.sax.SAXException {
    71
    72      // replace the document content with the one without markups
    73      doc.setContent(new DocumentContentImpl(tmpDocContent.toString()));
    74      long docSize = doc.getContent().size().longValue();
    75
    76      // fire the status listener
    77      fireStatusChangedEvent("Total elements: " + elements);
    78
    79    }// endDocument
    80
    81    /**
    82      * This method is called when the SAX parser encounts the beginning of an
    83      * XML element.
    84      */
    85    public void startElement (String uri, String qName, String elemName,
    86                                                               Attributes atts){
    87
    88      // Inform the progress listener to fire only if no of elements processed
    89      // so far is a multiple of ELEMENTS_RATE
    90      if ((++elements % ELEMENTS_RATE) == 0 )
    91          fireStatusChangedEvent("Processed elements : " + elements);
    92
    93      // Set the curent element being processed
    94      currentElementStack.add(elemName);
    95
    96      if("AnnotationSet".equals(elemName))
    97        processAnnotationSetElement(atts);
    98
    99      if("Annotation".equals(elemName))
    100       processAnnotationElement(atts);
    101
    102     if("Feature".equals(elemName))
    103       processFeatureElement(atts);
    104
    105     if("Name".equals(elemName))
    106       processNameElement(atts);
    107
    108     if("Value".equals(elemName))
    109       processValueElement(atts);
    110
    111     if("Node".equals(elemName))
    112       processNodeElement(atts);
    113   }// startElement
    114
    115   /**
    116     * This method is called when the SAX parser encounts the end of an
    117     * XML element.
    118     */
    119     public void endElement (String uri, String qName, String elemName )
    120                                                            throws SAXException{
    121
    122     currentElementStack.pop();
    123     // Deal with Annotation
    124     if ("Annotation".equals(elemName)){
    125       if (currentFeatureMap == null)
    126         currentFeatureMap = Factory.newFeatureMap();
    127       currentAnnot.setFM(currentFeatureMap);
    128       colector.add(currentAnnot);
    129       // Reset current Annot and current featue map
    130       currentAnnot = null;
    131       currentFeatureMap = null;
    132       return;
    133     }// End if
    134     // Deal with Value
    135     if ("Value".equals(elemName) && "Feature".equals(
    136                         (String)currentElementStack.peek())){
    137       // If the Value tag was empty, then an empty string will be created.
    138       if (currentFeatureValue == null) currentFeatureValue = "";
    139     }// End if
    140     // Deal with Feature
    141     if ("Feature".equals(elemName)){
    142       if(currentFeatureName == null){
    143         // Cannot add the (key,value) pair to the map
    144         // One of them is null something was wrong in the XML file.
    145         throw new GateSaxException("A feature name was empty." +
    146           "The annotation that cause it is " +
    147           currentAnnot +
    148           ".Please check the document with a text editor before trying again.");
    149       }else {
    150         if (currentFeatureMap == null){
    151           // The XMl file was somehow altered and a start Feature wasn't found.
    152           throw new GateSaxException("Document not consistent. A start"+
    153           " feature element is missing. " +
    154           "The annotation that cause it is " +
    155           currentAnnot +
    156           "Please check the document with a text editor before trying again.");
    157         }// End if
    158         // Create the appropiate feature name and values
    159         // If those object cannot be created, their string representation will
    160         // be used.
    161         currentFeatureMap.put(createFeatKey(),createFeatValue());
    162 //        currentFeatureMap.put(currentFeatureName,currentFeatureValue);
    163         // Reset current key
    164         currentFeatureKeyClassName = null;
    165         currentFeatureKeyItemClassName = null;
    166         currentFeatureName = null;
    167         // Reset current value
    168         currentFeatureValueClassName = null;
    169         currentFeatureValueItemClassName = null;
    170         currentFeatureValue = null;
    171       }// End if
    172       // Reset the Name & Value pair.
    173       currentFeatureName = null;
    174       currentFeatureValue = null;
    175       return;
    176     }//End if
    177     // Deal GateDocumentFeatures
    178     if ("GateDocumentFeatures".equals(elemName)){
    179       if (currentFeatureMap == null)
    180         currentFeatureMap = Factory.newFeatureMap();
    181       doc.setFeatures(currentFeatureMap);
    182       currentFeatureMap = null;
    183       return;
    184     }// End if
    185
    186     // Deal with AnnotationSet
    187     if ("AnnotationSet".equals(elemName)){
    188       // Create and add annotations to the currentAnnotationSet
    189       Iterator iterator = colector.iterator();
    190       while (iterator.hasNext()){
    191         AnnotationObject annot = (AnnotationObject) iterator.next();
    192         // Clear the annot from the colector
    193         iterator.remove();
    194         // Create a new annotation and add it to the annotation set
    195         try{
    196           currentAnnotationSet.add(annot.getStart(),
    197                                    annot.getEnd(),
    198                                    annot.getElemName(),
    199                                    annot.getFM());
    200         }catch (gate.util.InvalidOffsetException e){
    201           throw new GateSaxException(e);
    202         }// End try
    203       }// End while
    204       // The colector is empty and ready for the next AnnotationSet
    205       return;
    206     }// End if
    207
    208
    209   }//endElement
    210
    211   /**
    212     * This method is called when the SAX parser encounts text in the XML doc.
    213     * Here we calculate the end indices for all the elements present inside the
    214     * stack and update with the new values.
    215     */
    216   public void characters( char[] text,int start,int length) throws SAXException{
    217     // Create a string object based on the reported text
    218     String content = new String(text, start, length);
    219     if ("TextWithNodes".equals((String)currentElementStack.peek())){
    220       processTextOfTextWithNodesElement(content);
    221       return;
    222     }// End if
    223     if ("Name".equals((String)currentElementStack.peek())){
    224       processTextOfNameElement(content);
    225       return;
    226     }// End if
    227     if ("Value".equals((String)currentElementStack.peek())){
    228 //if (currentFeatureName != null && "string".equals(currentFeatureName) &&
    229 //currentAnnot!= null && "Token".equals(currentAnnot.getElemName()) &&
    230 //currentAnnot.getEnd().longValue() == 1063)
    231 //System.out.println("Content=" + content + " start="+ start + " length=" + length);
    232       processTextOfValueElement(content);
    233       return;
    234     }// End if
    235   }//characters
    236
    237   /**
    238     * This method is called when the SAX parser encounts white spaces
    239     */
    240   public void ignorableWhitespace(char ch[],int start,int length) throws
    241                                                                    SAXException{
    242   }//ignorableWhitespace
    243
    244   /**
    245     * Error method.We deal with this exception inside SimpleErrorHandler class
    246     */
    247   public void error(SAXParseException ex) throws SAXException {
    248     // deal with a SAXParseException
    249     // see SimpleErrorhandler class
    250     _seh.error(ex);
    251   }//error
    252
    253   /**
    254     * FatalError method.
    255     */
    256   public void fatalError(SAXParseException ex) throws SAXException {
    257     // deal with a SAXParseException
    258     // see SimpleErrorhandler class
    259     _seh.fatalError(ex);
    260   }//fatalError
    261
    262   /**
    263     * Warning method comment.
    264     */
    265   public void warning(SAXParseException ex) throws SAXException {
    266     // deal with a SAXParseException
    267     // see SimpleErrorhandler class
    268     _seh.warning(ex);
    269   }//warning
    270
    271   // Custom methods section
    272
    273
    274   /** This method deals with a AnnotationSet element. */
    275   private void processAnnotationSetElement(Attributes atts){
    276     if (atts != null){
    277       for (int i = 0; i < atts.getLength(); i++) {
    278        // Extract name and value
    279        String attName  = atts.getLocalName(i);
    280        String attValue = atts.getValue(i);
    281        if ("Name".equals(attName))
    282           currentAnnotationSet = doc.getAnnotations(attValue);
    283       }// End for
    284     }// End if
    285   }//processAnnotationSetElement
    286
    287   /** This method deals with the start of a Name element*/
    288   private void processNameElement(Attributes atts){
    289     if (atts == null) return;
    290     currentFeatureKeyClassName = atts.getValue("className");
    291     currentFeatureKeyItemClassName = atts.getValue("itemClassName");
    292   }// End processNameElement();
    293
    294   /** This method deals with the start of a Value element*/
    295   private void processValueElement(Attributes atts){
    296     if (atts == null) return;
    297     currentFeatureValueClassName = atts.getValue("className");
    298     currentFeatureValueItemClassName = atts.getValue("itemClassName");
    299   }// End processValueElement();
    300
    301   /** This method deals with a Annotation element. */
    302   private void processAnnotationElement(Attributes atts){
    303     if (atts != null){
    304       currentAnnot = new AnnotationObject();
    305       for (int i = 0; i < atts.getLength(); i++) {
    306        // Extract name and value
    307        String attName  = atts.getLocalName(i);
    308        String attValue = atts.getValue(i);
    309
    310        if ("Type".equals(attName))
    311          currentAnnot.setElemName(attValue);
    312
    313        try{
    314          if ("StartNode".equals(attName)){
    315           Integer id = new Integer(attValue);
    316           Long offset = (Long)id2Offset.get(id);
    317           if (offset == null){
    318             throw new GateRuntimeException("Couldn't found Node with id = " +
    319             id +
    320             ".It was specified in annot " +
    321             currentAnnot+
    322             " as a start node!" +
    323             "Check the document with a text editor or something"+
    324             " before trying again.");
    325
    326           }else
    327             currentAnnot.setStart(offset);
    328          }// Endif
    329          if ("EndNode".equals(attName)){
    330           Integer id = new Integer(attValue);
    331           Long offset = (Long) id2Offset.get(id);
    332           if (offset == null){
    333             throw new GateRuntimeException("Couldn't found Node with id = " +
    334             id+
    335             ".It was specified in annot " +
    336             currentAnnot+
    337             " as a end node!" +
    338             "Check the document with a text editor or something"+
    339             " before trying again.");
    340           }else
    341             currentAnnot.setEnd(offset);
    342          }// End if
    343        } catch (NumberFormatException e){
    344           throw new GateRuntimeException("Offsets problems.Couldn't create"+
    345           " Integers from" + " id[" +
    346           attValue + "]) in annot " +
    347           currentAnnot+
    348           "Check the document with a text editor or something,"+
    349           " before trying again");
    350        }// End try
    351       }// End For
    352     }// End if
    353   }//processAnnotationElement
    354
    355   /** This method deals with a Features element. */
    356   private void processFeatureElement(Attributes atts){
    357     // The first time feature is calle it will create a features map.
    358     if (currentFeatureMap == null)
    359       currentFeatureMap = Factory.newFeatureMap();
    360   }//processFeatureElement
    361
    362   /** This method deals with a Node element. */
    363   private void processNodeElement(Attributes atts){
    364     if (atts != null){
    365       for (int i = 0; i < atts.getLength(); i++) {
    366         // Extract name and value
    367         String attName  = atts.getLocalName(i);
    368         String attValue = atts.getValue(i);
    369 //System.out.println("Node : " + attName + "=" +attValue);
    370         if ("id".equals(attName)){
    371           try{
    372             Integer id = new Integer(attValue);
    373             id2Offset.put(id,new Long(tmpDocContent.length()));
    374           }catch(NumberFormatException e){
    375             throw new GateRuntimeException("Coudn't create a node from " +
    376                         attValue + " Expected an integer.");
    377           }// End try
    378         }// End if
    379       }// End for
    380     }// End if
    381   }// processNodeElement();
    382
    383   /** This method deals with a Text belonging to TextWithNodes element. */
    384   private void processTextOfTextWithNodesElement(String text){
    385     tmpDocContent.append(text);
    386   }//processTextOfTextWithNodesElement
    387
    388   /** This method deals with a Text belonging to Name element. */
    389   private void processTextOfNameElement(String text) throws GateSaxException{
    390     if (currentFeatureMap == null)
    391       throw new GateSaxException("Gate xml format processing error:" +
    392       " Found a Name element that is not enclosed into a Feature one while" +
    393       " analyzing the annotation " +
    394       currentAnnot +
    395       "Please check the document with a text editor or something before" +
    396       " trying again.");
    397     else{
    398       // In the entities case, characters() gets called separately for each
    399       // entity so the text needs to be appended.
    400       if (currentFeatureName == null)
    401           currentFeatureName = text;
    402       else
    403         currentFeatureName = currentFeatureName + text;
    404     }// End If
    405   }//processTextOfNameElement();
    406
    407   /** This method deals with a Text belonging to Value element. */
    408   private void processTextOfValueElement(String text) throws GateSaxException{
    409     if (currentFeatureMap == null)
    410       throw new GateSaxException("Gate xml format processing error:" +
    411       " Found a Value element that is not enclosed into a Feature one while" +
    412       " analyzing the annotation " +
    413       currentAnnot+
    414       "Please check the document with a text editor or something before" +
    415       " trying again.");
    416     else{
    417       // In the entities case, characters() gets called separately for each
    418       // entity so the text needs to be appended.
    419       if (currentFeatureValue == null)
    420         currentFeatureValue = text;
    421       else
    422         currentFeatureValue = currentFeatureValue + text;
    423     }// End If
    424   }//processTextOfValueElement();
    425
    426   /** Creates a feature key using this information:
    427     * currentFeatureKeyClassName, currentFeatureKeyItemClassName,
    428     * currentFeatureName. See createFeatObject() method for more details.
    429     */
    430   private Object createFeatKey(){
    431     return createFeatObject(currentFeatureKeyClassName,
    432                             currentFeatureKeyItemClassName,
    433                             currentFeatureName);
    434   }//createFeatKey()
    435
    436   /** Creates a feature value using this information:
    437     * currentFeatureValueClassName, currentFeatureValueItemClassName,
    438     * currentFeatureValue. See createFeatObject() method for more details.
    439     */
    440   private Object createFeatValue(){
    441     return createFeatObject(currentFeatureValueClassName,
    442                             currentFeatureValueItemClassName,
    443                             currentFeatureValue);
    444   }//createFeatValue()
    445
    446   /** This method tries to reconstruct an object given its class name and its
    447    *  string representation. If the object is a Collection then the items
    448    *  from its string representation must be separated by a ";". In that
    449    *  case, the currentFeatureValueItemClassName is used to create items
    450    *  belonging to this class.
    451    *  @param aFeatClassName represents the name of the class of
    452    *  the feat object being created. If it is null then the javaLang.String will
    453    *  be used as default.
    454    *  @param aFeatItemClassName is it used only if aFeatClassName is a
    455    *  collection.If it is null then java.lang.String will be used as default;
    456    *  @param aFeatStringRepresentation sais it all
    457    *  @return an Object created from  aFeatClassName and its
    458    *  aFeatStringRepresentation. If not possible, then aFeatStringRepresentation
    459    *  is returned.
    460    *  @throws GateRuntimeException If it can't create an item, that
    461    *  does not comply with its class definition, to add to the
    462    *  collection.
    463    */
    464   private Object createFeatObject( String aFeatClassName,
    465                                    String aFeatItemClassName,
    466                                    String aFeatStringRepresentation){
    467     // If the string rep is null then the object will be null;
    468     if (aFeatStringRepresentation == null) return null;
    469     if (aFeatClassName == null) aFeatClassName = "java.lang.String";
    470     if (aFeatItemClassName == null) aFeatItemClassName = "java.lang.String";
    471     Class currentFeatClass = null;
    472     try{
    473       currentFeatClass = Gate.getClassLoader().loadClass(aFeatClassName);
    474     }catch (ClassNotFoundException cnfex){
    475       return aFeatStringRepresentation;
    476     }// End try
    477     if (java.util.Collection.class.isAssignableFrom(currentFeatClass)){
    478       Class itemClass = null;
    479       Collection featObject = null;
    480       try{
    481         featObject = (Collection) currentFeatClass.newInstance();
    482         try{
    483           itemClass = Gate.getClassLoader().loadClass(aFeatItemClassName);
    484         }catch(ClassNotFoundException cnfex){
    485           Out.prln("Warning: Item class "+ aFeatItemClassName + " not found."+
    486           "Adding items as Strings to the feature called \"" + currentFeatureName
    487           + "\" in the annotation " + currentAnnot);
    488           itemClass = java.lang.String.class;
    489         }// End try
    490         // Let's detect if itemClass takes a constructor with a String as param
    491         Class[] paramsArray = new Class[1];
    492         paramsArray[0] = java.lang.String.class;
    493         Constructor itemConstructor = null;
    494         boolean addItemAsString = false;
    495         try{
    496          itemConstructor = itemClass.getConstructor(paramsArray);
    497         }catch (NoSuchMethodException  nsme){
    498           addItemAsString = true;
    499         }catch (SecurityException se){
    500           addItemAsString = true;
    501         }// End try
    502         StringTokenizer strTok = new StringTokenizer(
    503                                                 aFeatStringRepresentation,";");
    504         Object[] params = new Object[1];
    505         Object itemObj = null;
    506         while (strTok.hasMoreTokens()){
    507           String itemStrRep = strTok.nextToken();
    508           if (addItemAsString) featObject.add(itemStrRep);
    509           else{
    510             params[0] = itemStrRep;
    511             try{
    512               itemObj = itemConstructor.newInstance(params);
    513             }catch (Exception e){
    514               throw new GateRuntimeException("An item("+
    515                itemStrRep +
    516               ")  does not comply with its class" +
    517               " definition("+aFeatItemClassName+").Happened while tried to"+
    518               " add feature: " +
    519               aFeatStringRepresentation + " to the annotation " + currentAnnot);
    520             }// End try
    521             featObject.add(itemObj);
    522           }// End if
    523         }// End while
    524       }catch(InstantiationException instex ){
    525         return aFeatStringRepresentation;
    526       }catch (IllegalAccessException iae){
    527         return aFeatStringRepresentation;
    528       }// End try
    529       return featObject;
    530     }// End if
    531     // If currentfeatClass is not a Collection,test to see if
    532     // it has a constructor that takes a String as param
    533     Class[] params = new Class[1];
    534     params[0] = java.lang.String.class;
    535     try{
    536       Constructor featConstr = currentFeatClass.getConstructor(params);
    537       Object[] featConstrParams = new Object[1];
    538       featConstrParams[0] = aFeatStringRepresentation;
    539       Object featObject = featConstr.newInstance(featConstrParams);
    540       return featObject;
    541     } catch(Exception e){
    542       return aFeatStringRepresentation;
    543     }// End try
    544   }// createFeatObject()
    545
    546   /**
    547     * This method is called when the SAX parser encounts a comment
    548     * It works only if the XmlDocumentHandler implements a
    549     * com.sun.parser.LexicalEventListener
    550     */
    551   public void comment(String text) throws SAXException {
    552   }//comment
    553
    554   /**
    555     * This method is called when the SAX parser encounts a start of a CDATA
    556     * section
    557     * It works only if the XmlDocumentHandler implements a
    558     * com.sun.parser.LexicalEventListener
    559     */
    560   public void startCDATA()throws SAXException {
    561   }//startCDATA
    562
    563   /**
    564     * This method is called when the SAX parser encounts the end of a CDATA
    565     * section.
    566     * It works only if the XmlDocumentHandler implements a
    567     * com.sun.parser.LexicalEventListener
    568     */
    569   public void endCDATA() throws SAXException {
    570   }//endCDATA
    571
    572   /**
    573     * This method is called when the SAX parser encounts a parsed Entity
    574     * It works only if the XmlDocumentHandler implements a
    575     * com.sun.parser.LexicalEventListener
    576     */
    577   public void startParsedEntity(String name) throws SAXException {
    578   }//startParsedEntity
    579
    580   /**
    581     * This method is called when the SAX parser encounts a parsed entity and
    582     * informs the application if that entity was parsed or not
    583     * It's working only if the CustomDocumentHandler implements a
    584     *  com.sun.parser.LexicalEventListener
    585     */
    586   public void endParsedEntity(String name, boolean included)throws SAXException{
    587   }//endParsedEntity
    588
    589   //StatusReporter Implementation
    590
    591   /**
    592     * This methos is called when a listener is registered with this class
    593     */
    594   public void addStatusListener(StatusListener listener){
    595     myStatusListeners.add(listener);
    596   }//addStatusListener
    597   /**
    598     * This methos is called when a listener is removed
    599     */
    600   public void removeStatusListener(StatusListener listener){
    601     myStatusListeners.remove(listener);
    602   }//removeStatusListener
    603   /**
    604     * This methos is called whenever we need to inform the listener about an
    605     * event.
    606   */
    607   protected void fireStatusChangedEvent(String text){
    608     Iterator listenersIter = myStatusListeners.iterator();
    609     while(listenersIter.hasNext())
    610       ((StatusListener)listenersIter.next()).statusChanged(text);
    611   }//fireStatusChangedEvent
    612
    613   // XmlDocumentHandler member data
    614
    615   /** This constant indicates when to fire the status listener.
    616     * This listener will add an overhead and we don't want a big overhead.
    617     * It will be callled from ELEMENTS_RATE to ELEMENTS_RATE
    618     */
    619   final static  int ELEMENTS_RATE = 128;
    620
    621   /** This object indicates what to do when the parser encounts an error */
    622   private SimpleErrorHandler _seh = new SimpleErrorHandler();
    623
    624   /** The content of the XML document, without any tag */
    625   private StringBuffer tmpDocContent = new StringBuffer("");
    626
    627   /** A gate document */
    628   private gate.Document doc = null;
    629
    630   /** Listeners for status report */
    631   protected List myStatusListeners = new LinkedList();
    632
    633   /** This reports the the number of elements that have beed processed so far*/
    634   private int elements = 0;
    635
    636   /** We need a colection to retain all the CustomObjects that will be
    637     * transformed into annotation over the gate document...
    638     * At the end of every annotation set read the objects in the colector are
    639     * transformed into annotations...
    640     */
    641   private List colector = null;
    642   /** Maps nodes Ids to their offset in the document text. Those offsets will
    643     * be used when creating annotations
    644     */
    645   private Map id2Offset = new TreeMap();
    646   /** Holds the current element read.*/
    647   private Stack currentElementStack = new Stack();
    648   /** This inner objects maps an annotation object. When an annotation from the
    649     * xml document was read this structure is filled out
    650     */
    651   private AnnotationObject currentAnnot = null;
    652   /** A map holding current annotation's features*/
    653   private FeatureMap  currentFeatureMap = null;
    654   /** A key of the current feature*/
    655   private String currentFeatureName = null;
    656   /** The value of the current feature*/
    657   private String currentFeatureValue = null;
    658   /** The class name of the key in the current feature*/
    659   private String currentFeatureKeyClassName = null;
    660   /** If the key is a collection then we need to know the class name of the
    661     * items present in this collection. The next field holds just that.
    662     */
    663   private String currentFeatureKeyItemClassName = null;
    664   /** The class name for the value in the current feature*/
    665   private String currentFeatureValueClassName = null;
    666   /** If the value is a collection then we need to know the class name of the
    667     * items present in this collection. The next field holds just that.
    668     */
    669   private String currentFeatureValueItemClassName = null;
    670   /** the current annotation set that is being created and filled with
    671     * annotations
    672     */
    673   private AnnotationSet currentAnnotationSet = null;
    674
    675   /** An inner class modeling the information contained by an annotation.*/
    676   class  AnnotationObject {
    677     /** Constructor */
    678     public AnnotationObject(){}//AnnotationObject
    679     /** Accesor for the annotation type modeled here as ElemName */
    680     public String getElemName(){
    681       return elemName;
    682     }//getElemName
    683     /** Accesor for the feature map*/
    684     public FeatureMap getFM(){
    685       return fm;
    686     }// getFM()
    687     /** Accesor for the start ofset*/
    688     public Long getStart(){
    689       return start;
    690     }// getStart()
    691     /** Accesor for the end offset*/
    692     public Long getEnd(){
    693       return end;
    694     }// getEnd()
    695     /** Mutator for the annotation type */
    696     public void setElemName(String anElemName){
    697       elemName = anElemName;
    698     }// setElemName();
    699     /** Mutator for the feature map*/
    700     public void setFM(FeatureMap aFm){
    701       fm = aFm;
    702     }// setFM();
    703     /** Mutator for the start offset*/
    704     public void setStart(Long aStart){
    705       start = aStart;
    706     }// setStart();
    707     /** Mutator for the end offset*/
    708     public void setEnd(Long anEnd){
    709       end = anEnd;
    710     }// setEnd();
    711
    712     public String toString(){
    713       return " [type=" + elemName +
    714       " startNode=" + start+
    715       " endNode=" + end+
    716       " features="+ fm +"] ";
    717     }
    718     // Data fields
    719     private String elemName = null;
    720     private FeatureMap fm = null;
    721     private Long start = null;
    722     private Long end  = null;
    723   } // AnnotationObject
    724 }//GateFormatXmlDocumentHandler
    725
    726

| GateFormatXmlDocumentHandler |