From: Yegor Kozlov Date: Tue, 17 May 2011 10:46:35 +0000 (+0000) Subject: Bugzilla 51160: Initial version of SXSSF, a low memory foortprint API to produce... X-Git-Tag: REL_3_8_BETA3~24 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=50a40785edac18968aa83e50297ee53896aed241;p=poi.git Bugzilla 51160: Initial version of SXSSF, a low memory foortprint API to produce xlsx files git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1104120 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index abb4d41832..719af3029d 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,7 +34,8 @@ - 51171 - Improved performance of opening large .xls files + 51160 - Initial version of SXSSF, a low memory foortprint API to produce xlsx files + 51171 - Improved performance of opening large .xls files 51172 - Add XWPF support for GIF pictures NPOIFS Mini Streams now support extending the underlying big block stream to fit more data 51148 - XWPFDocument now properly tracks paragraphs and tables when adding/removing them 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 index 0000000000..4d6dee448b --- /dev/null +++ b/src/examples/src/org/apache/poi/ss/examples/SSPerformanceTest.java @@ -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 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 createStyles(Workbook wb) { + Map styles = new HashMap(); + 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 index 0000000000..fba4308ecc --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFCell.java @@ -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. + *

+ * Note - There is actually no 'DATE' cell type in Excel. In many + * cases (when entering date values), Excel automatically adjusts the + * cell style 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. + *

+ * 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 setCellValue(value.getTime()) which will + * automatically shift the times to the default timezone. + *

+ * + * @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. + *

+ * 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)} + *

+ * + * @param formula the formula to set, e.g. "SUM(C4:E4)". + * If the argument is null 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, SUM(C4:E4) + * + * @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. + *

+ * For strings we throw an exception. For blank cells we return a 0. + * For formulas or error cells we return the precalculated value; + *

+ * @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 double. + * @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. + *

+ * For strings we throw an exception. For blank cells we return a null. + *

+ * @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 double. + * @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 + *

+ * 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. + *

+ * @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 + *

+ * 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. + *

+ * @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. + *

+ * For strings, numbers, and errors, we throw an exception. For blank cells we return a false. + *

+ * @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. + *

+ * For strings, numbers, and booleans, we throw an exception. + * For blank cells we return a 0. + *

+ * + * @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 + * workbook.getCellStyleAt(0) + * @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 null 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 null 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 true 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&¤t.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&¤t.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&¤t.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 index 0000000000..25808c9c5f --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java @@ -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 allCellsIterator() + { + return new CellIterator(); + } + public boolean hasCustomHeight() + { + return _height!=-1; + } +//begin of interface implementation + public Iterator iterator() + { + return new FilledCellIterator(); + } + + /** + * Use this to create new cells within the row and return it. + *

+ * 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 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. + *

+ * 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 PLUS ONE. 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: + *

+     * short minColIx = row.getFirstCellNum();
+     * short maxColIx = row.getLastCellNum();
+     * for(short colIx=minColIx; colIx<maxColIx; colIx++) {
+     *   Cell cell = row.getCell(colIx);
+     *   if(cell == null) {
+     *     continue;
+     *   }
+     *   //... do something with cell
+     * }
+     * 
+ * + * @return short representing the last logical cell in the row PLUS ONE, + * 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. -1 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 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 + { + 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 + { + 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 index 0000000000..86ac777844 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java @@ -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 _rows=new TreeMap(); + SheetDataWriter _writer; + int _randomAccessWindowSize=5000; + + public SXSSFSheet(SXSSFWorkbook workbook,XSSFSheet xSheet) throws IOException + { + _workbook=workbook; + _sh=xSheet; + _writer=new SheetDataWriter(); + + } +/* Gets "" document fragment*/ + public InputStream getWorksheetXMLInputStream() throws IOException + { + flushRows(0); + return _writer.getWorksheetXMLInputStream(); + } + +//start of interface implementation + public Iterator 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> iter=_rows.entrySet().iterator();iter.hasNext();) + { + Map.Entry 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 - false 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) + *

+ * 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. + *

+ * + * @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 rowIterator() + { + @SuppressWarnings("unchecked") + Iterator result = (Iterator)(Iterator)_rows.values().iterator(); + return result; + } + + /** + * Flag indicating whether the sheet displays Automatic Page Breaks. + * + * @param value true 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 true 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. + * + *

+ * When true a summary row is inserted below the detailed data being summarized and a + * new outline level is established on that row. + *

+ *

+ * When false a summary row is inserted above the detailed data being summarized and a new outline level + * is established on that row. + *

+ * @param value true 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. + * + *

+ * 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. + *

+ *

+ * 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. + *

+ * @param value true 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 true 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 true 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. + * + *

+ * When true a summary row is inserted below the detailed data being summarized and a + * new outline level is established on that row. + *

+ *

+ * When false a summary row is inserted above the detailed data being summarized and a new outline level + * is established on that row. + *

+ * @return true 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. + * + *

+ * 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. + *

+ *

+ * 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. + *

+ * @return true 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 true 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 true 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. + *

+ * Note that XSSF offers more kinds of document headers than HSSF does + *

+ * @return the document header. Never null + */ + public Header getHeader() + { + return _sh.getHeader(); + } + + /** + * Gets the user model for the default document footer. + *

+ * Note that XSSF offers more kinds of document footers than HSSF does. + * + * @return the document footer. Never null + */ + public Footer getFooter() + { + return _sh.getFooter(); + } + + /** + * Sets a flag indicating whether this sheet is selected. + *

+ * Note: multiple sheets can be selected, but only one sheet can be active at one time. + *

+ * @param value true 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 null 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); + * + *

+ * 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 + * + *

+ * 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 + * @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. + * + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 null 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 true 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 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 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 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> iter=_rows.entrySet().iterator();iter.hasNext();) + { + Map.Entry 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("\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(""); + _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 cells=row.allCellsIterator(); + int columnIndex=0; + while(cells.hasNext()) + { + writeCell(columnIndex++,cells.next()); + } + endRow(); + } + void beginRow(int rownum,SXSSFRow row) throws IOException + { + _out.write("\n"); + this._rownum = rownum; + _rowContainedNullCells=false; + } + + void endRow() throws IOException + { + _out.write("\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(""); + _out.write(""); + outputQuotedString(cell.getStringCellValue()); + _out.write(""); + break; + } + case Cell.CELL_TYPE_NUMERIC: + { + _out.write(" t=\"n\">"); + _out.write(""+cell.getNumericCellValue()+""); + break; + } + case Cell.CELL_TYPE_BOOLEAN: + { + _out.write(" t=\"n\">"); + _out.write(""+(cell.getBooleanCellValue()?"1":"0")+""); + break; + } + case Cell.CELL_TYPE_ERROR: + { +//TODO: What needs to be done here? + _out.write(" t=\"inlineStr\">"); + _out.write(""); + break; + } + default: + { + assert false; + throw new RuntimeException("Huh?"); + } + } + _out.write(""); + } +//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("<"); + break; + case '>': + if(counter > last) + { + _out.write(chars,last,counter-last); + } + last=counter+1; + _out.write(">"); + break; + case '&': + if(counter>last) + { + _out.write(chars,last,counter-last); + } + last=counter+1; + _out.write("&"); + break; + case '"': + if (counter>last) + { + _out.write(chars,last,counter-last); + } + last=counter+1; + _out.write("""); + break; + // Special characters + case '\n': + if(counter>last) + { + _out.write(chars,last,counter-last); + } + _out.write(" "); + last=counter+1; + break; + case '\t': + if(counter>last) + { + _out.write(chars,last,counter-last); + } + _out.write(" "); + last=counter+1; + break; + case '\r': + if(counter>last) + { + _out.write(chars,last,counter-last); + } + _out.write(" "); + last=counter+1; + break; + case 0xa0: + if(counter>last) + { + _out.write(chars,last,counter-last); + } + _out.write(" "); + 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 _sxFromXHash=new Hashtable(); + Hashtable _xFromSxHash=new Hashtable(); + + 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 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 en = (Enumeration) 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=""; + int n=s.length(); +//Copy from "in" to "out" up to the string "" (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 /\?*[] + */ + 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 /\?*[] + */ + 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 null 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. + *

+ * To set just repeating columns: + *

+     *  workbook.setRepeatingRowsAndColumns(0,0,1,-1-1);
+     * 
+ * To set just repeating rows: + *
+     *  workbook.setRepeatingRowsAndColumns(0,-1,-1,0,4);
+     * 
+ * To remove all repeating rows and columns for a sheet. + *
+     *  workbook.setRepeatingRowsAndColumns(0,-1,-1,-1,-1);
+     * 
+ * + * @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 null + */ + 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. null 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
+ * Note: 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. -1 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 + *

+ * 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. + *

+ * The default is to return blank and null cells. + * {@link MissingCellPolicy} + *

+ */ + 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 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 false if this workbook is not visible in the GUI + */ + public boolean isHidden() + { + return _wb.isHidden(); + } + + /** + * @param hiddenFlag pass false to make the workbook visible in the GUI + */ + public void setHidden(boolean hiddenFlag) + { + _wb.setHidden(hiddenFlag); + } + + /** + * Check whether a sheet is hidden. + *

+ * Note that a sheet could instead be set to be very hidden, which is different + * ({@link #isSheetVeryHidden(int)}) + *

+ * @param sheetIx Number + * @return true if sheet is hidden + */ + public boolean isSheetHidden(int sheetIx) + { + return _wb.isSheetHidden(sheetIx); + } + + /** + * Check whether a sheet is very hidden. + *

+ * This is different from the normal hidden status + * ({@link #isSheetHidden(int)}) + *

+ * @param sheetIx sheet index to check + * @return true 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. + * + *
    + *
  • 0 - visible.
  • + *
  • 1 - hidden.
  • + *
  • 2 - very hidden.
  • + *
+ * @param sheetIx the sheet index (0-based) + * @param hidden one of the following Workbook constants: + * Workbook.SHEET_STATE_VISIBLE, + * Workbook.SHEET_STATE_HIDDEN, or + * Workbook.SHEET_STATE_VERY_HIDDEN. + * @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 +}