You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by fa...@apache.org on 2020/06/28 11:57:37 UTC

svn commit: r1879302 - in /poi/trunk/src/ooxml: java/org/apache/poi/xssf/streaming/ testcases/org/apache/poi/xssf/streaming/

Author: fanningpj
Date: Sun Jun 28 11:57:37 2020
New Revision: 1879302

URL: http://svn.apache.org/viewvc?rev=1879302&view=rev
Log:
[github-184] New EmittingSXSSFWorkbook. Thanks to mobreza. This closes #184

Added:
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFSheet.java   (with props)
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFWorkbook.java   (with props)
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/IRowGenerator.java   (with props)
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/StreamingSheetWriter.java   (with props)
    poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestEmittingSXSSFWorkbook.java   (with props)
Modified:
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFFormulaEvaluator.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFPicture.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java

Added: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFSheet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFSheet.java?rev=1879302&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFSheet.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFSheet.java Sun Jun 28 11:57:37 2020
@@ -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();
+        }
+    }
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFSheet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFWorkbook.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFWorkbook.java?rev=1879302&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFWorkbook.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFWorkbook.java Sun Jun 28 11:57:37 2020
@@ -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);
+            }
+        }
+    }
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/EmittingSXSSFWorkbook.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/IRowGenerator.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/IRowGenerator.java?rev=1879302&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/IRowGenerator.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/IRowGenerator.java Sun Jun 28 11:57:37 2020
@@ -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;
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/IRowGenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFFormulaEvaluator.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFFormulaEvaluator.java?rev=1879302&r1=1879301&r2=1879302&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFFormulaEvaluator.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFFormulaEvaluator.java Sun Jun 28 11:57:37 2020
@@ -106,11 +106,13 @@ public final class SXSSFFormulaEvaluator
         // 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

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFPicture.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFPicture.java?rev=1879302&r1=1879301&r2=1879302&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFPicture.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFPicture.java Sun Jun 28 11:57:37 2020
@@ -24,6 +24,7 @@ import org.apache.poi.openxml4j.opc.Pack
 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 implemen
         // 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;

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java?rev=1879302&r1=1879301&r2=1879302&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java Sun Jun 28 11:57:37 2020
@@ -61,19 +61,26 @@ import org.openxmlformats.schemas.spread
 
 /**
  * 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");
     }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java?rev=1879302&r1=1879301&r2=1879302&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java Sun Jun 28 11:57:37 2020
@@ -96,13 +96,17 @@ public class SXSSFWorkbook implements Wo
     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 Wo
     /**
      * 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 Wo
      *   </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 Wo
      * @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 Wo
      * @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 Wo
      * @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 Wo
      * @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 Wo
      * @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 Wo
      * @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 Wo
      *     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 Wo
     public void setCompressTempFiles(boolean compress) {
         _compressTmpFiles = compress;
     }
-    
+
     @Internal
     protected SharedStringsTable getSharedStringSource() {
         return _sharedStringSource;
@@ -341,7 +345,7 @@ public class SXSSFWorkbook implements Wo
         if(_compressTmpFiles) {
             return new GZIPSheetDataWriter(_sharedStringSource);
         }
-        
+
         return new SheetDataWriter(_sharedStringSource);
     }
 
@@ -364,20 +368,20 @@ public class SXSSFWorkbook implements Wo
     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 Wo
                     // 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 Wo
         }
     }
 
-    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 Wo
                 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 Wo
         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 Wo
     {
         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 Wo
     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 Wo
                     "Use Sheet.removeSheetAt(int) instead.");
         }
     }
-    
+
     /**
      * Alias for {@link #sheetIterator()} to allow
      * foreach loops
@@ -816,11 +827,11 @@ public class SXSSFWorkbook implements Wo
         // 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 Wo
     {
         return _wb.createFont();
     }
-    
+
     /**
      * Finds a font that matches the one with the supplied attributes
      *
@@ -905,7 +916,7 @@ public class SXSSFWorkbook implements Wo
     }
 
     /**
-     * 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 Wo
         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 Wo
             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 Wo
         _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 Wo
     protected boolean isDate1904() {
         return _wb.isDate1904();
     }
-    
+
     @Override
     @NotImplemented("XSSFWorkbook#isHidden is not implemented")
     public boolean isHidden()
@@ -1214,7 +1226,7 @@ public class SXSSFWorkbook implements Wo
     {
         return _wb.isSheetVeryHidden(sheetIx);
     }
-    
+
     @Override
     public SheetVisibility getSheetVisibility(int sheetIx) {
         return _wb.getSheetVisibility(sheetIx);
@@ -1230,13 +1242,13 @@ public class SXSSFWorkbook implements Wo
     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 Wo
     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 Wo
 
     /**
      * 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 Wo
     public int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException {
         return _wb.addOlePackage(oleData, label, fileName, command);
     }
-    
+
 //end of interface implementation
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java?rev=1879302&r1=1879301&r2=1879302&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java Sun Jun 28 11:57:37 2020
@@ -55,7 +55,7 @@ public class SheetDataWriter implements
     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
         _fd = createTempFile();
         _out = createWriter(_fd);
     }
+    
+    public SheetDataWriter(Writer writer) throws IOException {
+        _fd = null;
+        _out = writer;
+    }
 
     public SheetDataWriter(SharedStringsTable sharedStringsTable) throws IOException {
         this();

Added: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/StreamingSheetWriter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/StreamingSheetWriter.java?rev=1879302&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/StreamingSheetWriter.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/StreamingSheetWriter.java Sun Jun 28 11:57:37 2020
@@ -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;
+    }
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/StreamingSheetWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestEmittingSXSSFWorkbook.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestEmittingSXSSFWorkbook.java?rev=1879302&view=auto
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestEmittingSXSSFWorkbook.java (added)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestEmittingSXSSFWorkbook.java Sun Jun 28 11:57:37 2020
@@ -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();
+    }
+}

Propchange: poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestEmittingSXSSFWorkbook.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org