From 972d56189768e6471512cb7e2bd4f13c40f05b75 Mon Sep 17 00:00:00 2001 From: Greg Woolsey Date: Sat, 24 Jun 2017 05:55:45 +0000 Subject: [PATCH] Add file that failed regression tests as a unit test. Adjust table style processing to allow for AlternateContent wrappers for table element style definitions. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1799733 13f79535-47bb-0310-9956-ffa450edef68 --- .../xssf/usermodel/XSSFBuiltinTableStyle.java | 6 ++- .../poi/xssf/usermodel/XSSFTableStyle.java | 40 +++++++++++++++--- .../poi/xssf/model/TestStylesTable.java | 10 +++++ .../spreadsheet/style-alternate-content.xlsx | Bin 0 -> 10103 bytes 4 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 test-data/spreadsheet/style-alternate-content.xlsx diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBuiltinTableStyle.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBuiltinTableStyle.java index aab86abca..828ceeeec 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBuiltinTableStyle.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBuiltinTableStyle.java @@ -388,20 +388,22 @@ public enum XSSFBuiltinTableStyle { final InputStream is = XSSFBuiltinTableStyle.class.getResourceAsStream("presetTableStyles.xml"); try { final Document doc = DocumentHelper.readDocument(is); + final NodeList styleNodes = doc.getDocumentElement().getChildNodes(); for (int i=0; i < styleNodes.getLength(); i++) { final Node node = styleNodes.item(i); if (node.getNodeType() != Node.ELEMENT_NODE) continue; // only care about elements final Element tag = (Element) node; String styleName = tag.getTagName(); + XSSFBuiltinTableStyle builtIn = XSSFBuiltinTableStyle.valueOf(styleName); + Node dxfsNode = tag.getElementsByTagName("dxfs").item(0); Node tableStyleNode = tag.getElementsByTagName("tableStyles").item(0); - + // hack because I can't figure out how to get XMLBeans to parse a sub-element in a standalone manner // - build a fake styles.xml file with just this built-in StylesTable styles = new StylesTable(); styles.readFrom(new ByteArrayInputStream(styleXML(dxfsNode, tableStyleNode).getBytes(Charset.forName("UTF-8")))); - XSSFBuiltinTableStyle builtIn = XSSFBuiltinTableStyle.valueOf(styleName); styleMap.put(builtIn, new XSSFBuiltinTypeStyleStyle(builtIn, styles.getExplicitTableStyle(styleName))); } } finally { diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java index 0b7be4963..8c6306d21 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java @@ -17,12 +17,18 @@ package org.apache.poi.xssf.usermodel; +import java.util.ArrayList; import java.util.EnumMap; +import java.util.List; import java.util.Map; import org.apache.poi.ss.usermodel.DifferentialStyleProvider; import org.apache.poi.ss.usermodel.TableStyle; import org.apache.poi.ss.usermodel.TableStyleType; +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxf; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxfs; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyle; @@ -48,17 +54,41 @@ public class XSSFTableStyle implements TableStyle { public XSSFTableStyle(int index, CTDxfs dxfs, CTTableStyle tableStyle, IndexedColorMap colorMap) { this.name = tableStyle.getName(); this.index = index; + + List dxfList = new ArrayList(); + + // CT* classes don't handle "mc:AlternateContent" elements, so get the Dxf instances manually + XmlCursor cur = dxfs.newCursor(); + // sometimes there are namespaces sometimes not. + String xquery = "declare namespace x='"+XSSFRelation.NS_SPREADSHEETML+"' .//x:dxf | .//dxf"; + cur.selectPath(xquery); + while (cur.toNextSelection()) { + XmlObject obj = cur.getObject(); + String parentName = obj.getDomNode().getParentNode().getNodeName(); + // ignore alternate content choices, we won't know anything about their namespaces + if (parentName.equals("mc:Fallback") || parentName.equals("x:dxfs") || parentName.contentEquals("dxfs")) { + CTDxf dxf; + try { + if (obj instanceof CTDxf) { + dxf = (CTDxf) obj; + } else { + dxf = CTDxf.Factory.parse(obj.newXMLStreamReader(), new XmlOptions().setDocumentType(CTDxf.type)); + } + if (dxf != null) dxfList.add(dxf); + } catch (XmlException e) { + // ignore + e.printStackTrace(); + } + } + } + for (CTTableStyleElement element : tableStyle.getTableStyleElementList()) { TableStyleType type = TableStyleType.valueOf(element.getType().toString()); DifferentialStyleProvider dstyle = null; if (element.isSetDxfId()) { int idx = (int) element.getDxfId(); CTDxf dxf; - if (idx >= 0 && idx < dxfs.getCount()) { - dxf = dxfs.getDxfArray(idx); - } else { - dxf = null; - } + dxf = dxfList.get(idx); int stripeSize = 0; if (element.isSetSize()) stripeSize = (int) element.getSize(); if (dxf != null) dstyle = new XSSFDxfStyleProvider(dxf, stripeSize, colorMap); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/model/TestStylesTable.java b/src/ooxml/testcases/org/apache/poi/xssf/model/TestStylesTable.java index 9d29fc453..652d40fbc 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/model/TestStylesTable.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/model/TestStylesTable.java @@ -356,4 +356,14 @@ public final class TestStylesTable { wb.close(); } } + + @Test + public void testLoadWithAlternateContent() { + XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("style-alternate-content.xlsx"); + assertNotNull(workbook.getStylesSource()); + + StylesTable st = workbook.getStylesSource(); + + assertNotNull(XSSFTestDataSamples.writeOutAndReadBack(workbook)); + } } diff --git a/test-data/spreadsheet/style-alternate-content.xlsx b/test-data/spreadsheet/style-alternate-content.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9ae63becf254a12ba3f499f5ac0f804564f6e519 GIT binary patch literal 10103 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 literal 0 HcmV?d00001