TikaFormat.java
001 package gate.corpora;
002 
003 import gate.Document;
004 import gate.DocumentFormat;
005 import gate.FeatureMap;
006 import gate.Resource;
007 import gate.creole.ResourceInstantiationException;
008 import gate.creole.metadata.AutoInstance;
009 import gate.creole.metadata.CreoleResource;
010 import gate.event.StatusListener;
011 import gate.util.DocumentFormatException;
012 import gate.xml.XmlDocumentHandler;
013 
014 import java.io.File;
015 import java.io.IOException;
016 import java.io.InputStream;
017 import java.net.URISyntaxException;
018 
019 import org.apache.commons.io.IOUtils;
020 import org.apache.log4j.Logger;
021 import org.apache.tika.config.TikaConfig;
022 import org.apache.tika.exception.TikaException;
023 import org.apache.tika.metadata.Metadata;
024 import org.apache.tika.metadata.Office;
025 import org.apache.tika.metadata.Property;
026 import org.apache.tika.metadata.TikaCoreProperties;
027 import org.apache.tika.parser.ParseContext;
028 import org.apache.tika.parser.Parser;
029 import org.xml.sax.SAXException;
030 
031 @CreoleResource(name = "Apache Tika Document Format", isPrivate = true, autoinstances = {@AutoInstance(hidden = true)})
032 public class TikaFormat extends DocumentFormat {
033 
034   private static final long serialVersionUID = 1L;
035 
036   private static final Logger log = Logger.getLogger(TikaFormat.class);
037 
038   @Override
039   public Resource init() throws ResourceInstantiationException {    
040     super.init();
041     setMimeType(new MimeType("application","tika"));
042     assignMime(getMimeType());
043     assignMime(new MimeType("application","pdf")"pdf");
044     assignMime(new MimeType("application","msword")"doc");
045     assignMime(new MimeType("application","vnd.ms-powerpoint")"ppt");
046     assignMime(new MimeType("application","vnd.ms-excel")"xls");
047     assignMime(new MimeType("application","vnd.openxmlformats-officedocument.wordprocessingml.document")"docx");   
048     assignMime(new MimeType("application","vnd.openxmlformats-officedocument.presentationml.presentation")"pptx");
049     assignMime(new MimeType("application","vnd.openxmlformats-officedocument.spreadsheetml.sheet")"xlsx");       
050     assignMime(new MimeType("application""vnd.oasis.opendocument.text")"odt");
051     assignMime(new MimeType("application""vnd.oasis.opendocument.presentation")"odp");
052     assignMime(new MimeType("application""vnd.oasis.opendocument.spreadsheet")"ods");
053     assignMime(new MimeType("application""rtf")"rtf");
054     
055     //There are bugs in Tika related to ePub as of 0.7
056     //assignMime(new MimeType("application", "epub+zip"), "epub");
057     return this;
058   }
059 
060   private void assignMime(MimeType mime, String... exts) {
061     String mimeString = mime.getType()"/" + mime.getSubtype();
062     mimeString2ClassHandlerMap.put(mimeString, this);
063     mimeString2mimeTypeMap.put(mimeString, mime);
064     for (String ext : exts)
065       suffixes2mimeTypeMap.put(ext,mime);
066   }
067 
068   @Override
069   public Boolean supportsRepositioning() {    
070     return true;
071   }
072 
073   @Override
074   public void unpackMarkup(Document docthrows DocumentFormatException {
075     unpackMarkup(doc, null, null);
076 
077   }
078 
079   @Override
080   public void unpackMarkup(Document doc, RepositioningInfo repInfo,
081           RepositioningInfo ampCodingInfothrows DocumentFormatException {
082     if(doc == null || doc.getSourceUrl() == null) {
083 
084       throw new DocumentFormatException(
085       "GATE document is null or no content found. Nothing to parse!");
086     }// End if
087 
088     // Create a status listener
089     StatusListener statusListener = new StatusListener() {
090       @Override
091       public void statusChanged(String text) {
092         // This is implemented in DocumentFormat.java and inherited here
093         fireStatusChanged(text);
094       }
095     };
096 
097     XmlDocumentHandler ch = new XmlDocumentHandler(doc, this.markupElementsMap,
098             this.element2StringMap);
099     Metadata metadata = extractParserTips(doc);
100 
101     ch.addStatusListener(statusListener);
102     ch.setRepositioningInfo(repInfo);
103     // set the object with ampersand coding positions
104     ch.setAmpCodingInfo(ampCodingInfo);
105     InputStream input = null;     
106     try {
107       Parser tikaParser = new TikaConfig().getParser();      
108       input = doc.getSourceUrl().openStream();
109       tikaParser.parse(input, ch, metadata, new ParseContext());
110       setDocumentFeatures(metadata, doc);
111     catch (IOException e) {
112       throw new DocumentFormatException(e);
113     catch (SAXException e) {
114       throw new DocumentFormatException(e);
115     catch (TikaException e) {
116       throw new DocumentFormatException(e);
117     }
118     finally {
119       IOUtils.closeQuietly(input)// null safe
120       ch.removeStatusListener(statusListener);
121     }
122 
123     if (doc instanceof DocumentImpl) {
124       ((DocumentImpl)doc).setNextAnnotationId(ch.getCustomObjectsId());
125     }
126   }
127 
128   private void setDocumentFeatures(Metadata metadata, Document doc) {
129     FeatureMap fmap = doc.getFeatures();
130     setTikaFeature(metadata, TikaCoreProperties.TITLE, fmap);
131     setTikaFeature(metadata, Office.AUTHOR, fmap);
132     setTikaFeature(metadata, TikaCoreProperties.COMMENTS, fmap);
133     setTikaFeature(metadata, TikaCoreProperties.CREATOR, fmap);
134     if (fmap.get("AUTHORS"== null && fmap.get("AUTHOR"!= null)
135       fmap.put("AUTHORS", fmap.get(Office.AUTHOR));
136     fmap.put("MimeType", metadata.get(Metadata.CONTENT_TYPE));
137   }
138 
139   private void setTikaFeature(Metadata metadata, Property property, FeatureMap fmap) {
140     String value = metadata.get(property);
141     if (value == null) {
142       return;
143     }
144 
145     value = value.trim();
146     if (value.length() == 0) {
147       return;
148     }
149     String key = property.getName().toUpperCase();
150     if (fmap.containsKey(key)) {
151       fmap.put("TIKA_" + key, value);
152     }
153     else {
154       fmap.put(key, value);
155       fmap.put("TIKA_" + key, value);
156     }    
157   }
158 
159   /**
160    * Tries to extract tips for the parser as specified here -
161    * http://tika.apache.org/0.7/parser.html . The tips are not critical
162    * for successful parsing.
163    
164    @param doc
165    @return metadata, not null but may be empty
166    */
167   private Metadata extractParserTips(Document doc) {
168     Metadata metadata = new Metadata();
169     Object inputMime = doc.getFeatures().get("MimeType");
170     if (inputMime instanceof String) {  
171       if (!"application/tika".equals(inputMime)) {
172         metadata.add(Metadata.CONTENT_TYPE, (Stringdoc.getFeatures().get("MimeType"));
173       }
174     }
175     if (doc instanceof DocumentImpl) {
176       if (((DocumentImpl)doc).getMimeType() != null) {
177         metadata.add(Metadata.CONTENT_TYPE, ((DocumentImpl)doc).getMimeType());
178       }
179     }
180     if (doc.getSourceUrl() != null && doc.getSourceUrl().getProtocol().startsWith("file")) {
181       try {
182         File fn =new File(doc.getSourceUrl().toURI());
183         metadata.add(Metadata.RESOURCE_NAME_KEY, fn.getName());
184       catch (URISyntaxException e) {
185         log.debug("Could not extract filename from uri: " + doc.getSourceUrl(), e);
186       catch (IllegalArgumentException e) {
187         log.debug("Could not extract filename from uri: " + doc.getSourceUrl(), e);
188       }
189     }
190     return metadata;
191   }
192 }