]> source.dussan.org Git - poi.git/commitdiff
[bug-66230] don't fail to create SXSSFSheet if auto size tracker can't be initialized
authorPJ Fanning <fanningpj@apache.org>
Thu, 18 Aug 2022 17:40:22 +0000 (17:40 +0000)
committerPJ Fanning <fanningpj@apache.org>
Thu, 18 Aug 2022 17:40:22 +0000 (17:40 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1903529 13f79535-47bb-0310-9956-ffa450edef68

poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
poi-ooxml/src/test/java/org/apache/poi/xssf/SXSSFITestDataProvider.java
poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/SXSSFITestDataProviderWithNullAutoSizeTracker.java [new file with mode: 0644]
poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithNullAutoSizeTracker.java [new file with mode: 0644]

index 4fa4138b3f89a9ee440fccb8fe551724f4120478..c0007cfcdc3e0e39b6f9a97f9fad0a30159d3b2f 100644 (file)
@@ -19,13 +19,7 @@ package org.apache.poi.xssf.streaming;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.TreeMap;
+import java.util.*;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -56,7 +50,7 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
     private final TreeMap<Integer,SXSSFRow> _rows = new TreeMap<>();
     protected SheetDataWriter _writer;
     private int _randomAccessWindowSize = SXSSFWorkbook.DEFAULT_WINDOW_SIZE;
-    protected final AutoSizeColumnTracker _autoSizeColumnTracker;
+    protected AutoSizeColumnTracker _autoSizeColumnTracker;
     private int outlineLevelRow;
     private int lastFlushedRowNumber = -1;
     private boolean allFlushed;
@@ -97,7 +91,11 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
         _sh = xSheet;
         _writer = workbook.createSheetDataWriter();
         setRandomAccessWindowSize(_workbook.getRandomAccessWindowSize());
-        _autoSizeColumnTracker = new AutoSizeColumnTracker(this);
+        try {
+            _autoSizeColumnTracker = new AutoSizeColumnTracker(this);
+        } catch (Exception e) {
+            LOG.atWarn().log("Failed to create AutoSizeColumnTracker, possibly due to fonts not being installed in your OS", e);
+        }
     }
 
     /**
@@ -1439,11 +1437,15 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
      * If <code>column</code> is already tracked, this call does nothing.
      *
      * @param column the column to track for auto-sizing
+     * @throws IllegalStateException if autoSizeColumnTracker failed to initialize (possibly due to fonts not being installed in your OS)
      * @since 3.14beta1
      * @see #trackColumnsForAutoSizing(Collection)
      * @see #trackAllColumnsForAutoSizing()
      */
     public void trackColumnForAutoSizing(int column) {
+        if (_autoSizeColumnTracker == null) {
+            throw new IllegalStateException("Cannot trackColumnForAutoSizing because autoSizeColumnTracker failed to initialize (possibly due to fonts not being installed in your OS)");
+        }
         _autoSizeColumnTracker.trackColumn(column);
     }
 
@@ -1453,18 +1455,26 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
      * Any column in <code>columns</code> that are already tracked are ignored by this call.
      *
      * @param columns the columns to track for auto-sizing
+     * @throws IllegalStateException if autoSizeColumnTracker failed to initialize (possibly due to fonts not being installed in your OS)
      * @since 3.14beta1
      */
     public void trackColumnsForAutoSizing(Collection<Integer> columns) {
+        if (_autoSizeColumnTracker == null) {
+            throw new IllegalStateException("Cannot trackColumnForAutoSizing because autoSizeColumnTracker failed to initialize (possibly due to fonts not being installed in your OS)");
+        }
         _autoSizeColumnTracker.trackColumns(columns);
     }
 
     /**
      * Tracks all columns in the sheet for auto-sizing. If this is called, individual columns do not need to be tracked.
      * Because determining the best-fit width for a cell is expensive, this may affect the performance.
+     * @throws IllegalStateException if autoSizeColumnTracker failed to initialize (possibly due to fonts not being installed in your OS)
      * @since 3.14beta1
      */
     public void trackAllColumnsForAutoSizing() {
+        if (_autoSizeColumnTracker == null) {
+            throw new IllegalStateException("Cannot trackColumnForAutoSizing because autoSizeColumnTracker failed to initialize (possibly due to fonts not being installed in your OS)");
+        }
         _autoSizeColumnTracker.trackAllColumns();
     }
 
@@ -1480,7 +1490,7 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
      * @see #untrackAllColumnsForAutoSizing()
      */
     public boolean untrackColumnForAutoSizing(int column) {
-        return _autoSizeColumnTracker.untrackColumn(column);
+        return _autoSizeColumnTracker != null && _autoSizeColumnTracker.untrackColumn(column);
     }
 
     /**
@@ -1493,7 +1503,7 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
      * @since 3.14beta1
      */
     public boolean untrackColumnsForAutoSizing(Collection<Integer> columns) {
-        return _autoSizeColumnTracker.untrackColumns(columns);
+        return _autoSizeColumnTracker != null && _autoSizeColumnTracker.untrackColumns(columns);
     }
 
     /**
@@ -1502,7 +1512,9 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
      * @since 3.14beta1
      */
     public void untrackAllColumnsForAutoSizing() {
-        _autoSizeColumnTracker.untrackAllColumns();
+        if (_autoSizeColumnTracker != null) {
+            _autoSizeColumnTracker.untrackAllColumns();
+        }
     }
 
     /**
@@ -1513,7 +1525,7 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
      * @since 3.14beta1
      */
     public boolean isColumnTrackedForAutoSizing(int column) {
-        return _autoSizeColumnTracker.isColumnTracked(column);
+        return _autoSizeColumnTracker != null && _autoSizeColumnTracker.isColumnTracked(column);
     }
 
     /**
@@ -1525,7 +1537,7 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
      * @since 3.14beta1
      */
     public Set<Integer> getTrackedColumnsForAutoSizing() {
-        return _autoSizeColumnTracker.getTrackedColumns();
+        return _autoSizeColumnTracker == null ? Collections.emptySet() : _autoSizeColumnTracker.getTrackedColumns();
     }
 
     /**
@@ -1576,9 +1588,14 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
      *
      * @param column the column index to auto-size
      * @param useMergedCells whether to use the contents of merged cells when calculating the width of the column
+     * @throws IllegalStateException if autoSizeColumnTracker failed to initialize (possibly due to fonts not being installed in your OS)
      */
     @Override
     public void autoSizeColumn(int column, boolean useMergedCells) {
+        if (_autoSizeColumnTracker == null) {
+            throw new IllegalStateException("Cannot trackColumnForAutoSizing because autoSizeColumnTracker failed to initialize (possibly due to fonts not being installed in your OS)");
+        }
+
         // Multiple calls to autoSizeColumn need to look up the best-fit width
         // of rows already flushed to disk plus re-calculate the best-fit width
         // of rows in the current window. It isn't safe to update the column
@@ -1889,9 +1906,13 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
         if (firstRowNum!=null) {
             int rowIndex = firstRowNum;
             SXSSFRow row = _rows.get(firstRowNum);
-            // Update the best fit column widths for auto-sizing just before the rows are flushed
-            _autoSizeColumnTracker.updateColumnWidths(row);
-            if (_writer != null) _writer.writeRow(rowIndex, row);
+            if (_autoSizeColumnTracker != null) {
+                // Update the best fit column widths for auto-sizing just before the rows are flushed
+                _autoSizeColumnTracker.updateColumnWidths(row);
+            }
+            if (_writer != null) {
+                _writer.writeRow(rowIndex, row);
+            }
             _rows.remove(firstRowNum);
             lastFlushedRowNumber = rowIndex;
         }
index 8c722151eebc1dbcbd1eeab70358eb7211074815..c43a9cb38cd70ec43d22c4f8b42eb439e707856e 100644 (file)
@@ -36,14 +36,14 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook;
 import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
-public final class SXSSFITestDataProvider implements ITestDataProvider {
+public class SXSSFITestDataProvider implements ITestDataProvider {
     public static final SXSSFITestDataProvider instance = new SXSSFITestDataProvider();
 
     // an instance of all SXSSFWorkbooks opened by this TestDataProvider,
     // so that the temporary files created can be disposed up by cleanup()
-    private final Collection<SXSSFWorkbook> instances = new ArrayList<>();
+    protected final Collection<SXSSFWorkbook> instances = new ArrayList<>();
 
-    private SXSSFITestDataProvider() {
+    protected SXSSFITestDataProvider() {
         // enforce singleton
     }
 
diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/SXSSFITestDataProviderWithNullAutoSizeTracker.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/SXSSFITestDataProviderWithNullAutoSizeTracker.java
new file mode 100644 (file)
index 0000000..c24670b
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  ====================================================================
+ *    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.poi.xssf.streaming;
+
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.xssf.SXSSFITestDataProvider;
+
+public class SXSSFITestDataProviderWithNullAutoSizeTracker extends SXSSFITestDataProvider {
+    public static final SXSSFITestDataProviderWithNullAutoSizeTracker instance = new SXSSFITestDataProviderWithNullAutoSizeTracker();
+
+    private SXSSFITestDataProviderWithNullAutoSizeTracker() {
+        // enforce singleton
+    }
+
+    @Override
+    public SXSSFWorkbook createWorkbook() {
+        SXSSFWorkbook wb = new SXSSFWorkbook();
+        instances.add(wb);
+        return wb;
+    }
+
+    //************ SXSSF-specific methods ***************//
+    @Override
+    public SXSSFWorkbook createWorkbook(int rowAccessWindowSize) {
+        SXSSFWorkbook wb = new SXSSFWorkbook(rowAccessWindowSize);
+        instances.add(wb);
+        return wb;
+    }
+
+    @Override
+    public void trackAllColumnsForAutosizing(Sheet sheet) {
+        ((SXSSFSheet)sheet).trackAllColumnsForAutoSizing();
+    }
+    //************ End SXSSF-specific methods ***************//
+
+    private static class SXSSFWorkbookWithNullAutoSizeTracker extends SXSSFWorkbook {
+        SXSSFWorkbookWithNullAutoSizeTracker() {
+            super();
+        }
+
+        SXSSFWorkbookWithNullAutoSizeTracker(int rowAccessWindowSize) {
+            super(rowAccessWindowSize);
+        }
+
+        @Override
+        public SXSSFSheet createSheet() {
+            SXSSFSheet sheet = super.createSheet();
+            sheet._autoSizeColumnTracker = null;
+            return sheet;
+        }
+
+        @Override
+        public SXSSFSheet createSheet(String name) {
+            SXSSFSheet sheet = super.createSheet(name);
+            sheet._autoSizeColumnTracker = null;
+            return sheet;
+        }
+    }
+}
diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithNullAutoSizeTracker.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithNullAutoSizeTracker.java
new file mode 100644 (file)
index 0000000..cdaca74
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ *  ====================================================================
+ *    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.poi.xssf.streaming;
+
+import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.common.usermodel.HyperlinkType;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackageAccess;
+import org.apache.poi.ss.tests.usermodel.BaseTestXWorkbook;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.SXSSFITestDataProvider;
+import org.apache.poi.xssf.XSSFTestDataSamples;
+import org.apache.poi.xssf.model.SharedStringsTable;
+import org.apache.poi.xssf.usermodel.*;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import static org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM;
+import static org.apache.poi.POITestCase.assertEndsWith;
+import static org.apache.poi.POITestCase.assertStartsWith;
+import static org.junit.jupiter.api.Assertions.*;
+
+public final class TestSXSSFWorkbookWithNullAutoSizeTracker extends BaseTestXWorkbook {
+
+    public TestSXSSFWorkbookWithNullAutoSizeTracker() {
+        super(SXSSFITestDataProviderWithNullAutoSizeTracker.instance);
+    }
+
+    @AfterEach
+    void tearDown(){
+        ((SXSSFITestDataProvider)_testDataProvider).cleanup();
+    }
+
+    /**
+     * cloning of sheets is not supported in SXSSF
+     */
+    @Override
+    @Test
+    public void cloneSheet() throws IOException {
+        RuntimeException e = assertThrows(RuntimeException.class, super::cloneSheet);
+        assertEquals("Not Implemented", e.getMessage());
+    }
+
+    /**
+     * cloning of sheets is not supported in SXSSF
+     */
+    @Override
+    @Test
+    public void sheetClone() {
+        RuntimeException e = assertThrows(RuntimeException.class, super::sheetClone);
+        assertEquals("Not Implemented", e.getMessage());
+    }
+
+    /**
+     * Skip this test, as SXSSF doesn't update formulas on sheet name
+     *  changes.
+     */
+    @Override
+    @Disabled("SXSSF doesn't update formulas on sheet name changes, as most cells probably aren't in memory at the time")
+    protected void setSheetName() {
+    }
+
+    @Test
+    void existingWorkbook() throws IOException {
+        try (XSSFWorkbook xssfWb1 = new XSSFWorkbook()) {
+            xssfWb1.createSheet("S1");
+            try (SXSSFWorkbook wb1 = new SXSSFWorkbook(xssfWb1);
+                 XSSFWorkbook xssfWb2 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb1)) {
+                assertTrue(wb1.dispose());
+
+                try (SXSSFWorkbook wb2 = new SXSSFWorkbook(xssfWb2)) {
+                    assertEquals(1, wb2.getNumberOfSheets());
+                    Sheet sheet = wb2.getSheetAt(0);
+                    assertNotNull(sheet);
+                    assertEquals("S1", sheet.getSheetName());
+                    assertTrue(wb2.dispose());
+                }
+            }
+        }
+    }
+
+    @Test
+    void useSharedStringsTable() throws Exception {
+        try (SXSSFWorkbook wb = new SXSSFWorkbook(null, 10, false, true)) {
+
+            SharedStringsTable sss = wb.getSharedStringSource();
+
+            assertNotNull(sss);
+
+            Row row = wb.createSheet("S1").createRow(0);
+
+            row.createCell(0).setCellValue("A");
+            row.createCell(1).setCellValue("B");
+            row.createCell(2).setCellValue("A");
+
+            try (XSSFWorkbook xssfWorkbook = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb)) {
+                sss = wb.getSharedStringSource();
+                assertEquals(2, sss.getUniqueCount());
+                assertTrue(wb.dispose());
+
+                Sheet sheet1 = xssfWorkbook.getSheetAt(0);
+                assertEquals("S1", sheet1.getSheetName());
+                assertEquals(1, sheet1.getPhysicalNumberOfRows());
+                row = sheet1.getRow(0);
+                assertNotNull(row);
+                Cell cell = row.getCell(0);
+                assertNotNull(cell);
+                assertEquals("A", cell.getStringCellValue());
+                cell = row.getCell(1);
+                assertNotNull(cell);
+                assertEquals("B", cell.getStringCellValue());
+                cell = row.getCell(2);
+                assertNotNull(cell);
+                assertEquals("A", cell.getStringCellValue());
+            }
+        }
+    }
+
+    @Test
+    void useSharedStringsTableWithRichText() throws Exception {
+        testUseSharedStringsTableWithRichText(false);
+    }
+
+    @Test
+    void useSharedStringsTableWithRichTextAndCompression() throws Exception {
+        testUseSharedStringsTableWithRichText(true);
+    }
+
+    @Test
+    void addToExistingWorkbook() throws IOException {
+        try (XSSFWorkbook xssfWb1 = new XSSFWorkbook()) {
+            xssfWb1.createSheet("S1");
+            Sheet sheet = xssfWb1.createSheet("S2");
+            Row row = sheet.createRow(1);
+            Cell cell = row.createCell(1);
+            cell.setCellValue("value 2_1_1");
+            try (SXSSFWorkbook wb1 = new SXSSFWorkbook(xssfWb1);
+                 XSSFWorkbook xssfWb2 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb1)) {
+                assertTrue(wb1.dispose());
+
+                try (SXSSFWorkbook wb2 = new SXSSFWorkbook(xssfWb2)) {
+                    // Add a row to the existing empty sheet
+                    Sheet sheet1 = wb2.getSheetAt(0);
+                    Row row1_1 = sheet1.createRow(1);
+                    Cell cell1_1_1 = row1_1.createCell(1);
+                    cell1_1_1.setCellValue("value 1_1_1");
+
+                    // Add a row to the existing non-empty sheet
+                    Sheet sheet2 = wb2.getSheetAt(1);
+                    Row row2_2 = sheet2.createRow(2);
+                    Cell cell2_2_1 = row2_2.createCell(1);
+                    cell2_2_1.setCellValue("value 2_2_1");
+
+                    // Add a sheet with one row
+                    Sheet sheet3 = wb2.createSheet("S3");
+                    Row row3_1 = sheet3.createRow(1);
+                    Cell cell3_1_1 = row3_1.createCell(1);
+                    cell3_1_1.setCellValue("value 3_1_1");
+
+                    try (XSSFWorkbook xssfWb3 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb2)) {
+                        assertEquals(3, xssfWb3.getNumberOfSheets());
+                        // Verify sheet 1
+                        sheet1 = xssfWb3.getSheetAt(0);
+                        assertEquals("S1", sheet1.getSheetName());
+                        assertEquals(1, sheet1.getPhysicalNumberOfRows());
+                        row1_1 = sheet1.getRow(1);
+                        assertNotNull(row1_1);
+                        cell1_1_1 = row1_1.getCell(1);
+                        assertNotNull(cell1_1_1);
+                        assertEquals("value 1_1_1", cell1_1_1.getStringCellValue());
+                        // Verify sheet 2
+                        sheet2 = xssfWb3.getSheetAt(1);
+                        assertEquals("S2", sheet2.getSheetName());
+                        assertEquals(2, sheet2.getPhysicalNumberOfRows());
+                        Row row2_1 = sheet2.getRow(1);
+                        assertNotNull(row2_1);
+                        Cell cell2_1_1 = row2_1.getCell(1);
+                        assertNotNull(cell2_1_1);
+                        assertEquals("value 2_1_1", cell2_1_1.getStringCellValue());
+                        row2_2 = sheet2.getRow(2);
+                        assertNotNull(row2_2);
+                        cell2_2_1 = row2_2.getCell(1);
+                        assertNotNull(cell2_2_1);
+                        assertEquals("value 2_2_1", cell2_2_1.getStringCellValue());
+                        // Verify sheet 3
+                        sheet3 = xssfWb3.getSheetAt(2);
+                        assertEquals("S3", sheet3.getSheetName());
+                        assertEquals(1, sheet3.getPhysicalNumberOfRows());
+                        row3_1 = sheet3.getRow(1);
+                        assertNotNull(row3_1);
+                        cell3_1_1 = row3_1.getCell(1);
+                        assertNotNull(cell3_1_1);
+                        assertEquals("value 3_1_1", cell3_1_1.getStringCellValue());
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    void sheetdataWriter() throws IOException{
+        try (SXSSFWorkbook wb = new SXSSFWorkbook()) {
+            SXSSFSheet sh = wb.createSheet();
+            SheetDataWriter wr = sh.getSheetDataWriter();
+            assertSame(wr.getClass(), SheetDataWriter.class);
+            File tmp = wr.getTempFile();
+            assertStartsWith(tmp.getName(), "poi-sxssf-sheet");
+            assertEndsWith(tmp.getName(), ".xml");
+            assertTrue(wb.dispose());
+        }
+
+        try (SXSSFWorkbook wb = new SXSSFWorkbook()) {
+            wb.setCompressTempFiles(true);
+            SXSSFSheet sh = wb.createSheet();
+            SheetDataWriter wr = sh.getSheetDataWriter();
+            assertSame(wr.getClass(), GZIPSheetDataWriter.class);
+            File tmp = wr.getTempFile();
+            assertStartsWith(tmp.getName(), "poi-sxssf-sheet-xml");
+            assertEndsWith(tmp.getName(), ".gz");
+            assertTrue(wb.dispose());
+        }
+
+        //Test escaping of Unicode control characters
+        try (SXSSFWorkbook wb = new SXSSFWorkbook()) {
+            wb.createSheet("S1").createRow(0).createCell(0).setCellValue("value\u0019");
+            try (XSSFWorkbook xssfWorkbook = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb)) {
+                Cell cell = xssfWorkbook.getSheet("S1").getRow(0).getCell(0);
+                assertEquals("value?", cell.getStringCellValue());
+                assertTrue(wb.dispose());
+            }
+        }
+    }
+
+    @Test
+    void gzipSheetdataWriter() throws IOException {
+        try (SXSSFWorkbook wb = new SXSSFWorkbook()) {
+            wb.setCompressTempFiles(true);
+
+            final int rowNum = 1000;
+            final int sheetNum = 5;
+            populateData(wb);
+
+            try (XSSFWorkbook xwb = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb)) {
+                for (int i = 0; i < sheetNum; i++) {
+                    Sheet sh = xwb.getSheetAt(i);
+                    assertEquals("sheet" + i, sh.getSheetName());
+                    for (int j = 0; j < rowNum; j++) {
+                        Row row = sh.getRow(j);
+                        assertNotNull(row, "row[" + j + "]");
+                        Cell cell1 = row.getCell(0);
+                        assertEquals(new CellReference(cell1).formatAsString(), cell1.getStringCellValue());
+
+                        Cell cell2 = row.getCell(1);
+                        assertEquals(i, (int) cell2.getNumericCellValue());
+
+                        Cell cell3 = row.getCell(2);
+                        assertEquals(j, (int) cell3.getNumericCellValue());
+                    }
+                }
+
+                assertTrue(wb.dispose());
+            }
+        }
+    }
+
+    private static void populateData(Workbook wb) {
+        for(int i = 0; i < 5; i++){
+            Sheet sh = wb.createSheet("sheet" + i);
+            for(int j = 0; j < 1000; j++){
+                Row row = sh.createRow(j);
+                Cell cell1 = row.createCell(0);
+                cell1.setCellValue(new CellReference(cell1).formatAsString());
+
+                Cell cell2 = row.createCell(1);
+                cell2.setCellValue(i);
+
+                Cell cell3 = row.createCell(2);
+                cell3.setCellValue(j);
+            }
+        }
+    }
+
+    @ParameterizedTest
+    @ValueSource(booleans = {false, true})
+    void workbookDispose(boolean compressTempFiles) throws IOException {
+        try (SXSSFWorkbook wb = new SXSSFWorkbook()) {
+            // compressTempFiles == false: the underlying writer is SheetDataWriter
+            // compressTempFiles == true: the underlying writer is GZIPSheetDataWriter
+            wb.setCompressTempFiles(compressTempFiles);
+
+            populateData(wb);
+
+            for (Sheet sheet : wb) {
+                SXSSFSheet sxSheet = (SXSSFSheet) sheet;
+                assertTrue(sxSheet.getSheetDataWriter().getTempFile().exists());
+            }
+
+            assertTrue(wb.dispose());
+
+            for (Sheet sheet : wb) {
+                SXSSFSheet sxSheet = (SXSSFSheet) sheet;
+                assertFalse(sxSheet.getSheetDataWriter().getTempFile().exists());
+            }
+        }
+    }
+
+    @Test
+    void bug53515() throws Exception {
+        try (Workbook wb1 = new SXSSFWorkbook(10)) {
+            populateWorkbook(wb1);
+            assertDoesNotThrow(() -> wb1.write(NULL_OUTPUT_STREAM));
+            assertDoesNotThrow(() -> wb1.write(NULL_OUTPUT_STREAM));
+            try (Workbook wb2 = new XSSFWorkbook()) {
+                populateWorkbook(wb2);
+                assertDoesNotThrow(() -> wb2.write(NULL_OUTPUT_STREAM));
+                assertDoesNotThrow(() -> wb2.write(NULL_OUTPUT_STREAM));
+            }
+        }
+    }
+
+    @Disabled("Crashes the JVM because of documented JVM behavior with concurrent writing/reading of zip-files, "
+            + "see http://www.oracle.com/technetwork/java/javase/documentation/overview-156328.html")
+    @Test
+    void bug53515a() throws Exception {
+        File out = new File("Test.xlsx");
+        assertTrue(!out.exists() || out.delete());
+        for (int i = 0; i < 2; i++) {
+            final SXSSFWorkbook wb;
+            if (out.exists()) {
+                wb = new SXSSFWorkbook(
+                        (XSSFWorkbook) WorkbookFactory.create(out));
+            } else {
+                wb = new SXSSFWorkbook(10);
+            }
+
+            try {
+                FileOutputStream outSteam = new FileOutputStream(out);
+                if (i == 0) {
+                    populateWorkbook(wb);
+                } else {
+                    /*
+                        Code explicitly invokes garbage collection. Except for specific use in benchmarking,
+                        this is very dubious.
+
+                        In the past, situations where people have explicitly invoked the garbage collector in
+                        routines such as close or finalize methods has led to huge performance black holes.
+                        Garbage collection can be expensive. Any situation that forces hundreds or thousands
+                        of garbage collections will bring the machine to a crawl.
+                     */
+
+                    //System.gc();
+                    //System.gc();
+                    //System.gc();
+                }
+
+                    wb.write(outSteam);
+                // assertTrue(wb.dispose());
+                outSteam.close();
+            } finally {
+                assertTrue(wb.dispose());
+            }
+            wb.close();
+        }
+        assertTrue(out.exists());
+        assertTrue(out.delete());
+    }
+
+    private static void populateWorkbook(Workbook wb) {
+        Sheet sh = wb.createSheet();
+        for (int rownum = 0; rownum < 100; rownum++) {
+            Row row = sh.createRow(rownum);
+            for (int cellnum = 0; cellnum < 10; cellnum++) {
+                Cell cell = row.createCell(cellnum);
+                String address = new CellReference(cell).formatAsString();
+                cell.setCellValue(address);
+            }
+        }
+    }
+
+    @Test
+    void closeDoesNotModifyWorkbook() throws IOException {
+        final String filename = "SampleSS.xlsx";
+        final File file = POIDataSamples.getSpreadSheetInstance().getFile(filename);
+
+        // Some tests commented out because close() modifies the file
+        // See bug 58779
+
+        // String
+        //wb = new SXSSFWorkbook(new XSSFWorkbook(file.getPath()));
+        //assertCloseDoesNotModifyFile(filename, wb);
+
+        // File
+        //wb = new SXSSFWorkbook(new XSSFWorkbook(file));
+        //assertCloseDoesNotModifyFile(filename, wb);
+
+        // InputStream
+
+        try (FileInputStream fis = new FileInputStream(file);
+             XSSFWorkbook xwb = new XSSFWorkbook(fis);
+             SXSSFWorkbook wb = new SXSSFWorkbook(xwb)) {
+            assertCloseDoesNotModifyFile(filename, wb);
+        }
+
+        // OPCPackage
+        //wb = new SXSSFWorkbook(new XSSFWorkbook(OPCPackage.open(file)));
+        //assertCloseDoesNotModifyFile(filename, wb);
+    }
+
+    /**
+     * Bug #59743
+     *
+     * this is only triggered on other files apart of sheet[1,2,...].xml
+     * as those are either copied uncompressed or with the use of GZIPInputStream
+     * so we use shared strings
+     */
+    @Test
+    void testZipBombNotTriggeredOnUselessContent() throws IOException {
+        try (SXSSFWorkbook swb = new SXSSFWorkbook(null, 1, true, true)) {
+            SXSSFSheet s = swb.createSheet();
+            char[] useless = new char[32767];
+            Arrays.fill(useless, ' ');
+
+            for (int row = 0; row < 10; row++) {
+                Row r = s.createRow(row);
+                for (int col = 0; col < 10; col++) {
+                    char[] prefix = Integer.toHexString(row * 10 + col).toCharArray();
+                    Arrays.fill(useless, 0, 10, ' ');
+                    System.arraycopy(prefix, 0, useless, 0, prefix.length);
+                    String ul = new String(useless);
+                    r.createCell(col, CellType.STRING).setCellValue(ul);
+                }
+            }
+
+            assertDoesNotThrow(() -> swb.write(NULL_OUTPUT_STREAM));
+            swb.dispose();
+        }
+    }
+
+    /**
+     * To avoid accident changes to the template, you should be able
+     *  to create a SXSSFWorkbook from a read-only XSSF one, then
+     *  change + save that (only). See bug #60010
+     * TODO Fix this to work!
+     */
+    @Test
+    @Disabled
+    void createFromReadOnlyWorkbook() throws Exception {
+        String sheetName = "Test SXSSF";
+        File input = XSSFTestDataSamples.getSampleFile("sample.xlsx");
+
+        try (OPCPackage pkg = OPCPackage.open(input, PackageAccess.READ)) {
+            UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
+            try (XSSFWorkbook xssf = new XSSFWorkbook(pkg)) {
+                try (SXSSFWorkbook wb = new SXSSFWorkbook(xssf, 2)) {
+                    Sheet s = wb.createSheet(sheetName);
+                    for (int i = 0; i < 10; i++) {
+                        Row r = s.createRow(i);
+                        r.createCell(0).setCellValue(true);
+                        r.createCell(1).setCellValue(2.4);
+                        r.createCell(2).setCellValue("Test Row " + i);
+                    }
+                    assertEquals(10, s.getLastRowNum());
+
+                    wb.write(bos);
+                    wb.dispose();
+                }
+            }
+
+            try (XSSFWorkbook xssf = new XSSFWorkbook(bos.toInputStream())) {
+                Sheet s = xssf.getSheet(sheetName);
+                assertEquals(10, s.getLastRowNum());
+                assertTrue(s.getRow(0).getCell(0).getBooleanCellValue());
+                assertEquals("Test Row 9", s.getRow(9).getCell(2).getStringCellValue());
+            }
+        }
+    }
+
+
+    @Test
+    void test56557() throws IOException {
+        try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56557.xlsx");
+             // Using streaming XSSFWorkbook makes the output file invalid
+             Workbook wb2 = new SXSSFWorkbook(wb);
+             // Should not throw POIXMLException: java.io.IOException: Unable to parse xml bean when reading back
+             Workbook wbBack = XSSFTestDataSamples.writeOutAndReadBack(wb2)
+         ) {
+            assertNotNull(wbBack);
+        }
+    }
+
+    @Test
+    void addHyperlink() throws Exception {
+        try (
+            SXSSFWorkbook wb = new SXSSFWorkbook();
+            UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()
+        ) {
+            SXSSFSheet sheet = wb.createSheet("s1");
+            SXSSFRow row = sheet.createRow(0);
+            SXSSFCell cell = row.createCell(0);
+            cell.setCellValue("Example Website");
+            XSSFHyperlink hyperlink = (XSSFHyperlink)wb.getCreationHelper().createHyperlink(HyperlinkType.URL);
+            hyperlink.setAddress("http://example.com");
+            hyperlink.setCellReference("A1");
+            sheet.addHyperlink(hyperlink);
+            wb.write(bos);
+
+            try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
+                XSSFSheet xssfSheet = xssfWorkbook.getSheet(sheet.getSheetName());
+                XSSFCell xssfCell = xssfSheet.getRow(0).getCell(0);
+                assertEquals("Example Website", xssfCell.getStringCellValue());
+                XSSFHyperlink xssfHyperlink = xssfCell.getHyperlink();
+                assertEquals(hyperlink.getAddress(), xssfHyperlink.getAddress());
+            }
+        }
+    }
+
+    @Test
+    void addDimension() throws IOException {
+        try (
+                SXSSFWorkbook wb = new SXSSFWorkbook();
+                UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()
+        ) {
+            SXSSFSheet sheet = wb.createSheet();
+            sheet.createRow(2).createCell(3).setCellValue("top left");
+            sheet.createRow(6).createCell(5).setCellValue("bottom right");
+            assertEquals(2, sheet.getFirstRowNum());
+            assertEquals(6, sheet.getLastRowNum());
+            wb.write(bos);
+            try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
+                XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
+                assertEquals(CellRangeAddress.valueOf("D3:F7"), xssfSheet.getDimension());
+            }
+        }
+    }
+
+    @Test
+    void addDimension1() throws IOException {
+        try (
+                SXSSFWorkbook wb = new SXSSFWorkbook(1);
+                UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()
+        ) {
+            SXSSFSheet sheet = wb.createSheet();
+            sheet.createRow(2).createCell(3).setCellValue("top left");
+            sheet.createRow(6).createCell(5).setCellValue("bottom right");
+            assertEquals(2, sheet.getFirstRowNum());
+            assertEquals(6, sheet.getLastRowNum());
+            wb.write(bos);
+            try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
+                XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
+                assertEquals(CellRangeAddress.valueOf("D3:F7"), xssfSheet.getDimension());
+            }
+        }
+    }
+
+    @Test
+    void addDimensionXSSFtoSXSSF() throws IOException {
+        try (XSSFWorkbook wb = new XSSFWorkbook()) {
+            XSSFSheet sheet = wb.createSheet();
+            sheet.createRow(2).createCell(3).setCellValue("top left");
+            sheet.createRow(6).createCell(5).setCellValue("bottom right");
+            assertEquals(2, sheet.getFirstRowNum());
+            assertEquals(6, sheet.getLastRowNum());
+            try (
+                    SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(wb);
+                    UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()
+            ) {
+                sxssfWorkbook.write(bos);
+                try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
+                    XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
+                    assertEquals(CellRangeAddress.valueOf("D3:F7"), xssfSheet.getDimension());
+                }
+            }
+        }
+    }
+
+    @Test
+    void addDimensionDisabled() throws IOException {
+        try (
+                SXSSFWorkbook wb = new SXSSFWorkbook();
+                UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()
+        ) {
+            wb.setShouldCalculateSheetDimensions(false);
+            SXSSFSheet sheet = wb.createSheet();
+            sheet.createRow(2).createCell(3).setCellValue("top left");
+            sheet.createRow(6).createCell(5).setCellValue("bottom right");
+            assertEquals(2, sheet.getFirstRowNum());
+            assertEquals(6, sheet.getLastRowNum());
+            wb.write(bos);
+            try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
+                XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
+                assertEquals(CellRangeAddress.valueOf("A1:A1"), xssfSheet.getDimension());
+            }
+        }
+    }
+
+    @Override
+    @Disabled("not implemented")
+    protected void changeSheetNameWithSharedFormulas() {
+    }
+
+    private void testUseSharedStringsTableWithRichText(boolean compressTempFiles) throws Exception {
+        try (SXSSFWorkbook wb = new SXSSFWorkbook(null, 10, compressTempFiles, true)) {
+
+            SharedStringsTable sss = wb.getSharedStringSource();
+
+            assertNotNull(sss);
+
+            XSSFFont redFont = new XSSFFont();
+            redFont.setColor(new XSSFColor(new java.awt.Color(241,76,93), new DefaultIndexedColorMap()));
+
+            Row row = wb.createSheet("S1").createRow(0);
+
+            row.createCell(0).setCellValue("A");
+            row.createCell(1).setCellValue("B");
+            XSSFRichTextString rts = new XSSFRichTextString("A");
+            rts.applyFont(redFont);
+            row.createCell(2).setCellValue(rts);
+
+            try (XSSFWorkbook xssfWorkbook = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb)) {
+                sss = wb.getSharedStringSource();
+                assertEquals(3, sss.getUniqueCount());
+                assertTrue(wb.dispose());
+
+                Sheet sheet1 = xssfWorkbook.getSheetAt(0);
+                assertEquals("S1", sheet1.getSheetName());
+                assertEquals(1, sheet1.getPhysicalNumberOfRows());
+                row = sheet1.getRow(0);
+                assertNotNull(row);
+                Cell cell = row.getCell(0);
+                assertNotNull(cell);
+                assertEquals("A", cell.getStringCellValue());
+                cell = row.getCell(1);
+                assertNotNull(cell);
+                assertEquals("B", cell.getStringCellValue());
+                cell = row.getCell(2);
+                assertNotNull(cell);
+                assertEquals("A", cell.getStringCellValue());
+                XSSFRichTextString outputRichTextString = (XSSFRichTextString) cell.getRichStringCellValue();
+                XSSFFont outputFont = outputRichTextString.getFontAtIndex(0);
+                assertEquals(redFont, outputFont);
+            }
+        }
+    }
+
+}