Parser.java
001 /*
002  *  Parser.java
003  *
004  *  Niraj Aswani, 19/March/07
005  *
006  *  $Id: Parser.html,v 1.0 2007/03/19 16:22:01 niraj Exp $
007  */
008 package gate.creole.annic;
009 
010 import java.io.IOException;
011 import java.io.StringReader;
012 import java.util.ArrayList;
013 import java.util.Iterator;
014 import java.util.List;
015 import java.util.Map;
016 import java.util.Set;
017 
018 import org.jdom.Element;
019 import org.jdom.JDOMException;
020 import org.jdom.input.SAXBuilder;
021 
022 /**
023  * This class provides utility methods to export the Hits to XML and
024  * read them back from XML to HIT objects.
025  
026  @author niraj
027  
028  */
029 public class Parser {
030 
031   /**
032    * HITS XML Element
033    */
034   public static final String HITS = "HITS";
035 
036   /**
037    * HIT  XML Element
038    */
039   public static final String HIT = "HIT";
040 
041   /**
042    *  DOC_ID XML Element
043    */
044   public static final String DOC_ID = "DOC_ID";
045   
046   /**
047    *  ANNOTATION_SET_NAME XML Element
048    */
049   public static final String ANNOTATION_SET_NAME = "ANNOTATION_SET_NAME";
050 
051   /**
052    * START_OFFSET  XML Element
053    */
054   public static final String START_OFFSET = "START_OFFSET";
055 
056   /**
057    * END_OFFSET XML Element
058    */
059   public static final String END_OFFSET = "END_OFFSET";
060 
061   /**
062    * QUERY XML Element
063    */
064   public static final String QUERY = "QUERY";
065 
066   /**
067    * LEFT_CONTEXT_START_OFFSET XML Element
068    */
069   public static final String LEFT_CONTEXT_START_OFFSET = "LEFT_CONTEXT_START_OFFSET";
070 
071   /**
072    * RIGHT_CONTEXT_END_OFFSET XML Element
073    */
074   public static final String RIGHT_CONTEXT_END_OFFSET = "RIGHT_CONTEXT_END_OFFSET";
075 
076   /**
077    * PATTERN_TEXT XML Element
078    */
079   public static final String PATTERN_TEXT = "PATTERN_TEXT";
080 
081   /**
082    * PATTERN_ANNOTATIONS XML Element
083    */
084   public static final String PATTERN_ANNOTATIONS = "PATTERN_ANNOTATIONS";
085 
086   /**
087    * PATTERN_ANNOTATION XML Element
088    */
089   public static final String PATTERN_ANNOTATION = "PATTERN_ANNOTATION";
090 
091   /**
092    * START XML Element
093    */
094   public static final String START = "START";
095 
096   /**
097    * END XML Element
098    */
099   public static final String END = "END";
100 
101   /**
102    * TEXT XML Element
103    */
104   public static final String TEXT = "TEXT";
105 
106   /**
107    * TYPE XML Element
108    */
109   public static final String TYPE = "TYPE";
110 
111   /**
112    * POSITION XML Element
113    */
114   public static final String POSITION = "POSITION";
115 
116   /**
117    * FEATURES XML Element
118    */
119   public static final String FEATURES = "FEATURES";
120 
121   /**
122    * FEATURE XML Element
123    */
124   public static final String FEATURE = "FEATURE";
125 
126   /**
127    * KEY XML Element
128    */
129   public static final String KEY = "KEY";
130 
131   /**
132    * VALUE XML Element
133    */
134   public static final String VALUE = "VALUE";
135 
136   /**
137    * Given an array of instances of Hit, this method returns an xml
138    * representation of the Hit
139    */
140   public static String toXML(Hit[] hits) {
141     StringBuffer sb = new StringBuffer();
142 
143     // first sentence
144     sb.append("<?xml version=\"1.0\"?>\n");
145 
146     // root element
147     sb.append(wrap(HITS, true));
148 
149     // iterating through each hit
150     for(int i = 0; i < hits.length; i++) {
151 
152       // adding a hit element
153       sb.append(wrap(HIT, true));
154       sb.append(wrap(DOC_ID, hits[i].documentID));
155       sb.append(wrap(ANNOTATION_SET_NAME, hits[i].annotationSetName));
156       sb.append(wrap(START_OFFSET, hits[i].startOffset));
157       sb.append(wrap(END_OFFSET, hits[i].endOffset));
158       sb.append(wrap(QUERY, hits[i].queryString));
159       
160 
161       // it hit is an instance of Pattern, we need to add further
162       // information as well
163       if(hits[iinstanceof Pattern) {
164         Pattern pat = (Pattern)hits[i];
165         sb.append(wrap(LEFT_CONTEXT_START_OFFSET, pat
166                 .getLeftContextStartOffset()));
167         sb
168                 .append(wrap(RIGHT_CONTEXT_END_OFFSET, pat
169                         .getRightContextEndOffset()));
170         sb.append(wrap(PATTERN_TEXT, pat.getPatternText()));
171 
172         PatternAnnotation[] annots = pat.getPatternAnnotations();
173 
174         // all annotations should be exported
175         sb.append(wrap(PATTERN_ANNOTATIONS, true));
176 
177         // one annotation at a time
178         for(int j = 0; j < annots.length; j++) {
179           sb.append(wrap(PATTERN_ANNOTATION, true));
180           sb.append(wrap(START, annots[j].getStartOffset()));
181           sb.append(wrap(END, annots[j].getEndOffset()));
182           sb.append(wrap(TEXT, annots[j].getText()));
183           sb.append(wrap(TYPE, annots[j].getType()));
184           sb.append(wrap(POSITION, annots[j].getPosition()));
185           // exporting features as well
186           Map<String, String> features = annots[j].getFeatures();
187           sb.append(wrap(FEATURES, true));
188           // one feature at a time
189           if(features != null) {
190             Set<String> keySet = features.keySet();
191             if(keySet != null) {
192               Iterator<String> iter = keySet.iterator();
193               while(iter.hasNext()) {
194                 sb.append(wrap(FEATURE, true));
195                 String key = iter.next();
196                 sb.append(wrap(KEY, key));
197                 String value = features.get(key);
198                 sb.append(wrap(VALUE, value));
199                 sb.append(wrap(FEATURE, false));
200               }
201             }
202           }
203           sb.append(wrap(FEATURES, false));
204           sb.append(wrap(PATTERN_ANNOTATION, false));
205         }
206         sb.append(wrap(PATTERN_ANNOTATIONS, false));
207       }
208       sb.append(wrap(HIT, false));
209     }
210     sb.append(wrap(HITS, false));
211     return sb.toString();
212   }
213 
214   /**
215    * This method replaces all the special characters (invalid xml characters) with their respective legal sequences.
216    * These includes &, <, >, \ and '.
217    */
218   public static String replaceAmpChars(String s) {
219     s = s.replaceAll("&""&amp;");
220     s = s.replaceAll("<""&lt;");
221     s = s.replaceAll(">""&gt;");
222     s = s.replaceAll("\"""&quot;");
223     s = s.replaceAll("'""&apos;");
224     return s;
225   }
226 
227   /**
228    * Given xml representation of HIT converts them into an array of hits
229    @throws IOException
230    */
231   public static Hit[] fromXML(String xmlthrows IOException, JDOMException {
232     SAXBuilder saxBuilder = new SAXBuilder(false);
233     org.jdom.Document jdomDoc = saxBuilder.build(new StringReader(xml));
234     Element rootElement = jdomDoc.getRootElement();
235     if(!rootElement.getName().equalsIgnoreCase(HITS)) {
236       throw new IOException("Root element must be " + HITS);
237     }
238 
239     // rootElement is HITS
240     // this will internally contains instances of HIT
241     List<?> hitsChildren = rootElement.getChildren(HIT);
242     Hit[] hits = new Hit[hitsChildren.size()];
243 
244     for(int i = 0; i < hitsChildren.size(); i++) {
245       Element hitElem = (Element)hitsChildren.get(i);
246       int startOffset = Integer.parseInt(hitElem.getChildText(START_OFFSET));
247       int endOffset = Integer.parseInt(hitElem.getChildText(END_OFFSET));
248       String docID = hitElem.getChildText(DOC_ID);
249       String annotationSetName = hitElem.getChildText(ANNOTATION_SET_NAME);
250       String queryString = hitElem.getChildText(QUERY);
251 
252       Element patternAnnotations = hitElem.getChild(PATTERN_ANNOTATIONS);
253       if(patternAnnotations == null) {
254         hits[inew Hit(docID, annotationSetName, startOffset, endOffset, queryString);
255         continue;
256       }
257 
258       List<?> patAnnots = patternAnnotations.getChildren(PATTERN_ANNOTATION);
259       List<PatternAnnotation> patAnnotsList = new ArrayList<PatternAnnotation>();
260       for(int j = 0; j < patAnnots.size(); j++) {
261         Element patAnnot = (Element)patAnnots.get(j);
262         PatternAnnotation pa = new PatternAnnotation();
263         pa.setStOffset(Integer.parseInt(patAnnot.getChildText(START)));
264         pa.setEnOffset(Integer.parseInt(patAnnot.getChildText(END)));
265         pa.setPosition(Integer.parseInt(patAnnot.getChildText(POSITION)));
266         pa.setText(patAnnot.getChildText(TEXT));
267         pa.setType(patAnnot.getChildText(TYPE));
268 
269         // we need to find out its features
270         Element featuresElem = patAnnot.getChild(FEATURES);
271         // more than one features possible
272         List<?> featuresElemsList = featuresElem.getChildren(FEATURE);
273         for(int k = 0; k < featuresElemsList.size(); k++) {
274           Element featureElem = (Element)featuresElemsList.get(k);
275           String key = featureElem.getChildText(KEY);
276           String value = featureElem.getChildText(VALUE);
277           pa.addFeature(key, value);
278         }
279         patAnnotsList.add(pa);
280       }
281 
282       String patternText = hitElem.getChildText(PATTERN_TEXT);
283       int leftCSO = Integer.parseInt(hitElem
284               .getChildText(LEFT_CONTEXT_START_OFFSET));
285       int rightCEO = Integer.parseInt(hitElem
286               .getChildText(RIGHT_CONTEXT_END_OFFSET));
287 
288       hits[inew Pattern(docID, annotationSetName, patternText, startOffset, endOffset,
289               leftCSO, rightCEO, patAnnotsList, queryString);
290     }
291     return hits;
292   }
293 
294   /**
295    * wraps the element into the following format
296    @return &lt;elementText&gt;elementValue&lt;/elementText&gt;
297    */
298   public static String wrap(String elementText, String elementValue) {
299     if(elementValue == null) {
300       return "<" + elementText + "> </" + elementText + ">\n";
301     }
302     return "<" + elementText + ">" + replaceAmpChars(elementValue"</"
303             + elementText + ">\n";
304   }
305 
306   /**
307    * wraps the element into the following format
308    
309    @return &lt;elementText&gt;elementValue&lt;/elementText&gt;
310    */
311   public static String wrap(String elementText, int elementValue) {
312     return wrap(elementText, "" + elementValue);
313   }
314 
315   /**
316    * wraps the element into the following format
317    @return "&lt;" + (startElement ? "" : "/") + elementText + "&gt;\n";
318    */
319   public static String wrap(String elementText, boolean startElement) {
320     return "<" (startElement ? "" "/"+ elementText + ">\n";
321   }
322 }