UimaDocumentFormat.java
001 /*
002  *  CasDocumentFormat.java
003  *
004  *  Copyright (c) 2011, 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  *  Thomas Heitz, 27/June/2011
013  *
014  *  $Id:$
015  */
016 
017 package gate.corpora;
018 
019 import gate.Annotation;
020 import gate.AnnotationSet;
021 import gate.Document;
022 import gate.Factory;
023 import gate.FeatureMap;
024 import gate.Resource;
025 import gate.creole.ResourceInstantiationException;
026 import gate.creole.metadata.AutoInstance;
027 import gate.creole.metadata.CreoleResource;
028 import gate.util.DocumentFormatException;
029 import gate.util.InvalidOffsetException;
030 import gate.util.Out;
031 
032 import java.util.ArrayList;
033 import java.util.Arrays;
034 import java.util.HashMap;
035 import java.util.HashSet;
036 import java.util.List;
037 import java.util.Map;
038 import java.util.Set;
039 
040 /**
041  * UIMA XCAS and XMICAS document formats.
042  */
043 @CreoleResource(name = "UIMA Document Format", isPrivate = true,
044     autoinstances = {@AutoInstance(hidden = true)})
045 public class UimaDocumentFormat extends XmlDocumentFormat {
046 
047   private static final long serialVersionUID = -3804187336078120808L;
048 
049   @Override
050   public void unpackMarkup(Document doc, RepositioningInfo repInfo,
051           RepositioningInfo ampCodingInfothrows DocumentFormatException {
052     super.unpackMarkup(doc, repInfo, ampCodingInfo);
053     unpackCasMarkup(doc);
054   }
055 
056   /**
057    * Convert UIMA CAS markups to GATE markups.
058    @param doc XML document already parsed
059    @throws DocumentFormatException error when parsing the file
060    */
061   private void unpackCasMarkup(Document doc)
062     throws DocumentFormatException {
063 
064     AnnotationSet inputAS = doc.getAnnotations("Original markups");
065     AnnotationSet outputAS = doc.getAnnotations("Original markups");
066 
067     // set format specific names
068     String casPrefix;
069     String idName;
070     if (!inputAS.get("CAS").isEmpty()) {
071       casPrefix = "uima.cas.";
072       idName = "_id";
073     else if (!inputAS.get("xmi:XMI").isEmpty()) {
074       casPrefix = "cas:";
075       idName = "xmi:id";
076     else {
077       throw new DocumentFormatException("The document \"" + doc.getName()
078         "\" is neither of XCAS nor XMICAS format.");
079     }
080 
081     // get array/list contained elements annotations
082     for (Annotation annotation : inputAS) {
083       if (annotation.getType().matches(casPrefix + "[a-zA-Z]+(List|Array)")) {
084         try {
085           String elements = doc.getContent().getContent(
086             annotation.getStartNode().getOffset(),
087             annotation.getEndNode().getOffset()).toString();
088           // add contained values as a feature to the array annotation
089           if (!elements.trim().equals("")) {
090             annotation.getFeatures().put("elements", elements);
091           }
092         catch (InvalidOffsetException e) {
093           throw new DocumentFormatException(e);
094         }
095       }
096     }
097 
098     // get document content from SOFA annotations
099     Set<Annotation> sofaSet = inputAS.get(casPrefix + "Sofa");
100     if (sofaSet.size() 1) {
101       Out.prln("More than one UIMA SOFA, annotation offsets won't be correct.");
102     }
103     String documentContent = "";
104     for (Annotation annotation : sofaSet) {
105       documentContent += (Stringannotation.getFeatures().get("sofaString");
106     }
107     doc.setContent(new DocumentContentImpl(documentContent));
108 
109     // remove SOFA annotations
110     inputAS.removeAll(sofaSet);
111 
112     // remove non document annotations
113     inputAS.removeAll(inputAS.get("CAS"));
114     inputAS.removeAll(inputAS.get("xmi:XMI"));
115     inputAS.removeAll(inputAS.get("cas:NULL"));
116 
117     // get the views members, views will be added later as annotation sets
118     List<List<String>> viewList = new ArrayList<List<String>>();
119     for (Annotation view : inputAS.get(casPrefix + "View")) {
120       viewList.add(Arrays.asList(((String)
121         view.getFeatures().get("members")).split("\\s+")));
122     }
123     inputAS.removeAll(inputAS.get(casPrefix + "View"));
124 
125     // fill a map with the id as key and the entity name as value
126     // this is specific to the Temis Luxid CAS format
127     Map<String, String> entityMap = new HashMap<String,String>();
128     for (Annotation entity : inputAS.get("com.temis.uima.Entity")) {
129       FeatureMap features = entity.getFeatures();
130       entityMap.put((Stringfeatures.get(idName),
131         (Stringfeatures.get("value"));
132     }
133 
134     try {
135       // for each UIMA annotation
136       for (Annotation annotation : new HashSet<Annotation>(inputAS)) {
137 
138         FeatureMap features = Factory.newFeatureMap();
139         features.putAll(annotation.getFeatures());
140         String start = (Stringfeatures.get("begin");
141         String end = (Stringfeatures.get("end");
142         String id = (Stringfeatures.get(idName);
143         features.remove("begin")// UIMA feature
144         features.remove("end")// UIMA feature
145         features.remove("isEmptyAndSpan")// GATE feature
146         features.remove("_indexed")// UIMA XCAS feature
147 
148         if (start == null || end == null) {
149           // no offsets so add it as a GATE document feature
150           features.remove(idName);
151           for (Map.Entry<Object,Object> entry : features.entrySet()) {
152             doc.getFeatures().put(annotation.getType()
153               '_' + id + '.' + entry.getKey(), entry.getValue());
154           }
155         else // offsets so add it as a GATE document annotation
156           String entityReference = (Stringfeatures.get("_ref_entity");
157           String type = entityMap.containsKey(entityReference)?
158             entityMap.get(entityReference: annotation.getType();
159           Integer gateId = outputAS.add(
160             Long.valueOf(start), Long.valueOf(end), type, features);
161           int viewCount = 0;
162           for (List<String> viewMembers : viewList) {
163             if (viewMembers.contains(id)) {
164               // add the annotation to the annotation set
165               doc.getAnnotations("CasView" + viewCount)
166                 .add(outputAS.get(gateId));
167             }
168             viewCount++;
169           }
170         }
171         // delete UIMA annotation
172         inputAS.remove(annotation);
173       }
174     catch (InvalidOffsetException e) {
175       throw new DocumentFormatException("Couldn't create annotation.", e);
176     }
177   }
178 
179   @Override
180   public Resource init() throws ResourceInstantiationException {
181     // Register XML mime type
182     MimeType mime = new MimeType("text""xmi+xml");
183     // Register the class handler for this mime type
184     mimeString2ClassHandlerMap.put(mime.getType() "/" + mime.getSubtype(),
185       this);
186     // Register the mime type with mine string
187     mimeString2mimeTypeMap.put(mime.getType() "/" + mime.getSubtype(), mime);
188     // Register file suffixes for this mime type
189     suffixes2mimeTypeMap.put("xcas", mime);
190     suffixes2mimeTypeMap.put("xmicas", mime);
191     suffixes2mimeTypeMap.put("xmi", mime);
192     // Register magic numbers for this mime type
193     magic2mimeTypeMap.put("<CAS version=\"2\">", mime);
194     magic2mimeTypeMap.put("xmlns:cas=", mime);
195     // Set the mimeType for this language resource
196     setMimeType(mime);
197     return this;
198   }
199 
200 }