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:47 UTC

[04/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/ooxml/XSSFExcelExtractorDecorator.java
----------------------------------------------------------------------
diff --git a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XSSFExcelExtractorDecorator.java b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XSSFExcelExtractorDecorator.java
index 6117690..5006d0b 100644
--- a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XSSFExcelExtractorDecorator.java
+++ b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XSSFExcelExtractorDecorator.java
@@ -1,396 +1,396 @@
-/*
- * 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.ooxml;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-import org.apache.poi.hssf.extractor.ExcelExtractor;
-import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
-import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
-import org.apache.poi.openxml4j.opc.OPCPackage;
-import org.apache.poi.openxml4j.opc.PackagePart;
-import org.apache.poi.openxml4j.opc.PackagePartName;
-import org.apache.poi.openxml4j.opc.PackageRelationship;
-import org.apache.poi.openxml4j.opc.PackagingURIHelper;
-import org.apache.poi.openxml4j.opc.TargetMode;
-import org.apache.poi.ss.usermodel.DataFormatter;
-import org.apache.poi.ss.usermodel.HeaderFooter;
-import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
-import org.apache.poi.xssf.eventusermodel.XSSFReader;
-import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
-import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler.SheetContentsHandler;
-import org.apache.poi.xssf.extractor.XSSFEventBasedExcelExtractor;
-import org.apache.poi.xssf.model.CommentsTable;
-import org.apache.poi.xssf.model.StylesTable;
-import org.apache.poi.xssf.usermodel.XSSFComment;
-import org.apache.poi.xssf.usermodel.XSSFRelation;
-import org.apache.poi.xssf.usermodel.XSSFShape;
-import org.apache.poi.xssf.usermodel.XSSFSimpleShape;
-import org.apache.poi.xssf.usermodel.helpers.HeaderFooterHelper;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.metadata.TikaMetadataKeys;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.sax.XHTMLContentHandler;
-import org.apache.xmlbeans.XmlException;
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-
-public class XSSFExcelExtractorDecorator extends AbstractOOXMLExtractor {
-    /**
-     * Allows access to headers/footers from raw xml strings
-     */
-    private static HeaderFooterHelper hfHelper = new HeaderFooterHelper();
-    private final XSSFEventBasedExcelExtractor extractor;
-    private final DataFormatter formatter;
-    private final List<PackagePart> sheetParts = new ArrayList<PackagePart>();
-    private Metadata metadata;
-
-    public XSSFExcelExtractorDecorator(
-            ParseContext context, XSSFEventBasedExcelExtractor extractor, Locale locale) {
-        super(context, extractor);
-
-        this.extractor = extractor;
-        extractor.setFormulasNotResults(false);
-        extractor.setLocale(locale);
-
-        if (locale == null) {
-            formatter = new DataFormatter();
-        } else {
-            formatter = new DataFormatter(locale);
-        }
-    }
-
-    @Override
-    public void getXHTML(
-            ContentHandler handler, Metadata metadata, ParseContext context)
-            throws SAXException, XmlException, IOException, TikaException {
-
-        this.metadata = metadata;
-        metadata.set(TikaMetadataKeys.PROTECTED, "false");
-
-        super.getXHTML(handler, metadata, context);
-    }
-
-    /**
-     * @see org.apache.poi.xssf.extractor.XSSFExcelExtractor#getText()
-     */
-    @Override
-    protected void buildXHTML(XHTMLContentHandler xhtml) throws SAXException,
-            XmlException, IOException {
-        OPCPackage container = extractor.getPackage();
-
-        ReadOnlySharedStringsTable strings;
-        XSSFReader.SheetIterator iter;
-        XSSFReader xssfReader;
-        StylesTable styles;
-        try {
-            xssfReader = new XSSFReader(container);
-            styles = xssfReader.getStylesTable();
-            iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
-            strings = new ReadOnlySharedStringsTable(container);
-        } catch (InvalidFormatException e) {
-            throw new XmlException(e);
-        } catch (OpenXML4JException oe) {
-            throw new XmlException(oe);
-        }
-
-        while (iter.hasNext()) {
-            InputStream stream = iter.next();
-            sheetParts.add(iter.getSheetPart());
-
-            SheetTextAsHTML sheetExtractor = new SheetTextAsHTML(xhtml);
-            CommentsTable comments = iter.getSheetComments();
-
-            // Start, and output the sheet name
-            xhtml.startElement("div");
-            xhtml.element("h1", iter.getSheetName());
-
-            // Extract the main sheet contents
-            xhtml.startElement("table");
-            xhtml.startElement("tbody");
-
-            processSheet(sheetExtractor, comments, styles, strings, stream);
-
-            xhtml.endElement("tbody");
-            xhtml.endElement("table");
-
-            // Output any headers and footers
-            // (Need to process the sheet to get them, so we can't
-            //  do the headers before the contents)
-            for (String header : sheetExtractor.headers) {
-                extractHeaderFooter(header, xhtml);
-            }
-            for (String footer : sheetExtractor.footers) {
-                extractHeaderFooter(footer, xhtml);
-            }
-            processShapes(iter.getShapes(), xhtml);
-            // All done with this sheet
-            xhtml.endElement("div");
-        }
-    }
-
-    private void extractHeaderFooter(String hf, XHTMLContentHandler xhtml)
-            throws SAXException {
-        String content = ExcelExtractor._extractHeaderFooter(
-                new HeaderFooterFromString(hf));
-        if (content.length() > 0) {
-            xhtml.element("p", content);
-        }
-    }
-
-    private void processShapes(List<XSSFShape> shapes, XHTMLContentHandler xhtml) throws SAXException {
-        if (shapes == null) {
-            return;
-        }
-        for (XSSFShape shape : shapes) {
-            if (shape instanceof XSSFSimpleShape) {
-                String sText = ((XSSFSimpleShape) shape).getText();
-                if (sText != null && sText.length() > 0) {
-                    xhtml.element("p", sText);
-                }
-            }
-        }
-    }
-
-    public void processSheet(
-            SheetContentsHandler sheetContentsExtractor,
-            CommentsTable comments,
-            StylesTable styles,
-            ReadOnlySharedStringsTable strings,
-            InputStream sheetInputStream)
-            throws IOException, SAXException {
-        InputSource sheetSource = new InputSource(sheetInputStream);
-        SAXParserFactory saxFactory = SAXParserFactory.newInstance();
-        saxFactory.setNamespaceAware(true);
-        try {
-            SAXParser saxParser = saxFactory.newSAXParser();
-            XMLReader sheetParser = saxParser.getXMLReader();
-            XSSFSheetInterestingPartsCapturer handler =
-                    new XSSFSheetInterestingPartsCapturer(new XSSFSheetXMLHandler(
-                            styles, comments, strings, sheetContentsExtractor, formatter, false));
-            sheetParser.setContentHandler(handler);
-            sheetParser.parse(sheetSource);
-            sheetInputStream.close();
-
-            if (handler.hasProtection) {
-                metadata.set(TikaMetadataKeys.PROTECTED, "true");
-            }
-        } catch (ParserConfigurationException e) {
-            throw new RuntimeException("SAX parser appears to be broken - " + e.getMessage());
-        }
-    }
-
-    /**
-     * In Excel files, sheets have things embedded in them,
-     * and sheet drawings which have the images
-     */
-    @Override
-    protected List<PackagePart> getMainDocumentParts() throws TikaException {
-        List<PackagePart> parts = new ArrayList<PackagePart>();
-        for (PackagePart part : sheetParts) {
-            // Add the sheet
-            parts.add(part);
-
-            // If it has drawings, return those too
-            try {
-                for (PackageRelationship rel : part.getRelationshipsByType(XSSFRelation.DRAWINGS.getRelation())) {
-                    if (rel.getTargetMode() == TargetMode.INTERNAL) {
-                        PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI());
-                        parts.add(rel.getPackage().getPart(relName));
-                    }
-                }
-                for (PackageRelationship rel : part.getRelationshipsByType(XSSFRelation.VML_DRAWINGS.getRelation())) {
-                    if (rel.getTargetMode() == TargetMode.INTERNAL) {
-                        PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI());
-                        parts.add(rel.getPackage().getPart(relName));
-                    }
-                }
-            } catch (InvalidFormatException e) {
-                throw new TikaException("Broken OOXML file", e);
-            }
-        }
-
-        return parts;
-    }
-
-    /**
-     * Turns formatted sheet events into HTML
-     */
-    protected static class SheetTextAsHTML implements SheetContentsHandler {
-        private XHTMLContentHandler xhtml;
-        private List<String> headers;
-        private List<String> footers;
-
-        protected SheetTextAsHTML(XHTMLContentHandler xhtml) {
-            this.xhtml = xhtml;
-            headers = new ArrayList<String>();
-            footers = new ArrayList<String>();
-        }
-
-        public void startRow(int rowNum) {
-            try {
-                xhtml.startElement("tr");
-            } catch (SAXException e) {
-            }
-        }
-
-        public void endRow(int rowNum) {
-            try {
-                xhtml.endElement("tr");
-            } catch (SAXException e) {
-            }
-        }
-
-        public void cell(String cellRef, String formattedValue, XSSFComment comment) {
-            try {
-                xhtml.startElement("td");
-
-                // Main cell contents
-                if (formattedValue != null) {
-                    xhtml.characters(formattedValue);
-                }
-
-                // Comments
-                if (comment != null) {
-                    xhtml.startElement("br");
-                    xhtml.endElement("br");
-                    xhtml.characters(comment.getAuthor());
-                    xhtml.characters(": ");
-                    xhtml.characters(comment.getString().getString());
-                }
-
-                xhtml.endElement("td");
-            } catch (SAXException e) {
-            }
-        }
-
-        public void headerFooter(String text, boolean isHeader, String tagName) {
-            if (isHeader) {
-                headers.add(text);
-            } else {
-                footers.add(text);
-            }
-        }
-    }
-
-    protected static class HeaderFooterFromString implements HeaderFooter {
-        private String text;
-
-        protected HeaderFooterFromString(String text) {
-            this.text = text;
-        }
-
-        public String getCenter() {
-            return hfHelper.getCenterSection(text);
-        }
-
-        public void setCenter(String paramString) {
-        }
-
-        public String getLeft() {
-            return hfHelper.getLeftSection(text);
-        }
-
-        public void setLeft(String paramString) {
-        }
-
-        public String getRight() {
-            return hfHelper.getRightSection(text);
-        }
-
-        public void setRight(String paramString) {
-        }
-    }
-
-    /**
-     * Captures information on interesting tags, whilst
-     * delegating the main work to the formatting handler
-     */
-    protected static class XSSFSheetInterestingPartsCapturer implements ContentHandler {
-        private ContentHandler delegate;
-        private boolean hasProtection = false;
-
-        protected XSSFSheetInterestingPartsCapturer(ContentHandler delegate) {
-            this.delegate = delegate;
-        }
-
-        public void startElement(String uri, String localName, String qName,
-                                 Attributes atts) throws SAXException {
-            if ("sheetProtection".equals(qName)) {
-                hasProtection = true;
-            }
-            delegate.startElement(uri, localName, qName, atts);
-        }
-
-        public void characters(char[] ch, int start, int length)
-                throws SAXException {
-            delegate.characters(ch, start, length);
-        }
-
-        public void endDocument() throws SAXException {
-            delegate.endDocument();
-        }
-
-        public void endElement(String uri, String localName, String qName)
-                throws SAXException {
-            delegate.endElement(uri, localName, qName);
-        }
-
-        public void endPrefixMapping(String prefix) throws SAXException {
-            delegate.endPrefixMapping(prefix);
-        }
-
-        public void ignorableWhitespace(char[] ch, int start, int length)
-                throws SAXException {
-            delegate.ignorableWhitespace(ch, start, length);
-        }
-
-        public void processingInstruction(String target, String data)
-                throws SAXException {
-            delegate.processingInstruction(target, data);
-        }
-
-        public void setDocumentLocator(Locator locator) {
-            delegate.setDocumentLocator(locator);
-        }
-
-        public void skippedEntity(String name) throws SAXException {
-            delegate.skippedEntity(name);
-        }
-
-        public void startDocument() throws SAXException {
-            delegate.startDocument();
-        }
-
-        public void startPrefixMapping(String prefix, String uri)
-                throws SAXException {
-            delegate.startPrefixMapping(prefix, uri);
-        }
-    }
-}
+/*
+ * 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.ooxml;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.poi.hssf.extractor.ExcelExtractor;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.TargetMode;
+import org.apache.poi.ss.usermodel.DataFormatter;
+import org.apache.poi.ss.usermodel.HeaderFooter;
+import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
+import org.apache.poi.xssf.eventusermodel.XSSFReader;
+import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
+import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler.SheetContentsHandler;
+import org.apache.poi.xssf.extractor.XSSFEventBasedExcelExtractor;
+import org.apache.poi.xssf.model.CommentsTable;
+import org.apache.poi.xssf.model.StylesTable;
+import org.apache.poi.xssf.usermodel.XSSFComment;
+import org.apache.poi.xssf.usermodel.XSSFRelation;
+import org.apache.poi.xssf.usermodel.XSSFShape;
+import org.apache.poi.xssf.usermodel.XSSFSimpleShape;
+import org.apache.poi.xssf.usermodel.helpers.HeaderFooterHelper;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.TikaMetadataKeys;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.XHTMLContentHandler;
+import org.apache.xmlbeans.XmlException;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+public class XSSFExcelExtractorDecorator extends AbstractOOXMLExtractor {
+    /**
+     * Allows access to headers/footers from raw xml strings
+     */
+    private static HeaderFooterHelper hfHelper = new HeaderFooterHelper();
+    private final XSSFEventBasedExcelExtractor extractor;
+    private final DataFormatter formatter;
+    private final List<PackagePart> sheetParts = new ArrayList<PackagePart>();
+    private Metadata metadata;
+
+    public XSSFExcelExtractorDecorator(
+            ParseContext context, XSSFEventBasedExcelExtractor extractor, Locale locale) {
+        super(context, extractor);
+
+        this.extractor = extractor;
+        extractor.setFormulasNotResults(false);
+        extractor.setLocale(locale);
+
+        if (locale == null) {
+            formatter = new DataFormatter();
+        } else {
+            formatter = new DataFormatter(locale);
+        }
+    }
+
+    @Override
+    public void getXHTML(
+            ContentHandler handler, Metadata metadata, ParseContext context)
+            throws SAXException, XmlException, IOException, TikaException {
+
+        this.metadata = metadata;
+        metadata.set(TikaMetadataKeys.PROTECTED, "false");
+
+        super.getXHTML(handler, metadata, context);
+    }
+
+    /**
+     * @see org.apache.poi.xssf.extractor.XSSFExcelExtractor#getText()
+     */
+    @Override
+    protected void buildXHTML(XHTMLContentHandler xhtml) throws SAXException,
+            XmlException, IOException {
+        OPCPackage container = extractor.getPackage();
+
+        ReadOnlySharedStringsTable strings;
+        XSSFReader.SheetIterator iter;
+        XSSFReader xssfReader;
+        StylesTable styles;
+        try {
+            xssfReader = new XSSFReader(container);
+            styles = xssfReader.getStylesTable();
+            iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
+            strings = new ReadOnlySharedStringsTable(container);
+        } catch (InvalidFormatException e) {
+            throw new XmlException(e);
+        } catch (OpenXML4JException oe) {
+            throw new XmlException(oe);
+        }
+
+        while (iter.hasNext()) {
+            InputStream stream = iter.next();
+            sheetParts.add(iter.getSheetPart());
+
+            SheetTextAsHTML sheetExtractor = new SheetTextAsHTML(xhtml);
+            CommentsTable comments = iter.getSheetComments();
+
+            // Start, and output the sheet name
+            xhtml.startElement("div");
+            xhtml.element("h1", iter.getSheetName());
+
+            // Extract the main sheet contents
+            xhtml.startElement("table");
+            xhtml.startElement("tbody");
+
+            processSheet(sheetExtractor, comments, styles, strings, stream);
+
+            xhtml.endElement("tbody");
+            xhtml.endElement("table");
+
+            // Output any headers and footers
+            // (Need to process the sheet to get them, so we can't
+            //  do the headers before the contents)
+            for (String header : sheetExtractor.headers) {
+                extractHeaderFooter(header, xhtml);
+            }
+            for (String footer : sheetExtractor.footers) {
+                extractHeaderFooter(footer, xhtml);
+            }
+            processShapes(iter.getShapes(), xhtml);
+            // All done with this sheet
+            xhtml.endElement("div");
+        }
+    }
+
+    private void extractHeaderFooter(String hf, XHTMLContentHandler xhtml)
+            throws SAXException {
+        String content = ExcelExtractor._extractHeaderFooter(
+                new HeaderFooterFromString(hf));
+        if (content.length() > 0) {
+            xhtml.element("p", content);
+        }
+    }
+
+    private void processShapes(List<XSSFShape> shapes, XHTMLContentHandler xhtml) throws SAXException {
+        if (shapes == null) {
+            return;
+        }
+        for (XSSFShape shape : shapes) {
+            if (shape instanceof XSSFSimpleShape) {
+                String sText = ((XSSFSimpleShape) shape).getText();
+                if (sText != null && sText.length() > 0) {
+                    xhtml.element("p", sText);
+                }
+            }
+        }
+    }
+
+    public void processSheet(
+            SheetContentsHandler sheetContentsExtractor,
+            CommentsTable comments,
+            StylesTable styles,
+            ReadOnlySharedStringsTable strings,
+            InputStream sheetInputStream)
+            throws IOException, SAXException {
+        InputSource sheetSource = new InputSource(sheetInputStream);
+        SAXParserFactory saxFactory = SAXParserFactory.newInstance();
+        saxFactory.setNamespaceAware(true);
+        try {
+            SAXParser saxParser = saxFactory.newSAXParser();
+            XMLReader sheetParser = saxParser.getXMLReader();
+            XSSFSheetInterestingPartsCapturer handler =
+                    new XSSFSheetInterestingPartsCapturer(new XSSFSheetXMLHandler(
+                            styles, comments, strings, sheetContentsExtractor, formatter, false));
+            sheetParser.setContentHandler(handler);
+            sheetParser.parse(sheetSource);
+            sheetInputStream.close();
+
+            if (handler.hasProtection) {
+                metadata.set(TikaMetadataKeys.PROTECTED, "true");
+            }
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException("SAX parser appears to be broken - " + e.getMessage());
+        }
+    }
+
+    /**
+     * In Excel files, sheets have things embedded in them,
+     * and sheet drawings which have the images
+     */
+    @Override
+    protected List<PackagePart> getMainDocumentParts() throws TikaException {
+        List<PackagePart> parts = new ArrayList<PackagePart>();
+        for (PackagePart part : sheetParts) {
+            // Add the sheet
+            parts.add(part);
+
+            // If it has drawings, return those too
+            try {
+                for (PackageRelationship rel : part.getRelationshipsByType(XSSFRelation.DRAWINGS.getRelation())) {
+                    if (rel.getTargetMode() == TargetMode.INTERNAL) {
+                        PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI());
+                        parts.add(rel.getPackage().getPart(relName));
+                    }
+                }
+                for (PackageRelationship rel : part.getRelationshipsByType(XSSFRelation.VML_DRAWINGS.getRelation())) {
+                    if (rel.getTargetMode() == TargetMode.INTERNAL) {
+                        PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI());
+                        parts.add(rel.getPackage().getPart(relName));
+                    }
+                }
+            } catch (InvalidFormatException e) {
+                throw new TikaException("Broken OOXML file", e);
+            }
+        }
+
+        return parts;
+    }
+
+    /**
+     * Turns formatted sheet events into HTML
+     */
+    protected static class SheetTextAsHTML implements SheetContentsHandler {
+        private XHTMLContentHandler xhtml;
+        private List<String> headers;
+        private List<String> footers;
+
+        protected SheetTextAsHTML(XHTMLContentHandler xhtml) {
+            this.xhtml = xhtml;
+            headers = new ArrayList<String>();
+            footers = new ArrayList<String>();
+        }
+
+        public void startRow(int rowNum) {
+            try {
+                xhtml.startElement("tr");
+            } catch (SAXException e) {
+            }
+        }
+
+        public void endRow(int rowNum) {
+            try {
+                xhtml.endElement("tr");
+            } catch (SAXException e) {
+            }
+        }
+
+        public void cell(String cellRef, String formattedValue, XSSFComment comment) {
+            try {
+                xhtml.startElement("td");
+
+                // Main cell contents
+                if (formattedValue != null) {
+                    xhtml.characters(formattedValue);
+                }
+
+                // Comments
+                if (comment != null) {
+                    xhtml.startElement("br");
+                    xhtml.endElement("br");
+                    xhtml.characters(comment.getAuthor());
+                    xhtml.characters(": ");
+                    xhtml.characters(comment.getString().getString());
+                }
+
+                xhtml.endElement("td");
+            } catch (SAXException e) {
+            }
+        }
+
+        public void headerFooter(String text, boolean isHeader, String tagName) {
+            if (isHeader) {
+                headers.add(text);
+            } else {
+                footers.add(text);
+            }
+        }
+    }
+
+    protected static class HeaderFooterFromString implements HeaderFooter {
+        private String text;
+
+        protected HeaderFooterFromString(String text) {
+            this.text = text;
+        }
+
+        public String getCenter() {
+            return hfHelper.getCenterSection(text);
+        }
+
+        public void setCenter(String paramString) {
+        }
+
+        public String getLeft() {
+            return hfHelper.getLeftSection(text);
+        }
+
+        public void setLeft(String paramString) {
+        }
+
+        public String getRight() {
+            return hfHelper.getRightSection(text);
+        }
+
+        public void setRight(String paramString) {
+        }
+    }
+
+    /**
+     * Captures information on interesting tags, whilst
+     * delegating the main work to the formatting handler
+     */
+    protected static class XSSFSheetInterestingPartsCapturer implements ContentHandler {
+        private ContentHandler delegate;
+        private boolean hasProtection = false;
+
+        protected XSSFSheetInterestingPartsCapturer(ContentHandler delegate) {
+            this.delegate = delegate;
+        }
+
+        public void startElement(String uri, String localName, String qName,
+                                 Attributes atts) throws SAXException {
+            if ("sheetProtection".equals(qName)) {
+                hasProtection = true;
+            }
+            delegate.startElement(uri, localName, qName, atts);
+        }
+
+        public void characters(char[] ch, int start, int length)
+                throws SAXException {
+            delegate.characters(ch, start, length);
+        }
+
+        public void endDocument() throws SAXException {
+            delegate.endDocument();
+        }
+
+        public void endElement(String uri, String localName, String qName)
+                throws SAXException {
+            delegate.endElement(uri, localName, qName);
+        }
+
+        public void endPrefixMapping(String prefix) throws SAXException {
+            delegate.endPrefixMapping(prefix);
+        }
+
+        public void ignorableWhitespace(char[] ch, int start, int length)
+                throws SAXException {
+            delegate.ignorableWhitespace(ch, start, length);
+        }
+
+        public void processingInstruction(String target, String data)
+                throws SAXException {
+            delegate.processingInstruction(target, data);
+        }
+
+        public void setDocumentLocator(Locator locator) {
+            delegate.setDocumentLocator(locator);
+        }
+
+        public void skippedEntity(String name) throws SAXException {
+            delegate.skippedEntity(name);
+        }
+
+        public void startDocument() throws SAXException {
+            delegate.startDocument();
+        }
+
+        public void startPrefixMapping(String prefix, String uri)
+                throws SAXException {
+            delegate.startPrefixMapping(prefix, uri);
+        }
+    }
+}

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/ooxml/XWPFWordExtractorDecorator.java
----------------------------------------------------------------------
diff --git a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XWPFWordExtractorDecorator.java b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XWPFWordExtractorDecorator.java
index 284d21f..6caf803 100644
--- a/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XWPFWordExtractorDecorator.java
+++ b/tika-parser-modules/tika-parser-office-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XWPFWordExtractorDecorator.java
@@ -1,459 +1,459 @@
-/*
- * 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.ooxml;
-
-import javax.xml.namespace.QName;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.poi.openxml4j.opc.PackagePart;
-import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
-import org.apache.poi.xwpf.model.XWPFCommentsDecorator;
-import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
-import org.apache.poi.xwpf.usermodel.BodyType;
-import org.apache.poi.xwpf.usermodel.IBody;
-import org.apache.poi.xwpf.usermodel.IBodyElement;
-import org.apache.poi.xwpf.usermodel.ICell;
-import org.apache.poi.xwpf.usermodel.IRunElement;
-import org.apache.poi.xwpf.usermodel.ISDTContent;
-import org.apache.poi.xwpf.usermodel.XWPFDocument;
-import org.apache.poi.xwpf.usermodel.XWPFHeaderFooter;
-import org.apache.poi.xwpf.usermodel.XWPFHyperlink;
-import org.apache.poi.xwpf.usermodel.XWPFHyperlinkRun;
-import org.apache.poi.xwpf.usermodel.XWPFParagraph;
-import org.apache.poi.xwpf.usermodel.XWPFPicture;
-import org.apache.poi.xwpf.usermodel.XWPFPictureData;
-import org.apache.poi.xwpf.usermodel.XWPFRun;
-import org.apache.poi.xwpf.usermodel.XWPFSDT;
-import org.apache.poi.xwpf.usermodel.XWPFSDTCell;
-import org.apache.poi.xwpf.usermodel.XWPFStyle;
-import org.apache.poi.xwpf.usermodel.XWPFStyles;
-import org.apache.poi.xwpf.usermodel.XWPFTable;
-import org.apache.poi.xwpf.usermodel.XWPFTableCell;
-import org.apache.poi.xwpf.usermodel.XWPFTableRow;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.parser.microsoft.WordExtractor;
-import org.apache.tika.parser.microsoft.WordExtractor.TagAndStyle;
-import org.apache.tika.sax.XHTMLContentHandler;
-import org.apache.xmlbeans.XmlCursor;
-import org.apache.xmlbeans.XmlException;
-import org.apache.xmlbeans.XmlObject;
-import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
-import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTObject;
-import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
-import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-public class XWPFWordExtractorDecorator extends AbstractOOXMLExtractor {
-
-    // could be improved by using the real delimiter in xchFollow [MS-DOC], v20140721, 2.4.6.3, Part 3, Step 3
-    private static final String LIST_DELIMITER = " ";
-
-
-    private XWPFDocument document;
-    private XWPFStyles styles;
-
-    public XWPFWordExtractorDecorator(ParseContext context, XWPFWordExtractor extractor) {
-        super(context, extractor);
-
-        document = (XWPFDocument) extractor.getDocument();
-        styles = document.getStyles();
-    }
-
-    /**
-     * @see org.apache.poi.xwpf.extractor.XWPFWordExtractor#getText()
-     */
-    @Override
-    protected void buildXHTML(XHTMLContentHandler xhtml)
-            throws SAXException, XmlException, IOException {
-        XWPFHeaderFooterPolicy hfPolicy = document.getHeaderFooterPolicy();
-        XWPFListManager listManager = new XWPFListManager(document);
-        // headers
-        if (hfPolicy != null) {
-            extractHeaders(xhtml, hfPolicy, listManager);
-        }
-
-        // process text in the order that it occurs in
-        extractIBodyText(document, listManager, xhtml);
-
-        // then all document tables
-        if (hfPolicy != null) {
-            extractFooters(xhtml, hfPolicy, listManager);
-        }
-    }
-
-    private void extractIBodyText(IBody bodyElement, XWPFListManager listManager,
-                                  XHTMLContentHandler xhtml)
-            throws SAXException, XmlException, IOException {
-        for (IBodyElement element : bodyElement.getBodyElements()) {
-            if (element instanceof XWPFParagraph) {
-                XWPFParagraph paragraph = (XWPFParagraph) element;
-                extractParagraph(paragraph, listManager, xhtml);
-            }
-            if (element instanceof XWPFTable) {
-                XWPFTable table = (XWPFTable) element;
-                extractTable(table, listManager, xhtml);
-            }
-            if (element instanceof XWPFSDT) {
-                extractSDT((XWPFSDT) element, xhtml);
-            }
-
-        }
-    }
-
-    private void extractSDT(XWPFSDT element, XHTMLContentHandler xhtml) throws SAXException,
-            XmlException, IOException {
-        ISDTContent content = element.getContent();
-        String tag = "p";
-        xhtml.startElement(tag);
-        xhtml.characters(content.getText());
-        xhtml.endElement(tag);
-    }
-
-    private void extractParagraph(XWPFParagraph paragraph, XWPFListManager listManager,
-                                  XHTMLContentHandler xhtml)
-            throws SAXException, XmlException, IOException {
-        // If this paragraph is actually a whole new section, then
-        //  it could have its own headers and footers
-        // Check and handle if so
-        XWPFHeaderFooterPolicy headerFooterPolicy = null;
-        if (paragraph.getCTP().getPPr() != null) {
-            CTSectPr ctSectPr = paragraph.getCTP().getPPr().getSectPr();
-            if (ctSectPr != null) {
-                headerFooterPolicy =
-                        new XWPFHeaderFooterPolicy(document, ctSectPr);
-                extractHeaders(xhtml, headerFooterPolicy, listManager);
-            }
-        }
-
-        // Is this a paragraph, or a heading?
-        String tag = "p";
-        String styleClass = null;
-        if (paragraph.getStyleID() != null) {
-            XWPFStyle style = styles.getStyle(
-                    paragraph.getStyleID()
-            );
-
-            if (style != null && style.getName() != null) {
-                TagAndStyle tas = WordExtractor.buildParagraphTagAndStyle(
-                        style.getName(), paragraph.getPartType() == BodyType.TABLECELL
-                );
-                tag = tas.getTag();
-                styleClass = tas.getStyleClass();
-            }
-        }
-
-        if (styleClass == null) {
-            xhtml.startElement(tag);
-        } else {
-            xhtml.startElement(tag, "class", styleClass);
-        }
-
-        writeParagraphNumber(paragraph, listManager, xhtml);
-        // Output placeholder for any embedded docs:
-
-        // TODO: replace w/ XPath/XQuery:
-        for (XWPFRun run : paragraph.getRuns()) {
-            XmlCursor c = run.getCTR().newCursor();
-            c.selectPath("./*");
-            while (c.toNextSelection()) {
-                XmlObject o = c.getObject();
-                if (o instanceof CTObject) {
-                    XmlCursor c2 = o.newCursor();
-                    c2.selectPath("./*");
-                    while (c2.toNextSelection()) {
-                        XmlObject o2 = c2.getObject();
-
-                        XmlObject embedAtt = o2.selectAttribute(new QName("Type"));
-                        if (embedAtt != null && embedAtt.getDomNode().getNodeValue().equals("Embed")) {
-                            // Type is "Embed"
-                            XmlObject relIDAtt = o2.selectAttribute(new QName("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id"));
-                            if (relIDAtt != null) {
-                                String relID = relIDAtt.getDomNode().getNodeValue();
-                                AttributesImpl attributes = new AttributesImpl();
-                                attributes.addAttribute("", "class", "class", "CDATA", "embedded");
-                                attributes.addAttribute("", "id", "id", "CDATA", relID);
-                                xhtml.startElement("div", attributes);
-                                xhtml.endElement("div");
-                            }
-                        }
-                    }
-                    c2.dispose();
-                }
-            }
-
-            c.dispose();
-        }
-
-        // Attach bookmarks for the paragraph
-        // (In future, we might put them in the right place, for now
-        //  we just put them in the correct paragraph)
-        for (int i = 0; i < paragraph.getCTP().sizeOfBookmarkStartArray(); i++) {
-            CTBookmark bookmark = paragraph.getCTP().getBookmarkStartArray(i);
-            xhtml.startElement("a", "name", bookmark.getName());
-            xhtml.endElement("a");
-        }
-
-        TmpFormatting fmtg = new TmpFormatting(false, false);
-
-        // Do the iruns
-        for (IRunElement run : paragraph.getIRuns()) {
-            if (run instanceof XWPFSDT) {
-                fmtg = closeStyleTags(xhtml, fmtg);
-                processSDTRun((XWPFSDT) run, xhtml);
-                //for now, we're ignoring formatting in sdt
-                //if you hit an sdt reset to false
-                fmtg.setBold(false);
-                fmtg.setItalic(false);
-            } else {
-                fmtg = processRun((XWPFRun) run, paragraph, xhtml, fmtg);
-            }
-        }
-        closeStyleTags(xhtml, fmtg);
-
-
-        // Now do any comments for the paragraph
-        XWPFCommentsDecorator comments = new XWPFCommentsDecorator(paragraph, null);
-        String commentText = comments.getCommentText();
-        if (commentText != null && commentText.length() > 0) {
-            xhtml.characters(commentText);
-        }
-
-        String footnameText = paragraph.getFootnoteText();
-        if (footnameText != null && footnameText.length() > 0) {
-            xhtml.characters(footnameText + "\n");
-        }
-
-        // Also extract any paragraphs embedded in text boxes:
-        for (XmlObject embeddedParagraph : paragraph.getCTP().selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' declare namespace wps='http://schemas.microsoft.com/office/word/2010/wordprocessingShape' .//*/wps:txbx/w:txbxContent/w:p")) {
-            extractParagraph(new XWPFParagraph(CTP.Factory.parse(embeddedParagraph.xmlText()), paragraph.getBody()), listManager, xhtml);
-        }
-
-        // Finish this paragraph
-        xhtml.endElement(tag);
-
-        if (headerFooterPolicy != null) {
-            extractFooters(xhtml, headerFooterPolicy, listManager);
-        }
-    }
-
-    private void writeParagraphNumber(XWPFParagraph paragraph,
-                                      XWPFListManager listManager,
-                                      XHTMLContentHandler xhtml) throws SAXException {
-        if (paragraph.getNumIlvl() == null) {
-            return;
-        }
-        String number = listManager.getFormattedNumber(paragraph);
-        if (number != null) {
-            xhtml.characters(number);
-        }
-
-    }
-
-    private TmpFormatting closeStyleTags(XHTMLContentHandler xhtml,
-                                         TmpFormatting fmtg) throws SAXException {
-        // Close any still open style tags
-        if (fmtg.isItalic()) {
-            xhtml.endElement("i");
-            fmtg.setItalic(false);
-        }
-        if (fmtg.isBold()) {
-            xhtml.endElement("b");
-            fmtg.setBold(false);
-        }
-        return fmtg;
-    }
-
-    private TmpFormatting processRun(XWPFRun run, XWPFParagraph paragraph,
-                                     XHTMLContentHandler xhtml, TmpFormatting tfmtg)
-            throws SAXException, XmlException, IOException {
-        // True if we are currently in the named style tag:
-        if (run.isBold() != tfmtg.isBold()) {
-            if (tfmtg.isItalic()) {
-                xhtml.endElement("i");
-                tfmtg.setItalic(false);
-            }
-            if (run.isBold()) {
-                xhtml.startElement("b");
-            } else {
-                xhtml.endElement("b");
-            }
-            tfmtg.setBold(run.isBold());
-        }
-
-        if (run.isItalic() != tfmtg.isItalic()) {
-            if (run.isItalic()) {
-                xhtml.startElement("i");
-            } else {
-                xhtml.endElement("i");
-            }
-            tfmtg.setItalic(run.isItalic());
-        }
-
-        boolean addedHREF = false;
-        if (run instanceof XWPFHyperlinkRun) {
-            XWPFHyperlinkRun linkRun = (XWPFHyperlinkRun) run;
-            XWPFHyperlink link = linkRun.getHyperlink(document);
-            if (link != null && link.getURL() != null) {
-                xhtml.startElement("a", "href", link.getURL());
-                addedHREF = true;
-            } else if (linkRun.getAnchor() != null && linkRun.getAnchor().length() > 0) {
-                xhtml.startElement("a", "href", "#" + linkRun.getAnchor());
-                addedHREF = true;
-            }
-        }
-
-        xhtml.characters(run.toString());
-
-        // If we have any pictures, output them
-        for (XWPFPicture picture : run.getEmbeddedPictures()) {
-            if (paragraph.getDocument() != null) {
-                XWPFPictureData data = picture.getPictureData();
-                if (data != null) {
-                    AttributesImpl attr = new AttributesImpl();
-
-                    attr.addAttribute("", "src", "src", "CDATA", "embedded:" + data.getFileName());
-                    attr.addAttribute("", "alt", "alt", "CDATA", picture.getDescription());
-
-                    xhtml.startElement("img", attr);
-                    xhtml.endElement("img");
-                }
-            }
-        }
-
-        if (addedHREF) {
-            xhtml.endElement("a");
-        }
-
-        return tfmtg;
-    }
-
-    private void processSDTRun(XWPFSDT run, XHTMLContentHandler xhtml)
-            throws SAXException, XmlException, IOException {
-        xhtml.characters(run.getContent().getText());
-    }
-
-    private void extractTable(XWPFTable table, XWPFListManager listManager,
-                              XHTMLContentHandler xhtml)
-            throws SAXException, XmlException, IOException {
-        xhtml.startElement("table");
-        xhtml.startElement("tbody");
-        for (XWPFTableRow row : table.getRows()) {
-            xhtml.startElement("tr");
-            for (ICell cell : row.getTableICells()) {
-                xhtml.startElement("td");
-                if (cell instanceof XWPFTableCell) {
-                    extractIBodyText((XWPFTableCell) cell, listManager, xhtml);
-                } else if (cell instanceof XWPFSDTCell) {
-                    xhtml.characters(((XWPFSDTCell) cell).getContent().getText());
-                }
-                xhtml.endElement("td");
-            }
-            xhtml.endElement("tr");
-        }
-        xhtml.endElement("tbody");
-        xhtml.endElement("table");
-    }
-
-    private void extractFooters(
-            XHTMLContentHandler xhtml, XWPFHeaderFooterPolicy hfPolicy,
-            XWPFListManager listManager)
-            throws SAXException, XmlException, IOException {
-        // footers
-        if (hfPolicy.getFirstPageFooter() != null) {
-            extractHeaderText(xhtml, hfPolicy.getFirstPageFooter(), listManager);
-        }
-        if (hfPolicy.getEvenPageFooter() != null) {
-            extractHeaderText(xhtml, hfPolicy.getEvenPageFooter(), listManager);
-        }
-        if (hfPolicy.getDefaultFooter() != null) {
-            extractHeaderText(xhtml, hfPolicy.getDefaultFooter(), listManager);
-        }
-    }
-
-    private void extractHeaders(
-            XHTMLContentHandler xhtml, XWPFHeaderFooterPolicy hfPolicy, XWPFListManager listManager)
-            throws SAXException, XmlException, IOException {
-        if (hfPolicy == null) return;
-
-        if (hfPolicy.getFirstPageHeader() != null) {
-            extractHeaderText(xhtml, hfPolicy.getFirstPageHeader(), listManager);
-        }
-
-        if (hfPolicy.getEvenPageHeader() != null) {
-            extractHeaderText(xhtml, hfPolicy.getEvenPageHeader(), listManager);
-        }
-
-        if (hfPolicy.getDefaultHeader() != null) {
-            extractHeaderText(xhtml, hfPolicy.getDefaultHeader(), listManager);
-        }
-    }
-
-    private void extractHeaderText(XHTMLContentHandler xhtml, XWPFHeaderFooter header, XWPFListManager listManager) throws SAXException, XmlException, IOException {
-
-        for (IBodyElement e : header.getBodyElements()) {
-            if (e instanceof XWPFParagraph) {
-                extractParagraph((XWPFParagraph) e, listManager, xhtml);
-            } else if (e instanceof XWPFTable) {
-                extractTable((XWPFTable) e, listManager, xhtml);
-            } else if (e instanceof XWPFSDT) {
-                extractSDT((XWPFSDT) e, xhtml);
-            }
-        }
-    }
-
-    /**
-     * Word documents are simple, they only have the one
-     * main part
-     */
-    @Override
-    protected List<PackagePart> getMainDocumentParts() {
-        List<PackagePart> parts = new ArrayList<PackagePart>();
-        parts.add(document.getPackagePart());
-        return parts;
-    }
-
-    private class TmpFormatting {
-        private boolean bold = false;
-        private boolean italic = false;
-
-        private TmpFormatting(boolean bold, boolean italic) {
-            this.bold = bold;
-            this.italic = italic;
-        }
-
-        public boolean isBold() {
-            return bold;
-        }
-
-        public void setBold(boolean bold) {
-            this.bold = bold;
-        }
-
-        public boolean isItalic() {
-            return italic;
-        }
-
-        public void setItalic(boolean italic) {
-            this.italic = italic;
-        }
-
-    }
-
-}
+/*
+ * 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.ooxml;
+
+import javax.xml.namespace.QName;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
+import org.apache.poi.xwpf.model.XWPFCommentsDecorator;
+import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
+import org.apache.poi.xwpf.usermodel.BodyType;
+import org.apache.poi.xwpf.usermodel.IBody;
+import org.apache.poi.xwpf.usermodel.IBodyElement;
+import org.apache.poi.xwpf.usermodel.ICell;
+import org.apache.poi.xwpf.usermodel.IRunElement;
+import org.apache.poi.xwpf.usermodel.ISDTContent;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFHeaderFooter;
+import org.apache.poi.xwpf.usermodel.XWPFHyperlink;
+import org.apache.poi.xwpf.usermodel.XWPFHyperlinkRun;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+import org.apache.poi.xwpf.usermodel.XWPFPicture;
+import org.apache.poi.xwpf.usermodel.XWPFPictureData;
+import org.apache.poi.xwpf.usermodel.XWPFRun;
+import org.apache.poi.xwpf.usermodel.XWPFSDT;
+import org.apache.poi.xwpf.usermodel.XWPFSDTCell;
+import org.apache.poi.xwpf.usermodel.XWPFStyle;
+import org.apache.poi.xwpf.usermodel.XWPFStyles;
+import org.apache.poi.xwpf.usermodel.XWPFTable;
+import org.apache.poi.xwpf.usermodel.XWPFTableCell;
+import org.apache.poi.xwpf.usermodel.XWPFTableRow;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.microsoft.WordExtractor;
+import org.apache.tika.parser.microsoft.WordExtractor.TagAndStyle;
+import org.apache.tika.sax.XHTMLContentHandler;
+import org.apache.xmlbeans.XmlCursor;
+import org.apache.xmlbeans.XmlException;
+import org.apache.xmlbeans.XmlObject;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTObject;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+public class XWPFWordExtractorDecorator extends AbstractOOXMLExtractor {
+
+    // could be improved by using the real delimiter in xchFollow [MS-DOC], v20140721, 2.4.6.3, Part 3, Step 3
+    private static final String LIST_DELIMITER = " ";
+
+
+    private XWPFDocument document;
+    private XWPFStyles styles;
+
+    public XWPFWordExtractorDecorator(ParseContext context, XWPFWordExtractor extractor) {
+        super(context, extractor);
+
+        document = (XWPFDocument) extractor.getDocument();
+        styles = document.getStyles();
+    }
+
+    /**
+     * @see org.apache.poi.xwpf.extractor.XWPFWordExtractor#getText()
+     */
+    @Override
+    protected void buildXHTML(XHTMLContentHandler xhtml)
+            throws SAXException, XmlException, IOException {
+        XWPFHeaderFooterPolicy hfPolicy = document.getHeaderFooterPolicy();
+        XWPFListManager listManager = new XWPFListManager(document);
+        // headers
+        if (hfPolicy != null) {
+            extractHeaders(xhtml, hfPolicy, listManager);
+        }
+
+        // process text in the order that it occurs in
+        extractIBodyText(document, listManager, xhtml);
+
+        // then all document tables
+        if (hfPolicy != null) {
+            extractFooters(xhtml, hfPolicy, listManager);
+        }
+    }
+
+    private void extractIBodyText(IBody bodyElement, XWPFListManager listManager,
+                                  XHTMLContentHandler xhtml)
+            throws SAXException, XmlException, IOException {
+        for (IBodyElement element : bodyElement.getBodyElements()) {
+            if (element instanceof XWPFParagraph) {
+                XWPFParagraph paragraph = (XWPFParagraph) element;
+                extractParagraph(paragraph, listManager, xhtml);
+            }
+            if (element instanceof XWPFTable) {
+                XWPFTable table = (XWPFTable) element;
+                extractTable(table, listManager, xhtml);
+            }
+            if (element instanceof XWPFSDT) {
+                extractSDT((XWPFSDT) element, xhtml);
+            }
+
+        }
+    }
+
+    private void extractSDT(XWPFSDT element, XHTMLContentHandler xhtml) throws SAXException,
+            XmlException, IOException {
+        ISDTContent content = element.getContent();
+        String tag = "p";
+        xhtml.startElement(tag);
+        xhtml.characters(content.getText());
+        xhtml.endElement(tag);
+    }
+
+    private void extractParagraph(XWPFParagraph paragraph, XWPFListManager listManager,
+                                  XHTMLContentHandler xhtml)
+            throws SAXException, XmlException, IOException {
+        // If this paragraph is actually a whole new section, then
+        //  it could have its own headers and footers
+        // Check and handle if so
+        XWPFHeaderFooterPolicy headerFooterPolicy = null;
+        if (paragraph.getCTP().getPPr() != null) {
+            CTSectPr ctSectPr = paragraph.getCTP().getPPr().getSectPr();
+            if (ctSectPr != null) {
+                headerFooterPolicy =
+                        new XWPFHeaderFooterPolicy(document, ctSectPr);
+                extractHeaders(xhtml, headerFooterPolicy, listManager);
+            }
+        }
+
+        // Is this a paragraph, or a heading?
+        String tag = "p";
+        String styleClass = null;
+        if (paragraph.getStyleID() != null) {
+            XWPFStyle style = styles.getStyle(
+                    paragraph.getStyleID()
+            );
+
+            if (style != null && style.getName() != null) {
+                TagAndStyle tas = WordExtractor.buildParagraphTagAndStyle(
+                        style.getName(), paragraph.getPartType() == BodyType.TABLECELL
+                );
+                tag = tas.getTag();
+                styleClass = tas.getStyleClass();
+            }
+        }
+
+        if (styleClass == null) {
+            xhtml.startElement(tag);
+        } else {
+            xhtml.startElement(tag, "class", styleClass);
+        }
+
+        writeParagraphNumber(paragraph, listManager, xhtml);
+        // Output placeholder for any embedded docs:
+
+        // TODO: replace w/ XPath/XQuery:
+        for (XWPFRun run : paragraph.getRuns()) {
+            XmlCursor c = run.getCTR().newCursor();
+            c.selectPath("./*");
+            while (c.toNextSelection()) {
+                XmlObject o = c.getObject();
+                if (o instanceof CTObject) {
+                    XmlCursor c2 = o.newCursor();
+                    c2.selectPath("./*");
+                    while (c2.toNextSelection()) {
+                        XmlObject o2 = c2.getObject();
+
+                        XmlObject embedAtt = o2.selectAttribute(new QName("Type"));
+                        if (embedAtt != null && embedAtt.getDomNode().getNodeValue().equals("Embed")) {
+                            // Type is "Embed"
+                            XmlObject relIDAtt = o2.selectAttribute(new QName("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id"));
+                            if (relIDAtt != null) {
+                                String relID = relIDAtt.getDomNode().getNodeValue();
+                                AttributesImpl attributes = new AttributesImpl();
+                                attributes.addAttribute("", "class", "class", "CDATA", "embedded");
+                                attributes.addAttribute("", "id", "id", "CDATA", relID);
+                                xhtml.startElement("div", attributes);
+                                xhtml.endElement("div");
+                            }
+                        }
+                    }
+                    c2.dispose();
+                }
+            }
+
+            c.dispose();
+        }
+
+        // Attach bookmarks for the paragraph
+        // (In future, we might put them in the right place, for now
+        //  we just put them in the correct paragraph)
+        for (int i = 0; i < paragraph.getCTP().sizeOfBookmarkStartArray(); i++) {
+            CTBookmark bookmark = paragraph.getCTP().getBookmarkStartArray(i);
+            xhtml.startElement("a", "name", bookmark.getName());
+            xhtml.endElement("a");
+        }
+
+        TmpFormatting fmtg = new TmpFormatting(false, false);
+
+        // Do the iruns
+        for (IRunElement run : paragraph.getIRuns()) {
+            if (run instanceof XWPFSDT) {
+                fmtg = closeStyleTags(xhtml, fmtg);
+                processSDTRun((XWPFSDT) run, xhtml);
+                //for now, we're ignoring formatting in sdt
+                //if you hit an sdt reset to false
+                fmtg.setBold(false);
+                fmtg.setItalic(false);
+            } else {
+                fmtg = processRun((XWPFRun) run, paragraph, xhtml, fmtg);
+            }
+        }
+        closeStyleTags(xhtml, fmtg);
+
+
+        // Now do any comments for the paragraph
+        XWPFCommentsDecorator comments = new XWPFCommentsDecorator(paragraph, null);
+        String commentText = comments.getCommentText();
+        if (commentText != null && commentText.length() > 0) {
+            xhtml.characters(commentText);
+        }
+
+        String footnameText = paragraph.getFootnoteText();
+        if (footnameText != null && footnameText.length() > 0) {
+            xhtml.characters(footnameText + "\n");
+        }
+
+        // Also extract any paragraphs embedded in text boxes:
+        for (XmlObject embeddedParagraph : paragraph.getCTP().selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' declare namespace wps='http://schemas.microsoft.com/office/word/2010/wordprocessingShape' .//*/wps:txbx/w:txbxContent/w:p")) {
+            extractParagraph(new XWPFParagraph(CTP.Factory.parse(embeddedParagraph.xmlText()), paragraph.getBody()), listManager, xhtml);
+        }
+
+        // Finish this paragraph
+        xhtml.endElement(tag);
+
+        if (headerFooterPolicy != null) {
+            extractFooters(xhtml, headerFooterPolicy, listManager);
+        }
+    }
+
+    private void writeParagraphNumber(XWPFParagraph paragraph,
+                                      XWPFListManager listManager,
+                                      XHTMLContentHandler xhtml) throws SAXException {
+        if (paragraph.getNumIlvl() == null) {
+            return;
+        }
+        String number = listManager.getFormattedNumber(paragraph);
+        if (number != null) {
+            xhtml.characters(number);
+        }
+
+    }
+
+    private TmpFormatting closeStyleTags(XHTMLContentHandler xhtml,
+                                         TmpFormatting fmtg) throws SAXException {
+        // Close any still open style tags
+        if (fmtg.isItalic()) {
+            xhtml.endElement("i");
+            fmtg.setItalic(false);
+        }
+        if (fmtg.isBold()) {
+            xhtml.endElement("b");
+            fmtg.setBold(false);
+        }
+        return fmtg;
+    }
+
+    private TmpFormatting processRun(XWPFRun run, XWPFParagraph paragraph,
+                                     XHTMLContentHandler xhtml, TmpFormatting tfmtg)
+            throws SAXException, XmlException, IOException {
+        // True if we are currently in the named style tag:
+        if (run.isBold() != tfmtg.isBold()) {
+            if (tfmtg.isItalic()) {
+                xhtml.endElement("i");
+                tfmtg.setItalic(false);
+            }
+            if (run.isBold()) {
+                xhtml.startElement("b");
+            } else {
+                xhtml.endElement("b");
+            }
+            tfmtg.setBold(run.isBold());
+        }
+
+        if (run.isItalic() != tfmtg.isItalic()) {
+            if (run.isItalic()) {
+                xhtml.startElement("i");
+            } else {
+                xhtml.endElement("i");
+            }
+            tfmtg.setItalic(run.isItalic());
+        }
+
+        boolean addedHREF = false;
+        if (run instanceof XWPFHyperlinkRun) {
+            XWPFHyperlinkRun linkRun = (XWPFHyperlinkRun) run;
+            XWPFHyperlink link = linkRun.getHyperlink(document);
+            if (link != null && link.getURL() != null) {
+                xhtml.startElement("a", "href", link.getURL());
+                addedHREF = true;
+            } else if (linkRun.getAnchor() != null && linkRun.getAnchor().length() > 0) {
+                xhtml.startElement("a", "href", "#" + linkRun.getAnchor());
+                addedHREF = true;
+            }
+        }
+
+        xhtml.characters(run.toString());
+
+        // If we have any pictures, output them
+        for (XWPFPicture picture : run.getEmbeddedPictures()) {
+            if (paragraph.getDocument() != null) {
+                XWPFPictureData data = picture.getPictureData();
+                if (data != null) {
+                    AttributesImpl attr = new AttributesImpl();
+
+                    attr.addAttribute("", "src", "src", "CDATA", "embedded:" + data.getFileName());
+                    attr.addAttribute("", "alt", "alt", "CDATA", picture.getDescription());
+
+                    xhtml.startElement("img", attr);
+                    xhtml.endElement("img");
+                }
+            }
+        }
+
+        if (addedHREF) {
+            xhtml.endElement("a");
+        }
+
+        return tfmtg;
+    }
+
+    private void processSDTRun(XWPFSDT run, XHTMLContentHandler xhtml)
+            throws SAXException, XmlException, IOException {
+        xhtml.characters(run.getContent().getText());
+    }
+
+    private void extractTable(XWPFTable table, XWPFListManager listManager,
+                              XHTMLContentHandler xhtml)
+            throws SAXException, XmlException, IOException {
+        xhtml.startElement("table");
+        xhtml.startElement("tbody");
+        for (XWPFTableRow row : table.getRows()) {
+            xhtml.startElement("tr");
+            for (ICell cell : row.getTableICells()) {
+                xhtml.startElement("td");
+                if (cell instanceof XWPFTableCell) {
+                    extractIBodyText((XWPFTableCell) cell, listManager, xhtml);
+                } else if (cell instanceof XWPFSDTCell) {
+                    xhtml.characters(((XWPFSDTCell) cell).getContent().getText());
+                }
+                xhtml.endElement("td");
+            }
+            xhtml.endElement("tr");
+        }
+        xhtml.endElement("tbody");
+        xhtml.endElement("table");
+    }
+
+    private void extractFooters(
+            XHTMLContentHandler xhtml, XWPFHeaderFooterPolicy hfPolicy,
+            XWPFListManager listManager)
+            throws SAXException, XmlException, IOException {
+        // footers
+        if (hfPolicy.getFirstPageFooter() != null) {
+            extractHeaderText(xhtml, hfPolicy.getFirstPageFooter(), listManager);
+        }
+        if (hfPolicy.getEvenPageFooter() != null) {
+            extractHeaderText(xhtml, hfPolicy.getEvenPageFooter(), listManager);
+        }
+        if (hfPolicy.getDefaultFooter() != null) {
+            extractHeaderText(xhtml, hfPolicy.getDefaultFooter(), listManager);
+        }
+    }
+
+    private void extractHeaders(
+            XHTMLContentHandler xhtml, XWPFHeaderFooterPolicy hfPolicy, XWPFListManager listManager)
+            throws SAXException, XmlException, IOException {
+        if (hfPolicy == null) return;
+
+        if (hfPolicy.getFirstPageHeader() != null) {
+            extractHeaderText(xhtml, hfPolicy.getFirstPageHeader(), listManager);
+        }
+
+        if (hfPolicy.getEvenPageHeader() != null) {
+            extractHeaderText(xhtml, hfPolicy.getEvenPageHeader(), listManager);
+        }
+
+        if (hfPolicy.getDefaultHeader() != null) {
+            extractHeaderText(xhtml, hfPolicy.getDefaultHeader(), listManager);
+        }
+    }
+
+    private void extractHeaderText(XHTMLContentHandler xhtml, XWPFHeaderFooter header, XWPFListManager listManager) throws SAXException, XmlException, IOException {
+
+        for (IBodyElement e : header.getBodyElements()) {
+            if (e instanceof XWPFParagraph) {
+                extractParagraph((XWPFParagraph) e, listManager, xhtml);
+            } else if (e instanceof XWPFTable) {
+                extractTable((XWPFTable) e, listManager, xhtml);
+            } else if (e instanceof XWPFSDT) {
+                extractSDT((XWPFSDT) e, xhtml);
+            }
+        }
+    }
+
+    /**
+     * Word documents are simple, they only have the one
+     * main part
+     */
+    @Override
+    protected List<PackagePart> getMainDocumentParts() {
+        List<PackagePart> parts = new ArrayList<PackagePart>();
+        parts.add(document.getPackagePart());
+        return parts;
+    }
+
+    private class TmpFormatting {
+        private boolean bold = false;
+        private boolean italic = false;
+
+        private TmpFormatting(boolean bold, boolean italic) {
+            this.bold = bold;
+            this.italic = italic;
+        }
+
+        public boolean isBold() {
+            return bold;
+        }
+
+        public void setBold(boolean bold) {
+            this.bold = bold;
+        }
+
+        public boolean isItalic() {
+            return italic;
+        }
+
+        public void setItalic(boolean italic) {
+            this.italic = italic;
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d8231f48/tika-parser-modules/tika-parser-office-module/src/test/java/org/apache/tika/parser/microsoft/WriteProtectedParserTest.java
----------------------------------------------------------------------
diff --git a/tika-parser-modules/tika-parser-office-module/src/test/java/org/apache/tika/parser/microsoft/WriteProtectedParserTest.java b/tika-parser-modules/tika-parser-office-module/src/test/java/org/apache/tika/parser/microsoft/WriteProtectedParserTest.java
index 931ecd0..aeca99b 100644
--- a/tika-parser-modules/tika-parser-office-module/src/test/java/org/apache/tika/parser/microsoft/WriteProtectedParserTest.java
+++ b/tika-parser-modules/tika-parser-office-module/src/test/java/org/apache/tika/parser/microsoft/WriteProtectedParserTest.java
@@ -1,42 +1,42 @@
-/*
- * 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.TikaTest.assertContains;
-
-import java.io.InputStream;
-
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.sax.BodyContentHandler;
-import org.junit.Test;
-import org.xml.sax.ContentHandler;
-
-public class WriteProtectedParserTest {
-
-    @Test
-    public void testWriteProtected() throws Exception {
-        InputStream input = ExcelParserTest.class.getResourceAsStream(
-                "/test-documents/protect.xlsx");
-
-        Metadata metadata = new Metadata();
-        ContentHandler handler = new BodyContentHandler();
-        new OfficeParser().parse(input, handler, metadata, new ParseContext());
-        String content = handler.toString();
-        assertContains("Office", content);
-    }
-}
+/*
+ * 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.TikaTest.assertContains;
+
+import java.io.InputStream;
+
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.BodyContentHandler;
+import org.junit.Test;
+import org.xml.sax.ContentHandler;
+
+public class WriteProtectedParserTest {
+
+    @Test
+    public void testWriteProtected() throws Exception {
+        InputStream input = ExcelParserTest.class.getResourceAsStream(
+                "/test-documents/protect.xlsx");
+
+        Metadata metadata = new Metadata();
+        ContentHandler handler = new BodyContentHandler();
+        new OfficeParser().parse(input, handler, metadata, new ParseContext());
+        String content = handler.toString();
+        assertContains("Office", content);
+    }
+}