]> source.dussan.org Git - poi.git/commitdiff
Bugzilla 51160: Initial version of SXSSF, a low memory foortprint API to produce...
authorYegor Kozlov <yegor@apache.org>
Tue, 17 May 2011 10:46:35 +0000 (10:46 +0000)
committerYegor Kozlov <yegor@apache.org>
Tue, 17 May 2011 10:46:35 +0000 (10:46 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1104120 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/status.xml
src/examples/src/org/apache/poi/ss/examples/SSPerformanceTest.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFCell.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java [new file with mode: 0644]

index abb4d41832518f0f6b0376317f88d4a7c41d266c..719af3029d88661f43591af7c360974110188b50 100644 (file)
@@ -34,7 +34,8 @@
 
     <changes>
         <release version="3.8-beta3" date="2011-??-??">
-           <action dev="poi-developers" type="add">51171 - Improved performance of opening large .xls files</action>
+           <action dev="poi-developers" type="add">51160 - Initial version of SXSSF, a low memory foortprint API to produce xlsx files</action>
+           <action dev="poi-developers" type="fix">51171 - Improved performance of opening large .xls files</action>
            <action dev="poi-developers" type="add">51172 - Add XWPF support for GIF pictures</action>
            <action dev="poi-developers" type="add">NPOIFS Mini Streams now support extending the underlying big block stream to fit more data</action>
            <action dev="poi-developers" type="fix">51148 - XWPFDocument now properly tracks paragraphs and tables when adding/removing them</action>
diff --git a/src/examples/src/org/apache/poi/ss/examples/SSPerformanceTest.java b/src/examples/src/org/apache/poi/ss/examples/SSPerformanceTest.java
new file mode 100644 (file)
index 0000000..4d6dee4
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ *  ====================================================================
+ *    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.ss.examples;
+
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SSPerformanceTest {
+    public static void main(String[] args) {
+        if (args.length != 4) usage("need four command arguments");
+
+        String type = args[0];
+        long timeStarted = System.currentTimeMillis();
+        Workbook workBook = createWorkbook(type);
+        boolean isHType = workBook instanceof HSSFWorkbook;
+
+        int rows = parseInt(args[1], "Failed to parse rows value as integer");
+        int cols = parseInt(args[2], "Failed to parse cols value as integer");
+        boolean saveFile = parseInt(args[3], "Failed to parse saveFile value as integer") != 0;
+
+        Map<String, CellStyle> styles = createStyles(workBook);
+
+        Sheet sheet = workBook.createSheet("Main Sheet");
+
+        Cell headerCell = sheet.createRow(0).createCell(0);
+        headerCell.setCellValue("Header text is spanned across multiple cells");
+        headerCell.setCellStyle(styles.get("header"));
+        sheet.addMergedRegion(CellRangeAddress.valueOf("$A$1:$F$1"));
+
+        int sheetNo = 0;
+        int rowIndexInSheet = 1;
+        double value = 0;
+        Calendar calendar = Calendar.getInstance();
+        for (int rowIndex = 0; rowIndex < rows; rowIndex++) {
+            if (isHType && sheetNo != rowIndex / 0x10000) {
+                sheet = workBook.createSheet("Spillover from sheet " + (++sheetNo));
+                headerCell.setCellValue("Header text is spanned across multiple cells");
+                headerCell.setCellStyle(styles.get("header"));
+                sheet.addMergedRegion(CellRangeAddress.valueOf("$A$1:$F$1"));
+                rowIndexInSheet = 1;
+            }
+
+            Row row = sheet.createRow(rowIndexInSheet);
+            for (int colIndex = 0; colIndex < cols; colIndex++) {
+                Cell cell = row.createCell(colIndex);
+                String address = new CellReference(cell).formatAsString();
+                switch (colIndex){
+                    case 0:
+                        // column A: default number format
+                        cell.setCellValue(value++);
+                        break;
+                    case 1:
+                        // column B: #,##0
+                        cell.setCellValue(value++);
+                        cell.setCellStyle(styles.get("#,##0.00"));
+                        break;
+                    case 2:
+                        // column C: $#,##0.00
+                        cell.setCellValue(value++);
+                        cell.setCellStyle(styles.get("$#,##0.00"));
+                        break;
+                    case 3:
+                        // column D: red bold text on yellow background
+                        cell.setCellValue(address);
+                        cell.setCellStyle(styles.get("red-bold"));
+                        break;
+                    case 4:
+                        // column E: boolean
+                        // TODO booleans are shown as 1/0 instead of TRUE/FALSE
+                        cell.setCellValue(rowIndex % 2 == 0);
+                        break;
+                    case 5:
+                        // column F:  date / time
+                        cell.setCellValue(calendar);
+                        cell.setCellStyle(styles.get("m/d/yyyy"));
+                        calendar.roll(Calendar.DAY_OF_YEAR, -1);
+                        break;
+                    case 6:
+                        // column F: formula
+                        // TODO formulas are not yet supported  in SXSSF
+                        //cell.setCellFormula("SUM(A" + (rowIndex+1) + ":E" + (rowIndex+1)+ ")");
+                        //break;
+                    default:
+                        cell.setCellValue(value++);
+                        break;
+                }
+            }
+            rowIndexInSheet++;
+        }
+        if (saveFile) {
+            String fileName = type + "_" + rows + "_" + cols + "." + getFileSuffix(args[0]);
+            try {
+                FileOutputStream out = new FileOutputStream(fileName);
+                workBook.write(out);
+                out.close();
+            } catch (IOException ioe) {
+                System.err.println("Error: failed to write to file \"" + fileName + "\", reason=" + ioe.getMessage());
+            }
+        }
+        long timeFinished = System.currentTimeMillis();
+        System.out.println("Elapsed " + (timeFinished-timeStarted)/1000 + " seconds");
+    }
+
+    static Map<String, CellStyle> createStyles(Workbook wb) {
+        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
+        CellStyle style;
+
+        Font headerFont = wb.createFont();
+        headerFont.setFontHeightInPoints((short) 14);
+        headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
+        style = wb.createCellStyle();
+        style.setAlignment(CellStyle.ALIGN_CENTER);
+        style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
+        style.setFont(headerFont);
+        style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
+        style.setFillPattern(CellStyle.SOLID_FOREGROUND);
+        styles.put("header", style);
+
+        Font monthFont = wb.createFont();
+        monthFont.setFontHeightInPoints((short)12);
+        monthFont.setColor(IndexedColors.RED.getIndex());
+        monthFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
+        style = wb.createCellStyle();
+        style.setAlignment(CellStyle.ALIGN_CENTER);
+        style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
+        style.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
+        style.setFillPattern(CellStyle.SOLID_FOREGROUND);
+        style.setFont(monthFont);
+        styles.put("red-bold", style);
+
+        String[] nfmt = {"#,##0.00", "$#,##0.00", "m/d/yyyy"};
+        for(String fmt : nfmt){
+            style = wb.createCellStyle();
+            style.setDataFormat(wb.createDataFormat().getFormat(fmt));
+            styles.put(fmt, style);
+        }
+
+        return styles;
+    }
+
+
+    static void usage(String message) {
+        System.err.println(message);
+        System.err.println("usage: java SSPerformanceTest HSSF|XSSF|SXSSF rows cols saveFile (0|1)? ");
+        System.exit(1);
+    }
+
+    static Workbook createWorkbook(String type) {
+        if ("HSSF".equals(type))
+            return new HSSFWorkbook();
+        else if ("XSSF".equals(type))
+            return new XSSFWorkbook();
+        else if ("SXSSF".equals(type))
+            return new SXSSFWorkbook();
+        else
+            usage("Unknown type \"" + type + "\"");
+        return null;
+    }
+
+    static String getFileSuffix(String type) {
+        if ("HSSF".equals(type))
+            return "xls";
+        else if ("XSSF".equals(type))
+            return "xlsx";
+        else if ("SXSSF".equals(type))
+            return "xlsx";
+        return null;
+    }
+
+    static int parseInt(String value, String msg) {
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            usage(msg);
+        }
+        return 0;
+    }
+}
diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFCell.java
new file mode 100644 (file)
index 0000000..fba4308
--- /dev/null
@@ -0,0 +1,1013 @@
+/* ====================================================================
+   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.util.Calendar;
+import java.util.Date;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.RichTextString;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.Hyperlink;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.ss.formula.FormulaParseException;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+/**
+ * Streaming version of XSSFRow implementing the "BigGridDemo" strategy.
+ *
+ * @author Alex Geller, Four J's Development Tools
+*/
+public class SXSSFCell implements Cell 
+{
+
+    SXSSFRow _row;
+    Value _value;
+    CellStyle _style;
+    Property _firstProperty;
+
+    public SXSSFCell(SXSSFRow row,int cellType)
+    {
+        _row=row;
+        setType(cellType);
+    }
+
+//start of interface implementation
+
+    /**
+     * Returns column index of this cell
+     *
+     * @return zero-based column index of a column in a sheet.
+     */
+    public int getColumnIndex()
+    {
+        return _row.getCellIndex(this);
+    }
+
+    /**
+     * Returns row index of a row in the sheet that contains this cell
+     *
+     * @return zero-based row index of a row in the sheet that contains this cell
+     */
+    public int getRowIndex()
+    {
+        return _row.getRowNum();
+    }
+
+    /**
+     * Returns the sheet this cell belongs to
+     *
+     * @return the sheet this cell belongs to
+     */
+    public Sheet getSheet()
+    {
+        return _row.getSheet();
+    }
+
+    /**
+     * Returns the Row this cell belongs to
+     *
+     * @return the Row that owns this cell
+     */
+     public Row getRow()
+     {
+         return _row;
+     }
+
+    /**
+     * Set the cells type (numeric, formula or string)
+     *
+     * @throws IllegalArgumentException if the specified cell type is invalid
+     * @see #CELL_TYPE_NUMERIC
+     * @see #CELL_TYPE_STRING
+     * @see #CELL_TYPE_FORMULA
+     * @see #CELL_TYPE_BLANK
+     * @see #CELL_TYPE_BOOLEAN
+     * @see #CELL_TYPE_ERROR
+     */
+    public void setCellType(int cellType)
+    {
+        ensureType(cellType);
+    }
+
+    /**
+     * Return the cell type.
+     *
+     * @return the cell type
+     * @see Cell#CELL_TYPE_BLANK
+     * @see Cell#CELL_TYPE_NUMERIC
+     * @see Cell#CELL_TYPE_STRING
+     * @see Cell#CELL_TYPE_FORMULA
+     * @see Cell#CELL_TYPE_BOOLEAN
+     * @see Cell#CELL_TYPE_ERROR
+     */
+    public int getCellType()
+    {
+        return _value.getType();
+    }
+
+    /**
+     * Only valid for formula cells
+     * @return one of ({@link #CELL_TYPE_NUMERIC}, {@link #CELL_TYPE_STRING},
+     *     {@link #CELL_TYPE_BOOLEAN}, {@link #CELL_TYPE_ERROR}) depending
+     * on the cached value of the formula
+     */
+    public int getCachedFormulaResultType()
+    {
+//TODO: Implement this correctly
+        assert false;
+        return CELL_TYPE_NUMERIC;
+    }
+
+    /**
+     * Set a numeric value for the cell
+     *
+     * @param value  the numeric value to set this cell to.  For formulas we'll set the
+     *        precalculated value, for numerics we'll set its value. For other types we
+     *        will change the cell to a numeric cell and set its value.
+     */
+    public void setCellValue(double value)
+    {
+        ensureTypeOrFormulaType(CELL_TYPE_NUMERIC);
+        if(_value.getType()==CELL_TYPE_FORMULA)
+            ((NumericFormulaValue)_value).setPreEvaluatedValue(value);
+        else
+            ((NumericValue)_value).setValue(value);
+    }
+
+    /**
+     * Converts the supplied date to its equivalent Excel numeric value and sets
+     * that into the cell.
+     * <p/>
+     * <b>Note</b> - There is actually no 'DATE' cell type in Excel. In many
+     * cases (when entering date values), Excel automatically adjusts the
+     * <i>cell style</i> to some date format, creating the illusion that the cell
+     * data type is now something besides {@link Cell#CELL_TYPE_NUMERIC}.  POI
+     * does not attempt to replicate this behaviour.  To make a numeric cell
+     * display as a date, use {@link #setCellStyle(CellStyle)} etc.
+     *
+     * @param value the numeric value to set this cell to.  For formulas we'll set the
+     *        precalculated value, for numerics we'll set its value. For other types we
+     *        will change the cell to a numerics cell and set its value.
+     */
+    public void setCellValue(Date value)
+    {
+//TODO: activate this when compiling against 3.7.
+        //boolean date1904 = getSheet().getXSSFWorkbook().isDate1904();
+        boolean date1904 = false;
+        setCellValue(DateUtil.getExcelDate(value, date1904));
+    }
+
+    /**
+     * Set a date value for the cell. Excel treats dates as numeric so you will need to format the cell as
+     * a date.
+     * <p>
+     * This will set the cell value based on the Calendar's timezone. As Excel
+     * does not support timezones this means that both 20:00+03:00 and
+     * 20:00-03:00 will be reported as the same value (20:00) even that there
+     * are 6 hours difference between the two times. This difference can be
+     * preserved by using <code>setCellValue(value.getTime())</code> which will
+     * automatically shift the times to the default timezone.
+     * </p>
+     *
+     * @param value  the date value to set this cell to.  For formulas we'll set the
+     *        precalculated value, for numerics we'll set its value. For othertypes we
+     *        will change the cell to a numeric cell and set its value.
+     */
+    public void setCellValue(Calendar value)
+    {
+//TODO: activate this when compiling against 3.7.
+        //boolean date1904 = getSheet().getXSSFWorkbook().isDate1904();
+        boolean date1904 = false;
+        setCellValue( DateUtil.getExcelDate(value, date1904 ));
+    }
+
+    /**
+     * Set a rich string value for the cell.
+     *
+     * @param value  value to set the cell to.  For formulas we'll set the formula
+     * string, for String cells we'll set its value.  For other types we will
+     * change the cell to a string cell and set its value.
+     * If value is null then we will change the cell to a Blank cell.
+     */
+    public void setCellValue(RichTextString value)
+    {
+        ensureRichTextStringType();
+        ((RichTextValue)_value).setValue(value);
+    }
+
+    /**
+     * Set a string value for the cell.
+     *
+     * @param value  value to set the cell to.  For formulas we'll set the formula
+     * string, for String cells we'll set its value.  For other types we will
+     * change the cell to a string cell and set its value.
+     * If value is null then we will change the cell to a Blank cell.
+     */
+    public void setCellValue(String value)
+    {
+        ensureTypeOrFormulaType(CELL_TYPE_STRING);
+        if(_value.getType()==CELL_TYPE_FORMULA)
+            ((StringFormulaValue)_value).setPreEvaluatedValue(value);
+        else
+            ((PlainStringValue)_value).setValue(value);
+    }
+
+    /**
+     * Sets formula for this cell.
+     * <p>
+     * Note, this method only sets the formula string and does not calculate the formula value.
+     * To set the precalculated value use {@link #setCellValue(double)} or {@link #setCellValue(String)}
+     * </p>
+     *
+     * @param formula the formula to set, e.g. <code>"SUM(C4:E4)"</code>.
+     *  If the argument is <code>null</code> then the current formula is removed.
+     * @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
+     */
+    public void setCellFormula(String formula) throws FormulaParseException
+    {
+        ensureFormulaType(computeTypeFromFormula(formula));
+        ((FormulaValue)_value).setValue(formula);
+    }
+    /**
+     * Return a formula for the cell, for example, <code>SUM(C4:E4)</code>
+     *
+     * @return a formula for the cell
+     * @throws IllegalStateException if the cell type returned by {@link #getCellType()} is not CELL_TYPE_FORMULA
+     */
+    public String getCellFormula()
+    {
+       if(_value.getType()!=CELL_TYPE_FORMULA)
+           throw typeMismatch(CELL_TYPE_FORMULA,_value.getType(),false);
+        return ((FormulaValue)_value).getValue();
+    }
+
+    /**
+     * Get the value of the cell as a number.
+     * <p>
+     * For strings we throw an exception. For blank cells we return a 0.
+     * For formulas or error cells we return the precalculated value;
+     * </p>
+     * @return the value of the cell as a number
+     * @throws IllegalStateException if the cell type returned by {@link #getCellType()} is CELL_TYPE_STRING
+     * @exception NumberFormatException if the cell value isn't a parsable <code>double</code>.
+     * @see org.apache.poi.ss.usermodel.DataFormatter for turning this number into a string similar to that which Excel would render this number as.
+     */
+    public double getNumericCellValue()
+    {
+        int cellType = getCellType();
+        switch(cellType) 
+        {
+            case CELL_TYPE_BLANK:
+                return 0.0;
+            case CELL_TYPE_FORMULA:
+            {
+                FormulaValue fv=(FormulaValue)_value;
+                if(fv.getFormulaType()!=CELL_TYPE_NUMERIC)
+                      throw typeMismatch(CELL_TYPE_NUMERIC, CELL_TYPE_FORMULA, false);
+                return ((NumericFormulaValue)_value).getPreEvaluatedValue();
+            }
+            case CELL_TYPE_NUMERIC:
+                return ((NumericValue)_value).getValue();
+            default:
+                throw typeMismatch(CELL_TYPE_NUMERIC, cellType, false);
+        }
+    }
+
+    /**
+     * Get the value of the cell as a date.
+     * <p>
+     * For strings we throw an exception. For blank cells we return a null.
+     * </p>
+     * @return the value of the cell as a date
+     * @throws IllegalStateException if the cell type returned by {@link #getCellType()} is CELL_TYPE_STRING
+     * @exception NumberFormatException if the cell value isn't a parsable <code>double</code>.
+     * @see org.apache.poi.ss.usermodel.DataFormatter for formatting  this date into a string similar to how excel does.
+     */
+    public Date getDateCellValue()
+    {
+        int cellType = getCellType();
+        if (cellType == CELL_TYPE_BLANK) 
+        {
+            return null;
+        }
+
+        double value = getNumericCellValue();
+//TODO: activate this when compiling against 3.7.
+        //boolean date1904 = getSheet().getXSSFWorkbook().isDate1904();
+        boolean date1904 = false;
+        return DateUtil.getJavaDate(value, date1904);
+    }
+
+    /**
+     * Get the value of the cell as a XSSFRichTextString
+     * <p>
+     * For numeric cells we throw an exception. For blank cells we return an empty string.
+     * For formula cells we return the pre-calculated value if a string, otherwise an exception.
+     * </p>
+     * @return the value of the cell as a XSSFRichTextString
+     */
+    public RichTextString getRichStringCellValue()
+    {
+        int cellType = getCellType();
+        if(!(getCellType()==CELL_TYPE_STRING&&((StringValue)_value).isRichText()))
+            throw typeMismatch(CELL_TYPE_STRING, cellType, false);
+        return ((RichTextValue)_value).getValue();
+    }
+
+
+    /**
+     * Get the value of the cell as a string
+     * <p>
+     * For numeric cells we throw an exception. For blank cells we return an empty string.
+     * For formulaCells that are not string Formulas, we throw an exception.
+     * </p>
+     * @return the value of the cell as a string
+     */
+    public String getStringCellValue()
+    {
+        int cellType = getCellType();
+        switch(cellType) 
+        {
+            case CELL_TYPE_BLANK:
+                return "";
+            case CELL_TYPE_FORMULA:
+            {
+                FormulaValue fv=(FormulaValue)_value;
+                if(fv.getFormulaType()!=CELL_TYPE_STRING)
+                      throw typeMismatch(CELL_TYPE_STRING, CELL_TYPE_FORMULA, false);
+                return ((StringFormulaValue)_value).getPreEvaluatedValue();
+            }
+            case CELL_TYPE_STRING:
+            {
+                if(((StringValue)_value).isRichText())
+                    return ((RichTextValue)_value).getValue().getString();
+                else
+                    return ((PlainStringValue)_value).getValue();
+            }
+            default:
+                throw typeMismatch(CELL_TYPE_STRING, cellType, false);
+        }
+    }
+
+    /**
+     * Set a boolean value for the cell
+     *
+     * @param value the boolean value to set this cell to.  For formulas we'll set the
+     *        precalculated value, for booleans we'll set its value. For other types we
+     *        will change the cell to a boolean cell and set its value.
+     */
+    public void setCellValue(boolean value)
+    {
+        ensureTypeOrFormulaType(CELL_TYPE_BOOLEAN);
+        if(_value.getType()==CELL_TYPE_FORMULA)
+            ((BooleanFormulaValue)_value).setPreEvaluatedValue(value);
+        else
+            ((BooleanValue)_value).setValue(value);
+    }
+
+    /**
+     * Set a error value for the cell
+     *
+     * @param value the error value to set this cell to.  For formulas we'll set the
+     *        precalculated value , for errors we'll set
+     *        its value. For other types we will change the cell to an error
+     *        cell and set its value.
+     * @see org.apache.poi.ss.usermodel.FormulaError
+     */
+    public void setCellErrorValue(byte value)
+    {
+        ensureType(CELL_TYPE_ERROR);
+        if(_value.getType()==CELL_TYPE_FORMULA)
+            ((ErrorFormulaValue)_value).setPreEvaluatedValue(value);
+        else
+            ((ErrorValue)_value).setValue(value);
+    }
+
+    /**
+     * Get the value of the cell as a boolean.
+     * <p>
+     * For strings, numbers, and errors, we throw an exception. For blank cells we return a false.
+     * </p>
+     * @return the value of the cell as a boolean
+     * @throws IllegalStateException if the cell type returned by {@link #getCellType()}
+     *   is not CELL_TYPE_BOOLEAN, CELL_TYPE_BLANK or CELL_TYPE_FORMULA
+     */
+    public boolean getBooleanCellValue()
+    {
+        int cellType = getCellType();
+        switch(cellType) 
+        {
+            case CELL_TYPE_BLANK:
+                return false;
+            case CELL_TYPE_FORMULA:
+            {
+                FormulaValue fv=(FormulaValue)_value;
+                if(fv.getFormulaType()!=CELL_TYPE_BOOLEAN)
+                      throw typeMismatch(CELL_TYPE_BOOLEAN, CELL_TYPE_FORMULA, false);
+                return ((BooleanFormulaValue)_value).getPreEvaluatedValue();
+            }
+            case CELL_TYPE_BOOLEAN:
+            {
+                return ((BooleanValue)_value).getValue();
+            }
+            default:
+                throw typeMismatch(CELL_TYPE_BOOLEAN, cellType, false);
+        }
+    }
+
+    /**
+     * Get the value of the cell as an error code.
+     * <p>
+     * For strings, numbers, and booleans, we throw an exception.
+     * For blank cells we return a 0.
+     * </p>
+     *
+     * @return the value of the cell as an error code
+     * @throws IllegalStateException if the cell type returned by {@link #getCellType()} isn't CELL_TYPE_ERROR
+     * @see org.apache.poi.ss.usermodel.FormulaError for error codes
+     */
+    public byte getErrorCellValue()
+    {
+        int cellType = getCellType();
+        switch(cellType) 
+        {
+            case CELL_TYPE_BLANK:
+                return 0;
+            case CELL_TYPE_FORMULA:
+            {
+                FormulaValue fv=(FormulaValue)_value;
+                if(fv.getFormulaType()!=CELL_TYPE_ERROR)
+                      throw typeMismatch(CELL_TYPE_ERROR, CELL_TYPE_FORMULA, false);
+                return ((ErrorFormulaValue)_value).getPreEvaluatedValue();
+            }
+            case CELL_TYPE_ERROR:
+            {
+                return ((ErrorValue)_value).getValue();
+            }
+            default:
+                throw typeMismatch(CELL_TYPE_ERROR, cellType, false);
+        }
+    }
+
+    /**
+     * Set the style for the cell.  The style should be an CellStyle created/retreived from
+     * the Workbook.
+     *
+     * @param style  reference contained in the workbook.
+     * If the value is null then the style information is removed causing the cell to used the default workbook style.
+     * @see org.apache.poi.ss.usermodel.Workbook#createCellStyle()
+     */
+    public void setCellStyle(CellStyle style)
+    {
+        _style=style;
+    }
+
+    /**
+     * Return the cell's style.
+     *
+     * @return the cell's style. Always not-null. Default cell style has zero index and can be obtained as
+     * <code>workbook.getCellStyleAt(0)</code>
+     * @see org.apache.poi.ss.usermodel.Workbook#getCellStyleAt(short)
+     */
+    public CellStyle getCellStyle()
+    {
+        return _style;
+    }
+
+    /**
+     * Sets this cell as the active cell for the worksheet
+     */
+    public void setAsActiveCell()
+    {
+//TODO: What needs to be done here? Is there a "the active cell" at the sheet or even the workbook level?
+        //getRow().setAsActiveCell(this);
+    }
+
+    /**
+     * Assign a comment to this cell
+     *
+     * @param comment comment associated with this cell
+     */
+    public void setCellComment(Comment comment)
+    {
+        setProperty(Property.COMMENT,comment);
+    }
+
+    /**
+     * Returns comment associated with this cell
+     *
+     * @return comment associated with this cell or <code>null</code> if not found
+     */
+    public Comment getCellComment()
+    {
+        return (Comment)getPropertyValue(Property.COMMENT);
+    }
+
+    /**
+     * Removes the comment for this cell, if there is one.
+     */
+    public void removeCellComment()
+    {
+        removeProperty(Property.COMMENT);
+    }
+
+    /**
+     * @return hyperlink associated with this cell or <code>null</code> if not found
+     */
+    public Hyperlink getHyperlink()
+    {
+        return (Hyperlink)getPropertyValue(Property.HYPERLINK);
+    }
+
+    /**
+     * Assign a hyperlink to this cell
+     *
+     * @param link hyperlink associated with this cell
+     */
+    public void setHyperlink(Hyperlink link)
+    {
+        setProperty(Property.HYPERLINK,link);
+    }
+
+    /**
+     * Only valid for array formula cells
+     *
+     * @return range of the array formula group that the cell belongs to.
+     */
+//TODO: What is this?
+    public CellRangeAddress getArrayFormulaRange()
+    {
+        return null;
+    }
+
+    /**
+     * @return <code>true</code> if this cell is part of group of cells having a common array formula.
+     */
+//TODO: What is this?
+    public boolean isPartOfArrayFormulaGroup()
+    {
+        return false;
+    }
+//end of interface implementation
+
+    void removeProperty(int type)
+    {
+        Property current=_firstProperty;
+        Property previous=null;
+        while(current!=null&&current.getType()!=type)
+        {
+            previous=current;
+            current=current._next;
+        }
+        if(current!=null)
+        {
+            if(previous!=null)
+            {
+                previous._next=current._next;
+            }
+            else
+            {
+                _firstProperty=current._next;
+            }
+        }
+    }
+    void setProperty(int type,Object value)
+    {
+        Property current=_firstProperty;
+        Property previous=null;
+        while(current!=null&&current.getType()!=type)
+        {
+            previous=current;
+            current=current._next;
+        }
+        if(current!=null)
+        {
+            current.setValue(value);
+        }
+        else
+        {
+            switch(type)
+            {
+                case Property.COMMENT:
+                {
+                    current=new CommentProperty(value);
+                    break;
+                }
+                case Property.HYPERLINK:
+                {
+                    current=new HyperlinkProperty(value);
+                    break;
+                }
+            }
+            if(previous!=null)
+            {
+                previous._next=current;
+            }
+            else
+            {
+                _firstProperty=current;
+            }
+        }
+    }
+    Object getPropertyValue(int type)
+    {
+        return getPropertyValue(type,null);
+    }
+    Object getPropertyValue(int type,String defaultValue)
+    {
+        Property current=_firstProperty;
+        while(current!=null&&current.getType()!=type) current=current._next;
+        return current==null?defaultValue:current.getValue();
+    }
+    void ensurePlainStringType()
+    {
+        if(_value.getType()!=CELL_TYPE_STRING
+           ||((StringValue)_value).isRichText())
+            _value=new PlainStringValue();
+    }
+    void ensureRichTextStringType()
+    {
+        if(_value.getType()!=CELL_TYPE_STRING
+           ||!((StringValue)_value).isRichText())
+            _value=new RichTextValue();
+    }
+    void ensureType(int type)
+    {
+        if(_value.getType()!=type)
+            setType(type);
+    }
+    void ensureFormulaType(int type)
+    {
+        if(_value.getType()!=CELL_TYPE_FORMULA
+           ||((FormulaValue)_value).getFormulaType()!=type)
+            setFormulaType(type);
+    }
+    void ensureTypeOrFormulaType(int type)
+    {
+        assert type==CELL_TYPE_NUMERIC||
+               type==CELL_TYPE_STRING||
+               type==CELL_TYPE_BOOLEAN||
+               type==CELL_TYPE_ERROR;
+        if(_value.getType()==type)
+        {
+            if(type==CELL_TYPE_STRING&&((StringValue)_value).isRichText())
+                setType(CELL_TYPE_STRING);
+            return;
+        }
+        if(_value.getType()==CELL_TYPE_FORMULA)
+        {
+            if(((FormulaValue)_value).getFormulaType()==type)
+                return;
+            setFormulaType(type); // once a formula, always a formula
+            return;
+        }
+        setType(type);
+    }
+    void setType(int type)
+    {
+        switch(type)
+        {
+            case CELL_TYPE_NUMERIC:
+            {
+                _value=new NumericValue();
+                break;
+            }
+            case CELL_TYPE_STRING:
+            {
+                _value=new PlainStringValue();
+                break;
+            }
+            case CELL_TYPE_FORMULA:
+            {
+                _value=new NumericFormulaValue();
+                break;
+            }
+            case CELL_TYPE_BLANK:
+            {
+                _value=new BlankValue();
+                break;
+            }
+            case CELL_TYPE_BOOLEAN:
+            {
+                _value=new BooleanValue();
+                break;
+            }
+            case CELL_TYPE_ERROR:
+            {
+                _value=new ErrorValue();
+                break;
+            }
+            default:
+            {
+                throw new IllegalArgumentException("Illegal type " + type);
+            }
+        }
+    }
+    void setFormulaType(int type)
+    {
+        switch(type)
+        {
+            case CELL_TYPE_NUMERIC:
+            {
+                _value=new NumericFormulaValue();
+                break;
+            }
+            case CELL_TYPE_STRING:
+            {
+                _value=new StringFormulaValue();
+                break;
+            }
+            case CELL_TYPE_BOOLEAN:
+            {
+                _value=new BooleanFormulaValue();
+                break;
+            }
+            case CELL_TYPE_ERROR:
+            {
+                _value=new ErrorFormulaValue();
+                break;
+            }
+            default:
+            {
+                throw new IllegalArgumentException("Illegal type " + type);
+            }
+        }
+    }
+//TODO: implement this correctly
+    int computeTypeFromFormula(String formula)
+    {
+        return CELL_TYPE_NUMERIC;
+    }
+//COPIED FROM https://svn.apache.org/repos/asf/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java since the functions are declared private there
+    /**
+     * Used to help format error messages
+     */
+    private static RuntimeException typeMismatch(int expectedTypeCode, int actualTypeCode, boolean isFormulaCell) {
+        String msg = "Cannot get a "
+            + getCellTypeName(expectedTypeCode) + " value from a "
+            + getCellTypeName(actualTypeCode) + " " + (isFormulaCell ? "formula " : "") + "cell";
+        return new IllegalStateException(msg);
+    }
+/**
+     * Used to help format error messages
+     */
+    private static String getCellTypeName(int cellTypeCode) {
+        switch (cellTypeCode) {
+            case CELL_TYPE_BLANK:   return "blank";
+            case CELL_TYPE_STRING:  return "text";
+            case CELL_TYPE_BOOLEAN: return "boolean";
+            case CELL_TYPE_ERROR:   return "error";
+            case CELL_TYPE_NUMERIC: return "numeric";
+            case CELL_TYPE_FORMULA: return "formula";
+        }
+        return "#unknown cell type (" + cellTypeCode + ")#";
+    }
+//END OF COPIED CODE
+
+    static abstract class Property
+    {
+        final static int COMMENT=1;
+        final static int HYPERLINK=2;
+        Object _value;
+        Property _next;
+        public Property(Object value)
+        {
+            _value=value;
+        }
+        abstract int getType();
+        void setValue(Object value)
+        {
+            _value=value;
+        }
+        Object getValue()
+        {
+            return _value;
+        }
+    }
+    static class CommentProperty extends Property
+    {
+        public CommentProperty(Object value)
+        {
+            super(value);
+        }
+        public int getType()
+        {
+            return COMMENT;
+        }
+    }
+    static class HyperlinkProperty extends Property
+    {
+        public HyperlinkProperty(Object value)
+        {
+            super(value);
+        }
+        public int getType()
+        {
+            return HYPERLINK;
+        }
+    }
+    interface Value
+    {
+        int getType();
+    }
+    static class NumericValue implements Value
+    {
+        double _value;
+        public int getType()
+        {
+            return CELL_TYPE_NUMERIC;
+        }
+        void setValue(double value)
+        {
+            _value=value;
+        }
+        double getValue()
+        {
+            return _value;
+        }
+    }
+    static abstract class StringValue implements Value
+    {
+        public int getType()
+        {
+            return CELL_TYPE_STRING;
+        }
+//We cannot introduce a new type CELL_TYPE_RICH_TEXT because the types are public so we have to make rich text as a type of string
+        abstract boolean isRichText(); // using the POI style which seems to avoid "instanceof".
+    }
+    static class PlainStringValue extends StringValue
+    {
+        String _value;
+        void setValue(String value)
+        {
+            _value=value;
+        }
+        String getValue()
+        {
+            return _value;
+        }
+        boolean isRichText()
+        {
+            return false;
+        }
+    }
+    static class RichTextValue implements Value
+    {
+        RichTextString _value;
+        public int getType()
+        {
+            return CELL_TYPE_STRING;
+        }
+        void setValue(RichTextString value)
+        {
+            _value=value;
+        }
+        RichTextString getValue()
+        {
+            return _value;
+        }
+        boolean isRichText()
+        {
+            return true;
+        }
+    }
+    static abstract class FormulaValue implements Value
+    {
+        String _value;
+        public int getType()
+        {
+            return CELL_TYPE_FORMULA;
+        }
+        void setValue(String value)
+        {
+            _value=value;
+        }
+        String getValue()
+        {
+            return _value;
+        }
+        abstract int getFormulaType();
+    }
+    static class NumericFormulaValue extends FormulaValue
+    {
+        double _preEvaluatedValue;
+        int getFormulaType()
+        {
+            return CELL_TYPE_NUMERIC;
+        }
+        void setPreEvaluatedValue(double value)
+        {
+            _preEvaluatedValue=value;
+        }
+        double getPreEvaluatedValue()
+        {
+            return _preEvaluatedValue;
+        }
+    }
+    static class StringFormulaValue extends FormulaValue
+    {
+        String _preEvaluatedValue;
+        int getFormulaType()
+        {
+            return CELL_TYPE_STRING;
+        }
+        void setPreEvaluatedValue(String value)
+        {
+            _preEvaluatedValue=value;
+        }
+        String getPreEvaluatedValue()
+        {
+            return _preEvaluatedValue;
+        }
+    }
+    static class BooleanFormulaValue extends FormulaValue
+    {
+        boolean _preEvaluatedValue;
+        int getFormulaType()
+        {
+            return CELL_TYPE_BOOLEAN;
+        }
+        void setPreEvaluatedValue(boolean value)
+        {
+            _preEvaluatedValue=value;
+        }
+        boolean getPreEvaluatedValue()
+        {
+            return _preEvaluatedValue;
+        }
+    }
+    static class ErrorFormulaValue extends FormulaValue
+    {
+        byte _preEvaluatedValue;
+        int getFormulaType()
+        {
+            return CELL_TYPE_ERROR;
+        }
+        void setPreEvaluatedValue(byte value)
+        {
+            _preEvaluatedValue=value;
+        }
+        byte getPreEvaluatedValue()
+        {
+            return _preEvaluatedValue;
+        }
+    }
+    static class BlankValue implements Value
+    {
+        public int getType()
+        {
+            return CELL_TYPE_BLANK;
+        }
+    }
+    static class BooleanValue implements Value
+    {
+        boolean _value;
+        public int getType()
+        {
+            return CELL_TYPE_BOOLEAN;
+        }
+        void setValue(boolean value)
+        {
+            _value=value;
+        }
+        boolean getValue()
+        {
+            return _value;
+        }
+    }
+    static class ErrorValue implements Value
+    {
+        byte _value;
+        public int getType()
+        {
+            return CELL_TYPE_ERROR;
+        }
+        void setValue(byte value)
+        {
+            _value=value;
+        }
+        byte getValue()
+        {
+            return _value;
+        }
+    }
+}
diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java
new file mode 100644 (file)
index 0000000..25808c9
--- /dev/null
@@ -0,0 +1,388 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.xssf.streaming;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Streaming version of XSSFRow implementing the "BigGridDemo" strategy.
+ *
+ * @author Alex Geller, Four J's Development Tools
+*/
+public class SXSSFRow implements Row
+{
+    SXSSFSheet _sheet;
+    SXSSFCell[] _cells;
+    int _maxColumn=-1;
+    short _height=-1;
+//TODO: Need to set the correct default value for _zHeight
+    boolean _zHeight;
+
+    public SXSSFRow(SXSSFSheet sheet, int initialSize)
+    {
+        _sheet=sheet;
+        _cells=new SXSSFCell[initialSize];
+    }
+    public Iterator<Cell> allCellsIterator()
+    {
+        return new CellIterator();
+    }
+    public boolean hasCustomHeight()
+    {
+        return _height!=-1;
+    }
+//begin of interface implementation
+    public Iterator<Cell> iterator()
+    {
+        return new FilledCellIterator();
+    }
+
+    /**
+     * Use this to create new cells within the row and return it.
+     * <p>
+     * The cell that is returned is a {@link Cell#CELL_TYPE_BLANK}. The type can be changed
+     * either through calling <code>setCellValue</code> or <code>setCellType</code>.
+     *
+     * @param column - the column number this cell represents
+     * @return Cell a high level representation of the created cell.
+     * @throws IllegalArgumentException if columnIndex < 0 or greater than the maximum number of supported columns
+     * (255 for *.xls, 1048576 for *.xlsx)
+     */
+    public Cell createCell(int column)
+    {
+        return createCell(column,Cell.CELL_TYPE_BLANK);
+    }
+
+    /**
+     * Use this to create new cells within the row and return it.
+     * <p>
+     * The cell that is returned is a {@link Cell#CELL_TYPE_BLANK}. The type can be changed
+     * either through calling setCellValue or setCellType.
+     *
+     * @param column - the column number this cell represents
+     * @return Cell a high level representation of the created cell.
+     * @throws IllegalArgumentException if columnIndex < 0 or greate than a maximum number of supported columns
+     * (255 for *.xls, 1048576 for *.xlsx)
+     */
+    public Cell createCell(int column, int type)
+    {
+        if(column>=_cells.length)
+        {
+            SXSSFCell[] newCells=new SXSSFCell[Math.max(column+1,_cells.length*2)];
+            System.arraycopy(_cells,0,newCells,0,_cells.length);
+            _cells=newCells;
+        }
+        _cells[column]=new SXSSFCell(this,type);
+        if(column>_maxColumn) _maxColumn=column;
+        return _cells[column];
+    }
+
+    /**
+     * Remove the Cell from this row.
+     *
+     * @param cell the cell to remove
+     */
+    public void removeCell(Cell cell)
+    {
+        int index=getCellIndex(cell);
+        if(index>=0)
+        {
+            _cells[index]=null;
+            while(_maxColumn>=0&&_cells[_maxColumn]==null) _maxColumn--;
+        }
+    }
+
+    int getCellIndex(Cell cell)
+    {
+        for(int i=0;i<=_maxColumn;i++)
+        {
+            if(_cells[i]==cell) return i;
+        }
+        return -1;
+    }
+
+    /**
+     * Set the row number of this row.
+     *
+     * @param rowNum  the row number (0-based)
+     * @throws IllegalArgumentException if rowNum < 0
+     */
+    public void setRowNum(int rowNum)
+    {
+        _sheet.changeRowNum(this,rowNum);
+    }
+
+    /**
+     * Get row number this row represents
+     *
+     * @return the row number (0 based)
+     */
+    public int getRowNum()
+    {
+        return _sheet.getRowNum(this);
+    }
+
+    /**
+     * Get the cell representing a given column (logical cell) 0-based.  If you
+     * ask for a cell that is not defined....you get a null.
+     *
+     * @param cellnum  0 based column number
+     * @return Cell representing that column or null if undefined.
+     * @see #getCell(int, org.apache.poi.ss.usermodel.Row.MissingCellPolicy)
+     */
+    public Cell getCell(int cellnum)
+    {
+        return cellnum>_maxColumn?null:_cells[cellnum];
+    }
+
+    /**
+     * Returns the cell at the given (0 based) index, with the specified {@link org.apache.poi.ss.usermodel.Row.MissingCellPolicy}
+     *
+     * @return the cell at the given (0 based) index
+     * @throws IllegalArgumentException if cellnum < 0 or the specified MissingCellPolicy is invalid
+     * @see Row#RETURN_NULL_AND_BLANK
+     * @see Row#RETURN_BLANK_AS_NULL
+     * @see Row#CREATE_NULL_AS_BLANK
+     */
+    public Cell getCell(int cellnum, MissingCellPolicy policy)
+    {
+        assert false;
+        Cell cell = getCell(cellnum);
+        if(policy == RETURN_NULL_AND_BLANK)
+        {
+            return cell;
+        }
+        if(policy == RETURN_BLANK_AS_NULL)
+        {
+            if(cell == null) return cell;
+            if(cell.getCellType() == Cell.CELL_TYPE_BLANK)
+            {
+                return null;
+            }
+            return cell;
+        }
+        if(policy == CREATE_NULL_AS_BLANK)
+        {
+            if(cell == null)
+            {
+                return createCell(cellnum, Cell.CELL_TYPE_BLANK);
+            }
+            return cell;
+        }
+        throw new IllegalArgumentException("Illegal policy " + policy + " (" + policy.id + ")");
+    }
+
+    /**
+     * Get the number of the first cell contained in this row.
+     *
+     * @return short representing the first logical cell in the row,
+     *  or -1 if the row does not contain any cells.
+     */
+    public short getFirstCellNum()
+    {
+        for(int i=0;i<=_maxColumn;i++)
+            if(_cells[i]!=null) return (short)i;
+        return -1;
+    }
+
+    /**
+     * Gets the index of the last cell contained in this row <b>PLUS ONE</b>. The result also
+     * happens to be the 1-based column number of the last cell.  This value can be used as a
+     * standard upper bound when iterating over cells:
+     * <pre>
+     * short minColIx = row.getFirstCellNum();
+     * short maxColIx = row.getLastCellNum();
+     * for(short colIx=minColIx; colIx&lt;maxColIx; colIx++) {
+     *   Cell cell = row.getCell(colIx);
+     *   if(cell == null) {
+     *     continue;
+     *   }
+     *   //... do something with cell
+     * }
+     * </pre>
+     *
+     * @return short representing the last logical cell in the row <b>PLUS ONE</b>,
+     *   or -1 if the row does not contain any cells.
+     */
+    public short getLastCellNum()
+    {
+        return (short)(_maxColumn+1);
+    }
+
+    /**
+     * Gets the number of defined cells (NOT number of cells in the actual row!).
+     * That is to say if only columns 0,4,5 have values then there would be 3.
+     *
+     * @return int representing the number of defined cells in the row.
+     */
+    public int getPhysicalNumberOfCells()
+    {
+        int count=0;
+        for(int i=0;i<=_maxColumn;i++)
+        {
+            if(_cells[i]!=null) count++;
+        }
+        return count;
+    }
+
+    /**
+     * Set the row's height or set to ff (-1) for undefined/default-height.  Set the height in "twips" or
+     * 1/20th of a point.
+     *
+     * @param height  rowheight or 0xff for undefined (use sheet default)
+     */
+    public void setHeight(short height)
+    {
+        _height=height;
+    }
+
+    /**
+     * Set whether or not to display this row with 0 height
+     *
+     * @param zHeight  height is zero or not.
+     */
+    public void setZeroHeight(boolean zHeight)
+    {
+        _zHeight=zHeight;
+    }
+
+    /**
+     * Get whether or not to display this row with 0 height
+     *
+     * @return - zHeight height is zero or not.
+     */
+    public boolean getZeroHeight()
+    {
+        return _zHeight;
+    }
+
+    /**
+     * Set the row's height in points.
+     *
+     * @param height the height in points. <code>-1</code>  resets to the default height
+     */
+    public void setHeightInPoints(float height)
+    {
+        if(height==-1)
+            _height=-1;
+        else
+            _height=(short)(height*20);
+    }
+
+    /**
+     * Get the row's height measured in twips (1/20th of a point). If the height is not set, the default worksheet value is returned,
+     * See {@link Sheet#getDefaultRowHeightInPoints()}
+     *
+     * @return row height measured in twips (1/20th of a point)
+     */
+    public short getHeight()
+    {
+        return (short)(_height==-1?getSheet().getDefaultRowHeightInPoints()*20:_height);
+    }
+
+    /**
+     * Returns row height measured in point size. If the height is not set, the default worksheet value is returned,
+     * See {@link Sheet#getDefaultRowHeightInPoints()}
+     *
+     * @return row height measured in point size
+     * @see Sheet#getDefaultRowHeightInPoints()
+     */
+    public float getHeightInPoints()
+    {
+        return (float)(_height==-1?getSheet().getDefaultRowHeightInPoints():(float)_height/20.0);
+    }
+
+    /**
+     * @return Cell iterator of the physically defined cells.  Note element 4 may
+     * actually be row cell depending on how many are defined!
+     */
+    public Iterator<Cell> cellIterator()
+    {
+        return iterator();
+    }
+
+    /**
+     * Returns the Sheet this row belongs to
+     *
+     * @return the Sheet that owns this row
+     */
+    public Sheet getSheet()
+    {
+        return _sheet;
+    }
+//end of interface implementation
+
+
+/** returns all filled cells (created via Row.createCell())*/
+    public class FilledCellIterator implements Iterator<Cell>
+    {
+        int pos=0;
+        public boolean hasNext()
+        {
+            return pos <= _maxColumn;
+        }
+        void advanceToNext()
+        {
+            pos++;
+            while(pos<=_maxColumn&&_cells[pos]==null) pos++;
+        }
+        public Cell next() throws NoSuchElementException
+        {
+            if (hasNext())
+            {
+                Cell retval=_cells[pos];
+                advanceToNext();
+                return retval;
+            }
+            else
+            {
+                throw new NoSuchElementException();
+            }
+        }
+        public void remove()
+        {
+            throw new UnsupportedOperationException();
+        }
+    }
+/** returns all cells including empty cells in which case "null" is returned*/
+    public class CellIterator implements Iterator<Cell>
+    {
+        int pos=0;
+        public boolean hasNext()
+        {
+            return pos <= _maxColumn;
+        }
+        public Cell next() throws NoSuchElementException
+        {
+            if (hasNext())
+                return _cells[pos++];
+            else
+                throw new NoSuchElementException();
+        }
+        public void remove()
+        {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
+
diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
new file mode 100644 (file)
index 0000000..86ac777
--- /dev/null
@@ -0,0 +1,1475 @@
+/* ====================================================================
+   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.util.Iterator;
+import java.util.TreeMap;
+import java.util.Map;
+import java.io.Writer;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.PrintSetup;
+import org.apache.poi.ss.usermodel.Header;
+import org.apache.poi.ss.usermodel.Footer;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.DataValidation;
+import org.apache.poi.ss.usermodel.CellRange;
+import org.apache.poi.ss.usermodel.DataValidationHelper;
+import org.apache.poi.ss.usermodel.AutoFilter;
+import org.apache.poi.ss.util.CellReference;
+
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+
+import org.apache.poi.hssf.util.PaneInformation;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+/**
+ * Streaming version of XSSFSheet implementing the "BigGridDemo" strategy.
+ *
+ * @author Alex Geller, Four J's Development Tools
+*/
+public class SXSSFSheet implements Sheet, Cloneable
+{
+    SXSSFWorkbook _workbook;
+    XSSFSheet _sh;
+    TreeMap<Integer,SXSSFRow> _rows=new TreeMap<Integer,SXSSFRow>();
+    SheetDataWriter _writer;
+    int _randomAccessWindowSize=5000;
+
+    public SXSSFSheet(SXSSFWorkbook workbook,XSSFSheet xSheet) throws IOException
+    {
+       _workbook=workbook;
+       _sh=xSheet;
+        _writer=new SheetDataWriter();
+
+    }
+/* Gets "<sheetData>" document fragment*/
+    public InputStream getWorksheetXMLInputStream() throws IOException 
+    {
+        flushRows(0);
+        return _writer.getWorksheetXMLInputStream();
+    }
+
+//start of interface implementation
+    public Iterator<Row> iterator()
+    {
+        return rowIterator();
+    }
+
+    /**
+     * Create a new row within the sheet and return the high level representation
+     *
+     * @param rownum  row number
+     * @return high level Row object representing a row in the sheet
+     * @see #removeRow(Row)
+     */
+    public Row createRow(int rownum)
+    {
+//Make the initial allocation as big as the row above.
+        Row previousRow=rownum>0?getRow(rownum-1):null;
+        int initialAllocationSize=0;
+//have previous row in memory -> take that value.
+        if(previousRow!=null)
+            initialAllocationSize=previousRow.getLastCellNum();
+//are we called after a flush(0)? If yes, ask the writer for the value.
+        if(initialAllocationSize<=0&&_writer.getNumberOfFlushedRows()>0)
+            initialAllocationSize=_writer.getNumberOfCellsOfLastFlushedRow();
+//default to 10 on the first row.
+        if(initialAllocationSize<=0)
+            initialAllocationSize=10;
+        SXSSFRow newRow=new SXSSFRow(this,initialAllocationSize);
+        _rows.put(new Integer(rownum),newRow);
+        if(_randomAccessWindowSize>=0&&_rows.size()>_randomAccessWindowSize)
+        {
+            try
+            {
+               flushRows(_randomAccessWindowSize);
+            }
+            catch (IOException ioe)
+            {
+                throw new RuntimeException(ioe);
+            }
+        }
+        return newRow;
+    }
+
+    /**
+     * Remove a row from this sheet.  All cells contained in the row are removed as well
+     *
+     * @param row   representing a row to remove.
+     */
+    public void removeRow(Row row)
+    {
+        for(Iterator<Map.Entry<Integer,SXSSFRow>> iter=_rows.entrySet().iterator();iter.hasNext();)
+        {
+            Map.Entry<Integer,SXSSFRow> entry=iter.next();
+            if(entry.getValue()==row)
+            {
+                iter.remove();
+                return;
+            }
+        }
+    }
+
+    /**
+     * Returns the logical row (not physical) 0-based.  If you ask for a row that is not
+     * defined you get a null.  This is to say row 4 represents the fifth row on a sheet.
+     *
+     * @param rownum  row to get (0-based)
+     * @return Row representing the rownumber or null if its not defined on the sheet
+     */
+    public Row getRow(int rownum)
+    {
+        return _rows.get(new Integer(rownum));
+    }
+
+    /**
+     * Returns the number of physically defined rows (NOT the number of rows in the sheet)
+     *
+     * @return the number of physically defined rows in this sheet
+     */
+    public int getPhysicalNumberOfRows()
+    {
+        return _rows.size()+_writer.getNumberOfFlushedRows();
+    }
+
+    /**
+     * Gets the first row on the sheet
+     *
+     * @return the number of the first logical row on the sheet (0-based)
+     */
+    public int getFirstRowNum()
+    {
+        if(_writer.getNumberOfFlushedRows()>0)
+            return _writer.getLowestIndexOfFlushedRows();
+        Integer firstKey=_rows.firstKey();
+        return firstKey==null?-1:firstKey.intValue();
+    }
+
+    /**
+     * Gets the last row on the sheet
+     *
+     * @return last row contained n this sheet (0-based)
+     */
+    public int getLastRowNum()
+    {
+        Integer lastKey=_rows.lastKey();
+        return lastKey==null?-1:lastKey.intValue();
+    }
+
+    /**
+     * Get the visibility state for a given column
+     *
+     * @param columnIndex - the column to get (0-based)
+     * @param hidden - the visiblity state of the column
+     */
+    public void setColumnHidden(int columnIndex, boolean hidden)
+    {
+        _sh.setColumnHidden(columnIndex,hidden);
+    }
+
+    /**
+     * Get the hidden state for a given column
+     *
+     * @param columnIndex - the column to set (0-based)
+     * @return hidden - <code>false</code> if the column is visible
+     */
+    public boolean isColumnHidden(int columnIndex)
+    {
+        return _sh.isColumnHidden(columnIndex);
+    }
+
+    /**
+     * Set the width (in units of 1/256th of a character width)
+     * <p>
+     * The maximum column width for an individual cell is 255 characters.
+     * This value represents the number of characters that can be displayed
+     * in a cell that is formatted with the standard font.
+     * </p>
+     *
+     * @param columnIndex - the column to set (0-based)
+     * @param width - the width in units of 1/256th of a character width
+     */
+    public void setColumnWidth(int columnIndex, int width)
+    {
+        _sh.setColumnWidth(columnIndex,width);
+    }
+
+    /**
+     * get the width (in units of 1/256th of a character width )
+     * @param columnIndex - the column to set (0-based)
+     * @return width - the width in units of 1/256th of a character width
+     */
+    public int getColumnWidth(int columnIndex)
+    {
+        return _sh.getColumnWidth(columnIndex);
+    }
+
+    /**
+     * Set the default column width for the sheet (if the columns do not define their own width)
+     * in characters
+     *
+     * @param width default column width measured in characters
+     */
+    public void setDefaultColumnWidth(int width)
+    {
+        _sh.setDefaultColumnWidth(width);
+    }
+
+    /**
+     * Get the default column width for the sheet (if the columns do not define their own width)
+     * in characters
+     *
+     * @return default column width measured in characters
+     */
+    public int getDefaultColumnWidth()
+    {
+        return _sh.getDefaultColumnWidth();
+    }
+
+    /**
+     * Get the default row height for the sheet (if the rows do not define their own height) in
+     * twips (1/20 of  a point)
+     *
+     * @return  default row height measured in twips (1/20 of  a point)
+     */
+    public short getDefaultRowHeight()
+    {
+        return _sh.getDefaultRowHeight();
+    }
+
+    /**
+     * Get the default row height for the sheet (if the rows do not define their own height) in
+     * points.
+     *
+     * @return  default row height in points
+     */
+    public float getDefaultRowHeightInPoints()
+    {
+        return _sh.getDefaultRowHeightInPoints();
+    }
+
+    /**
+     * Set the default row height for the sheet (if the rows do not define their own height) in
+     * twips (1/20 of  a point)
+     *
+     * @param  height default row height measured in twips (1/20 of  a point)
+     */
+    public void setDefaultRowHeight(short height)
+    {
+        _sh.setDefaultRowHeight(height);
+    }
+
+    /**
+     * Set the default row height for the sheet (if the rows do not define their own height) in
+     * points
+     * @param height default row height
+     */
+    public void setDefaultRowHeightInPoints(float height)
+    {
+        _sh.setDefaultRowHeightInPoints(height);
+    }
+    
+
+    /**
+     * Returns the CellStyle that applies to the given
+     *  (0 based) column, or null if no style has been
+     *  set for that column
+     */
+    public CellStyle getColumnStyle(int column)
+    {
+        return _sh.getColumnStyle(column);
+    }
+
+    /**
+     * Sets the CellStyle that applies to the given
+     *  (0 based) column.
+     */
+//    public CellStyle setColumnStyle(int column, CellStyle style);
+
+    /**
+     * Adds a merged region of cells (hence those cells form one)
+     *
+     * @param region (rowfrom/colfrom-rowto/colto) to merge
+     * @return index of this region
+     */
+    public int addMergedRegion(CellRangeAddress region)
+    {
+        return _sh.addMergedRegion(region);
+    }
+
+    /**
+     * Determines whether the output is vertically centered on the page.
+     *
+     * @param value true to vertically center, false otherwise.
+     */
+    public void setVerticallyCenter(boolean value)
+    {
+        _sh.setVerticallyCenter(value);
+    }
+
+    /**
+     * Determines whether the output is horizontally centered on the page.
+     *
+     * @param value true to horizontally center, false otherwise.
+     */
+    public void setHorizontallyCenter(boolean value)
+    {
+        _sh.setHorizontallyCenter(value);
+    }
+
+    /**
+     * Determine whether printed output for this sheet will be horizontally centered.
+     */
+
+    public boolean getHorizontallyCenter()
+    {
+        return _sh.getHorizontallyCenter();
+    }
+
+    /**
+     * Determine whether printed output for this sheet will be vertically centered.
+     */
+    public boolean getVerticallyCenter()
+    {
+        return _sh.getVerticallyCenter();
+    }
+
+    /**
+     * Removes a merged region of cells (hence letting them free)
+     *
+     * @param index of the region to unmerge
+     */
+    public void removeMergedRegion(int index)
+    {
+        _sh.removeMergedRegion(index);
+    }
+
+    /**
+     * Returns the number of merged regions
+     *
+     * @return number of merged regions
+     */
+    public int getNumMergedRegions()
+    {
+        return _sh.getNumMergedRegions();
+    }
+
+    /**
+     * Returns the merged region at the specified index
+     *
+     * @return the merged region at the specified index
+     */
+    public CellRangeAddress getMergedRegion(int index)
+    {
+        return _sh.getMergedRegion(index);
+    }
+
+    /**
+     *  Returns an iterator of the physical rows
+     *
+     * @return an iterator of the PHYSICAL rows.  Meaning the 3rd element may not
+     * be the third row if say for instance the second row is undefined.
+     */
+    public Iterator<Row> rowIterator()
+    {
+        @SuppressWarnings("unchecked")
+        Iterator<Row> result = (Iterator<Row>)(Iterator<? extends Row>)_rows.values().iterator();
+        return result;
+    }
+
+    /**
+     * Flag indicating whether the sheet displays Automatic Page Breaks.
+     *
+     * @param value <code>true</code> if the sheet displays Automatic Page Breaks.
+     */
+    public void setAutobreaks(boolean value)
+    {
+        _sh.setAutobreaks(value);
+    }
+
+    /**
+     * Set whether to display the guts or not
+     *
+     * @param value - guts or no guts
+     */
+    public void setDisplayGuts(boolean value)
+    {
+        _sh.setDisplayGuts(value);
+    }
+
+    /**
+     * Set whether the window should show 0 (zero) in cells containing zero value.
+     * When false, cells with zero value appear blank instead of showing the number zero.
+     *
+     * @param value whether to display or hide all zero values on the worksheet
+     */
+    public void setDisplayZeros(boolean value)
+    {
+        _sh.setDisplayZeros(value);
+    }
+
+
+    /**
+     * Gets the flag indicating whether the window should show 0 (zero) in cells containing zero value.
+     * When false, cells with zero value appear blank instead of showing the number zero.
+     *
+     * @return whether all zero values on the worksheet are displayed
+     */
+    public boolean isDisplayZeros()
+    {
+        return _sh.isDisplayZeros();
+    }
+
+    /**
+     * Flag indicating whether the Fit to Page print option is enabled.
+     *
+     * @param value <code>true</code> if the Fit to Page print option is enabled.
+     */
+    public void setFitToPage(boolean value)
+    {
+        _sh.setFitToPage(value);
+    }
+
+    /**
+     * Flag indicating whether summary rows appear below detail in an outline, when applying an outline.
+     *
+     * <p>
+     * When true a summary row is inserted below the detailed data being summarized and a
+     * new outline level is established on that row.
+     * </p>
+     * <p>
+     * When false a summary row is inserted above the detailed data being summarized and a new outline level
+     * is established on that row.
+     * </p>
+     * @param value <code>true</code> if row summaries appear below detail in the outline
+     */
+    public void setRowSumsBelow(boolean value)
+    {
+        _sh.setRowSumsBelow(value);
+    }
+
+    /**
+     * Flag indicating whether summary columns appear to the right of detail in an outline, when applying an outline.
+     *
+     * <p>
+     * When true a summary column is inserted to the right of the detailed data being summarized
+     * and a new outline level is established on that column.
+     * </p>
+     * <p>
+     * When false a summary column is inserted to the left of the detailed data being
+     * summarized and a new outline level is established on that column.
+     * </p>
+     * @param value <code>true</code> if col summaries appear right of the detail in the outline
+     */
+    public void setRowSumsRight(boolean value)
+    {
+        _sh.setRowSumsRight(value);
+    }
+
+    /**
+     * Flag indicating whether the sheet displays Automatic Page Breaks.
+     *
+     * @return <code>true</code> if the sheet displays Automatic Page Breaks.
+     */
+    public boolean getAutobreaks()
+    {
+        return _sh.getAutobreaks();
+    }
+
+    /**
+     * Get whether to display the guts or not,
+     * default value is true
+     *
+     * @return boolean - guts or no guts
+     */
+    public boolean getDisplayGuts()
+    {
+        return _sh.getDisplayGuts();
+    }
+
+    /**
+     * Flag indicating whether the Fit to Page print option is enabled.
+     *
+     * @return <code>true</code> if the Fit to Page print option is enabled.
+     */
+    public boolean getFitToPage()
+    {
+        return _sh.getFitToPage();
+    }
+
+    /**
+     * Flag indicating whether summary rows appear below detail in an outline, when applying an outline.
+     *
+     * <p>
+     * When true a summary row is inserted below the detailed data being summarized and a
+     * new outline level is established on that row.
+     * </p>
+     * <p>
+     * When false a summary row is inserted above the detailed data being summarized and a new outline level
+     * is established on that row.
+     * </p>
+     * @return <code>true</code> if row summaries appear below detail in the outline
+     */
+    public boolean getRowSumsBelow()
+    {
+        return _sh.getRowSumsBelow();
+    }
+
+    /**
+     * Flag indicating whether summary columns appear to the right of detail in an outline, when applying an outline.
+     *
+     * <p>
+     * When true a summary column is inserted to the right of the detailed data being summarized
+     * and a new outline level is established on that column.
+     * </p>
+     * <p>
+     * When false a summary column is inserted to the left of the detailed data being
+     * summarized and a new outline level is established on that column.
+     * </p>
+     * @return <code>true</code> if col summaries appear right of the detail in the outline
+     */
+    public boolean getRowSumsRight()
+    {
+        return _sh.getRowSumsRight();
+    }
+
+    /**
+     * Gets the flag indicating whether this sheet displays the lines
+     * between rows and columns to make editing and reading easier.
+     *
+     * @return <code>true</code> if this sheet displays gridlines.
+     * @see #isPrintGridlines() to check if printing of gridlines is turned on or off
+     */
+    public boolean isPrintGridlines()
+    {
+        return _sh.isPrintGridlines();
+    }
+
+    /**
+     * Sets the flag indicating whether this sheet should display the lines
+     * between rows and columns to make editing and reading easier.
+     * To turn printing of gridlines use {@link #setPrintGridlines(boolean)}
+     *
+     *
+     * @param show <code>true</code> if this sheet should display gridlines.
+     * @see #setPrintGridlines(boolean)
+     */
+    public void setPrintGridlines(boolean show)
+    {
+        _sh.setPrintGridlines(show);
+    }
+
+    /**
+     * Gets the print setup object.
+     *
+     * @return The user model for the print setup object.
+     */
+    public PrintSetup getPrintSetup()
+    {
+        return _sh.getPrintSetup();
+    }
+
+    /**
+     * Gets the user model for the default document header.
+     * <p/>
+     * Note that XSSF offers more kinds of document headers than HSSF does
+     * </p>
+     * @return the document header. Never <code>null</code>
+     */
+    public Header getHeader()
+    {
+        return _sh.getHeader();
+    }
+
+    /**
+     * Gets the user model for the default document footer.
+     * <p/>
+     * Note that XSSF offers more kinds of document footers than HSSF does.
+     *
+     * @return the document footer. Never <code>null</code>
+     */
+    public Footer getFooter()
+    {
+        return _sh.getFooter();
+    }
+
+    /**
+     * Sets a flag indicating whether this sheet is selected.
+     *<p>
+     * Note: multiple sheets can be selected, but only one sheet can be active at one time.
+     *</p>
+     * @param value <code>true</code> if this sheet is selected
+     * @see Workbook#setActiveSheet(int)
+     */
+    public void setSelected(boolean value)
+    {
+        _sh.setSelected(value);
+    }
+
+    /**
+     * Gets the size of the margin in inches.
+     *
+     * @param margin which margin to get
+     * @return the size of the margin
+     */
+    public double getMargin(short margin)
+    {
+        return _sh.getMargin(margin);
+    }
+
+    /**
+     * Sets the size of the margin in inches.
+     *
+     * @param margin which margin to get
+     * @param size the size of the margin
+     */
+    public void setMargin(short margin, double size)
+    {
+        _sh.setMargin(margin,size);
+    }
+
+    /**
+     * Answer whether protection is enabled or disabled
+     *
+     * @return true => protection enabled; false => protection disabled
+     */
+    public boolean getProtect()
+    {
+        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
+     */
+    public void protectSheet(String password)
+    {
+        _sh.protectSheet(password);
+    }
+    
+    /**
+     * Answer whether scenario protection is enabled or disabled
+     *
+     * @return true => protection enabled; false => protection disabled
+     */
+    public boolean getScenarioProtect()
+    {
+        return _sh.getScenarioProtect();
+    }
+
+    /**
+     * Sets the zoom magnication for the sheet.  The zoom is expressed as a
+     * fraction.  For example to express a zoom of 75% use 3 for the numerator
+     * and 4 for the denominator.
+     *
+     * @param numerator     The numerator for the zoom magnification.
+     * @param denominator   The denominator for the zoom magnification.
+     */
+    public void setZoom(int numerator, int denominator)
+    {
+        _sh.setZoom(numerator,denominator);
+    }
+
+    /**
+     * The top row in the visible view when the sheet is
+     * first viewed after opening it in a viewer
+     *
+     * @return short indicating the rownum (0 based) of the top row
+     */
+    public short getTopRow()
+    {
+        return _sh.getTopRow();
+    }
+
+    /**
+     * The left col in the visible view when the sheet is
+     * first viewed after opening it in a viewer
+     *
+     * @return short indicating the rownum (0 based) of the top row
+     */
+    public short getLeftCol()
+    {
+        return _sh.getLeftCol();
+    }
+
+    /**
+     * Sets desktop window pane display area, when the
+     * file is first opened in a viewer.
+     *
+     * @param toprow the top row to show in desktop window pane
+     * @param leftcol the left column to show in desktop window pane
+     */
+    public void showInPane(short toprow, short leftcol)
+    {
+        _sh.showInPane(toprow, leftcol);
+    }
+
+    /**
+     * Control if Excel should be asked to recalculate all formulas when the
+     *  workbook is opened, via the "sheetCalcPr fullCalcOnLoad" option.
+     *  Calculating the formula values with {@link org.apache.poi.ss.usermodel.FormulaEvaluator} is the
+     *  recommended solution, but this may be used for certain cases where
+     *  evaluation in POI is not possible.
+     */
+    public void setForceFormulaRecalculation(boolean value) {
+       _sh.setForceFormulaRecalculation(value);
+    }
+
+    /**
+     * Whether Excel will be asked to recalculate all formulas when the
+     *  workbook is opened.
+     */
+    public boolean getForceFormulaRecalculation() {
+       return _sh.getForceFormulaRecalculation();
+    }
+
+    /**
+     * 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.
+     *
+     * Calls shiftRows(startRow, endRow, n, false, false);
+     *
+     * <p>
+     * Additionally shifts merged regions that are completely defined in these
+     * rows (ie. merged 2 cells on a row to be shifted).
+     * @param startRow the row to start shifting
+     * @param endRow the row to end shifting
+     * @param n the number of rows to shift
+     */
+    public void shiftRows(int startRow, int endRow, int n)
+    {
+        throw new RuntimeException("NotImplemented");
+    }
+
+    /**
+     * 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
+     *
+     * <p>
+     * Additionally shifts merged regions that are completely defined in these
+     * rows (ie. merged 2 cells on a row to be shifted).
+     * <p>
+     * @param startRow the row to start shifting
+     * @param endRow the row to end shifting
+     * @param n the number of rows to shift
+     * @param copyRowHeight whether to copy the row height during the shift
+     * @param resetOriginalRowHeight whether to set the original row's height to the default
+     */
+    public void shiftRows(int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight)
+    {
+        throw new RuntimeException("NotImplemented");
+    }
+
+    /**
+     * Creates a split (freezepane). Any existing freezepane or split pane is overwritten.
+     * @param colSplit      Horizonatal position of split.
+     * @param rowSplit      Vertical position of split.
+     * @param leftmostColumn   Left column visible in right pane.
+     * @param topRow        Top row visible in bottom pane
+     */
+    public void createFreezePane(int colSplit, int rowSplit, int leftmostColumn, int topRow)
+    {
+        _sh.createFreezePane(colSplit, rowSplit, leftmostColumn, topRow);
+    }
+
+    /**
+     * Creates a split (freezepane). Any existing freezepane or split pane is overwritten.
+     * @param colSplit      Horizonatal position of split.
+     * @param rowSplit      Vertical position of split.
+     */
+    public void createFreezePane(int colSplit, int rowSplit)
+    {
+        _sh.createFreezePane(colSplit,rowSplit);
+    }
+
+    /**
+     * Creates a split pane. Any existing freezepane or split pane is overwritten.
+     * @param xSplitPos      Horizonatal position of split (in 1/20th of a point).
+     * @param ySplitPos      Vertical position of split (in 1/20th of a point).
+     * @param topRow        Top row visible in bottom pane
+     * @param leftmostColumn   Left column visible in right pane.
+     * @param activePane    Active pane.  One of: PANE_LOWER_RIGHT,
+     *                      PANE_UPPER_RIGHT, PANE_LOWER_LEFT, PANE_UPPER_LEFT
+     * @see #PANE_LOWER_LEFT
+     * @see #PANE_LOWER_RIGHT
+     * @see #PANE_UPPER_LEFT
+     * @see #PANE_UPPER_RIGHT
+     */
+    public void createSplitPane(int xSplitPos, int ySplitPos, int leftmostColumn, int topRow, int activePane)
+    {
+        _sh.createSplitPane(xSplitPos, ySplitPos, leftmostColumn, topRow, activePane);
+    }
+
+    /**
+     * Returns the information regarding the currently configured pane (split or freeze)
+     *
+     * @return null if no pane configured, or the pane information.
+     */
+    public PaneInformation getPaneInformation()
+    {
+        return _sh.getPaneInformation();
+    }
+
+    /**
+     * Sets whether the gridlines are shown in a viewer
+     *
+     * @param show whether to show gridlines or not
+     */
+    public void setDisplayGridlines(boolean show)
+    {
+        _sh.setDisplayGridlines(show);
+    }
+
+    /**
+     * Returns if gridlines are displayed
+     *
+     * @return whether gridlines are displayed
+     */
+    public boolean isDisplayGridlines()
+    {
+        return _sh.isDisplayGridlines();
+    }
+
+    /**
+     * Sets whether the formulas are shown in a viewer
+     *
+     * @param show whether to show formulas or not
+     */
+    public void setDisplayFormulas(boolean show)
+    {
+        _sh.setDisplayFormulas(show);
+    }
+
+    /**
+     * Returns if formulas are displayed
+     *
+     * @return whether formulas are displayed
+     */
+    public boolean isDisplayFormulas()
+    {
+        return _sh.isDisplayFormulas();
+    }
+
+    /**
+     * Sets whether the RowColHeadings are shown in a viewer
+     *
+     * @param show whether to show RowColHeadings or not
+     */
+    public void setDisplayRowColHeadings(boolean show)
+    {
+        _sh.setDisplayRowColHeadings(show);
+    }
+
+    /**
+     * Returns if RowColHeadings are displayed.
+     * @return whether RowColHeadings are displayed
+     */
+    public boolean isDisplayRowColHeadings()
+    {
+        return _sh.isDisplayRowColHeadings();
+    }
+
+    /**
+     * Sets a page break at the indicated row
+     * @param row FIXME: Document this!
+     */
+    public void setRowBreak(int row)
+    {
+        _sh.setRowBreak(row);
+    }
+
+    /**
+     * Determines if there is a page break at the indicated row
+     * @param row FIXME: Document this!
+     * @return FIXME: Document this!
+     */
+    public boolean isRowBroken(int row)
+    {
+        return _sh.isRowBroken(row);
+    }
+
+    /**
+     * Removes the page break at the indicated row
+     * @param row
+     */
+    public void removeRowBreak(int row)
+    {
+        _sh.removeRowBreak(row);
+    }
+
+    /**
+     * Retrieves all the horizontal page breaks
+     * @return all the horizontal page breaks, or null if there are no row page breaks
+     */
+    public int[] getRowBreaks()
+    {
+        return _sh.getRowBreaks();
+    }
+
+    /**
+     * Retrieves all the vertical page breaks
+     * @return all the vertical page breaks, or null if there are no column page breaks
+     */
+    public int[] getColumnBreaks()
+    {
+        return _sh.getColumnBreaks();
+    }
+
+    /**
+     * Sets a page break at the indicated column
+     * @param column
+     */
+    public void setColumnBreak(int column)
+    {
+        _sh.setColumnBreak(column);
+    }
+
+    /**
+     * Determines if there is a page break at the indicated column
+     * @param column FIXME: Document this!
+     * @return FIXME: Document this!
+     */
+    public boolean isColumnBroken(int column)
+    {
+        return _sh.isColumnBroken(column);
+    }
+
+    /**
+     * Removes a page break at the indicated column
+     * @param column
+     */
+    public void removeColumnBreak(int column)
+    {
+        _sh.removeColumnBreak(column);
+    }
+
+    /**
+     * Expands or collapses a column group.
+     *
+     * @param columnNumber      One of the columns in the group.
+     * @param collapsed         true = collapse group, false = expand group.
+     */
+    public void setColumnGroupCollapsed(int columnNumber, boolean collapsed)
+    {
+        _sh.setColumnGroupCollapsed(columnNumber, collapsed);
+    }
+
+    /**
+     * Create an outline for the provided column range.
+     *
+     * @param fromColumn        beginning of the column range.
+     * @param toColumn          end of the column range.
+     */
+    public void groupColumn(int fromColumn, int toColumn)
+    {
+        _sh.groupColumn(fromColumn,toColumn);
+    }
+
+    /**
+     * Ungroup a range of columns that were previously groupped
+     *
+     * @param fromColumn   start column (0-based)
+     * @param toColumn     end column (0-based)
+     */
+    public void ungroupColumn(int fromColumn, int toColumn)
+    {
+        _sh.ungroupColumn(fromColumn, toColumn);
+    }
+
+    /**
+     * Tie a range of rows together so that they can be collapsed or expanded
+     *
+     * @param fromRow   start row (0-based)
+     * @param toRow     end row (0-based)
+     */
+    public void groupRow(int fromRow, int toRow)
+    {
+        _sh.groupRow(fromRow, toRow);
+    }
+
+    /**
+     * Ungroup a range of rows that were previously groupped
+     *
+     * @param fromRow   start row (0-based)
+     * @param toRow     end row (0-based)
+     */
+    public void ungroupRow(int fromRow, int toRow)
+    {
+        _sh.ungroupRow(fromRow, toRow);
+    }
+
+    /**
+     * Set view state of a groupped range of rows
+     *
+     * @param row   start row of a groupped range of rows (0-based)
+     * @param collapse whether to expand/collapse the detail rows
+     */
+    public void setRowGroupCollapsed(int row, boolean collapse)
+    {
+        _sh.setRowGroupCollapsed(row, collapse);
+    }
+
+    /**
+     * Sets the default column style for a given column.  POI will only apply this style to new cells added to the sheet.
+     *
+     * @param column the column index
+     * @param style the style to set
+     */
+    public void setDefaultColumnStyle(int column, CellStyle style)
+    {
+        _sh.setDefaultColumnStyle(column, style);
+    }
+
+    /**
+     * Adjusts the column width to fit the contents.
+     *
+     * <p>
+     * This process can be relatively slow on large sheets, so this should
+     *  normally only be called once per column, at the end of your
+     *  processing.
+     * </p>
+     * You can specify whether the content of merged cells should be considered or ignored.
+     *  Default is to ignore merged cells.
+     *
+     * @param column the column index
+     */
+    public void autoSizeColumn(int column)
+    {
+        _sh.autoSizeColumn(column);
+    }
+
+    /**
+     * Adjusts the column width to fit the contents.
+     * <p>
+     * This process can be relatively slow on large sheets, so this should
+     *  normally only be called once per column, at the end of your
+     *  processing.
+     * </p>
+     * You can specify whether the content of merged cells should be considered or ignored.
+     *  Default is to ignore merged cells.
+     *
+     * @param column the column index
+     * @param useMergedCells whether to use the contents of merged cells when calculating the width of the column
+     */
+    public void autoSizeColumn(int column, boolean useMergedCells)
+    {
+        _sh.autoSizeColumn(column, useMergedCells);
+    }
+
+    /**
+     * Returns cell comment for the specified row and column
+     *
+     * @return cell comment or <code>null</code> if not found
+     */
+    public Comment getCellComment(int row, int column)
+    {
+        return _sh.getCellComment(row, column);
+    }
+
+    /**
+     * Creates the top-level drawing patriarch.
+     *
+     * @return  The new drawing patriarch.
+     */
+    public Drawing createDrawingPatriarch()
+    {
+        return _sh.createDrawingPatriarch();
+    }
+
+
+    /**
+     * Return the parent workbook
+     *
+     * @return the parent workbook
+     */
+    public Workbook getWorkbook()
+    {
+        return _workbook;
+    }
+
+    /**
+     * Returns the name of this sheet
+     *
+     * @return the name of this sheet
+     */
+    public String getSheetName()
+    {
+        return _sh.getSheetName();
+    }
+
+    /**
+     * Note - this is not the same as whether the sheet is focused (isActive)
+     * @return <code>true</code> if this sheet is currently selected
+     */
+    public boolean isSelected()
+    {
+        return _sh.isSelected();
+    }
+
+
+    /**
+     * Sets array formula to specified region for result.
+     *
+     * @param formula text representation of the formula
+     * @param range Region of array formula for result.
+     * @return the {@link CellRange} of cells affected by this change
+     */
+    public CellRange<? extends Cell> setArrayFormula(String formula, CellRangeAddress range)
+    {
+        return _sh.setArrayFormula(formula, range);
+    }
+
+    /**
+     * Remove a Array Formula from this sheet.  All cells contained in the Array Formula range are removed as well
+     *
+     * @param cell   any cell within Array Formula range
+     * @return the {@link CellRange} of cells affected by this change
+     */
+    public CellRange<? extends Cell> removeArrayFormula(Cell cell)
+    {
+        return _sh.removeArrayFormula(cell);
+    }
+    
+    public DataValidationHelper getDataValidationHelper()
+    {
+        return _sh.getDataValidationHelper();
+    }
+
+    /**
+     * Creates a data validation object
+     * @param dataValidation The Data validation object settings
+     */
+    public void addValidationData(DataValidation dataValidation)
+    {
+        _sh.addValidationData(dataValidation);
+    }
+
+    /**
+     * Enable filtering for a range of cells
+     * 
+     * @param range the range of cells to filter
+     */
+    public AutoFilter setAutoFilter(CellRangeAddress range)
+    {
+        return _sh.setAutoFilter(range);
+    }
+//end of interface implementation
+    /**
+     * Specifies how many rows can be accessed at most via getRow().
+     * When a new node is created via createRow() and the total number
+     * of unflushed records would exeed the specified value, then the
+     * row with the lowest index value is flushed and cannot be accessed
+     * via getRow() anymore.
+     * A value of -1 indicates unlimited access. In this case all
+     * records that have not been flushed by a call to flush() are available
+     * for random access.
+     * A value of 0 is not allowed because it would flush any newly created row
+     * without having a chance to specify any cells.
+     */
+    void setRandomAccessWindowSize(int value)
+    {
+         assert value!=0;
+         _randomAccessWindowSize=value;
+    }
+    /**
+     * Specifies how many rows can be accessed at most via getRow().
+     * The exeeding rows (if any) are flushed to the disk while rows
+     * with lower index values are flushed first.
+     */
+    void flushRows(int remaining) throws IOException
+    {
+        while(_rows.size()>remaining) flushOneRow();
+    }
+    private void flushOneRow() throws IOException
+    {
+        Map.Entry<Integer,SXSSFRow> firstEntry=_rows.firstEntry();
+        if(firstEntry!=null)
+        {
+            
+            SXSSFRow row=firstEntry.getValue();
+            int rowIndex=firstEntry.getKey().intValue();
+            _writer.writeRow(rowIndex,row);
+            _rows.remove(firstEntry.getKey());
+        }
+    }
+    public void changeRowNum(SXSSFRow row, int newRowNum)
+    {
+        
+        removeRow(row);
+        _rows.put(new Integer(newRowNum),row);
+    }
+
+    public int getRowNum(SXSSFRow row)
+    {
+        for(Iterator<Map.Entry<Integer,SXSSFRow>> iter=_rows.entrySet().iterator();iter.hasNext();)
+        {
+            Map.Entry<Integer,SXSSFRow> entry=iter.next();
+            if(entry.getValue()==row)
+                return entry.getKey().intValue();
+        }
+        assert false;
+        return -1;
+    }
+/*Initially copied from BigGridDemo "SpreadsheetWriter". Unlike the original code which wrote the entire document, this class only writes the "sheetData" document fragment so that it was renamed to "SheetDataWriter"*/
+    public class SheetDataWriter 
+    {
+        private final File _fd;
+        private final Writer _out;
+        private int _rownum;
+        private boolean _rowContainedNullCells=false;
+        int _numberOfFlushedRows;
+        int _lowestIndexOfFlushedRows; // meaningful only of _numberOfFlushedRows>0
+        int _numberOfCellsOfLastFlushedRow; // meaningful only of _numberOfFlushedRows>0
+
+        public SheetDataWriter() throws IOException 
+        {
+            _fd = File.createTempFile("sheet", ".xml");
+            _fd.deleteOnExit();
+            _out = new FileWriter(_fd);
+            _out.write("<sheetData>\n");
+        }
+        public int getNumberOfFlushedRows()
+        {
+            return _numberOfFlushedRows;
+        }
+        public int getNumberOfCellsOfLastFlushedRow()
+        {
+           return _numberOfCellsOfLastFlushedRow;
+        }
+        public int getLowestIndexOfFlushedRows()
+        {
+           return _lowestIndexOfFlushedRows;
+        }
+        protected void finalize() throws Throwable
+        {
+            _fd.delete();
+        }
+        public InputStream getWorksheetXMLInputStream() throws IOException 
+        {
+            _out.write("</sheetData>");
+            _out.close();
+            return new FileInputStream(_fd);
+        }
+
+        /**
+         * Write a row to the file
+         *
+         * @param rownum 0-based row number
+         * @param row a row
+         */
+        public void writeRow(int rownum,SXSSFRow row) throws IOException 
+        {
+            if(_numberOfFlushedRows==0)
+                _lowestIndexOfFlushedRows=rownum;
+            _numberOfCellsOfLastFlushedRow=row.getLastCellNum();
+            _numberOfFlushedRows++;
+            beginRow(rownum,row);
+            Iterator<Cell> cells=row.allCellsIterator();
+            int columnIndex=0;
+            while(cells.hasNext())
+            {
+                writeCell(columnIndex++,cells.next());
+            }
+            endRow();
+        }
+        void beginRow(int rownum,SXSSFRow row) throws IOException 
+        {
+            _out.write("<row r=\""+(rownum+1)+"\"");
+            if(row.hasCustomHeight())
+                _out.write(" customHeight=\"true\"  ht=\""+row.getHeightInPoints()+"\"");
+            _out.write(">\n");
+            this._rownum = rownum;
+            _rowContainedNullCells=false;
+        }
+
+        void endRow() throws IOException 
+        {
+            _out.write("</row>\n");
+        }
+
+        public void writeCell(int columnIndex,Cell cell) throws IOException 
+        {
+            if(cell==null)
+            {
+                _rowContainedNullCells=true;
+                return;
+            }
+            String ref = new CellReference(_rownum, columnIndex).formatAsString();
+            _out.write("<c r=\""+ref+"\"");
+            CellStyle cellStyle=cell.getCellStyle();
+            if(cellStyle!=null) _out.write(" s=\""+cellStyle.getIndex()+"\"");
+            int cellType=cell.getCellType();
+            switch(cellType)
+            {
+                case Cell.CELL_TYPE_BLANK:
+                {
+//TODO: What needs to be done here?
+                }
+                case Cell.CELL_TYPE_FORMULA:
+                {
+//TODO: What needs to be done here?
+                }
+                case Cell.CELL_TYPE_STRING:
+                {
+                    _out.write(" t=\"inlineStr\">");
+                    _out.write("<is><t>");
+                    outputQuotedString(cell.getStringCellValue());
+                    _out.write("</t></is>");
+                    break;
+                }
+                case Cell.CELL_TYPE_NUMERIC:
+                {
+                    _out.write(" t=\"n\">");
+                    _out.write("<v>"+cell.getNumericCellValue()+"</v>");
+                    break;
+                }
+                case Cell.CELL_TYPE_BOOLEAN:
+                {
+                    _out.write(" t=\"n\">");
+                    _out.write("<v>"+(cell.getBooleanCellValue()?"1":"0")+"</v>");
+                    break;
+                }
+                case Cell.CELL_TYPE_ERROR:
+                {
+//TODO: What needs to be done here?
+                    _out.write(" t=\"inlineStr\">");
+                    _out.write("<is><t></t></is>");
+                    break;
+                }
+                default:
+                {
+                    assert false;
+                    throw new RuntimeException("Huh?");
+                }
+            }
+            _out.write("</c>");
+        }
+//Taken from jdk1.3/src/javax/swing/text/html/HTMLWriter.java
+        protected void outputQuotedString(String s) throws IOException
+        {
+            char[] chars=s.toCharArray();
+            int last = 0;
+            int length=s.length();
+            for(int counter = 0; counter < length; counter++) 
+            {
+                char c = chars[counter];
+                switch(c) 
+                {
+                    case '<':
+                    if(counter>last) 
+                    {
+                        _out.write(chars,last,counter-last);
+                    }
+                    last=counter+1;
+                    _out.write("&lt;");
+                    break;
+                case '>':
+                    if(counter > last) 
+                    {
+                        _out.write(chars,last,counter-last);
+                    }
+                    last=counter+1;
+                    _out.write("&gt;");
+                    break;
+                case '&':
+                    if(counter>last) 
+                    {
+                        _out.write(chars,last,counter-last);
+                    }
+                    last=counter+1;
+                    _out.write("&amp;");
+                    break;
+                case '"':
+                    if (counter>last) 
+                    {
+                        _out.write(chars,last,counter-last);
+                    }
+                    last=counter+1;
+                    _out.write("&quot;");
+                    break;
+                    // Special characters
+                case '\n':
+                    if(counter>last) 
+                    {
+                        _out.write(chars,last,counter-last);
+                    }
+                    _out.write("&#xa;");
+                    last=counter+1;
+                    break;
+                case '\t':
+                    if(counter>last) 
+                    {
+                        _out.write(chars,last,counter-last);
+                    }
+                    _out.write("&#x9;");
+                    last=counter+1;
+                    break;
+                case '\r':
+                    if(counter>last) 
+                    {
+                        _out.write(chars,last,counter-last);
+                    }
+                    _out.write("&#xd;");
+                    last=counter+1;
+                    break;
+                case 0xa0:
+                    if(counter>last) 
+                    {
+                        _out.write(chars,last,counter-last);
+                    }
+                    _out.write("&nbsp;");
+                    last=counter+1;
+                    break;
+                default:
+                    if(c<' '||c>127) 
+                    {
+                        if(counter>last) 
+                        {
+                            _out.write(chars,last,counter-last);
+                        }
+                        last=counter+1;
+                        // If the character is outside of ascii, write the
+                        // numeric value.
+                        _out.write("&#");
+                        _out.write(String.valueOf((int)c));
+                        _out.write(";");
+                    }
+                    break;
+                }
+            }
+            if (last<length) 
+            {
+                _out.write(chars,last,length-last);
+            }
+        }
+    }
+}
diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java
new file mode 100644 (file)
index 0000000..d369d86
--- /dev/null
@@ -0,0 +1,779 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.xssf.streaming;
+
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Name;
+import org.apache.poi.ss.usermodel.DataFormat;
+import org.apache.poi.ss.usermodel.PictureData;
+import org.apache.poi.ss.usermodel.CreationHelper;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.FileOutputStream;
+import java.io.File;
+import java.util.List;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+import java.util.zip.ZipEntry;
+
+import org.apache.poi.ss.formula.udf.UDFFinder;
+import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
+
+/**
+ * Streaming version of XSSFWorkbook implementing the "BigGridDemo" strategy.
+ *
+ * @author Alex Geller, Four J's Development Tools
+*/
+public class SXSSFWorkbook implements Workbook
+{
+    XSSFWorkbook _wb=new XSSFWorkbook();
+
+    Hashtable<SXSSFSheet,XSSFSheet> _sxFromXHash=new Hashtable<SXSSFSheet,XSSFSheet>();
+    Hashtable<XSSFSheet,SXSSFSheet> _xFromSxHash=new Hashtable<XSSFSheet,SXSSFSheet>();
+
+    XSSFSheet getXSSFSheet(SXSSFSheet sheet)
+    {
+        XSSFSheet result=_sxFromXHash.get(sheet);
+        assert result!=null;
+        return result;
+    }
+
+    SXSSFSheet getSXSSFSheet(XSSFSheet sheet)
+    {
+        SXSSFSheet result=_xFromSxHash.get(sheet);
+        assert result!=null;
+        return result;
+    }
+
+    void registerSheetMapping(SXSSFSheet sxSheet,XSSFSheet xSheet)
+    {
+        _sxFromXHash.put(sxSheet,xSheet);
+        _xFromSxHash.put(xSheet,sxSheet);
+    }
+    void deregisterSheetMapping(XSSFSheet xSheet)
+    {
+        SXSSFSheet sxSheet=getSXSSFSheet(xSheet);
+        _sxFromXHash.remove(sxSheet);
+        _xFromSxHash.remove(xSheet);
+    }
+    private XSSFSheet getSheetFromZipEntryName(String sheetRef)
+    {
+        Enumeration<XSSFSheet> sheets=_sxFromXHash.elements();
+        while(sheets.hasMoreElements())
+        {
+            XSSFSheet sheet=sheets.nextElement();
+            if(sheetRef.equals(sheet.getPackagePart().getPartName().getName().substring(1))) return sheet;
+        }
+        return null;
+    }
+    private void injectData(File zipfile, OutputStream out) throws IOException 
+    {
+        ZipFile zip = new ZipFile(zipfile);
+
+        ZipOutputStream zos = new ZipOutputStream(out);
+
+        @SuppressWarnings("unchecked")
+        Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) zip.entries();
+        while (en.hasMoreElements()) 
+        {
+            ZipEntry ze = en.nextElement();
+            zos.putNextEntry(new ZipEntry(ze.getName()));
+            InputStream is = zip.getInputStream(ze);
+            XSSFSheet xSheet=getSheetFromZipEntryName(ze.getName());
+            if(xSheet!=null)
+            {
+                SXSSFSheet sxSheet=getSXSSFSheet(xSheet);
+                copyStreamAndInjectWorksheet(is,zos,sxSheet.getWorksheetXMLInputStream());
+            }
+            else
+            {
+                copyStream(is, zos);
+            }
+            is.close();
+        }
+
+        zos.close();
+    }
+    private static void copyStream(InputStream in, OutputStream out) throws IOException {
+        byte[] chunk = new byte[1024];
+        int count;
+        while ((count = in.read(chunk)) >=0 ) {
+          out.write(chunk,0,count);
+        }
+    }
+    private static void copyStreamAndInjectWorksheet(InputStream in, OutputStream out,InputStream worksheetData) throws IOException {
+        InputStreamReader inReader=new InputStreamReader(in,"UTF-8"); //TODO: Is it always UTF-8 or do we need to read the xml encoding declaration in the file? If not, we should perhaps use a SAX reader instead.
+        OutputStreamWriter outWriter=new OutputStreamWriter(out,"UTF-8");
+        int c;
+        int pos=0;
+        String s="<sheetData/>";
+        int n=s.length();
+//Copy from "in" to "out" up to the string "<sheetData/>" (excluding).
+        while(((c=inReader.read())!=-1))
+        {
+            if(c==s.charAt(pos))
+            {
+                pos++;
+                if(pos==n) break;
+            }
+            else
+            {
+                if(pos>0) outWriter.write(s,0,pos);
+                if(c==s.charAt(0))
+                {
+                    pos=1;
+                }
+                else
+                {
+                    outWriter.write(c);
+                    pos=0;
+                }
+            }
+        }
+        outWriter.flush();
+//Copy the worksheet data to "out".
+        copyStream(worksheetData,out);
+//Copy the rest of "in" to "out".
+        while(((c=inReader.read())!=-1))
+            outWriter.write(c);
+        outWriter.flush();
+    }
+
+    public XSSFWorkbook getXSSFWorkbook()
+    {
+        return _wb;
+    }
+
+//start of interface implementation
+
+    /**
+     * Convenience method to get the active sheet.  The active sheet is is the sheet
+     * which is currently displayed when the workbook is viewed in Excel.
+     * 'Selected' sheet(s) is a distinct concept.
+     *
+     * @return the index of the active sheet (0-based)
+     */
+    public int getActiveSheetIndex()
+    {
+        return _wb.getActiveSheetIndex();
+    }
+
+    /**
+     * Convenience method to set the active sheet.  The active sheet is is the sheet
+     * which is currently displayed when the workbook is viewed in Excel.
+     * 'Selected' sheet(s) is a distinct concept.
+     *
+     * @param sheetIndex index of the active sheet (0-based)
+     */
+    public void setActiveSheet(int sheetIndex)
+    {
+        _wb.setActiveSheet(sheetIndex);
+    }
+
+    /**
+     * Gets the first tab that is displayed in the list of tabs in excel.
+     *
+     * @return the first tab that to display in the list of tabs (0-based).
+     */
+    public int getFirstVisibleTab()
+    {
+        return _wb.getFirstVisibleTab();
+    }
+
+    /**
+     * Sets the first tab that is displayed in the list of tabs in excel.
+     *
+     * @param sheetIndex the first tab that to display in the list of tabs (0-based)
+     */
+    public void setFirstVisibleTab(int sheetIndex)
+    {
+        _wb.setFirstVisibleTab(sheetIndex);
+    }
+
+    /**
+     * Sets the order of appearance for a given sheet.
+     *
+     * @param sheetname the name of the sheet to reorder
+     * @param pos the position that we want to insert the sheet into (0 based)
+     */
+    public void setSheetOrder(String sheetname, int pos)
+    {
+        _wb.setSheetOrder(sheetname,pos);
+    }
+
+    /**
+     * Sets the tab whose data is actually seen when the sheet is opened.
+     * This may be different from the "selected sheet" since excel seems to
+     * allow you to show the data of one sheet when another is seen "selected"
+     * in the tabs (at the bottom).
+     *
+     * @see Sheet#setSelected(boolean)
+     * @param index the index of the sheet to select (0 based)
+     */
+    public void setSelectedTab(int index)
+    {
+        _wb.setSelectedTab(index);
+    }
+
+    /**
+     * Set the sheet name.
+     *
+     * @param sheet number (0 based)
+     * @throws IllegalArgumentException if the name is greater than 31 chars or contains <code>/\?*[]</code>
+     */
+    public void setSheetName(int sheet, String name)
+    {
+        _wb.setSheetName(sheet,name);
+    }
+
+    /**
+     * Set the sheet name
+     *
+     * @param sheet sheet number (0 based)
+     * @return Sheet name
+     */
+    public String getSheetName(int sheet)
+    {
+        return _wb.getSheetName(sheet);
+    }
+
+    /**
+     * Returns the index of the sheet by his name
+     *
+     * @param name the sheet name
+     * @return index of the sheet (0 based)
+     */
+    public int getSheetIndex(String name)
+    {
+        return _wb.getSheetIndex(name);
+    }
+
+    /**
+     * Returns the index of the given sheet
+     *
+     * @param sheet the sheet to look up
+     * @return index of the sheet (0 based)
+     */
+    public int getSheetIndex(Sheet sheet)
+    {
+        assert sheet instanceof SXSSFSheet;
+        return _wb.getSheetIndex(getXSSFSheet((SXSSFSheet)sheet));
+    }
+
+    /**
+     * Sreate an Sheet for this Workbook, adds it to the sheets and returns
+     * the high level representation.  Use this to create new sheets.
+     *
+     * @return Sheet representing the new sheet.
+     */
+    public Sheet createSheet()
+    {
+        return createAndRegisterSXSSFSheet(_wb.createSheet());
+    }
+    SXSSFSheet createAndRegisterSXSSFSheet(XSSFSheet xSheet)
+    {
+        SXSSFSheet sxSheet=null;
+        try
+        {
+            sxSheet=new SXSSFSheet(this,xSheet);
+        }
+        catch (IOException ioe)
+        {
+            throw new RuntimeException(ioe);
+        }
+        registerSheetMapping(sxSheet,xSheet);
+        return sxSheet;
+    }
+
+    /**
+     * Create an Sheet for this Workbook, adds it to the sheets and returns
+     * the high level representation.  Use this to create new sheets.
+     *
+     * @param sheetname  sheetname to set for the sheet.
+     * @return Sheet representing the new sheet.
+     * @throws IllegalArgumentException if the name is greater than 31 chars or contains <code>/\?*[]</code>
+     */
+    public Sheet createSheet(String sheetname)
+    {
+        return createAndRegisterSXSSFSheet(_wb.createSheet(sheetname));
+    }
+
+    /**
+     * Create an Sheet from an existing sheet in the Workbook.
+     *
+     * @return Sheet representing the cloned sheet.
+     */
+    public Sheet cloneSheet(int sheetNum)
+    {
+        return createAndRegisterSXSSFSheet(_wb.cloneSheet(sheetNum));
+    }
+
+
+    /**
+     * Get the number of spreadsheets in the workbook
+     *
+     * @return the number of sheets
+     */
+    public int getNumberOfSheets()
+    {
+        return _wb.getNumberOfSheets();
+    }
+
+    /**
+     * Get the Sheet object at the given index.
+     *
+     * @param index of the sheet number (0-based physical & logical)
+     * @return Sheet at the provided index
+     */
+    public Sheet getSheetAt(int index)
+    {
+        return getSXSSFSheet(_wb.getSheetAt(index));
+    }
+
+    /**
+     * Get sheet with the given name
+     *
+     * @param name of the sheet
+     * @return Sheet with the name provided or <code>null</code> if it does not exist
+     */
+    public Sheet getSheet(String name)
+    {
+        return getSXSSFSheet(_wb.getSheet(name));
+    }
+
+    /**
+     * Removes sheet at the given index
+     *
+     * @param index of the sheet to remove (0-based)
+     */
+    public void removeSheetAt(int index)
+    {
+        XSSFSheet xSheet=_wb.getSheetAt(index);
+        _wb.removeSheetAt(index);
+        deregisterSheetMapping(xSheet);
+    }
+
+    /**
+     * Sets the repeating rows and columns for a sheet (as found in
+     * File->PageSetup->Sheet).  This is function is included in the workbook
+     * because it creates/modifies name records which are stored at the
+     * workbook level.
+     * <p>
+     * To set just repeating columns:
+     * <pre>
+     *  workbook.setRepeatingRowsAndColumns(0,0,1,-1-1);
+     * </pre>
+     * To set just repeating rows:
+     * <pre>
+     *  workbook.setRepeatingRowsAndColumns(0,-1,-1,0,4);
+     * </pre>
+     * To remove all repeating rows and columns for a sheet.
+     * <pre>
+     *  workbook.setRepeatingRowsAndColumns(0,-1,-1,-1,-1);
+     * </pre>
+     *
+     * @param sheetIndex    0 based index to sheet.
+     * @param startColumn   0 based start of repeating columns.
+     * @param endColumn     0 based end of repeating columns.
+     * @param startRow      0 based start of repeating rows.
+     * @param endRow        0 based end of repeating rows.
+     */
+    public void setRepeatingRowsAndColumns(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow)
+    {
+        _wb.setRepeatingRowsAndColumns(sheetIndex,startColumn,endColumn,startRow,endRow);
+    }
+
+    /**
+     * Create a new Font and add it to the workbook's font table
+     *
+     * @return new font object
+     */
+    public Font createFont()
+    {
+        return _wb.createFont();
+    }
+
+    /**
+     * Finds a font that matches the one with the supplied attributes
+     *
+     * @return the font with the matched attributes or <code>null</code>
+     */
+    public Font findFont(short boldWeight, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline)
+    {
+        return _wb.findFont(boldWeight, color, fontHeight, name, italic, strikeout, typeOffset, underline);
+    }
+   
+
+    /**
+     * Get the number of fonts in the font table
+     *
+     * @return number of fonts
+     */
+    public short getNumberOfFonts()
+    {
+        return _wb.getNumberOfFonts();
+    }
+
+    /**
+     * Get the font at the given index number
+     *
+     * @param idx  index number (0-based)
+     * @return font at the index
+     */
+    public Font getFontAt(short idx)
+    {
+        return _wb.getFontAt(idx);
+    }
+
+    /**
+     * Create a new Cell style and add it to the workbook's style table
+     *
+     * @return the new Cell Style object
+     */
+    public CellStyle createCellStyle()
+    {
+        return _wb.createCellStyle();
+    }
+
+    /**
+     * Get the number of styles the workbook contains
+     *
+     * @return count of cell styles
+     */
+    public short getNumCellStyles()
+    {
+        return _wb.getNumCellStyles();
+    }
+
+    /**
+     * Get the cell style object at the given index
+     *
+     * @param idx  index within the set of styles (0-based)
+     * @return CellStyle object at the index
+     */
+    public CellStyle getCellStyleAt(short idx)
+    {
+        return _wb.getCellStyleAt(idx);
+    }
+
+    /**
+     * Write out this workbook to an Outputstream.
+     *
+     * @param stream - the java OutputStream you wish to write to
+     * @exception IOException if anything can't be written.
+     */
+    public void write(OutputStream stream) throws IOException
+    {
+        //Save the template
+        File tmplFile = File.createTempFile("template", ".xlsx");
+        FileOutputStream os = new FileOutputStream(tmplFile);
+        _wb.write(os);
+        os.close();
+
+        //Substitute the template entries with the generated sheet data files
+        injectData(tmplFile, stream);
+        tmplFile.delete();
+    }
+
+    /**
+     * @return the total number of defined names in this workbook
+     */
+    public int getNumberOfNames()
+    {
+        return _wb.getNumberOfNames();
+    }
+
+    /**
+     * @param name the name of the defined name
+     * @return the defined name with the specified name. <code>null</code> if not found.
+     */
+    public Name getName(String name)
+    {
+        return _wb.getName(name);
+    }
+    /**
+     * @param nameIndex position of the named range (0-based)
+     * @return the defined name at the specified index
+     * @throws IllegalArgumentException if the supplied index is invalid
+     */
+    public Name getNameAt(int nameIndex)
+    {
+        return _wb.getNameAt(nameIndex);
+    }
+
+    /**
+     * Creates a new (uninitialised) defined name in this workbook
+     *
+     * @return new defined name object
+     */
+    public Name createName()
+    {
+        return _wb.createName();
+    }
+
+    /**
+     * Gets the defined name index by name<br/>
+     * <i>Note:</i> Excel defined names are case-insensitive and
+     * this method performs a case-insensitive search.
+     *
+     * @param name the name of the defined name
+     * @return zero based index of the defined name. <tt>-1</tt> if not found.
+     */
+    public int getNameIndex(String name)
+    {
+        return _wb.getNameIndex(name);
+    }
+
+    /**
+     * Remove the defined name at the specified index
+     *
+     * @param index named range index (0 based)
+     */
+    public void removeName(int index)
+    {
+        _wb.removeName(index);
+    }
+
+    /**
+     * Remove a defined name by name
+     *
+      * @param name the name of the defined name
+     */
+    public void removeName(String name)
+    {
+        _wb.removeName(name);
+    }
+
+     /**
+     * Sets the printarea for the sheet provided
+     * <p>
+     * i.e. Reference = $A$1:$B$2
+     * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
+     * @param reference Valid name Reference for the Print Area
+     */
+    public void setPrintArea(int sheetIndex, String reference)
+    {
+        _wb.setPrintArea(sheetIndex,reference);
+    }
+
+    /**
+     * For the Convenience of Java Programmers maintaining pointers.
+     * @see #setPrintArea(int, String)
+     * @param sheetIndex Zero-based sheet index (0 = First Sheet)
+     * @param startColumn Column to begin printarea
+     * @param endColumn Column to end the printarea
+     * @param startRow Row to begin the printarea
+     * @param endRow Row to end the printarea
+     */
+    public void setPrintArea(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow)
+    {
+        _wb.setPrintArea(sheetIndex, startColumn, endColumn, startRow, endRow);
+    }
+
+    /**
+     * Retrieves the reference for the printarea of the specified sheet,
+     * the sheet name is appended to the reference even if it was not specified.
+     *
+     * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
+     * @return String Null if no print area has been defined
+     */
+    public String getPrintArea(int sheetIndex)
+    {
+        return _wb.getPrintArea(sheetIndex);
+    }
+
+    /**
+     * Delete the printarea for the sheet specified
+     *
+     * @param sheetIndex Zero-based sheet index (0 = First Sheet)
+     */
+    public void removePrintArea(int sheetIndex)
+    {
+        _wb.removePrintArea(sheetIndex);
+    }
+
+    /**
+     * Retrieves the current policy on what to do when
+     *  getting missing or blank cells from a row.
+     * <p>
+     * The default is to return blank and null cells.
+     *  {@link MissingCellPolicy}
+     * </p>
+     */
+    public MissingCellPolicy getMissingCellPolicy()
+    {
+        return _wb.getMissingCellPolicy();
+    }
+
+    /**
+     * Sets the policy on what to do when
+     *  getting missing or blank cells from a row.
+     *
+     * This will then apply to all calls to
+     *  {@link org.apache.poi.ss.usermodel.Row#getCell(int)}. See
+     *  {@link MissingCellPolicy}
+     */
+    public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy)
+    {
+        _wb.setMissingCellPolicy(missingCellPolicy);
+    }
+
+    /**
+     * Returns the instance of DataFormat for this workbook.
+     *
+     * @return the DataFormat object
+     */
+    public DataFormat createDataFormat()
+    {
+        return _wb.createDataFormat();
+    }
+
+    /**
+     * Adds a picture to the workbook.
+     *
+     * @param pictureData       The bytes of the picture
+     * @param format            The format of the picture.
+     *
+     * @return the index to this picture (1 based).
+     * @see #PICTURE_TYPE_EMF
+     * @see #PICTURE_TYPE_WMF
+     * @see #PICTURE_TYPE_PICT
+     * @see #PICTURE_TYPE_JPEG
+     * @see #PICTURE_TYPE_PNG
+     * @see #PICTURE_TYPE_DIB
+     */
+    public int addPicture(byte[] pictureData, int format)
+    {
+        return _wb.addPicture(pictureData,format);
+    }
+
+    /**
+     * Gets all pictures from the Workbook.
+     *
+     * @return the list of pictures (a list of {@link PictureData} objects.)
+     */
+    public List<? extends PictureData> getAllPictures()
+    {
+        return _wb.getAllPictures();
+    }
+
+    /**
+     * Returns an object that handles instantiating concrete
+     * classes of the various instances one needs for  HSSF and XSSF.
+     */
+    public CreationHelper getCreationHelper()
+    {
+        return _wb.getCreationHelper();
+    }
+
+    /**
+     * @return <code>false</code> if this workbook is not visible in the GUI
+     */
+    public boolean isHidden()
+    {
+        return _wb.isHidden();
+    }
+
+    /**
+     * @param hiddenFlag pass <code>false</code> to make the workbook visible in the GUI
+     */
+    public void setHidden(boolean hiddenFlag)
+    {
+        _wb.setHidden(hiddenFlag);
+    }
+
+    /**
+     * Check whether a sheet is hidden.
+     * <p>
+     * Note that a sheet could instead be set to be very hidden, which is different
+     *  ({@link #isSheetVeryHidden(int)})
+     * </p>
+     * @param sheetIx Number
+     * @return <code>true</code> if sheet is hidden
+     */
+    public boolean isSheetHidden(int sheetIx)
+    {
+        return _wb.isSheetHidden(sheetIx);
+    }
+
+    /**
+     * Check whether a sheet is very hidden.
+     * <p>
+     * This is different from the normal hidden status
+     *  ({@link #isSheetHidden(int)})
+     * </p>
+     * @param sheetIx sheet index to check
+     * @return <code>true</code> if sheet is very hidden
+     */
+    public boolean isSheetVeryHidden(int sheetIx)
+    {
+        return _wb.isSheetVeryHidden(sheetIx);
+    }
+
+    /**
+     * Hide or unhide a sheet
+     *
+     * @param sheetIx the sheet index (0-based)
+     * @param hidden True to mark the sheet as hidden, false otherwise
+     */
+    public void setSheetHidden(int sheetIx, boolean hidden)
+    {
+        _wb.setSheetHidden(sheetIx,hidden);
+    }
+
+    /**
+     * Hide or unhide a sheet.
+     * 
+     * <ul>
+     *  <li>0 - visible. </li>
+     *  <li>1 - hidden. </li>
+     *  <li>2 - very hidden.</li>
+     * </ul>
+     * @param sheetIx the sheet index (0-based)
+     * @param hidden one of the following <code>Workbook</code> constants:
+     *        <code>Workbook.SHEET_STATE_VISIBLE</code>,
+     *        <code>Workbook.SHEET_STATE_HIDDEN</code>, or
+     *        <code>Workbook.SHEET_STATE_VERY_HIDDEN</code>.
+     * @throws IllegalArgumentException if the supplied sheet index or state is invalid
+     */
+    public void setSheetHidden(int sheetIx, int hidden)
+    {
+        _wb.setSheetHidden(sheetIx,hidden);
+    }
+    /**
+     * Register a new toolpack in this workbook.
+     *
+     * @param toopack the toolpack to register
+     */
+    public void addToolPack(UDFFinder toopack)
+    {
+        _wb.addToolPack(toopack);
+    }
+//end of interface implementation
+}