You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ta...@apache.org on 2016/04/07 21:36:50 UTC
[07/10] tika git commit: TIKA-1936 whitespace clean up
http://git-wip-us.apache.org/repos/asf/tika/blob/d8231f48/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/POIFSContainerDetector.java
----------------------------------------------------------------------
diff --git a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/POIFSContainerDetector.java b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/POIFSContainerDetector.java
index 6770cd1..d4f1e13 100644
--- a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/POIFSContainerDetector.java
+++ b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/POIFSContainerDetector.java
@@ -1,455 +1,455 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.microsoft;
-
-import static org.apache.tika.mime.MediaType.application;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.poi.poifs.filesystem.DirectoryEntry;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.poifs.filesystem.DocumentNode;
-import org.apache.poi.poifs.filesystem.Entry;
-import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
-import org.apache.tika.detect.Detector;
-import org.apache.tika.io.TikaInputStream;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.mime.MediaType;
-
-/**
- * A detector that works on a POIFS OLE2 document
- * to figure out exactly what the file is.
- * This should work for all OLE2 documents, whether
- * they are ones supported by POI or not.
- */
-public class POIFSContainerDetector implements Detector {
-
- /**
- * The OLE base file format
- */
- public static final MediaType OLE = application("x-tika-msoffice");
- /**
- * The protected OOXML base file format
- */
- public static final MediaType OOXML_PROTECTED = application("x-tika-ooxml-protected");
- /**
- * General embedded document type within an OLE2 container
- */
- public static final MediaType GENERAL_EMBEDDED = application("x-tika-msoffice-embedded");
- /**
- * An OLE10 Native embedded document within another OLE2 document
- */
- public static final MediaType OLE10_NATIVE =
- new MediaType(GENERAL_EMBEDDED, "format", "ole10_native");
- /**
- * Some other kind of embedded document, in a CompObj container within another OLE2 document
- */
- public static final MediaType COMP_OBJ =
- new MediaType(GENERAL_EMBEDDED, "format", "comp_obj");
- /**
- * Graph/Charts embedded in PowerPoint and Excel
- */
- public static final MediaType MS_GRAPH_CHART = application("vnd.ms-graph");
- /**
- * Microsoft Excel
- */
- public static final MediaType XLS = application("vnd.ms-excel");
- /**
- * Microsoft Word
- */
- public static final MediaType DOC = application("msword");
- /**
- * Microsoft PowerPoint
- */
- public static final MediaType PPT = application("vnd.ms-powerpoint");
- /**
- * Microsoft Publisher
- */
- public static final MediaType PUB = application("x-mspublisher");
- /**
- * Microsoft Visio
- */
- public static final MediaType VSD = application("vnd.visio");
- /**
- * Microsoft Works
- */
- public static final MediaType WPS = application("vnd.ms-works");
- /**
- * Microsoft Works Spreadsheet 7.0
- */
- public static final MediaType XLR = application("x-tika-msworks-spreadsheet");
- /**
- * Microsoft Outlook
- */
- public static final MediaType MSG = application("vnd.ms-outlook");
- /**
- * Microsoft Project
- */
- public static final MediaType MPP = application("vnd.ms-project");
- /**
- * StarOffice Calc
- */
- public static final MediaType SDC = application("vnd.stardivision.calc");
- /**
- * StarOffice Draw
- */
- public static final MediaType SDA = application("vnd.stardivision.draw");
- /**
- * StarOffice Impress
- */
- public static final MediaType SDD = application("vnd.stardivision.impress");
- /**
- * StarOffice Writer
- */
- public static final MediaType SDW = application("vnd.stardivision.writer");
- /**
- * SolidWorks CAD file
- */
- public static final MediaType SLDWORKS = application("sldworks");
- /**
- * Hangul Word Processor (Korean)
- */
- public static final MediaType HWP = application("x-hwp-v5");
- /**
- * Serial version UID
- */
- private static final long serialVersionUID = -3028021741663605293L;
- /**
- * An ASCII String "StarImpress"
- */
- private static final byte[] STAR_IMPRESS = new byte[]{
- 0x53, 0x74, 0x61, 0x72, 0x49, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73
- };
- /**
- * An ASCII String "StarDraw"
- */
- private static final byte[] STAR_DRAW = new byte[]{
- 0x53, 0x74, 0x61, 0x72, 0x44, 0x72, 0x61, 0x77
- };
- /**
- * An ASCII String "Quill96" for Works Files
- */
- private static final byte[] WORKS_QUILL96 = new byte[]{
- 0x51, 0x75, 0x69, 0x6c, 0x6c, 0x39, 0x36
- };
-
- /**
- * An ASCII String "MSGraph.Chart" for embedded MSGraph files
- * The full designator includes a version, e.g. MSGraph.Chart.8
- */
- private static final byte[] MS_GRAPH_CHART_BYTES = new byte[]{
- 0x4D, 0x53, 0x47, 0x72, 0x61, 0x70, 0x68,
- 0x2E, 0x43, 0x68, 0x61, 0x72, 0x74,
- };
- /**
- * Regexp for matching the MPP Project Data stream
- */
- private static final Pattern mppDataMatch = Pattern.compile("\\s\\s\\s\\d+");
-
- /**
- * Internal detection of the specific kind of OLE2 document, based on the
- * names of the top level streams within the file.
- *
- * @deprecated Use {@link #detect(Set, DirectoryEntry)} and pass the root
- * entry of the filesystem whose type is to be detected, as a
- * second argument.
- */
- protected static MediaType detect(Set<String> names) {
- return detect(names, null);
- }
-
- /**
- * Internal detection of the specific kind of OLE2 document, based on the
- * names of the top-level streams within the file. In some cases the
- * detection may need access to the root {@link DirectoryEntry} of that file
- * for best results. The entry can be given as a second, optional argument.
- *
- * @param names
- * @param root
- * @return
- */
- protected static MediaType detect(Set<String> names, DirectoryEntry root) {
- if (names != null) {
- if (names.contains("SwDocContentMgr") && names.contains("SwDocMgrTempStorage")) {
- return SLDWORKS;
- } else if (names.contains("StarCalcDocument")) {
- // Star Office Calc
- return SDC;
- } else if (names.contains("StarWriterDocument")) {
- return SDW;
- } else if (names.contains("StarDrawDocument3")) {
- if (root == null) {
- /*
- * This is either StarOfficeDraw or StarOfficeImpress, we have
- * to consult the CompObj to distinguish them, if this method is
- * called in "legacy mode", without the root, just return
- * x-tika-msoffice. The one-argument method is only for backward
- * compatibility, if someone calls old API he/she can get the
- * old result.
- */
- return OLE;
- } else {
- return processCompObjFormatType(root);
- }
- } else if (names.contains("\u0005HwpSummaryInformation")) {
- // Hangul Word Processor v5+ (previous aren't OLE2-based)
- return HWP;
- } else if (names.contains("WksSSWorkBook")) {
- // This check has to be before names.contains("Workbook")
- // Works 7.0 spreadsheet files contain both
- // we want to avoid classifying this as Excel
- return XLR;
- } else if (names.contains("Workbook") || names.contains("WORKBOOK")) {
- MediaType tmp = processCompObjFormatType(root);
- if (tmp.equals(MS_GRAPH_CHART)) {
- return MS_GRAPH_CHART;
- }
- return XLS;
- } else if (names.contains("Book")) {
- // Excel 95 or older, we won't be able to parse this....
- return XLS;
- } else if (names.contains("EncryptedPackage") &&
- names.contains("EncryptionInfo") &&
- names.contains("\u0006DataSpaces")) {
- // This is a protected OOXML document, which is an OLE2 file
- // with an Encrypted Stream which holds the OOXML data
- // Without decrypting the stream, we can't tell what kind of
- // OOXML file we have. Return a general OOXML Protected type,
- // and hope the name based detection can guess the rest!
- return OOXML_PROTECTED;
- } else if (names.contains("EncryptedPackage")) {
- return OLE;
- } else if (names.contains("WordDocument")) {
- return DOC;
- } else if (names.contains("Quill")) {
- return PUB;
- } else if (names.contains("PowerPoint Document")) {
- return PPT;
- } else if (names.contains("VisioDocument")) {
- return VSD;
- } else if (names.contains("\u0001Ole10Native")) {
- return OLE10_NATIVE;
- } else if (names.contains("MatOST")) {
- // this occurs on older Works Word Processor files (versions 3.0 and 4.0)
- return WPS;
- } else if (names.contains("CONTENTS") && names.contains("SPELLING")) {
- // Newer Works files
- return WPS;
- } else if (names.contains("Contents") && names.contains("\u0003ObjInfo")) {
- return COMP_OBJ;
- } else if (names.contains("CONTENTS") && names.contains("\u0001CompObj")) {
- // CompObj is a general kind of OLE2 embedding, but this may be an old Works file
- // If we have the Directory, check
- if (root != null) {
- MediaType type = processCompObjFormatType(root);
- if (type == WPS) {
- return WPS;
- } else {
- // Assume it's a general CompObj embedded resource
- return COMP_OBJ;
- }
- } else {
- // Assume it's a general CompObj embedded resource
- return COMP_OBJ;
- }
- } else if (names.contains("CONTENTS")) {
- // CONTENTS without SPELLING nor CompObj normally means some sort
- // of embedded non-office file inside an OLE2 document
- // This is most commonly triggered on nested directories
- return OLE;
- } else if (names.contains("\u0001CompObj") &&
- (names.contains("Props") || names.contains("Props9") || names.contains("Props12"))) {
- // Could be Project, look for common name patterns
- for (String name : names) {
- if (mppDataMatch.matcher(name).matches()) {
- return MPP;
- }
- }
- } else if (names.contains("PerfectOffice_MAIN")) {
- if (names.contains("SlideShow")) {
- return MediaType.application("x-corelpresentations"); // .shw
- } else if (names.contains("PerfectOffice_OBJECTS")) {
- return MediaType.application("x-quattro-pro"); // .wb?
- }
- } else if (names.contains("NativeContent_MAIN")) {
- return MediaType.application("x-quattro-pro"); // .qpw
- } else {
- for (String name : names) {
- if (name.startsWith("__substg1.0_")) {
- return MSG;
- }
- }
- }
- }
-
- // Couldn't detect a more specific type
- return OLE;
- }
-
- /**
- * Is this one of the kinds of formats which uses CompObj to
- * store all of their data, eg Star Draw, Star Impress or
- * (older) Works?
- * If not, it's likely an embedded resource
- */
- private static MediaType processCompObjFormatType(DirectoryEntry root) {
- try {
- Entry e = root.getEntry("\u0001CompObj");
- if (e != null && e.isDocumentEntry()) {
- DocumentNode dn = (DocumentNode) e;
- DocumentInputStream stream = new DocumentInputStream(dn);
- byte[] bytes = IOUtils.toByteArray(stream);
- /*
- * This array contains a string with a normal ASCII name of the
- * application used to create this file. We want to search for that
- * name.
- */
- if (arrayContains(bytes, MS_GRAPH_CHART_BYTES)) {
- return MS_GRAPH_CHART;
- } else if (arrayContains(bytes, STAR_DRAW)) {
- return SDA;
- } else if (arrayContains(bytes, STAR_IMPRESS)) {
- return SDD;
- } else if (arrayContains(bytes, WORKS_QUILL96)) {
- return WPS;
- }
- }
- } catch (Exception e) {
- /*
- * "root.getEntry" can throw FileNotFoundException. The code inside
- * "if" can throw IOExceptions. Theoretically. Practically no
- * exceptions will likely ever appear.
- *
- * Swallow all of them. If any occur, we just assume that we can't
- * distinguish between Draw and Impress and return something safe:
- * x-tika-msoffice
- */
- }
- return OLE;
- }
-
- // poor man's search for byte arrays, replace with some library call if
- // you know one without adding new dependencies
- private static boolean arrayContains(byte[] larger, byte[] smaller) {
- int largerCounter = 0;
- int smallerCounter = 0;
- while (largerCounter < larger.length) {
- if (larger[largerCounter] == smaller[smallerCounter]) {
- largerCounter++;
- smallerCounter++;
- if (smallerCounter == smaller.length) {
- return true;
- }
- } else {
- largerCounter = largerCounter - smallerCounter + 1;
- smallerCounter = 0;
- }
- }
- return false;
- }
-
- private static Set<String> getTopLevelNames(TikaInputStream stream)
- throws IOException {
- // Force the document stream to a (possibly temporary) file
- // so we don't modify the current position of the stream
- File file = stream.getFile();
-
- try {
- NPOIFSFileSystem fs = new NPOIFSFileSystem(file, true);
-
- // Optimize a possible later parsing process by keeping
- // a reference to the already opened POI file system
- stream.setOpenContainer(fs);
-
- return getTopLevelNames(fs.getRoot());
- } catch (IOException e) {
- // Parse error in POI, so we don't know the file type
- return Collections.emptySet();
- } catch (RuntimeException e) {
- // Another problem in POI
- return Collections.emptySet();
- }
- }
-
- private static Set<String> getTopLevelNames(DirectoryNode root) {
- Set<String> names = new HashSet<String>();
- for (Entry entry : root) {
- names.add(entry.getName());
- }
- return names;
- }
-
- public MediaType detect(InputStream input, Metadata metadata)
- throws IOException {
- // Check if we have access to the document
- if (input == null) {
- return MediaType.OCTET_STREAM;
- }
-
- // If this is a TikaInputStream wrapping an already
- // parsed NPOIFileSystem/DirectoryNode, just get the
- // names from the root:
- TikaInputStream tis = TikaInputStream.cast(input);
- Set<String> names = null;
- if (tis != null) {
- Object container = tis.getOpenContainer();
- if (container instanceof NPOIFSFileSystem) {
- names = getTopLevelNames(((NPOIFSFileSystem) container).getRoot());
- } else if (container instanceof DirectoryNode) {
- names = getTopLevelNames((DirectoryNode) container);
- }
- }
-
- if (names == null) {
- // Check if the document starts with the OLE header
- input.mark(8);
- try {
- if (input.read() != 0xd0 || input.read() != 0xcf
- || input.read() != 0x11 || input.read() != 0xe0
- || input.read() != 0xa1 || input.read() != 0xb1
- || input.read() != 0x1a || input.read() != 0xe1) {
- return MediaType.OCTET_STREAM;
- }
- } finally {
- input.reset();
- }
- }
-
- // We can only detect the exact type when given a TikaInputStream
- if (names == null && tis != null) {
- // Look for known top level entry names to detect the document type
- names = getTopLevelNames(tis);
- }
-
- // Detect based on the names (as available)
- if (tis != null &&
- tis.getOpenContainer() != null &&
- tis.getOpenContainer() instanceof NPOIFSFileSystem) {
- return detect(names, ((NPOIFSFileSystem) tis.getOpenContainer()).getRoot());
- } else {
- return detect(names, null);
- }
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.parser.microsoft;
+
+import static org.apache.tika.mime.MediaType.application;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.poi.poifs.filesystem.DirectoryEntry;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.poifs.filesystem.DocumentNode;
+import org.apache.poi.poifs.filesystem.Entry;
+import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
+import org.apache.tika.detect.Detector;
+import org.apache.tika.io.TikaInputStream;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.mime.MediaType;
+
+/**
+ * A detector that works on a POIFS OLE2 document
+ * to figure out exactly what the file is.
+ * This should work for all OLE2 documents, whether
+ * they are ones supported by POI or not.
+ */
+public class POIFSContainerDetector implements Detector {
+
+ /**
+ * The OLE base file format
+ */
+ public static final MediaType OLE = application("x-tika-msoffice");
+ /**
+ * The protected OOXML base file format
+ */
+ public static final MediaType OOXML_PROTECTED = application("x-tika-ooxml-protected");
+ /**
+ * General embedded document type within an OLE2 container
+ */
+ public static final MediaType GENERAL_EMBEDDED = application("x-tika-msoffice-embedded");
+ /**
+ * An OLE10 Native embedded document within another OLE2 document
+ */
+ public static final MediaType OLE10_NATIVE =
+ new MediaType(GENERAL_EMBEDDED, "format", "ole10_native");
+ /**
+ * Some other kind of embedded document, in a CompObj container within another OLE2 document
+ */
+ public static final MediaType COMP_OBJ =
+ new MediaType(GENERAL_EMBEDDED, "format", "comp_obj");
+ /**
+ * Graph/Charts embedded in PowerPoint and Excel
+ */
+ public static final MediaType MS_GRAPH_CHART = application("vnd.ms-graph");
+ /**
+ * Microsoft Excel
+ */
+ public static final MediaType XLS = application("vnd.ms-excel");
+ /**
+ * Microsoft Word
+ */
+ public static final MediaType DOC = application("msword");
+ /**
+ * Microsoft PowerPoint
+ */
+ public static final MediaType PPT = application("vnd.ms-powerpoint");
+ /**
+ * Microsoft Publisher
+ */
+ public static final MediaType PUB = application("x-mspublisher");
+ /**
+ * Microsoft Visio
+ */
+ public static final MediaType VSD = application("vnd.visio");
+ /**
+ * Microsoft Works
+ */
+ public static final MediaType WPS = application("vnd.ms-works");
+ /**
+ * Microsoft Works Spreadsheet 7.0
+ */
+ public static final MediaType XLR = application("x-tika-msworks-spreadsheet");
+ /**
+ * Microsoft Outlook
+ */
+ public static final MediaType MSG = application("vnd.ms-outlook");
+ /**
+ * Microsoft Project
+ */
+ public static final MediaType MPP = application("vnd.ms-project");
+ /**
+ * StarOffice Calc
+ */
+ public static final MediaType SDC = application("vnd.stardivision.calc");
+ /**
+ * StarOffice Draw
+ */
+ public static final MediaType SDA = application("vnd.stardivision.draw");
+ /**
+ * StarOffice Impress
+ */
+ public static final MediaType SDD = application("vnd.stardivision.impress");
+ /**
+ * StarOffice Writer
+ */
+ public static final MediaType SDW = application("vnd.stardivision.writer");
+ /**
+ * SolidWorks CAD file
+ */
+ public static final MediaType SLDWORKS = application("sldworks");
+ /**
+ * Hangul Word Processor (Korean)
+ */
+ public static final MediaType HWP = application("x-hwp-v5");
+ /**
+ * Serial version UID
+ */
+ private static final long serialVersionUID = -3028021741663605293L;
+ /**
+ * An ASCII String "StarImpress"
+ */
+ private static final byte[] STAR_IMPRESS = new byte[]{
+ 0x53, 0x74, 0x61, 0x72, 0x49, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73
+ };
+ /**
+ * An ASCII String "StarDraw"
+ */
+ private static final byte[] STAR_DRAW = new byte[]{
+ 0x53, 0x74, 0x61, 0x72, 0x44, 0x72, 0x61, 0x77
+ };
+ /**
+ * An ASCII String "Quill96" for Works Files
+ */
+ private static final byte[] WORKS_QUILL96 = new byte[]{
+ 0x51, 0x75, 0x69, 0x6c, 0x6c, 0x39, 0x36
+ };
+
+ /**
+ * An ASCII String "MSGraph.Chart" for embedded MSGraph files
+ * The full designator includes a version, e.g. MSGraph.Chart.8
+ */
+ private static final byte[] MS_GRAPH_CHART_BYTES = new byte[]{
+ 0x4D, 0x53, 0x47, 0x72, 0x61, 0x70, 0x68,
+ 0x2E, 0x43, 0x68, 0x61, 0x72, 0x74,
+ };
+ /**
+ * Regexp for matching the MPP Project Data stream
+ */
+ private static final Pattern mppDataMatch = Pattern.compile("\\s\\s\\s\\d+");
+
+ /**
+ * Internal detection of the specific kind of OLE2 document, based on the
+ * names of the top level streams within the file.
+ *
+ * @deprecated Use {@link #detect(Set, DirectoryEntry)} and pass the root
+ * entry of the filesystem whose type is to be detected, as a
+ * second argument.
+ */
+ protected static MediaType detect(Set<String> names) {
+ return detect(names, null);
+ }
+
+ /**
+ * Internal detection of the specific kind of OLE2 document, based on the
+ * names of the top-level streams within the file. In some cases the
+ * detection may need access to the root {@link DirectoryEntry} of that file
+ * for best results. The entry can be given as a second, optional argument.
+ *
+ * @param names
+ * @param root
+ * @return
+ */
+ protected static MediaType detect(Set<String> names, DirectoryEntry root) {
+ if (names != null) {
+ if (names.contains("SwDocContentMgr") && names.contains("SwDocMgrTempStorage")) {
+ return SLDWORKS;
+ } else if (names.contains("StarCalcDocument")) {
+ // Star Office Calc
+ return SDC;
+ } else if (names.contains("StarWriterDocument")) {
+ return SDW;
+ } else if (names.contains("StarDrawDocument3")) {
+ if (root == null) {
+ /*
+ * This is either StarOfficeDraw or StarOfficeImpress, we have
+ * to consult the CompObj to distinguish them, if this method is
+ * called in "legacy mode", without the root, just return
+ * x-tika-msoffice. The one-argument method is only for backward
+ * compatibility, if someone calls old API he/she can get the
+ * old result.
+ */
+ return OLE;
+ } else {
+ return processCompObjFormatType(root);
+ }
+ } else if (names.contains("\u0005HwpSummaryInformation")) {
+ // Hangul Word Processor v5+ (previous aren't OLE2-based)
+ return HWP;
+ } else if (names.contains("WksSSWorkBook")) {
+ // This check has to be before names.contains("Workbook")
+ // Works 7.0 spreadsheet files contain both
+ // we want to avoid classifying this as Excel
+ return XLR;
+ } else if (names.contains("Workbook") || names.contains("WORKBOOK")) {
+ MediaType tmp = processCompObjFormatType(root);
+ if (tmp.equals(MS_GRAPH_CHART)) {
+ return MS_GRAPH_CHART;
+ }
+ return XLS;
+ } else if (names.contains("Book")) {
+ // Excel 95 or older, we won't be able to parse this....
+ return XLS;
+ } else if (names.contains("EncryptedPackage") &&
+ names.contains("EncryptionInfo") &&
+ names.contains("\u0006DataSpaces")) {
+ // This is a protected OOXML document, which is an OLE2 file
+ // with an Encrypted Stream which holds the OOXML data
+ // Without decrypting the stream, we can't tell what kind of
+ // OOXML file we have. Return a general OOXML Protected type,
+ // and hope the name based detection can guess the rest!
+ return OOXML_PROTECTED;
+ } else if (names.contains("EncryptedPackage")) {
+ return OLE;
+ } else if (names.contains("WordDocument")) {
+ return DOC;
+ } else if (names.contains("Quill")) {
+ return PUB;
+ } else if (names.contains("PowerPoint Document")) {
+ return PPT;
+ } else if (names.contains("VisioDocument")) {
+ return VSD;
+ } else if (names.contains("\u0001Ole10Native")) {
+ return OLE10_NATIVE;
+ } else if (names.contains("MatOST")) {
+ // this occurs on older Works Word Processor files (versions 3.0 and 4.0)
+ return WPS;
+ } else if (names.contains("CONTENTS") && names.contains("SPELLING")) {
+ // Newer Works files
+ return WPS;
+ } else if (names.contains("Contents") && names.contains("\u0003ObjInfo")) {
+ return COMP_OBJ;
+ } else if (names.contains("CONTENTS") && names.contains("\u0001CompObj")) {
+ // CompObj is a general kind of OLE2 embedding, but this may be an old Works file
+ // If we have the Directory, check
+ if (root != null) {
+ MediaType type = processCompObjFormatType(root);
+ if (type == WPS) {
+ return WPS;
+ } else {
+ // Assume it's a general CompObj embedded resource
+ return COMP_OBJ;
+ }
+ } else {
+ // Assume it's a general CompObj embedded resource
+ return COMP_OBJ;
+ }
+ } else if (names.contains("CONTENTS")) {
+ // CONTENTS without SPELLING nor CompObj normally means some sort
+ // of embedded non-office file inside an OLE2 document
+ // This is most commonly triggered on nested directories
+ return OLE;
+ } else if (names.contains("\u0001CompObj") &&
+ (names.contains("Props") || names.contains("Props9") || names.contains("Props12"))) {
+ // Could be Project, look for common name patterns
+ for (String name : names) {
+ if (mppDataMatch.matcher(name).matches()) {
+ return MPP;
+ }
+ }
+ } else if (names.contains("PerfectOffice_MAIN")) {
+ if (names.contains("SlideShow")) {
+ return MediaType.application("x-corelpresentations"); // .shw
+ } else if (names.contains("PerfectOffice_OBJECTS")) {
+ return MediaType.application("x-quattro-pro"); // .wb?
+ }
+ } else if (names.contains("NativeContent_MAIN")) {
+ return MediaType.application("x-quattro-pro"); // .qpw
+ } else {
+ for (String name : names) {
+ if (name.startsWith("__substg1.0_")) {
+ return MSG;
+ }
+ }
+ }
+ }
+
+ // Couldn't detect a more specific type
+ return OLE;
+ }
+
+ /**
+ * Is this one of the kinds of formats which uses CompObj to
+ * store all of their data, eg Star Draw, Star Impress or
+ * (older) Works?
+ * If not, it's likely an embedded resource
+ */
+ private static MediaType processCompObjFormatType(DirectoryEntry root) {
+ try {
+ Entry e = root.getEntry("\u0001CompObj");
+ if (e != null && e.isDocumentEntry()) {
+ DocumentNode dn = (DocumentNode) e;
+ DocumentInputStream stream = new DocumentInputStream(dn);
+ byte[] bytes = IOUtils.toByteArray(stream);
+ /*
+ * This array contains a string with a normal ASCII name of the
+ * application used to create this file. We want to search for that
+ * name.
+ */
+ if (arrayContains(bytes, MS_GRAPH_CHART_BYTES)) {
+ return MS_GRAPH_CHART;
+ } else if (arrayContains(bytes, STAR_DRAW)) {
+ return SDA;
+ } else if (arrayContains(bytes, STAR_IMPRESS)) {
+ return SDD;
+ } else if (arrayContains(bytes, WORKS_QUILL96)) {
+ return WPS;
+ }
+ }
+ } catch (Exception e) {
+ /*
+ * "root.getEntry" can throw FileNotFoundException. The code inside
+ * "if" can throw IOExceptions. Theoretically. Practically no
+ * exceptions will likely ever appear.
+ *
+ * Swallow all of them. If any occur, we just assume that we can't
+ * distinguish between Draw and Impress and return something safe:
+ * x-tika-msoffice
+ */
+ }
+ return OLE;
+ }
+
+ // poor man's search for byte arrays, replace with some library call if
+ // you know one without adding new dependencies
+ private static boolean arrayContains(byte[] larger, byte[] smaller) {
+ int largerCounter = 0;
+ int smallerCounter = 0;
+ while (largerCounter < larger.length) {
+ if (larger[largerCounter] == smaller[smallerCounter]) {
+ largerCounter++;
+ smallerCounter++;
+ if (smallerCounter == smaller.length) {
+ return true;
+ }
+ } else {
+ largerCounter = largerCounter - smallerCounter + 1;
+ smallerCounter = 0;
+ }
+ }
+ return false;
+ }
+
+ private static Set<String> getTopLevelNames(TikaInputStream stream)
+ throws IOException {
+ // Force the document stream to a (possibly temporary) file
+ // so we don't modify the current position of the stream
+ File file = stream.getFile();
+
+ try {
+ NPOIFSFileSystem fs = new NPOIFSFileSystem(file, true);
+
+ // Optimize a possible later parsing process by keeping
+ // a reference to the already opened POI file system
+ stream.setOpenContainer(fs);
+
+ return getTopLevelNames(fs.getRoot());
+ } catch (IOException e) {
+ // Parse error in POI, so we don't know the file type
+ return Collections.emptySet();
+ } catch (RuntimeException e) {
+ // Another problem in POI
+ return Collections.emptySet();
+ }
+ }
+
+ private static Set<String> getTopLevelNames(DirectoryNode root) {
+ Set<String> names = new HashSet<String>();
+ for (Entry entry : root) {
+ names.add(entry.getName());
+ }
+ return names;
+ }
+
+ public MediaType detect(InputStream input, Metadata metadata)
+ throws IOException {
+ // Check if we have access to the document
+ if (input == null) {
+ return MediaType.OCTET_STREAM;
+ }
+
+ // If this is a TikaInputStream wrapping an already
+ // parsed NPOIFileSystem/DirectoryNode, just get the
+ // names from the root:
+ TikaInputStream tis = TikaInputStream.cast(input);
+ Set<String> names = null;
+ if (tis != null) {
+ Object container = tis.getOpenContainer();
+ if (container instanceof NPOIFSFileSystem) {
+ names = getTopLevelNames(((NPOIFSFileSystem) container).getRoot());
+ } else if (container instanceof DirectoryNode) {
+ names = getTopLevelNames((DirectoryNode) container);
+ }
+ }
+
+ if (names == null) {
+ // Check if the document starts with the OLE header
+ input.mark(8);
+ try {
+ if (input.read() != 0xd0 || input.read() != 0xcf
+ || input.read() != 0x11 || input.read() != 0xe0
+ || input.read() != 0xa1 || input.read() != 0xb1
+ || input.read() != 0x1a || input.read() != 0xe1) {
+ return MediaType.OCTET_STREAM;
+ }
+ } finally {
+ input.reset();
+ }
+ }
+
+ // We can only detect the exact type when given a TikaInputStream
+ if (names == null && tis != null) {
+ // Look for known top level entry names to detect the document type
+ names = getTopLevelNames(tis);
+ }
+
+ // Detect based on the names (as available)
+ if (tis != null &&
+ tis.getOpenContainer() != null &&
+ tis.getOpenContainer() instanceof NPOIFSFileSystem) {
+ return detect(names, ((NPOIFSFileSystem) tis.getOpenContainer()).getRoot());
+ } else {
+ return detect(names, null);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tika/blob/d8231f48/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/SummaryExtractor.java
----------------------------------------------------------------------
diff --git a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/SummaryExtractor.java b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/SummaryExtractor.java
index e1104de..ce9b2fa 100644
--- a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/SummaryExtractor.java
+++ b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/SummaryExtractor.java
@@ -1,260 +1,260 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.microsoft;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.poi.hpsf.CustomProperties;
-import org.apache.poi.hpsf.DocumentSummaryInformation;
-import org.apache.poi.hpsf.MarkUnsupportedException;
-import org.apache.poi.hpsf.NoPropertySetStreamException;
-import org.apache.poi.hpsf.PropertySet;
-import org.apache.poi.hpsf.SummaryInformation;
-import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.DocumentEntry;
-import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.metadata.MSOffice;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.metadata.Office;
-import org.apache.tika.metadata.OfficeOpenXMLCore;
-import org.apache.tika.metadata.OfficeOpenXMLExtended;
-import org.apache.tika.metadata.PagedText;
-import org.apache.tika.metadata.Property;
-import org.apache.tika.metadata.TikaCoreProperties;
-
-/**
- * Extractor for Common OLE2 (HPSF) metadata
- */
-public class SummaryExtractor {
- private static final Log logger = LogFactory.getLog(AbstractPOIFSExtractor.class);
-
- private static final String SUMMARY_INFORMATION =
- SummaryInformation.DEFAULT_STREAM_NAME;
-
- private static final String DOCUMENT_SUMMARY_INFORMATION =
- DocumentSummaryInformation.DEFAULT_STREAM_NAME;
-
- private final Metadata metadata;
-
- public SummaryExtractor(Metadata metadata) {
- this.metadata = metadata;
- }
-
- public void parseSummaries(NPOIFSFileSystem filesystem)
- throws IOException, TikaException {
- parseSummaries(filesystem.getRoot());
- }
-
- public void parseSummaries(DirectoryNode root)
- throws IOException, TikaException {
- parseSummaryEntryIfExists(root, SUMMARY_INFORMATION);
- parseSummaryEntryIfExists(root, DOCUMENT_SUMMARY_INFORMATION);
- }
-
- private void parseSummaryEntryIfExists(
- DirectoryNode root, String entryName)
- throws IOException, TikaException {
- try {
- DocumentEntry entry =
- (DocumentEntry) root.getEntry(entryName);
- PropertySet properties =
- new PropertySet(new DocumentInputStream(entry));
- if (properties.isSummaryInformation()) {
- parse(new SummaryInformation(properties));
- }
- if (properties.isDocumentSummaryInformation()) {
- parse(new DocumentSummaryInformation(properties));
- }
- } catch (FileNotFoundException e) {
- // entry does not exist, just skip it
- } catch (NoPropertySetStreamException e) {
- // no property stream, just skip it
- } catch (UnexpectedPropertySetTypeException e) {
- throw new TikaException("Unexpected HPSF document", e);
- } catch (MarkUnsupportedException e) {
- throw new TikaException("Invalid DocumentInputStream", e);
- } catch (Exception e) {
- logger.warn("Ignoring unexpected exception while parsing summary entry " + entryName, e);
- }
- }
-
- private void parse(SummaryInformation summary) {
- set(TikaCoreProperties.TITLE, summary.getTitle());
- addMulti(metadata, TikaCoreProperties.CREATOR, summary.getAuthor());
- set(TikaCoreProperties.KEYWORDS, summary.getKeywords());
- // TODO Move to OO subject in Tika 2.0
- set(TikaCoreProperties.TRANSITION_SUBJECT_TO_OO_SUBJECT, summary.getSubject());
- set(TikaCoreProperties.MODIFIER, summary.getLastAuthor());
- set(TikaCoreProperties.COMMENTS, summary.getComments());
- set(OfficeOpenXMLExtended.TEMPLATE, summary.getTemplate());
- set(OfficeOpenXMLExtended.APPLICATION, summary.getApplicationName());
- set(OfficeOpenXMLCore.REVISION, summary.getRevNumber());
- set(TikaCoreProperties.CREATED, summary.getCreateDateTime());
- set(TikaCoreProperties.MODIFIED, summary.getLastSaveDateTime());
- set(TikaCoreProperties.PRINT_DATE, summary.getLastPrinted());
- set(Metadata.EDIT_TIME, summary.getEditTime());
- set(OfficeOpenXMLExtended.DOC_SECURITY, summary.getSecurity());
-
- // New style counts
- set(Office.WORD_COUNT, summary.getWordCount());
- set(Office.CHARACTER_COUNT, summary.getCharCount());
- set(Office.PAGE_COUNT, summary.getPageCount());
- if (summary.getPageCount() > 0) {
- metadata.set(PagedText.N_PAGES, summary.getPageCount());
- }
-
- // Old style, Tika 1.0 properties
- // TODO Remove these in Tika 2.0
- set(Metadata.TEMPLATE, summary.getTemplate());
- set(Metadata.APPLICATION_NAME, summary.getApplicationName());
- set(Metadata.REVISION_NUMBER, summary.getRevNumber());
- set(Metadata.SECURITY, summary.getSecurity());
- set(MSOffice.WORD_COUNT, summary.getWordCount());
- set(MSOffice.CHARACTER_COUNT, summary.getCharCount());
- set(MSOffice.PAGE_COUNT, summary.getPageCount());
- }
-
- private void parse(DocumentSummaryInformation summary) {
- set(OfficeOpenXMLExtended.COMPANY, summary.getCompany());
- addMulti(metadata, OfficeOpenXMLExtended.MANAGER, summary.getManager());
- set(TikaCoreProperties.LANGUAGE, getLanguage(summary));
- set(OfficeOpenXMLCore.CATEGORY, summary.getCategory());
-
- // New style counts
- set(Office.SLIDE_COUNT, summary.getSlideCount());
- if (summary.getSlideCount() > 0) {
- metadata.set(PagedText.N_PAGES, summary.getSlideCount());
- }
- // Old style, Tika 1.0 counts
- // TODO Remove these in Tika 2.0
- set(Metadata.COMPANY, summary.getCompany());
- set(Metadata.MANAGER, summary.getManager());
- set(MSOffice.SLIDE_COUNT, summary.getSlideCount());
- set(Metadata.CATEGORY, summary.getCategory());
-
- parse(summary.getCustomProperties());
- }
-
- private String getLanguage(DocumentSummaryInformation summary) {
- CustomProperties customProperties = summary.getCustomProperties();
- if (customProperties != null) {
- Object value = customProperties.get("Language");
- if (value instanceof String) {
- return (String) value;
- }
- }
- return null;
- }
-
- /**
- * Attempt to parse custom document properties and add to the collection of metadata
- *
- * @param customProperties
- */
- private void parse(CustomProperties customProperties) {
- if (customProperties != null) {
- for (String name : customProperties.nameSet()) {
- // Apply the custom prefix
- String key = Metadata.USER_DEFINED_METADATA_NAME_PREFIX + name;
-
- // Get, convert and save property value
- Object value = customProperties.get(name);
- if (value instanceof String) {
- set(key, (String) value);
- } else if (value instanceof Date) {
- Property prop = Property.externalDate(key);
- metadata.set(prop, (Date) value);
- } else if (value instanceof Boolean) {
- Property prop = Property.externalBoolean(key);
- metadata.set(prop, value.toString());
- } else if (value instanceof Long) {
- Property prop = Property.externalInteger(key);
- metadata.set(prop, ((Long) value).intValue());
- } else if (value instanceof Double) {
- Property prop = Property.externalReal(key);
- metadata.set(prop, (Double) value);
- } else if (value instanceof Integer) {
- Property prop = Property.externalInteger(key);
- metadata.set(prop, ((Integer) value).intValue());
- }
- }
- }
- }
-
- private void set(String name, String value) {
- if (value != null) {
- metadata.set(name, value);
- }
- }
-
- private void set(Property property, String value) {
- if (value != null) {
- metadata.set(property, value);
- }
- }
-
- private void set(Property property, Date value) {
- if (value != null) {
- metadata.set(property, value);
- }
- }
-
- private void set(Property property, int value) {
- if (value > 0) {
- metadata.set(property, value);
- }
- }
-
- private void set(String name, long value) {
- if (value > 0) {
- metadata.set(name, Long.toString(value));
- }
- }
-
- //MS stores values that should be multiple values (e.g. dc:creator)
- //as a semicolon-delimited list. We need to split
- //on semicolon to add each value.
- public static void addMulti(Metadata metadata, Property property, String string) {
- if (string == null) {
- return;
- }
- String[] parts = string.split(";");
- String[] current = metadata.getValues(property);
- Set<String> seen = new HashSet<>();
- if (current != null) {
- for (String val : current) {
- seen.add(val);
- }
- }
- for (String part : parts) {
- if (! seen.contains(part)) {
- metadata.add(property, part);
- seen.add(part);
- }
- }
- }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.parser.microsoft;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.poi.hpsf.CustomProperties;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.MarkUnsupportedException;
+import org.apache.poi.hpsf.NoPropertySetStreamException;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.metadata.MSOffice;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.Office;
+import org.apache.tika.metadata.OfficeOpenXMLCore;
+import org.apache.tika.metadata.OfficeOpenXMLExtended;
+import org.apache.tika.metadata.PagedText;
+import org.apache.tika.metadata.Property;
+import org.apache.tika.metadata.TikaCoreProperties;
+
+/**
+ * Extractor for Common OLE2 (HPSF) metadata
+ */
+public class SummaryExtractor {
+ private static final Log logger = LogFactory.getLog(AbstractPOIFSExtractor.class);
+
+ private static final String SUMMARY_INFORMATION =
+ SummaryInformation.DEFAULT_STREAM_NAME;
+
+ private static final String DOCUMENT_SUMMARY_INFORMATION =
+ DocumentSummaryInformation.DEFAULT_STREAM_NAME;
+
+ private final Metadata metadata;
+
+ public SummaryExtractor(Metadata metadata) {
+ this.metadata = metadata;
+ }
+
+ public void parseSummaries(NPOIFSFileSystem filesystem)
+ throws IOException, TikaException {
+ parseSummaries(filesystem.getRoot());
+ }
+
+ public void parseSummaries(DirectoryNode root)
+ throws IOException, TikaException {
+ parseSummaryEntryIfExists(root, SUMMARY_INFORMATION);
+ parseSummaryEntryIfExists(root, DOCUMENT_SUMMARY_INFORMATION);
+ }
+
+ private void parseSummaryEntryIfExists(
+ DirectoryNode root, String entryName)
+ throws IOException, TikaException {
+ try {
+ DocumentEntry entry =
+ (DocumentEntry) root.getEntry(entryName);
+ PropertySet properties =
+ new PropertySet(new DocumentInputStream(entry));
+ if (properties.isSummaryInformation()) {
+ parse(new SummaryInformation(properties));
+ }
+ if (properties.isDocumentSummaryInformation()) {
+ parse(new DocumentSummaryInformation(properties));
+ }
+ } catch (FileNotFoundException e) {
+ // entry does not exist, just skip it
+ } catch (NoPropertySetStreamException e) {
+ // no property stream, just skip it
+ } catch (UnexpectedPropertySetTypeException e) {
+ throw new TikaException("Unexpected HPSF document", e);
+ } catch (MarkUnsupportedException e) {
+ throw new TikaException("Invalid DocumentInputStream", e);
+ } catch (Exception e) {
+ logger.warn("Ignoring unexpected exception while parsing summary entry " + entryName, e);
+ }
+ }
+
+ private void parse(SummaryInformation summary) {
+ set(TikaCoreProperties.TITLE, summary.getTitle());
+ addMulti(metadata, TikaCoreProperties.CREATOR, summary.getAuthor());
+ set(TikaCoreProperties.KEYWORDS, summary.getKeywords());
+ // TODO Move to OO subject in Tika 2.0
+ set(TikaCoreProperties.TRANSITION_SUBJECT_TO_OO_SUBJECT, summary.getSubject());
+ set(TikaCoreProperties.MODIFIER, summary.getLastAuthor());
+ set(TikaCoreProperties.COMMENTS, summary.getComments());
+ set(OfficeOpenXMLExtended.TEMPLATE, summary.getTemplate());
+ set(OfficeOpenXMLExtended.APPLICATION, summary.getApplicationName());
+ set(OfficeOpenXMLCore.REVISION, summary.getRevNumber());
+ set(TikaCoreProperties.CREATED, summary.getCreateDateTime());
+ set(TikaCoreProperties.MODIFIED, summary.getLastSaveDateTime());
+ set(TikaCoreProperties.PRINT_DATE, summary.getLastPrinted());
+ set(Metadata.EDIT_TIME, summary.getEditTime());
+ set(OfficeOpenXMLExtended.DOC_SECURITY, summary.getSecurity());
+
+ // New style counts
+ set(Office.WORD_COUNT, summary.getWordCount());
+ set(Office.CHARACTER_COUNT, summary.getCharCount());
+ set(Office.PAGE_COUNT, summary.getPageCount());
+ if (summary.getPageCount() > 0) {
+ metadata.set(PagedText.N_PAGES, summary.getPageCount());
+ }
+
+ // Old style, Tika 1.0 properties
+ // TODO Remove these in Tika 2.0
+ set(Metadata.TEMPLATE, summary.getTemplate());
+ set(Metadata.APPLICATION_NAME, summary.getApplicationName());
+ set(Metadata.REVISION_NUMBER, summary.getRevNumber());
+ set(Metadata.SECURITY, summary.getSecurity());
+ set(MSOffice.WORD_COUNT, summary.getWordCount());
+ set(MSOffice.CHARACTER_COUNT, summary.getCharCount());
+ set(MSOffice.PAGE_COUNT, summary.getPageCount());
+ }
+
+ private void parse(DocumentSummaryInformation summary) {
+ set(OfficeOpenXMLExtended.COMPANY, summary.getCompany());
+ addMulti(metadata, OfficeOpenXMLExtended.MANAGER, summary.getManager());
+ set(TikaCoreProperties.LANGUAGE, getLanguage(summary));
+ set(OfficeOpenXMLCore.CATEGORY, summary.getCategory());
+
+ // New style counts
+ set(Office.SLIDE_COUNT, summary.getSlideCount());
+ if (summary.getSlideCount() > 0) {
+ metadata.set(PagedText.N_PAGES, summary.getSlideCount());
+ }
+ // Old style, Tika 1.0 counts
+ // TODO Remove these in Tika 2.0
+ set(Metadata.COMPANY, summary.getCompany());
+ set(Metadata.MANAGER, summary.getManager());
+ set(MSOffice.SLIDE_COUNT, summary.getSlideCount());
+ set(Metadata.CATEGORY, summary.getCategory());
+
+ parse(summary.getCustomProperties());
+ }
+
+ private String getLanguage(DocumentSummaryInformation summary) {
+ CustomProperties customProperties = summary.getCustomProperties();
+ if (customProperties != null) {
+ Object value = customProperties.get("Language");
+ if (value instanceof String) {
+ return (String) value;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attempt to parse custom document properties and add to the collection of metadata
+ *
+ * @param customProperties
+ */
+ private void parse(CustomProperties customProperties) {
+ if (customProperties != null) {
+ for (String name : customProperties.nameSet()) {
+ // Apply the custom prefix
+ String key = Metadata.USER_DEFINED_METADATA_NAME_PREFIX + name;
+
+ // Get, convert and save property value
+ Object value = customProperties.get(name);
+ if (value instanceof String) {
+ set(key, (String) value);
+ } else if (value instanceof Date) {
+ Property prop = Property.externalDate(key);
+ metadata.set(prop, (Date) value);
+ } else if (value instanceof Boolean) {
+ Property prop = Property.externalBoolean(key);
+ metadata.set(prop, value.toString());
+ } else if (value instanceof Long) {
+ Property prop = Property.externalInteger(key);
+ metadata.set(prop, ((Long) value).intValue());
+ } else if (value instanceof Double) {
+ Property prop = Property.externalReal(key);
+ metadata.set(prop, (Double) value);
+ } else if (value instanceof Integer) {
+ Property prop = Property.externalInteger(key);
+ metadata.set(prop, ((Integer) value).intValue());
+ }
+ }
+ }
+ }
+
+ private void set(String name, String value) {
+ if (value != null) {
+ metadata.set(name, value);
+ }
+ }
+
+ private void set(Property property, String value) {
+ if (value != null) {
+ metadata.set(property, value);
+ }
+ }
+
+ private void set(Property property, Date value) {
+ if (value != null) {
+ metadata.set(property, value);
+ }
+ }
+
+ private void set(Property property, int value) {
+ if (value > 0) {
+ metadata.set(property, value);
+ }
+ }
+
+ private void set(String name, long value) {
+ if (value > 0) {
+ metadata.set(name, Long.toString(value));
+ }
+ }
+
+ //MS stores values that should be multiple values (e.g. dc:creator)
+ //as a semicolon-delimited list. We need to split
+ //on semicolon to add each value.
+ public static void addMulti(Metadata metadata, Property property, String string) {
+ if (string == null) {
+ return;
+ }
+ String[] parts = string.split(";");
+ String[] current = metadata.getValues(property);
+ Set<String> seen = new HashSet<>();
+ if (current != null) {
+ for (String val : current) {
+ seen.add(val);
+ }
+ }
+ for (String part : parts) {
+ if (! seen.contains(part)) {
+ metadata.add(property, part);
+ seen.add(part);
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/tika/blob/d8231f48/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TNEFParser.java
----------------------------------------------------------------------
diff --git a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TNEFParser.java b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TNEFParser.java
index e3be386..879546b 100644
--- a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TNEFParser.java
+++ b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TNEFParser.java
@@ -1,135 +1,135 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.microsoft;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.apache.poi.hmef.Attachment;
-import org.apache.poi.hmef.HMEFMessage;
-import org.apache.poi.hmef.attribute.MAPIAttribute;
-import org.apache.poi.hmef.attribute.MAPIRtfAttribute;
-import org.apache.poi.hsmf.datatypes.MAPIProperty;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.extractor.EmbeddedDocumentExtractor;
-import org.apache.tika.extractor.ParsingEmbeddedDocumentExtractor;
-import org.apache.tika.io.TikaInputStream;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.metadata.TikaCoreProperties;
-import org.apache.tika.mime.MediaType;
-import org.apache.tika.parser.AbstractParser;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.sax.EmbeddedContentHandler;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-/**
- * A POI-powered Tika Parser for TNEF (Transport Neutral
- * Encoding Format) messages, aka winmail.dat
- */
-public class TNEFParser extends AbstractParser {
- private static final long serialVersionUID = 4611820730372823452L;
-
- private static final Set<MediaType> SUPPORTED_TYPES =
- Collections.unmodifiableSet(new HashSet<MediaType>(Arrays.asList(
- MediaType.application("vnd.ms-tnef"),
- MediaType.application("ms-tnef"),
- MediaType.application("x-tnef")
- )));
-
- public Set<MediaType> getSupportedTypes(ParseContext context) {
- return SUPPORTED_TYPES;
- }
-
- /**
- * Extracts properties and text from an MS Document input stream
- */
- public void parse(
- InputStream stream, ContentHandler handler,
- Metadata metadata, ParseContext context)
- throws IOException, SAXException, TikaException {
-
- // We work by recursing, so get the appropriate bits
- EmbeddedDocumentExtractor ex = context.get(EmbeddedDocumentExtractor.class);
- EmbeddedDocumentExtractor embeddedExtractor;
- if (ex == null) {
- embeddedExtractor = new ParsingEmbeddedDocumentExtractor(context);
- } else {
- embeddedExtractor = ex;
- }
-
- // Ask POI to process the file for us
- HMEFMessage msg = new HMEFMessage(stream);
-
- // Set the message subject if known
- String subject = msg.getSubject();
- if (subject != null && subject.length() > 0) {
- // TODO: Move to title in Tika 2.0
- metadata.set(TikaCoreProperties.TRANSITION_SUBJECT_TO_DC_TITLE, subject);
- }
-
- // Recurse into the message body RTF
- MAPIAttribute attr = msg.getMessageMAPIAttribute(MAPIProperty.RTF_COMPRESSED);
- if (attr != null && attr instanceof MAPIRtfAttribute) {
- MAPIRtfAttribute rtf = (MAPIRtfAttribute) attr;
- handleEmbedded(
- "message.rtf", "application/rtf",
- rtf.getData(),
- embeddedExtractor, handler
- );
- }
-
- // Recurse into each attachment in turn
- for (Attachment attachment : msg.getAttachments()) {
- String name = attachment.getLongFilename();
- if (name == null || name.length() == 0) {
- name = attachment.getFilename();
- }
- if (name == null || name.length() == 0) {
- String ext = attachment.getExtension();
- if (ext != null) {
- name = "unknown" + ext;
- }
- }
- handleEmbedded(
- name, null, attachment.getContents(),
- embeddedExtractor, handler
- );
- }
- }
-
- private void handleEmbedded(String name, String type, byte[] contents,
- EmbeddedDocumentExtractor embeddedExtractor, ContentHandler handler)
- throws IOException, SAXException, TikaException {
- Metadata metadata = new Metadata();
- if (name != null)
- metadata.set(Metadata.RESOURCE_NAME_KEY, name);
- if (type != null)
- metadata.set(Metadata.CONTENT_TYPE, type);
-
- if (embeddedExtractor.shouldParseEmbedded(metadata)) {
- embeddedExtractor.parseEmbedded(
- TikaInputStream.get(contents),
- new EmbeddedContentHandler(handler),
- metadata, false);
- }
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.parser.microsoft;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.poi.hmef.Attachment;
+import org.apache.poi.hmef.HMEFMessage;
+import org.apache.poi.hmef.attribute.MAPIAttribute;
+import org.apache.poi.hmef.attribute.MAPIRtfAttribute;
+import org.apache.poi.hsmf.datatypes.MAPIProperty;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.extractor.EmbeddedDocumentExtractor;
+import org.apache.tika.extractor.ParsingEmbeddedDocumentExtractor;
+import org.apache.tika.io.TikaInputStream;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.TikaCoreProperties;
+import org.apache.tika.mime.MediaType;
+import org.apache.tika.parser.AbstractParser;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.EmbeddedContentHandler;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * A POI-powered Tika Parser for TNEF (Transport Neutral
+ * Encoding Format) messages, aka winmail.dat
+ */
+public class TNEFParser extends AbstractParser {
+ private static final long serialVersionUID = 4611820730372823452L;
+
+ private static final Set<MediaType> SUPPORTED_TYPES =
+ Collections.unmodifiableSet(new HashSet<MediaType>(Arrays.asList(
+ MediaType.application("vnd.ms-tnef"),
+ MediaType.application("ms-tnef"),
+ MediaType.application("x-tnef")
+ )));
+
+ public Set<MediaType> getSupportedTypes(ParseContext context) {
+ return SUPPORTED_TYPES;
+ }
+
+ /**
+ * Extracts properties and text from an MS Document input stream
+ */
+ public void parse(
+ InputStream stream, ContentHandler handler,
+ Metadata metadata, ParseContext context)
+ throws IOException, SAXException, TikaException {
+
+ // We work by recursing, so get the appropriate bits
+ EmbeddedDocumentExtractor ex = context.get(EmbeddedDocumentExtractor.class);
+ EmbeddedDocumentExtractor embeddedExtractor;
+ if (ex == null) {
+ embeddedExtractor = new ParsingEmbeddedDocumentExtractor(context);
+ } else {
+ embeddedExtractor = ex;
+ }
+
+ // Ask POI to process the file for us
+ HMEFMessage msg = new HMEFMessage(stream);
+
+ // Set the message subject if known
+ String subject = msg.getSubject();
+ if (subject != null && subject.length() > 0) {
+ // TODO: Move to title in Tika 2.0
+ metadata.set(TikaCoreProperties.TRANSITION_SUBJECT_TO_DC_TITLE, subject);
+ }
+
+ // Recurse into the message body RTF
+ MAPIAttribute attr = msg.getMessageMAPIAttribute(MAPIProperty.RTF_COMPRESSED);
+ if (attr != null && attr instanceof MAPIRtfAttribute) {
+ MAPIRtfAttribute rtf = (MAPIRtfAttribute) attr;
+ handleEmbedded(
+ "message.rtf", "application/rtf",
+ rtf.getData(),
+ embeddedExtractor, handler
+ );
+ }
+
+ // Recurse into each attachment in turn
+ for (Attachment attachment : msg.getAttachments()) {
+ String name = attachment.getLongFilename();
+ if (name == null || name.length() == 0) {
+ name = attachment.getFilename();
+ }
+ if (name == null || name.length() == 0) {
+ String ext = attachment.getExtension();
+ if (ext != null) {
+ name = "unknown" + ext;
+ }
+ }
+ handleEmbedded(
+ name, null, attachment.getContents(),
+ embeddedExtractor, handler
+ );
+ }
+ }
+
+ private void handleEmbedded(String name, String type, byte[] contents,
+ EmbeddedDocumentExtractor embeddedExtractor, ContentHandler handler)
+ throws IOException, SAXException, TikaException {
+ Metadata metadata = new Metadata();
+ if (name != null)
+ metadata.set(Metadata.RESOURCE_NAME_KEY, name);
+ if (type != null)
+ metadata.set(Metadata.CONTENT_TYPE, type);
+
+ if (embeddedExtractor.shouldParseEmbedded(metadata)) {
+ embeddedExtractor.parseEmbedded(
+ TikaInputStream.get(contents),
+ new EmbeddedContentHandler(handler),
+ metadata, false);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tika/blob/d8231f48/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TextCell.java
----------------------------------------------------------------------
diff --git a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TextCell.java b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TextCell.java
index 9d85a00..fe75ac9 100644
--- a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TextCell.java
+++ b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/TextCell.java
@@ -1,40 +1,40 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.microsoft;
-
-import org.apache.tika.sax.XHTMLContentHandler;
-import org.xml.sax.SAXException;
-
-/**
- * Text cell.
- */
-public class TextCell implements Cell {
-
- private final String text;
-
- public TextCell(String text) {
- this.text = text;
- }
-
- public void render(XHTMLContentHandler handler) throws SAXException {
- handler.characters(text);
- }
-
- public String toString() {
- return "Text Cell: \"" + text + "\"";
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.parser.microsoft;
+
+import org.apache.tika.sax.XHTMLContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Text cell.
+ */
+public class TextCell implements Cell {
+
+ private final String text;
+
+ public TextCell(String text) {
+ this.text = text;
+ }
+
+ public void render(XHTMLContentHandler handler) throws SAXException {
+ handler.characters(text);
+ }
+
+ public String toString() {
+ return "Text Cell: \"" + text + "\"";
+ }
+}