From 1f8de11b3177e828fc5e5b71d251f9eb20a36a5f Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 11 Jul 2017 16:38:49 +0000 Subject: [PATCH] [Bug-61281] guard against index out of bounds in XSSFExportToXml column mapping git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1801634 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/xssf/extractor/XSSFExportToXml.java | 19 +- .../xssf/extractor/TestXSSFExportToXML.java | 212 ++++++++++-------- test-data/spreadsheet/61281.xlsx | Bin 0 -> 9913 bytes 3 files changed, 134 insertions(+), 97 deletions(-) create mode 100644 test-data/spreadsheet/61281.xlsx diff --git a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java index 7febd236b4..0ed44d3048 100644 --- a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java +++ b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java @@ -188,14 +188,17 @@ public class XSSFExportToXml implements Comparator{ Node tableRootNode = getNodeByXPath(table.getCommonXpath(),doc.getFirstChild(),doc,true); short startColumnIndex = table.getStartCellReference().getCol(); - for(int j = startColumnIndex; j<= table.getEndCellReference().getCol();j++) { - XSSFCell cell = row.getCell(j); - if (cell!=null) { - XSSFXmlColumnPr pointer = tableColumns.get(j-startColumnIndex); - String localXPath = pointer.getLocalXPath(); - Node currentNode = getNodeByXPath(localXPath,tableRootNode,doc,false); - - mapCellOnNode(cell,currentNode); + for(int j = startColumnIndex; j<= table.getEndCellReference().getCol(); j++) { + int tableColumnIndex = j - startColumnIndex; + if (tableColumnIndex < tableColumns.size()) { + XSSFCell cell = row.getCell(j); + if (cell != null) { + XSSFXmlColumnPr pointer = tableColumns.get(tableColumnIndex); + String localXPath = pointer.getLocalXPath(); + Node currentNode = getNodeByXPath(localXPath,tableRootNode,doc,false); + + mapCellOnNode(cell,currentNode); + } } } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java index 783b7a0b26..5c92901079 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java @@ -17,37 +17,46 @@ package org.apache.poi.xssf.extractor; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.util.DocumentHelper; import org.apache.poi.util.XMLHelper; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.model.MapInfo; import org.apache.poi.xssf.usermodel.XSSFMap; import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.Ignore; +import org.junit.Test; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Collection; -import java.util.Date; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * @author Roberto Manicardi */ -public final class TestXSSFExportToXML extends TestCase { +public final class TestXSSFExportToXML { + @Test public void testExportToXML() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMappings.xlsx"); @@ -94,7 +103,8 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } - public void testExportToXMLInverseOrder() throws Exception { + @Test + public void testExportToXMLInverseOrder() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples .openSampleWorkbook("CustomXmlMappings-inverse-order.xlsx"); @@ -141,7 +151,8 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } - public void testXPathOrdering() { + @Test + public void testXPathOrdering() { XSSFWorkbook wb = XSSFTestDataSamples .openSampleWorkbook("CustomXmlMappings-inverse-order.xlsx"); @@ -164,7 +175,8 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } - public void testMultiTable() throws Exception { + @Test + public void testMultiTable() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples .openSampleWorkbook("CustomXMLMappings-complex-type.xlsx"); @@ -207,19 +219,20 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } + @Test + @Ignore(value="Fails, but I don't know if it is ok or not...") public void testExportToXMLSingleAttributeNamespace() throws Exception { - // TODO: Fails, but I don't know if it is ok or not... - -// XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMapping-singleattributenamespace.xlsx"); -// -// for (XSSFMap map : wb.getCustomXMLMappings()) { -// XSSFExportToXml exporter = new XSSFExportToXml(map); -// -// ByteArrayOutputStream os = new ByteArrayOutputStream(); -// exporter.exportToXML(os, true); -// } + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMapping-singleattributenamespace.xlsx"); + + for (XSSFMap map : wb.getCustomXMLMappings()) { + XSSFExportToXml exporter = new XSSFExportToXml(map); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + exporter.exportToXML(os, true); + } } + @Test public void test55850ComplexXmlExport() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples @@ -264,76 +277,79 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } - public void testFormulaCells_Bugzilla_55927() throws Exception { - XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55927.xlsx"); - - boolean found = false; - for (POIXMLDocumentPart p : wb.getRelations()) { - - if (!(p instanceof MapInfo)) { - continue; - } - MapInfo mapInfo = (MapInfo) p; - - XSSFMap map = mapInfo.getXSSFMapById(1); - - assertNotNull("XSSFMap is null", map); - - XSSFExportToXml exporter = new XSSFExportToXml(map); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - exporter.exportToXML(os, true); - String xmlData = os.toString("UTF-8"); - - assertNotNull(xmlData); - assertFalse(xmlData.equals("")); - - assertEquals("2012-01-13", xmlData.split("")[1].split("")[0].trim()); - assertEquals("2012-02-16", xmlData.split("")[1].split("")[0].trim()); - - parseXML(xmlData); - - found = true; - } - assertTrue(found); - } + @Test + public void testFormulaCells_Bugzilla_55927() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55927.xlsx"); - public void testFormulaCells_Bugzilla_55926() throws Exception { - XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55926.xlsx"); + boolean found = false; + for (POIXMLDocumentPart p : wb.getRelations()) { - boolean found = false; - for (POIXMLDocumentPart p : wb.getRelations()) { + if (!(p instanceof MapInfo)) { + continue; + } + MapInfo mapInfo = (MapInfo) p; - if (!(p instanceof MapInfo)) { - continue; - } - MapInfo mapInfo = (MapInfo) p; + XSSFMap map = mapInfo.getXSSFMapById(1); - XSSFMap map = mapInfo.getXSSFMapById(1); + assertNotNull("XSSFMap is null", map); - assertNotNull("XSSFMap is null", map); + XSSFExportToXml exporter = new XSSFExportToXml(map); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + exporter.exportToXML(os, true); + String xmlData = os.toString("UTF-8"); - XSSFExportToXml exporter = new XSSFExportToXml(map); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - exporter.exportToXML(os, true); - String xmlData = os.toString("UTF-8"); + assertNotNull(xmlData); + assertFalse(xmlData.equals("")); - assertNotNull(xmlData); - assertFalse(xmlData.equals("")); - - String a = xmlData.split("")[1].split("")[0].trim(); - String doubleValue = a.split("")[1].split("")[0].trim(); - String stringValue = a.split("")[1].split("")[0].trim(); - - assertEquals("Hello World", stringValue); - assertEquals("5.1", doubleValue); - - parseXML(xmlData); - - found = true; - } - assertTrue(found); - } + assertEquals("2012-01-13", xmlData.split("")[1].split("")[0].trim()); + assertEquals("2012-02-16", xmlData.split("")[1].split("")[0].trim()); + + parseXML(xmlData); + + found = true; + } + assertTrue(found); + } + + @Test + public void testFormulaCells_Bugzilla_55926() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55926.xlsx"); + + boolean found = false; + for (POIXMLDocumentPart p : wb.getRelations()) { + + if (!(p instanceof MapInfo)) { + continue; + } + MapInfo mapInfo = (MapInfo) p; + + XSSFMap map = mapInfo.getXSSFMapById(1); + + assertNotNull("XSSFMap is null", map); + + XSSFExportToXml exporter = new XSSFExportToXml(map); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + exporter.exportToXML(os, true); + String xmlData = os.toString("UTF-8"); + + assertNotNull(xmlData); + assertFalse(xmlData.equals("")); + + String a = xmlData.split("")[1].split("")[0].trim(); + String doubleValue = a.split("")[1].split("")[0].trim(); + String stringValue = a.split("")[1].split("")[0].trim(); + + assertEquals("Hello World", stringValue); + assertEquals("5.1", doubleValue); + + parseXML(xmlData); + + found = true; + } + assertTrue(found); + } + @Test public void testXmlExportIgnoresEmptyCells_Bugzilla_55924() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55924.xlsx"); @@ -369,6 +385,7 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } + @Test public void testXmlExportSchemaWithXSAllTag_Bugzilla_56169() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56169.xlsx"); @@ -403,6 +420,7 @@ public final class TestXSSFExportToXML extends TestCase { } } + @Test public void testXmlExportCompare_Bug_55923() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55923.xlsx"); @@ -432,6 +450,7 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } + @Test public void testXmlExportSchemaOrderingBug_Bugzilla_55923() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55923.xlsx"); @@ -498,6 +517,7 @@ public final class TestXSSFExportToXML extends TestCase { } } + @Test public void testExportDataTypes() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55923.xlsx"); @@ -558,6 +578,7 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } + @Test public void testValidateFalse() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55923.xlsx"); @@ -588,6 +609,7 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } + @Test public void testRefElementsInXmlSchema_Bugzilla_56730() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56730.xlsx"); @@ -621,7 +643,8 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(found); } - public void testBug59026() throws Exception { + @Test + public void testBug59026() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("59026.xlsx"); Collection mappings = wb.getCustomXMLMappings(); @@ -634,4 +657,15 @@ public final class TestXSSFExportToXML extends TestCase { assertNotNull(os.toString("UTF-8")); } } + + @Test + public void testExportTableWithNonMappedColumn_Bugzilla_61281() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("61281.xlsx"); + for (XSSFMap map : wb.getCustomXMLMappings()) { + XSSFExportToXml exporter = new XSSFExportToXml(map); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + exporter.exportToXML(bos, true); + assertNotNull(DocumentHelper.readDocument(new ByteArrayInputStream(bos.toByteArray()))); + } + } } diff --git a/test-data/spreadsheet/61281.xlsx b/test-data/spreadsheet/61281.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6e8cd739e1e0f0a27e52189cd539b7968dc11b38 GIT binary patch literal 9913 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 e4g&fgh5QxdmE~X|9`K{eLjx53XvC2JIQl=a