[github-184] New EmittingSXSSFWorkbook. Thanks to mobreza. This closes #184

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1879302 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
PJ Fanning 2020-06-28 11:57:37 +00:00
parent 09d81ea7da
commit f06c45421b
10 changed files with 1138 additions and 213 deletions

View File

@ -0,0 +1,69 @@
/* ====================================================================
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 java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.poi.util.Beta;
import org.apache.poi.xssf.usermodel.XSSFSheet;
/**
* A variant of SXSSFSheet that uses IRowGenerator to create rows.
*
* This variant is experimental and APIs may change at short notice.
*
* @see EmittingSXSSFWorkbook
* @since 5.0.0
*/
@Beta
public class EmittingSXSSFSheet extends SXSSFSheet {
private IRowGenerator rowGenerator;
public EmittingSXSSFSheet(EmittingSXSSFWorkbook workbook, XSSFSheet xSheet) throws IOException {
super(workbook, xSheet, workbook.getRandomAccessWindowSize());
}
@Override
public InputStream getWorksheetXMLInputStream() throws IOException {
throw new RuntimeException("Not supported by EmittingSXSSFSheet");
}
public void setRowGenerator(IRowGenerator rowGenerator) {
this.rowGenerator = rowGenerator;
}
public void writeRows(OutputStream out) throws IOException {
// delayed creation of SheetDataWriter
_writer = ((EmittingSXSSFWorkbook) _workbook).createSheetDataWriter(out);
try {
if (this.rowGenerator != null) {
this.rowGenerator.generateRows(this);
}
} catch (Exception e) {
throw new IOException("Error generating Excel rows", e);
} finally {
// flush buffered rows
flushRows(0);
// flush writer buffer
_writer.close();
out.flush();
}
}
}

View File

@ -0,0 +1,219 @@
/* ====================================================================
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 java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.util.Beta;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
/**
* An variant of SXSSFWorkbook that avoids generating a temporary file and writes data directly to
* the provided OutputStream.
*
* This variant is experimental and APIs may change at short notice.
*
* @since 5.0.0
*/
@Beta
public class EmittingSXSSFWorkbook extends SXSSFWorkbook {
private static final POILogger logger = POILogFactory.getLogger(EmittingSXSSFWorkbook.class);
public EmittingSXSSFWorkbook() {
this(null);
}
public EmittingSXSSFWorkbook(XSSFWorkbook workbook) {
this(workbook, SXSSFWorkbook.DEFAULT_WINDOW_SIZE);
}
public EmittingSXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize) {
super(workbook, rowAccessWindowSize, false, false);
}
@Override
protected SheetDataWriter createSheetDataWriter() throws IOException {
throw new RuntimeException("Not supported by EmittingSXSSFWorkbook");
}
protected StreamingSheetWriter createSheetDataWriter(OutputStream out) throws IOException {
return new StreamingSheetWriter(out);
}
@Override
protected ISheetInjector createSheetInjector(SXSSFSheet sxSheet) throws IOException {
EmittingSXSSFSheet ssxSheet = (EmittingSXSSFSheet) sxSheet;
return (output) -> {
ssxSheet.writeRows(output);
};
}
@Override
SXSSFSheet createAndRegisterSXSSFSheet(XSSFSheet xSheet) {
final EmittingSXSSFSheet sxSheet;
try {
sxSheet = new EmittingSXSSFSheet(this, xSheet);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
registerSheetMapping(sxSheet, xSheet);
return sxSheet;
}
public EmittingSXSSFSheet createSheet() {
return (EmittingSXSSFSheet) super.createSheet();
}
public EmittingSXSSFSheet createSheet(String sheetname) {
return (EmittingSXSSFSheet) super.createSheet(sheetname);
}
/**
* Returns an iterator of the sheets in the workbook in sheet order. Includes hidden and very hidden sheets.
*
* @return an iterator of the sheets.
*/
@Override
public Iterator<Sheet> sheetIterator() {
return new SheetIterator<Sheet>();
}
private final class SheetIterator<T extends Sheet> implements Iterator<T> {
final private Iterator<XSSFSheet> it;
@SuppressWarnings("unchecked")
public SheetIterator() {
it = (Iterator<XSSFSheet>) (Iterator<? extends Sheet>) _wb.iterator();
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
@SuppressWarnings("unchecked")
public T next() throws NoSuchElementException {
final XSSFSheet xssfSheet = it.next();
EmittingSXSSFSheet sxSheet = (EmittingSXSSFSheet) getSXSSFSheet(xssfSheet);
return (T) (sxSheet == null ? xssfSheet : sxSheet);
}
/**
* Unexpected behavior may occur if sheets are reordered after iterator has been created. Support for the remove
* method may be added in the future if someone can figure out a reliable implementation.
*/
@Override
public void remove() throws IllegalStateException {
throw new UnsupportedOperationException("remove method not supported on XSSFWorkbook.iterator(). "
+ "Use Sheet.removeSheetAt(int) instead.");
}
}
/**
* Alias for {@link #sheetIterator()} to allow foreach loops
*/
@Override
public Iterator<Sheet> iterator() {
return sheetIterator();
}
@Override
public SXSSFSheet getSheetAt(int index) {
throw new RuntimeException("Not supported by EmittingSXSSFWorkbook");
}
public XSSFSheet getXSSFSheetAt(int index) {
return _wb.getSheetAt(index);
}
/**
* Gets the sheet at the given index for streaming.
*
* @param index the index
* @return the streaming sheet at
*/
public EmittingSXSSFSheet getStreamingSheetAt(int index) {
XSSFSheet xSheet = _wb.getSheetAt(index);
SXSSFSheet sxSheet = getSXSSFSheet(xSheet);
if (sxSheet == null && xSheet != null) {
return (EmittingSXSSFSheet) createAndRegisterSXSSFSheet(xSheet);
} else {
return (EmittingSXSSFSheet) sxSheet;
}
}
@Override
public SXSSFSheet getSheet(String name) {
throw new RuntimeException("Not supported by EmittingSXSSFWorkbook");
}
public XSSFSheet getXSSFSheet(String name) {
return _wb.getSheet(name);
}
/**
* Gets sheet with the given name for streaming.
*
* @param name the name
* @return the streaming sheet
*/
public EmittingSXSSFSheet getStreamingSheet(String name) {
XSSFSheet xSheet = _wb.getSheet(name);
EmittingSXSSFSheet sxSheet = (EmittingSXSSFSheet) getSXSSFSheet(xSheet);
if (sxSheet == null && xSheet != null) {
return (EmittingSXSSFSheet) createAndRegisterSXSSFSheet(xSheet);
} else {
return sxSheet;
}
}
/**
* Removes sheet at the given index
*
* @param index of the sheet to remove (0-based)
*/
@Override
public void removeSheetAt(int index) {
// Get the sheet to be removed
XSSFSheet xSheet = _wb.getSheetAt(index);
SXSSFSheet sxSheet = getSXSSFSheet(xSheet);
// De-register it
_wb.removeSheetAt(index);
// The sheet may not be a streaming sheet and is not mapped
if (sxSheet != null) {
deregisterSheetMapping(xSheet);
// Clean up temporary resources
try {
sxSheet.dispose();
} catch (IOException e) {
logger.log(POILogger.WARN, e);
}
}
}
}

View File

@ -0,0 +1,37 @@
/* ====================================================================
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.util.Beta;
/**
* IRowGenerator for Emitting SXSSF sheets
*
* @see EmittingSXSSFWorkbook
*/
@Beta
public interface IRowGenerator {
/**
* Generate and add rows to the sheet
*
* @param sheet the sheet
* @throws Exception the exception
*/
void generateRows(SXSSFSheet sheet) throws Exception;
}

View File

@ -106,11 +106,13 @@ public final class SXSSFFormulaEvaluator extends BaseXSSFFormulaEvaluator {
// Process the sheets as best we can
for (Sheet sheet : wb) {
// Check if any rows have already been flushed out
int lastFlushedRowNum = ((SXSSFSheet) sheet).getLastFlushedRowNum();
if (lastFlushedRowNum > -1) {
if (! skipOutOfWindow) throw new RowFlushedException(0);
logger.log(POILogger.INFO, "Rows up to " + lastFlushedRowNum + " have already been flushed, skipping");
if (sheet instanceof SXSSFSheet) {
// Check if any rows have already been flushed out
int lastFlushedRowNum = ((SXSSFSheet) sheet).getLastFlushedRowNum();
if (lastFlushedRowNum > -1) {
if (! skipOutOfWindow) throw new RowFlushedException(0);
logger.log(POILogger.INFO, "Rows up to " + lastFlushedRowNum + " have already been flushed, skipping");
}
}
// Evaluate what we have

View File

@ -24,6 +24,7 @@ import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.ss.usermodel.Picture;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Shape;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.ImageUtils;
import org.apache.poi.util.Internal;
@ -201,7 +202,8 @@ public final class SXSSFPicture implements Picture {
// THE FOLLOWING THREE LINES ARE THE MAIN CHANGE compared to the non-streaming version: use the SXSSF sheet,
// not the XSSF sheet (which never contais rows when using SXSSF)
XSSFSheet xssfSheet = getSheet();
SXSSFSheet sheet = _wb.getSXSSFSheet(xssfSheet);
SXSSFSheet sxSheet = _wb.getSXSSFSheet(xssfSheet);
Sheet sheet = sxSheet == null ? xssfSheet : sxSheet;
Row row = sheet.getRow(rowIndex);
float height = row != null ? row.getHeightInPoints() : sheet.getDefaultRowHeightInPoints();
return height * Units.PIXEL_DPI / Units.POINT_DPI;

View File

@ -61,19 +61,26 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
/**
* Streaming version of XSSFSheet implementing the "BigGridDemo" strategy.
*/
*/
public class SXSSFSheet implements Sheet
{
/*package*/ final XSSFSheet _sh;
private final SXSSFWorkbook _workbook;
protected final SXSSFWorkbook _workbook;
private final TreeMap<Integer,SXSSFRow> _rows = new TreeMap<>();
private final SheetDataWriter _writer;
protected SheetDataWriter _writer;
private int _randomAccessWindowSize = SXSSFWorkbook.DEFAULT_WINDOW_SIZE;
private final AutoSizeColumnTracker _autoSizeColumnTracker;
protected final AutoSizeColumnTracker _autoSizeColumnTracker;
private int outlineLevelRow;
private int lastFlushedRowNumber = -1;
private boolean allFlushed;
protected SXSSFSheet(SXSSFWorkbook workbook, XSSFSheet xSheet, int randomAccessWindowSize) {
_workbook = workbook;
_sh = xSheet;
setRandomAccessWindowSize(randomAccessWindowSize);
_autoSizeColumnTracker = new AutoSizeColumnTracker(this);
}
public SXSSFSheet(SXSSFWorkbook workbook, XSSFSheet xSheet) throws IOException {
_workbook = workbook;
_sh = xSheet;
@ -90,8 +97,8 @@ public class SXSSFSheet implements Sheet
return _writer;
}
/* Gets "<sheetData>" document fragment*/
public InputStream getWorksheetXMLInputStream() throws IOException
/* Gets "<sheetData>" document fragment*/
public InputStream getWorksheetXMLInputStream() throws IOException
{
// flush all remaining data and close the temp file writer
flushRows(0);
@ -99,7 +106,7 @@ public class SXSSFSheet implements Sheet
return _writer.getWorksheetXMLInputStream();
}
//start of interface implementation
//start of interface implementation
@Override
public Iterator<Row> iterator()
{
@ -128,7 +135,7 @@ public class SXSSFSheet implements Sheet
if(rownum <= _writer.getLastFlushedRow() ) {
throw new IllegalArgumentException(
"Attempting to write a row["+rownum+"] " +
"in the range [0," + _writer.getLastFlushedRow() + "] that is already written to disk.");
"in the range [0," + _writer.getLastFlushedRow() + "] that is already written to disk.");
}
// attempt to overwrite a existing row in the input template
@ -143,7 +150,7 @@ public class SXSSFSheet implements Sheet
allFlushed = false;
if(_randomAccessWindowSize >= 0 && _rows.size() > _randomAccessWindowSize) {
try {
flushRows(_randomAccessWindowSize);
flushRows(_randomAccessWindowSize);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
@ -274,7 +281,7 @@ public class SXSSFSheet implements Sheet
/**
* Get the actual column width in pixels
*
*
* <p>
* Please note, that this method works correctly only for workbooks
* with the default font size (Calibri 11pt for .xlsx).
@ -283,8 +290,8 @@ public class SXSSFSheet implements Sheet
@Override
public float getColumnWidthInPixels(int columnIndex) {
return _sh.getColumnWidthInPixels(columnIndex);
}
}
/**
* Set the default column width for the sheet (if the columns do not define their own width)
* in characters
@ -308,7 +315,7 @@ public class SXSSFSheet implements Sheet
{
return _sh.getDefaultColumnWidth();
}
/**
* Get the default row height for the sheet (if the rows do not define their own height) in
@ -356,7 +363,7 @@ public class SXSSFSheet implements Sheet
{
_sh.setDefaultRowHeightInPoints(height);
}
/**
* Returns the CellStyle that applies to the given
@ -461,7 +468,7 @@ public class SXSSFSheet implements Sheet
{
_sh.removeMergedRegion(index);
}
/**
* Removes a merged region of cells (hence letting them free)
*
@ -568,7 +575,7 @@ public class SXSSFSheet implements Sheet
{
return _sh.isDisplayZeros();
}
/**
* Sets whether the worksheet is displayed from right to left instead of from left to right.
*
@ -577,7 +584,7 @@ public class SXSSFSheet implements Sheet
@Override
public void setRightToLeft(boolean value)
{
_sh.setRightToLeft(value);
_sh.setRightToLeft(value);
}
/**
@ -588,7 +595,7 @@ public class SXSSFSheet implements Sheet
@Override
public boolean isRightToLeft()
{
return _sh.isRightToLeft();
return _sh.isRightToLeft();
}
/**
@ -733,7 +740,7 @@ public class SXSSFSheet implements Sheet
{
_sh.setPrintGridlines(show);
}
/**
* Returns whether row and column headings are printed.
*
@ -841,7 +848,7 @@ public class SXSSFSheet implements Sheet
{
return _sh.getProtect();
}
/**
* Sets the protection enabled as well as the password
* @param password to set for protection. Pass <code>null</code> to remove protection
@ -851,7 +858,7 @@ public class SXSSFSheet implements Sheet
{
_sh.protectSheet(password);
}
/**
* Answer whether scenario protection is enabled or disabled
*
@ -862,7 +869,7 @@ public class SXSSFSheet implements Sheet
{
return _sh.getScenarioProtect();
}
/**
* Window zoom magnification for current view representing percent values.
* Valid values range from 10 to 400. Horizontal and Vertical scale together.
@ -933,7 +940,7 @@ public class SXSSFSheet implements Sheet
*/
@Override
public void setForceFormulaRecalculation(boolean value) {
_sh.setForceFormulaRecalculation(value);
_sh.setForceFormulaRecalculation(value);
}
/**
@ -942,12 +949,12 @@ public class SXSSFSheet implements Sheet
*/
@Override
public boolean getForceFormulaRecalculation() {
return _sh.getForceFormulaRecalculation();
return _sh.getForceFormulaRecalculation();
}
/**
* <i>Not implemented for SXSSFSheets</i>
*
*
* Shifts rows between startRow and endRow n number of rows.
* If you use a negative number, it will shift rows up.
* Code ensures that rows don't wrap around.
@ -969,7 +976,7 @@ public class SXSSFSheet implements Sheet
/**
* <i>Not implemented for SXSSFSheets</i>
*
*
* Shifts rows between startRow and endRow n number of rows.
* If you use a negative number, it will shift rows up.
* Code ensures that rows don't wrap around
@ -1236,7 +1243,7 @@ public class SXSSFSheet implements Sheet
* Please note the rows being grouped <em>must</em> be in the current window,
* if the rows are already flushed then groupRow has no effect.
* </p>
*
*
* Correct code:
* <pre><code>
* Workbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory
@ -1249,8 +1256,8 @@ public class SXSSFSheet implements Sheet
* }
*
* </code></pre>
*
*
*
*
* Incorrect code:
* <pre><code>
* Workbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory
@ -1261,7 +1268,7 @@ public class SXSSFSheet implements Sheet
* sh.groupRow(100, 200); // the rows in the range [100, 200] are already flushed and groupRows has no effect
*
* </code></pre>
*
*
*
* @param fromRow start row (0-based)
* @param toRow end row (0-based)
@ -1280,7 +1287,7 @@ public class SXSSFSheet implements Sheet
setWorksheetOutlineLevelRow();
}
/**
* Set row groupings (like groupRow) in a stream-friendly manner
*
@ -1308,8 +1315,8 @@ public class SXSSFSheet implements Sheet
private void setWorksheetOutlineLevelRow() {
CTWorksheet ct = _sh.getCTWorksheet();
CTSheetFormatPr pr = ct.isSetSheetFormatPr() ?
ct.getSheetFormatPr() :
ct.addNewSheetFormatPr();
ct.getSheetFormatPr() :
ct.addNewSheetFormatPr();
if(outlineLevelRow > 0) {
pr.setOutlineLevelRow((short)outlineLevelRow);
}
@ -1346,7 +1353,7 @@ public class SXSSFSheet implements Sheet
throw new RuntimeException("Unable to expand row: Not Implemented");
}
}
/**
* @param rowIndex the zero based row index to collapse
*/
@ -1359,7 +1366,7 @@ public class SXSSFSheet implements Sheet
// Hide all the columns until the end of the group
int lastRow = writeHidden(row, startRow);
SXSSFRow lastRowObj = getRow(lastRow);
SXSSFRow lastRowObj = getRow(lastRow);
if (lastRowObj != null) {
lastRowObj.setCollapsed(true);
} else {
@ -1368,7 +1375,7 @@ public class SXSSFSheet implements Sheet
}
}
}
/**
* @param rowIndex the zero based row index to find from
*/
@ -1388,7 +1395,7 @@ public class SXSSFSheet implements Sheet
}
return currentRow + 1;
}
private int writeHidden(SXSSFRow xRow, int rowIndex) {
int level = xRow.getOutlineLevel();
SXSSFRow currRow = getRow(rowIndex);
@ -1412,8 +1419,8 @@ public class SXSSFSheet implements Sheet
{
_sh.setDefaultColumnStyle(column, style);
}
/**
* Track a column in the sheet for auto-sizing.
* Note this has undefined behavior if a column is tracked after one or more rows are written to the sheet.
@ -1428,7 +1435,7 @@ public class SXSSFSheet implements Sheet
{
_autoSizeColumnTracker.trackColumn(column);
}
/**
* Track several columns in the sheet for auto-sizing.
* Note this has undefined behavior if columns are tracked after one or more rows are written to the sheet.
@ -1441,7 +1448,7 @@ public class SXSSFSheet implements Sheet
{
_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.
@ -1451,7 +1458,7 @@ public class SXSSFSheet implements Sheet
{
_autoSizeColumnTracker.trackAllColumns();
}
/**
* Removes a column that was previously marked for inclusion in auto-size column tracking.
* When a column is untracked, the best-fit width is forgotten.
@ -1467,7 +1474,7 @@ public class SXSSFSheet implements Sheet
{
return _autoSizeColumnTracker.untrackColumn(column);
}
/**
* Untracks several columns in the sheet for auto-sizing.
* When a column is untracked, the best-fit width is forgotten.
@ -1481,7 +1488,7 @@ public class SXSSFSheet implements Sheet
{
return _autoSizeColumnTracker.untrackColumns(columns);
}
/**
* Untracks all columns in the sheet for auto-sizing. Best-fit column widths are forgotten.
* If this is called, individual columns do not need to be untracked.
@ -1491,7 +1498,7 @@ public class SXSSFSheet implements Sheet
{
_autoSizeColumnTracker.untrackAllColumns();
}
/**
* Returns true if column is currently tracked for auto-sizing.
*
@ -1503,7 +1510,7 @@ public class SXSSFSheet implements Sheet
{
return _autoSizeColumnTracker.isColumnTracked(column);
}
/**
* Get the currently tracked columns for auto-sizing.
* Note if all columns are tracked, this will only return the columns that have been explicitly or implicitly tracked,
@ -1527,7 +1534,7 @@ public class SXSSFSheet implements Sheet
* </p>
* You can specify whether the content of merged cells should be considered or ignored.
* Default is to ignore merged cells.
*
*
* <p>
* Special note about SXSSF implementation: You must register the columns you wish to track with
* the SXSSFSheet using {@link #trackColumnForAutoSizing(int)} or {@link #trackAllColumnsForAutoSizing()}.
@ -1554,7 +1561,7 @@ public class SXSSFSheet implements Sheet
* </p>
* You can specify whether the content of merged cells should be considered or ignored.
* Default is to ignore merged cells.
*
*
* <p>
* Special note about SXSSF implementation: You must register the columns you wish to track with
* the SXSSFSheet using {@link #trackColumnForAutoSizing(int)} or {@link #trackAllColumnsForAutoSizing()}.
@ -1590,21 +1597,21 @@ public class SXSSFSheet implements Sheet
catch (final IllegalStateException e) {
throw new IllegalStateException("Could not auto-size column. Make sure the column was tracked prior to auto-sizing the column.", e);
}
// get the best-fit width of rows currently in the random access window
final int activeWidth = (int) (256 * SheetUtil.getColumnWidth(this, column, useMergedCells));
// the best-fit width for both flushed rows and random access window rows
// flushedWidth or activeWidth may be negative if column contains only blank cells
final int bestFitWidth = Math.max(flushedWidth, activeWidth);
if (bestFitWidth > 0) {
final int maxColumnWidth = 255*256; // The maximum column width for an individual cell is 255 characters
final int width = Math.min(bestFitWidth, maxColumnWidth);
setColumnWidth(column, width);
}
}
/**
* Returns cell comment for the specified row and column
*
@ -1615,7 +1622,7 @@ public class SXSSFSheet implements Sheet
{
return _sh.getCellComment(ref);
}
/**
* Returns all cell comments on this sheet.
* @return A map of each Comment in the sheet, keyed on the cell address where
@ -1625,7 +1632,7 @@ public class SXSSFSheet implements Sheet
public Map<CellAddress, XSSFComment> getCellComments() {
return _sh.getCellComments();
}
/**
* Get a Hyperlink in this sheet anchored at row, column
*
@ -1637,7 +1644,7 @@ public class SXSSFSheet implements Sheet
public XSSFHyperlink getHyperlink(int row, int column) {
return _sh.getHyperlink(row, column);
}
/**
* Get a Hyperlink in this sheet located in a cell specified by {code addr}
*
@ -1649,7 +1656,7 @@ public class SXSSFSheet implements Sheet
public XSSFHyperlink getHyperlink(CellAddress addr) {
return _sh.getHyperlink(addr);
}
/**
* Get a list of Hyperlinks in this sheet
*
@ -1659,7 +1666,7 @@ public class SXSSFSheet implements Sheet
public List<XSSFHyperlink> getHyperlinkList() {
return _sh.getHyperlinkList();
}
/**
* {@inheritDoc}
*/
@ -1744,7 +1751,7 @@ public class SXSSFSheet implements Sheet
throw new RuntimeException("Not Implemented");
}
@Override
public DataValidationHelper getDataValidationHelper()
{
@ -1769,7 +1776,7 @@ public class SXSSFSheet implements Sheet
/**
* Enable filtering for a range of cells
*
*
* @param range the range of cells to filter
*/
@Override
@ -1782,30 +1789,30 @@ public class SXSSFSheet implements Sheet
public SheetConditionalFormatting getSheetConditionalFormatting(){
return _sh.getSheetConditionalFormatting();
}
@Override
public CellRangeAddress getRepeatingRows() {
return _sh.getRepeatingRows();
return _sh.getRepeatingRows();
}
@Override
public CellRangeAddress getRepeatingColumns() {
return _sh.getRepeatingColumns();
return _sh.getRepeatingColumns();
}
@Override
public void setRepeatingRows(CellRangeAddress rowRangeRef) {
_sh.setRepeatingRows(rowRangeRef);
_sh.setRepeatingRows(rowRangeRef);
}
@Override
public void setRepeatingColumns(CellRangeAddress columnRangeRef) {
_sh.setRepeatingColumns(columnRangeRef);
_sh.setRepeatingColumns(columnRangeRef);
}
//end of interface implementation
/**
* Specifies how many rows can be accessed at most via getRow().
@ -1821,12 +1828,12 @@ public class SXSSFSheet implements Sheet
*/
public void setRandomAccessWindowSize(int value)
{
if(value == 0 || value < -1) {
throw new IllegalArgumentException("RandomAccessWindowSize must be either -1 or a positive integer");
}
_randomAccessWindowSize = value;
if(value == 0 || value < -1) {
throw new IllegalArgumentException("RandomAccessWindowSize must be either -1 or a positive integer");
}
_randomAccessWindowSize = value;
}
/**
* Are all rows flushed to disk?
*/
@ -1880,7 +1887,7 @@ public class SXSSFSheet implements Sheet
}
public void changeRowNum(SXSSFRow row, int newRowNum)
{
removeRow(row);
_rows.put(newRowNum,row);
}
@ -1904,7 +1911,7 @@ public class SXSSFSheet implements Sheet
if (!allFlushed) {
flushRows();
}
return _writer.dispose();
return _writer == null || _writer.dispose();
}
@Override
@ -1935,21 +1942,21 @@ public class SXSSFSheet implements Sheet
public void setTabColor(XSSFColor color) {
_sh.setTabColor(color);
}
/**
* Enable sheet protection
*/
public void enableLocking() {
safeGetProtectionField().setSheet(true);
}
/**
* Disable sheet protection
*/
public void disableLocking() {
safeGetProtectionField().setSheet(false);
}
/**
* Enable or disable Autofilters locking.
* This does not modify sheet protection status.
@ -1958,7 +1965,7 @@ public class SXSSFSheet implements Sheet
public void lockAutoFilter(boolean enabled) {
safeGetProtectionField().setAutoFilter(enabled);
}
/**
* Enable or disable Deleting columns locking.
* This does not modify sheet protection status.
@ -1967,7 +1974,7 @@ public class SXSSFSheet implements Sheet
public void lockDeleteColumns(boolean enabled) {
safeGetProtectionField().setDeleteColumns(enabled);
}
/**
* Enable or disable Deleting rows locking.
* This does not modify sheet protection status.
@ -1976,7 +1983,7 @@ public class SXSSFSheet implements Sheet
public void lockDeleteRows(boolean enabled) {
safeGetProtectionField().setDeleteRows(enabled);
}
/**
* Enable or disable Formatting cells locking.
* This does not modify sheet protection status.
@ -1985,7 +1992,7 @@ public class SXSSFSheet implements Sheet
public void lockFormatCells(boolean enabled) {
safeGetProtectionField().setFormatCells(enabled);
}
/**
* Enable or disable Formatting columns locking.
* This does not modify sheet protection status.
@ -1994,7 +2001,7 @@ public class SXSSFSheet implements Sheet
public void lockFormatColumns(boolean enabled) {
safeGetProtectionField().setFormatColumns(enabled);
}
/**
* Enable or disable Formatting rows locking.
* This does not modify sheet protection status.
@ -2003,7 +2010,7 @@ public class SXSSFSheet implements Sheet
public void lockFormatRows(boolean enabled) {
safeGetProtectionField().setFormatRows(enabled);
}
/**
* Enable or disable Inserting columns locking.
* This does not modify sheet protection status.
@ -2012,7 +2019,7 @@ public class SXSSFSheet implements Sheet
public void lockInsertColumns(boolean enabled) {
safeGetProtectionField().setInsertColumns(enabled);
}
/**
* Enable or disable Inserting hyperlinks locking.
* This does not modify sheet protection status.
@ -2021,7 +2028,7 @@ public class SXSSFSheet implements Sheet
public void lockInsertHyperlinks(boolean enabled) {
safeGetProtectionField().setInsertHyperlinks(enabled);
}
/**
* Enable or disable Inserting rows locking.
* This does not modify sheet protection status.
@ -2030,7 +2037,7 @@ public class SXSSFSheet implements Sheet
public void lockInsertRows(boolean enabled) {
safeGetProtectionField().setInsertRows(enabled);
}
/**
* Enable or disable Pivot Tables locking.
* This does not modify sheet protection status.
@ -2039,7 +2046,7 @@ public class SXSSFSheet implements Sheet
public void lockPivotTables(boolean enabled) {
safeGetProtectionField().setPivotTables(enabled);
}
/**
* Enable or disable Sort locking.
* This does not modify sheet protection status.
@ -2048,7 +2055,7 @@ public class SXSSFSheet implements Sheet
public void lockSort(boolean enabled) {
safeGetProtectionField().setSort(enabled);
}
/**
* Enable or disable Objects locking.
* This does not modify sheet protection status.
@ -2057,7 +2064,7 @@ public class SXSSFSheet implements Sheet
public void lockObjects(boolean enabled) {
safeGetProtectionField().setObjects(enabled);
}
/**
* Enable or disable Scenarios locking.
* This does not modify sheet protection status.
@ -2066,7 +2073,7 @@ public class SXSSFSheet implements Sheet
public void lockScenarios(boolean enabled) {
safeGetProtectionField().setScenarios(enabled);
}
/**
* Enable or disable Selection of locked cells locking.
* This does not modify sheet protection status.
@ -2075,7 +2082,7 @@ public class SXSSFSheet implements Sheet
public void lockSelectLockedCells(boolean enabled) {
safeGetProtectionField().setSelectLockedCells(enabled);
}
/**
* Enable or disable Selection of unlocked cells locking.
* This does not modify sheet protection status.
@ -2085,7 +2092,7 @@ public class SXSSFSheet implements Sheet
safeGetProtectionField().setSelectUnlockedCells(enabled);
}
private CTSheetProtection safeGetProtectionField() {
CTWorksheet ct = _sh.getCTWorksheet();
if (!isSheetProtectionEnabled()) {
@ -2093,12 +2100,12 @@ public class SXSSFSheet implements Sheet
}
return ct.getSheetProtection();
}
/* package */ boolean isSheetProtectionEnabled() {
CTWorksheet ct = _sh.getCTWorksheet();
return (ct.isSetSheetProtection());
}
/**
* Set background color of the sheet tab
*
@ -2112,10 +2119,10 @@ public class SXSSFSheet implements Sheet
color.setIndexed(colorIndex);
pr.setTabColor(color);
}
@NotImplemented
@Override
public void shiftColumns(int startColumn, int endColumn, int n){
throw new UnsupportedOperationException("Not Implemented");
@NotImplemented
@Override
public void shiftColumns(int startColumn, int endColumn, int n){
throw new UnsupportedOperationException("Not Implemented");
}
}

View File

@ -96,13 +96,17 @@ public class SXSSFWorkbook implements Workbook {
public static final int DEFAULT_WINDOW_SIZE = 100;
private static final POILogger logger = POILogFactory.getLogger(SXSSFWorkbook.class);
private final XSSFWorkbook _wb;
protected final XSSFWorkbook _wb;
private final Map<SXSSFSheet,XSSFSheet> _sxFromXHash = new HashMap<>();
private final Map<XSSFSheet,SXSSFSheet> _xFromSxHash = new HashMap<>();
private int _randomAccessWindowSize = DEFAULT_WINDOW_SIZE;
protected static interface ISheetInjector {
void writeSheetData(OutputStream out) throws IOException;
}
/**
* whether temp files should be compressed.
*/
@ -111,23 +115,23 @@ public class SXSSFWorkbook implements Workbook {
/**
* shared string table - a cache of strings in this workbook
*/
private final SharedStringsTable _sharedStringSource;
protected final SharedStringsTable _sharedStringSource;
/**
* controls whether Zip64 mode is used - Always became the default in POI 5.0.0
*/
private Zip64Mode zip64Mode = Zip64Mode.Always;
protected Zip64Mode zip64Mode = Zip64Mode.Always;
/**
* Construct a new workbook with default row window size
*/
public SXSSFWorkbook(){
this(null /*workbook*/);
this(null /*workbook*/);
}
/**
* <p>Construct a workbook from a template.</p>
*
*
* There are three use-cases to use SXSSFWorkbook(XSSFWorkbook) :
* <ol>
* <li>
@ -144,7 +148,7 @@ public class SXSSFWorkbook implements Workbook {
* </li>
* </ol>
* All three use cases can work in a combination.
*
*
* What is not supported:
* <ul>
* <li>
@ -161,9 +165,9 @@ public class SXSSFWorkbook implements Workbook {
* @param workbook the template workbook
*/
public SXSSFWorkbook(XSSFWorkbook workbook){
this(workbook, DEFAULT_WINDOW_SIZE);
this(workbook, DEFAULT_WINDOW_SIZE);
}
/**
* Constructs an workbook from an existing workbook.
@ -186,7 +190,7 @@ public class SXSSFWorkbook implements Workbook {
* @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see above.
*/
public SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize){
this(workbook,rowAccessWindowSize, false);
this(workbook,rowAccessWindowSize, false);
}
/**
@ -210,8 +214,8 @@ public class SXSSFWorkbook implements Workbook {
* @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see above.
* @param compressTmpFiles whether to use gzip compression for temporary files
*/
public SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize, boolean compressTmpFiles){
this(workbook,rowAccessWindowSize, compressTmpFiles, false);
public SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize, boolean compressTmpFiles) {
this(workbook,rowAccessWindowSize, compressTmpFiles, false);
}
/**
@ -237,11 +241,11 @@ public class SXSSFWorkbook implements Workbook {
* @param compressTmpFiles whether to use gzip compression for temporary files
* @param useSharedStringsTable whether to use a shared strings table
*/
public SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize, boolean compressTmpFiles, boolean useSharedStringsTable){
public SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize, boolean compressTmpFiles, boolean useSharedStringsTable) {
setRandomAccessWindowSize(rowAccessWindowSize);
setCompressTempFiles(compressTmpFiles);
if (workbook == null) {
_wb=new XSSFWorkbook();
_wb = new XSSFWorkbook();
_sharedStringSource = useSharedStringsTable ? _wb.getSharedStringSource() : null;
} else {
_wb=workbook;
@ -273,7 +277,7 @@ public class SXSSFWorkbook implements Workbook {
* @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see above.
*/
public SXSSFWorkbook(int rowAccessWindowSize){
this(null /*workbook*/, rowAccessWindowSize);
this(null /*workbook*/, rowAccessWindowSize);
}
/**
@ -282,10 +286,10 @@ public class SXSSFWorkbook implements Workbook {
* @return The number of rows that are kept in memory at once before flushing them out.
*/
public int getRandomAccessWindowSize() {
return _randomAccessWindowSize;
return _randomAccessWindowSize;
}
private void setRandomAccessWindowSize(int rowAccessWindowSize) {
protected void setRandomAccessWindowSize(int rowAccessWindowSize) {
if(rowAccessWindowSize == 0 || rowAccessWindowSize < -1) {
throw new IllegalArgumentException("rowAccessWindowSize must be greater than 0 or -1");
}
@ -323,7 +327,7 @@ public class SXSSFWorkbook implements Workbook {
* Please note the the "compress" option may cause performance penalty.
* </p>
* <p>
* Setting this option only affects compression for subsequent <code>createSheet()</code>
* Setting this option only affects compression for subsequent <code>createSheet()</code>
* calls.
* </p>
* @param compress whether to compress temp files
@ -331,7 +335,7 @@ public class SXSSFWorkbook implements Workbook {
public void setCompressTempFiles(boolean compress) {
_compressTmpFiles = compress;
}
@Internal
protected SharedStringsTable getSharedStringSource() {
return _sharedStringSource;
@ -341,7 +345,7 @@ public class SXSSFWorkbook implements Workbook {
if(_compressTmpFiles) {
return new GZIPSheetDataWriter(_sharedStringSource);
}
return new SheetDataWriter(_sharedStringSource);
}
@ -364,20 +368,20 @@ public class SXSSFWorkbook implements Workbook {
void deregisterSheetMapping(XSSFSheet xSheet)
{
SXSSFSheet sxSheet=getSXSSFSheet(xSheet);
// ensure that the writer is closed in all cases to not have lingering writers
try {
sxSheet.getSheetDataWriter().close();
} catch (IOException e) {
// ignore exception here
}
_sxFromXHash.remove(sxSheet);
_xFromSxHash.remove(xSheet);
}
private XSSFSheet getSheetFromZipEntryName(String sheetRef)
protected XSSFSheet getSheetFromZipEntryName(String sheetRef)
{
for(XSSFSheet sheet : _sxFromXHash.values())
{
@ -408,9 +412,7 @@ public class SXSSFWorkbook implements Workbook {
// See bug 56557, we should not inject data into the special ChartSheets
if (xSheet != null && !(xSheet instanceof XSSFChartSheet)) {
SXSSFSheet sxSheet = getSXSSFSheet(xSheet);
try (InputStream xis = sxSheet.getWorksheetXMLInputStream()) {
copyStreamAndInjectWorksheet(is, zos, xis);
}
copyStreamAndInjectWorksheet(is, zos, createSheetInjector(sxSheet));
} else {
IOUtils.copy(is, zos);
}
@ -434,7 +436,17 @@ public class SXSSFWorkbook implements Workbook {
}
}
private static void copyStreamAndInjectWorksheet(InputStream in, OutputStream out, InputStream worksheetData) throws IOException {
protected ISheetInjector createSheetInjector(SXSSFSheet sxSheet) throws IOException {
return (output) -> {
try (InputStream xis = sxSheet.getWorksheetXMLInputStream()) {
// Copy the worksheet data to "output".
IOUtils.copy(xis, output);
}
};
}
// private static void copyStreamAndInjectWorksheet(InputStream in, OutputStream out, InputStream worksheetData) throws IOException {
private static void copyStreamAndInjectWorksheet(InputStream in, OutputStream out, ISheetInjector sheetInjector) throws IOException {
InputStreamReader inReader = new InputStreamReader(in, StandardCharsets.UTF_8);
OutputStreamWriter outWriter = new OutputStreamWriter(out, StandardCharsets.UTF_8);
boolean needsStartTag = true;
@ -450,58 +462,58 @@ public class SXSSFWorkbook implements Workbook {
pos++;
if(pos==n)
{
if ("<sheetData".equals(s))
{
c = inReader.read();
if (c == -1)
{
outWriter.write(s);
break;
}
if (c == '>')
{
// Found <sheetData>
outWriter.write(s);
outWriter.write(c);
s = "</sheetData>";
n = s.length();
pos = 0;
needsStartTag = false;
continue;
}
if (c == '/')
{
// Found <sheetData/
c = inReader.read();
if (c == -1)
{
outWriter.write(s);
break;
}
if (c == '>')
{
// Found <sheetData/>
break;
}
outWriter.write(s);
outWriter.write('/');
outWriter.write(c);
pos = 0;
continue;
}
outWriter.write(s);
outWriter.write('/');
outWriter.write(c);
pos = 0;
continue;
}
else
{
// Found </sheetData>
break;
}
if ("<sheetData".equals(s))
{
c = inReader.read();
if (c == -1)
{
outWriter.write(s);
break;
}
if (c == '>')
{
// Found <sheetData>
outWriter.write(s);
outWriter.write(c);
s = "</sheetData>";
n = s.length();
pos = 0;
needsStartTag = false;
continue;
}
if (c == '/')
{
// Found <sheetData/
c = inReader.read();
if (c == -1)
{
outWriter.write(s);
break;
}
if (c == '>')
{
// Found <sheetData/>
break;
}
outWriter.write(s);
outWriter.write('/');
outWriter.write(c);
pos = 0;
continue;
}
outWriter.write(s);
outWriter.write('/');
outWriter.write(c);
pos = 0;
continue;
}
else
{
// Found </sheetData>
break;
}
}
}
else
@ -523,11 +535,10 @@ public class SXSSFWorkbook implements Workbook {
outWriter.flush();
if (needsStartTag)
{
outWriter.write("<sheetData>\n");
outWriter.flush();
outWriter.write("<sheetData>\n");
outWriter.flush();
}
//Copy the worksheet data to "out".
IOUtils.copy(worksheetData,out);
sheetInjector.writeSheetData(out);
outWriter.write("</sheetData>");
outWriter.flush();
//Copy the rest of "in" to "out".
@ -732,7 +743,7 @@ public class SXSSFWorkbook implements Workbook {
{
return _wb.getNumberOfSheets();
}
/**
* Returns an iterator of the sheets in the workbook
* in sheet order. Includes hidden and very hidden sheets.
@ -743,7 +754,7 @@ public class SXSSFWorkbook implements Workbook {
public Iterator<Sheet> sheetIterator() {
return new SheetIterator<>();
}
private final class SheetIterator<T extends Sheet> implements Iterator<T> {
final private Iterator<XSSFSheet> it;
@SuppressWarnings("unchecked")
@ -771,7 +782,7 @@ public class SXSSFWorkbook implements Workbook {
"Use Sheet.removeSheetAt(int) instead.");
}
}
/**
* Alias for {@link #sheetIterator()} to allow
* foreach loops
@ -816,11 +827,11 @@ public class SXSSFWorkbook implements Workbook {
// Get the sheet to be removed
XSSFSheet xSheet = _wb.getSheetAt(index);
SXSSFSheet sxSheet = getSXSSFSheet(xSheet);
// De-register it
_wb.removeSheetAt(index);
deregisterSheetMapping(xSheet);
// Clean up temporary resources
try {
sxSheet.dispose();
@ -839,7 +850,7 @@ public class SXSSFWorkbook implements Workbook {
{
return _wb.createFont();
}
/**
* Finds a font that matches the one with the supplied attributes
*
@ -905,7 +916,7 @@ public class SXSSFWorkbook implements Workbook {
}
/**
* Closes the underlying {@link XSSFWorkbook} and {@link OPCPackage}
* Closes the underlying {@link XSSFWorkbook} and {@link OPCPackage}
* on which this Workbook is based, if any.
*
* <p>Once this has been called, no further
@ -918,20 +929,21 @@ public class SXSSFWorkbook implements Workbook {
for (SXSSFSheet sheet : _xFromSxHash.values())
{
try {
sheet.getSheetDataWriter().close();
SheetDataWriter _writer = sheet.getSheetDataWriter();
if (_writer != null) _writer.close();
} catch (IOException e) {
logger.log(POILogger.WARN,
"An exception occurred while closing sheet data writer for sheet "
+ sheet.getSheetName() + ".", e);
+ sheet.getSheetName() + ".", e);
}
}
// Tell the base workbook to close, does nothing if
// Tell the base workbook to close, does nothing if
// it's a newly created one
_wb.close();
}
/**
* Write out this workbook to an OutputStream.
*
@ -962,14 +974,14 @@ public class SXSSFWorkbook implements Workbook {
throw new IOException("Could not delete temporary file after processing: " + tmplFile);
}
}
protected void flushSheets() throws IOException {
for (SXSSFSheet sheet : _xFromSxHash.values())
{
sheet.flushRows();
}
}
/**
* Dispose of temporary files backing this workbook on disk.
* Calling this method will render the workbook unusable.
@ -1053,7 +1065,7 @@ public class SXSSFWorkbook implements Workbook {
_wb.removeName(name);
}
/**
/**
* Sets the printarea for the sheet provided
* <p>
* i.e. Reference = $A$1:$B$2
@ -1188,7 +1200,7 @@ public class SXSSFWorkbook implements Workbook {
protected boolean isDate1904() {
return _wb.isDate1904();
}
@Override
@NotImplemented("XSSFWorkbook#isHidden is not implemented")
public boolean isHidden()
@ -1214,7 +1226,7 @@ public class SXSSFWorkbook implements Workbook {
{
return _wb.isSheetVeryHidden(sheetIx);
}
@Override
public SheetVisibility getSheetVisibility(int sheetIx) {
return _wb.getSheetVisibility(sheetIx);
@ -1230,13 +1242,13 @@ public class SXSSFWorkbook implements Workbook {
public void setSheetVisibility(int sheetIx, SheetVisibility visibility) {
_wb.setSheetVisibility(sheetIx, visibility);
}
/**
* <i>Not implemented for SXSSFWorkbook</i>
*
* Adds the LinkTable records required to allow formulas referencing
* the specified external workbook to be added to this one. Allows
* formulas such as "[MyOtherWorkbook]Sheet3!$A$5" to be added to the
* formulas such as "[MyOtherWorkbook]Sheet3!$A$5" to be added to the
* file, for workbooks not already referenced.
*
* Note: this is not implemented and thus currently throws an Exception stating this.
@ -1251,7 +1263,7 @@ public class SXSSFWorkbook implements Workbook {
public int linkExternalWorkbook(String name, Workbook workbook) {
throw new RuntimeException("Not Implemented");
}
/**
* Register a new toolpack in this workbook.
*
@ -1290,7 +1302,7 @@ public class SXSSFWorkbook implements Workbook {
/**
* Returns the spreadsheet version (EXCLE2007) of this workbook
*
*
* @return EXCEL2007 SpreadsheetVersion enum
* @since 3.14 beta 2
*/
@ -1303,6 +1315,6 @@ public class SXSSFWorkbook implements Workbook {
public int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException {
return _wb.addOlePackage(oleData, label, fileName, command);
}
//end of interface implementation
}

View File

@ -55,7 +55,7 @@ public class SheetDataWriter implements Closeable {
private static final POILogger logger = POILogFactory.getLogger(SheetDataWriter.class);
private final File _fd;
private final Writer _out;
protected final Writer _out;
private int _rownum;
private int _numberOfFlushedRows;
private int _lowestIndexOfFlushedRows; // meaningful only of _numberOfFlushedRows>0
@ -72,6 +72,11 @@ public class SheetDataWriter implements Closeable {
_fd = createTempFile();
_out = createWriter(_fd);
}
public SheetDataWriter(Writer writer) throws IOException {
_fd = null;
_out = writer;
}
public SheetDataWriter(SharedStringsTable sharedStringsTable) throws IOException {
this();

View File

@ -0,0 +1,84 @@
/* ====================================================================
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 java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.apache.poi.util.Beta;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
/**
* Unlike SheetDataWriter, this writer does not create a temporary file, it writes data directly
* to the provided OutputStream.
* @since 5.0.0
*/
@Beta
public class StreamingSheetWriter extends SheetDataWriter {
private static final POILogger logger = POILogFactory.getLogger(StreamingSheetWriter.class);
public StreamingSheetWriter() throws IOException {
throw new RuntimeException("StreamingSheetWriter requires OutputStream");
}
public StreamingSheetWriter(OutputStream out) throws IOException {
super(createWriter(out));
logger.log(POILogger.DEBUG, "Preparing SSXSSF sheet writer");
}
@Override
public File createTempFile() throws IOException {
throw new RuntimeException("Not supported with StreamingSheetWriter");
}
@Override
public Writer createWriter(File fd) throws IOException {
throw new RuntimeException("Not supported with StreamingSheetWriter");
}
/**
* Create a writer for the sheet data.
*
* @param out the output stream to write to
*/
protected static Writer createWriter(OutputStream out) throws IOException {
return new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
}
@Override
public void close() throws IOException {
_out.flush();
}
@Override
public InputStream getWorksheetXMLInputStream() throws IOException {
throw new RuntimeException("Not supported with StreamingSheetWriter");
}
@Override
boolean dispose() throws IOException {
_out.close();
return true;
}
}

View File

@ -0,0 +1,488 @@
/*
* ====================================================================
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import org.apache.poi.POIDataSamples;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.ss.usermodel.BaseTestXWorkbook;
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.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.NullOutputStream;
import org.apache.poi.xssf.SXSSFITestDataProvider;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
public final class TestEmittingSXSSFWorkbook extends BaseTestXWorkbook {
public TestEmittingSXSSFWorkbook() {
super(SXSSFITestDataProvider.instance);
}
@After
public void tearDown() {
((SXSSFITestDataProvider) _testDataProvider).cleanup();
}
/**
* cloning of sheets is not supported in SXSSF
*/
@Override
@Test
public void cloneSheet() throws IOException {
try {
super.cloneSheet();
fail("expected exception");
} catch (RuntimeException e) {
assertEquals("Not Implemented", e.getMessage());
}
}
/**
* cloning of sheets is not supported in SXSSF
*/
@Override
@Test
public void sheetClone() throws IOException {
try {
super.sheetClone();
fail("expected exception");
} catch (RuntimeException e) {
assertEquals("Not Implemented", e.getMessage());
}
}
/**
* Skip this test, as SXSSF doesn't update formulas on sheet name changes.
*/
@Override
@Ignore("SXSSF doesn't update formulas on sheet name changes, as most cells probably aren't in memory at the time")
@Test
public void setSheetName() {
}
@Test
public void existingWorkbook() throws IOException {
XSSFWorkbook xssfWb1 = new XSSFWorkbook();
xssfWb1.createSheet("S1");
EmittingSXSSFWorkbook wb1 = new EmittingSXSSFWorkbook(xssfWb1);
XSSFWorkbook xssfWb2 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb1);
assertTrue(wb1.dispose());
EmittingSXSSFWorkbook wb2 = new EmittingSXSSFWorkbook(xssfWb2);
assertEquals(1, wb2.getNumberOfSheets());
Sheet sheet = wb2.getStreamingSheetAt(0);
assertNotNull(sheet);
assertEquals("S1", sheet.getSheetName());
assertTrue(wb2.dispose());
xssfWb2.close();
xssfWb1.close();
wb2.close();
wb1.close();
}
@Test
public void useSharedStringsTable() throws Exception {
// not supported with EmittingSXSSF
}
@Test
public void addToExistingWorkbook() throws IOException {
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");
EmittingSXSSFWorkbook wb1 = new EmittingSXSSFWorkbook(xssfWb1);
XSSFWorkbook xssfWb2 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb1);
assertTrue(wb1.dispose());
xssfWb1.close();
EmittingSXSSFWorkbook wb2 = new EmittingSXSSFWorkbook(xssfWb2);
// Add a row to the existing empty sheet
EmittingSXSSFSheet ssheet1 = wb2.getStreamingSheetAt(0);
ssheet1.setRowGenerator((ssxSheet) -> {
Row row1_1 = ssxSheet.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
EmittingSXSSFSheet ssheet2 = wb2.getStreamingSheetAt(1);
ssheet2.setRowGenerator((ssxSheet) -> {
Row row2_2 = ssxSheet.createRow(2);
Cell cell2_2_1 = row2_2.createCell(1);
cell2_2_1.setCellValue("value 2_2_1");
});
// Add a sheet with one row
EmittingSXSSFSheet ssheet3 = wb2.createSheet("S3");
ssheet3.setRowGenerator((ssxSheet) -> {
Row row3_1 = ssxSheet.createRow(1);
Cell cell3_1_1 = row3_1.createCell(1);
cell3_1_1.setCellValue("value 3_1_1");
});
XSSFWorkbook xssfWb3 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb2);
wb2.close();
assertEquals(3, xssfWb3.getNumberOfSheets());
// Verify sheet 1
XSSFSheet sheet1 = xssfWb3.getSheetAt(0);
assertEquals("S1", sheet1.getSheetName());
assertEquals(1, sheet1.getPhysicalNumberOfRows());
XSSFRow row1_1 = sheet1.getRow(1);
assertNotNull(row1_1);
XSSFCell cell1_1_1 = row1_1.getCell(1);
assertNotNull(cell1_1_1);
assertEquals("value 1_1_1", cell1_1_1.getStringCellValue());
// Verify sheet 2
XSSFSheet 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());
XSSFRow row2_2 = sheet2.getRow(2);
assertNotNull(row2_2);
XSSFCell cell2_2_1 = row2_2.getCell(1);
assertNotNull(cell2_2_1);
assertEquals("value 2_2_1", cell2_2_1.getStringCellValue());
// Verify sheet 3
XSSFSheet sheet3 = xssfWb3.getSheetAt(2);
assertEquals("S3", sheet3.getSheetName());
assertEquals(1, sheet3.getPhysicalNumberOfRows());
XSSFRow row3_1 = sheet3.getRow(1);
assertNotNull(row3_1);
XSSFCell cell3_1_1 = row3_1.getCell(1);
assertNotNull(cell3_1_1);
assertEquals("value 3_1_1", cell3_1_1.getStringCellValue());
xssfWb2.close();
xssfWb3.close();
wb1.close();
}
@Test
public void sheetdataWriter() throws IOException {
EmittingSXSSFWorkbook wb = new EmittingSXSSFWorkbook();
SXSSFSheet sh = wb.createSheet();
assertSame(sh.getClass(), EmittingSXSSFSheet.class);
SheetDataWriter wr = sh.getSheetDataWriter();
assertNull(wr);
wb.close();
}
@Test
public void gzipSheetdataWriter() throws IOException {
EmittingSXSSFWorkbook wb = new EmittingSXSSFWorkbook();
wb.setCompressTempFiles(true);
final int rowNum = 1000;
final int sheetNum = 5;
populateData(wb, 1000, 5);
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[" + j + "]", row);
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());
xwb.close();
wb.close();
}
private static void assertWorkbookDispose(EmittingSXSSFWorkbook wb) {
populateData(wb, 1000, 5);
for (Sheet sheet : wb) {
EmittingSXSSFSheet sxSheet = (EmittingSXSSFSheet) sheet;
assertNull(sxSheet.getSheetDataWriter());
}
assertTrue(wb.dispose());
for (Sheet sheet : wb) {
EmittingSXSSFSheet sxSheet = (EmittingSXSSFSheet) sheet;
assertNull(sxSheet.getSheetDataWriter());
}
}
private static void populateData(EmittingSXSSFWorkbook wb, final int rowNum, final int sheetNum) {
for (int i = 0; i < sheetNum; i++) {
EmittingSXSSFSheet sheet = wb.createSheet("sheet" + i);
int index = i;
sheet.setRowGenerator((sh) -> {
for (int j = 0; j < rowNum; j++) {
Row row = sh.createRow(j);
Cell cell1 = row.createCell(0);
cell1.setCellValue(new CellReference(cell1).formatAsString());
Cell cell2 = row.createCell(1);
cell2.setCellValue(index);
Cell cell3 = row.createCell(2);
cell3.setCellValue(j);
}
});
}
}
@Test
public void workbookDispose() throws IOException {
EmittingSXSSFWorkbook wb1 = new EmittingSXSSFWorkbook();
// the underlying writer is SheetDataWriter
assertWorkbookDispose(wb1);
wb1.close();
EmittingSXSSFWorkbook wb2 = new EmittingSXSSFWorkbook();
wb2.setCompressTempFiles(true);
// the underlying writer is GZIPSheetDataWriter
assertWorkbookDispose(wb2);
wb2.close();
}
@Ignore("currently writing the same sheet multiple times is not supported...")
@Test
public void bug53515() throws Exception {
Workbook wb1 = new SXSSFWorkbook(10);
populateWorkbook(wb1);
saveTwice(wb1);
Workbook wb2 = new XSSFWorkbook();
populateWorkbook(wb2);
saveTwice(wb2);
wb2.close();
wb1.close();
}
@Ignore("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
public 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 {
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);
}
}
}
private static void saveTwice(Workbook wb) throws Exception {
for (int i = 0; i < 2; i++) {
try {
NullOutputStream out = new NullOutputStream();
wb.write(out);
out.close();
} catch (Exception e) {
throw new Exception("ERROR: failed on " + (i + 1) + "th time calling " + wb.getClass().getName()
+ ".write() with exception " + e.getMessage(), e);
}
}
}
@Test
public 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
public void testZipBombNotTriggeredOnUselessContent() throws IOException {
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 < 1; row++) {
Row r = s.createRow(row);
for (int col = 0; col < 10; col++) {
char[] prefix = Integer.toHexString(row * 1000 + 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);
}
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
swb.write(bos);
swb.dispose();
swb.close();
}
/**
* 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
@Ignore
public void createFromReadOnlyWorkbook() throws Exception {
String sheetName = "Test SXSSF";
File input = XSSFTestDataSamples.getSampleFile("sample.xlsx");
try (OPCPackage pkg = OPCPackage.open(input, PackageAccess.READ)) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
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(new ByteArrayInputStream(bos.toByteArray()))) {
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
public void test56557() throws IOException {
Workbook wb = XSSFTestDataSamples.openSampleWorkbook("56557.xlsx");
// Using streaming XSSFWorkbook makes the output file invalid
wb = new SXSSFWorkbook(((XSSFWorkbook) wb));
// Should not throw POIXMLException: java.io.IOException: Unable to parse xml bean when reading back
Workbook wbBack = XSSFTestDataSamples.writeOutAndReadBack(wb);
assertNotNull(wbBack);
wbBack.close();
wb.close();
}
}