From 6ed51c1db29c310fbeef9d1fbe5beab5861dd684 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 18 Aug 2022 17:40:22 +0000 Subject: [PATCH] [bug-66230] don't fail to create SXSSFSheet if auto size tracker can't be initialized git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1903529 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/xssf/streaming/SXSSFSheet.java | 55 +- .../poi/xssf/SXSSFITestDataProvider.java | 6 +- ...stDataProviderWithNullAutoSizeTracker.java | 76 ++ ...tSXSSFWorkbookWithNullAutoSizeTracker.java | 674 ++++++++++++++++++ 4 files changed, 791 insertions(+), 20 deletions(-) create mode 100644 poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/SXSSFITestDataProviderWithNullAutoSizeTracker.java create mode 100644 poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithNullAutoSizeTracker.java diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java index 4fa4138b3f..c0007cfcdc 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java @@ -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 _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 column 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 columns 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 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 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 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; } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/SXSSFITestDataProvider.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/SXSSFITestDataProvider.java index 8c722151ee..c43a9cb38c 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/SXSSFITestDataProvider.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/SXSSFITestDataProvider.java @@ -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 instances = new ArrayList<>(); + protected final Collection 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 index 0000000000..c24670ba80 --- /dev/null +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/SXSSFITestDataProviderWithNullAutoSizeTracker.java @@ -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 index 0000000000..cdaca74531 --- /dev/null +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithNullAutoSizeTracker.java @@ -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); + } + } + } + +} -- 2.39.5