Add GenericSSEvaluationWorkbook that has XSSF row/column size limits but cannot output a file, for much faster calculations

This commit is contained in:
Travis Burtrum 2019-03-25 12:34:15 -04:00
parent 3ad99da86e
commit d472f88a42
10 changed files with 138 additions and 40 deletions

View File

@ -34,6 +34,7 @@ import org.apache.poi.hssf.record.aggregates.RecordAggregate.PositionTrackingVis
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.FormulaShifter;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.PaneInformation;
@ -409,9 +410,12 @@ public final class InternalSheet {
* @return Sheet object with all values set to defaults
*/
public static InternalSheet createSheet() {
return new InternalSheet();
return new InternalSheet(SpreadsheetVersion.EXCEL97);
}
private InternalSheet() {
public static InternalSheet createSheet(final SpreadsheetVersion spreadsheetVersion) {
return new InternalSheet(spreadsheetVersion);
}
private InternalSheet(final SpreadsheetVersion spreadsheetVersion) {
_mergedCellsTable = new MergedCellsTable();
List<RecordBase> records = new ArrayList<>(32);
@ -452,7 +456,7 @@ public final class InternalSheet {
_columnInfos = columns;
_dimensions = createDimensions();
records.add(_dimensions);
_rowsAggregate = new RowRecordsAggregate();
_rowsAggregate = new RowRecordsAggregate(spreadsheetVersion);
records.add(_rowsAggregate);
// 'Sheet View Settings'
records.add(windowTwo = createWindowTwo());

View File

@ -40,6 +40,7 @@ public final class RowRecordsAggregate extends RecordAggregate {
private final ValueRecordsAggregate _valuesAgg;
private final List<Record> _unknownRecords;
private final SharedValueManager _sharedValueManager;
private final SpreadsheetVersion _spreadsheetVersion;
// Cache values to speed up performance of
// getStartRowNumberForBlock / getEndRowNumberForBlock, see Bugzilla 47405
@ -47,9 +48,15 @@ public final class RowRecordsAggregate extends RecordAggregate {
/** Creates a new instance of ValueRecordsAggregate */
public RowRecordsAggregate() {
this(SharedValueManager.createEmpty());
this(SharedValueManager.createEmpty(), SpreadsheetVersion.EXCEL97);
}
private RowRecordsAggregate(SharedValueManager svm) {
public RowRecordsAggregate(final SpreadsheetVersion spreadsheetVersion) {
this(SharedValueManager.createEmpty(), spreadsheetVersion);
if (spreadsheetVersion == null) {
throw new IllegalArgumentException("SpreadsheetVersion must be provided.");
}
}
private RowRecordsAggregate(SharedValueManager svm, final SpreadsheetVersion spreadsheetVersion) {
if (svm == null) {
throw new IllegalArgumentException("SharedValueManager must be provided.");
}
@ -57,6 +64,7 @@ public final class RowRecordsAggregate extends RecordAggregate {
_valuesAgg = new ValueRecordsAggregate();
_unknownRecords = new ArrayList<>();
_sharedValueManager = svm;
_spreadsheetVersion = spreadsheetVersion;
}
/**
@ -66,7 +74,7 @@ public final class RowRecordsAggregate extends RecordAggregate {
* and table records of the current sheet). Never <code>null</code>.
*/
public RowRecordsAggregate(RecordStream rs, SharedValueManager svm) {
this(svm);
this(svm, SpreadsheetVersion.EXCEL97);
while(rs.hasNext()) {
Record rec = rs.getNext();
switch (rec.getSid()) {
@ -143,7 +151,7 @@ public final class RowRecordsAggregate extends RecordAggregate {
}
public RowRecord getRow(int rowIndex) {
int maxrow = SpreadsheetVersion.EXCEL97.getLastRowIndex();
int maxrow = _spreadsheetVersion.getLastRowIndex();
if (rowIndex < 0 || rowIndex > maxrow) {
throw new IllegalArgumentException("The row number must be between 0 and " + maxrow + ", but had: " + rowIndex);
}

View File

@ -0,0 +1,72 @@
/* ====================================================================
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.hssf.usermodel;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.SpreadsheetVersion;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
/**
* This is a in-memory workbook only, with XSSF (xlsx) limits as defined by SpreadsheetVersion.EXCEL2007 which can be
* used to perform formula evaluations much much faster than XSSFWorkbook.
*
* You cannot write this to the filesystem, all write functions throw an IllegalStateException.
*
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook
*/
public class GenericSSEvaluationWorkbook extends HSSFWorkbook {
public GenericSSEvaluationWorkbook() {
super(SpreadsheetVersion.EXCEL2007);
}
@Override
public void write() throws IOException {
throw new IllegalStateException("CalcEngineHSSFWorkbook cannot write, meant only for in-memory calculations");
}
@Override
public void write(File newFile) throws IOException {
throw new IllegalStateException("CalcEngineHSSFWorkbook cannot write, meant only for in-memory calculations");
}
@Override
public void write(OutputStream stream) throws IOException {
throw new IllegalStateException("CalcEngineHSSFWorkbook cannot write, meant only for in-memory calculations");
}
@Override
protected void writeProperties() throws IOException {
throw new IllegalStateException("CalcEngineHSSFWorkbook cannot write, meant only for in-memory calculations");
}
@Override
protected void writeProperties(POIFSFileSystem outFS) throws IOException {
throw new IllegalStateException("CalcEngineHSSFWorkbook cannot write, meant only for in-memory calculations");
}
@Override
protected void writeProperties(POIFSFileSystem outFS, List<String> writtenEntries) throws IOException {
throw new IllegalStateException("CalcEngineHSSFWorkbook cannot write, meant only for in-memory calculations");
}
}

View File

@ -70,11 +70,6 @@ import org.apache.poi.util.Removal;
*/
public class HSSFCell implements Cell {
private static final String FILE_FORMAT_NAME = "BIFF8";
/**
* The maximum number of columns in BIFF8
*/
public static final int LAST_COLUMN_NUMBER = SpreadsheetVersion.EXCEL97.getLastColumnIndex(); // 2^8 - 1
private static final String LAST_COLUMN_NAME = SpreadsheetVersion.EXCEL97.getLastColumnName();
public final static short ENCODING_UNCHANGED = -1;
public final static short ENCODING_COMPRESSED_UNICODE = 0;
@ -105,7 +100,7 @@ public class HSSFCell implements Cell {
*/
protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col)
{
checkBounds(col);
checkBounds(col, book);
_stringValue = null;
_book = book;
_sheet = sheet;
@ -150,7 +145,7 @@ public class HSSFCell implements Cell {
protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col,
CellType type)
{
checkBounds(col);
checkBounds(col, book);
_cellType = CellType._NONE; // Force 'setCellType' to create a first Record
_stringValue = null;
_book = book;
@ -551,8 +546,8 @@ public class HSSFCell implements Cell {
return;
}
if(value.length() > SpreadsheetVersion.EXCEL97.getMaxTextLength()){
throw new IllegalArgumentException("The maximum length of cell contents (text) is 32,767 characters");
if(value.length() > _book.getSpreadsheetVersion().getMaxTextLength()){
throw new IllegalArgumentException("The maximum length of cell contents (text) is "+_book.getSpreadsheetVersion().getMaxTextLength()+" characters");
}
if (_cellType == CellType.FORMULA) {
@ -958,8 +953,10 @@ public class HSSFCell implements Cell {
/**
* @throws RuntimeException if the bounds are exceeded.
*/
private static void checkBounds(int cellIndex) {
private static void checkBounds(int cellIndex, final HSSFWorkbook book) {
final int LAST_COLUMN_NUMBER = book.getSpreadsheetVersion().getLastColumnIndex(); // 2^8 - 1
if (cellIndex < 0 || cellIndex > LAST_COLUMN_NUMBER) {
final String LAST_COLUMN_NAME = book.getSpreadsheetVersion().getLastColumnName();
throw new IllegalArgumentException("Invalid column index (" + cellIndex
+ "). Allowable column range for " + FILE_FORMAT_NAME + " is (0.."
+ LAST_COLUMN_NUMBER + ") or ('A'..'" + LAST_COLUMN_NAME + "')");

View File

@ -296,7 +296,7 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
@Override
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL97;
return _uBook.getSpreadsheetVersion();
}
/**

View File

@ -129,7 +129,7 @@ public final class HSSFName implements Name {
* @throws IllegalArgumentException if the name is invalid or the name already exists (case-insensitive)
*/
public void setNameName(String nameName){
validateName(nameName);
validateName(nameName, _book.getSpreadsheetVersion());
InternalWorkbook wb = _book.getWorkbook();
_definedNameRec.setNameText(nameName);
@ -176,7 +176,7 @@ public final class HSSFName implements Name {
*
* Uniqueness: must be unique (for names with the same scope)
*/
private static void validateName(String name) {
private static void validateName(String name, SpreadsheetVersion spreadsheetVersion) {
if (name.length() == 0) {
throw new IllegalArgumentException("Name cannot be blank");
@ -210,7 +210,7 @@ public final class HSSFName implements Name {
if (name.matches("[A-Za-z]+\\d+")) {
String col = name.replaceAll("\\d", "");
String row = name.replaceAll("[A-Za-z]", "");
if (CellReference.cellReferenceIsWithinRange(col, row, SpreadsheetVersion.EXCEL97)) {
if (CellReference.cellReferenceIsWithinRange(col, row, spreadsheetVersion)) {
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be $A$1-style cell reference");
}
}

View File

@ -228,7 +228,7 @@ public final class HSSFRow implements Row, Comparable<HSSFRow> {
*/
@Override
public void setRowNum(int rowIndex) {
int maxrow = SpreadsheetVersion.EXCEL97.getLastRowIndex();
int maxrow = book.getSpreadsheetVersion().getLastRowIndex();
if ((rowIndex < 0) || (rowIndex > maxrow)) {
throw new IllegalArgumentException("Invalid row number (" + rowIndex
+ ") outside allowable range (0.." + maxrow + ")");

View File

@ -126,7 +126,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createSheet()
*/
protected HSSFSheet(HSSFWorkbook workbook) {
_sheet = InternalSheet.createSheet();
_sheet = InternalSheet.createSheet(workbook.getSpreadsheetVersion());
_rows = new TreeMap<>();
this._workbook = workbook;
this._book = workbook.getWorkbook();
@ -719,7 +719,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
if (region.getNumberOfCells() < 2) {
throw new IllegalArgumentException("Merged region " + region.formatAsString() + " must contain 2 or more cells");
}
region.validate(SpreadsheetVersion.EXCEL97);
region.validate(_workbook.getSpreadsheetVersion());
if (validate) {
// throw IllegalStateException if the argument CellRangeAddress intersects with
@ -1457,7 +1457,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
*/
@Override
public void showInPane(int toprow, int leftcol) {
int maxrow = SpreadsheetVersion.EXCEL97.getLastRowIndex();
final int maxrow = _workbook.getSpreadsheetVersion().getLastRowIndex();
if (toprow > maxrow) throw new IllegalArgumentException("Maximum row number is " + maxrow);
showInPane((short)toprow, (short)leftcol);
@ -1534,10 +1534,10 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
*
* @param row the row number
*/
private static int clip(int row) {
private int clip(int row) {
return Math.min(
Math.max(0, row),
SpreadsheetVersion.EXCEL97.getLastRowIndex());
_workbook.getSpreadsheetVersion().getLastRowIndex());
}
/**
@ -1692,7 +1692,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
}
}
if (endRow + n > _lastrow) {
_lastrow = Math.min(endRow + n, SpreadsheetVersion.EXCEL97.getLastRowIndex());
_lastrow = Math.min(endRow + n, _workbook.getSpreadsheetVersion().getLastRowIndex());
}
} else {
// Rows are moving up
@ -1702,7 +1702,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
if (endRow == _lastrow) {
// Need to walk backward to find the last non-blank row
// NOTE: n is always negative here
_lastrow = Math.min(endRow + n, SpreadsheetVersion.EXCEL97.getLastRowIndex());
_lastrow = Math.min(endRow + n, _workbook.getSpreadsheetVersion().getLastRowIndex());
for (int i = endRow - 1; i > endRow + n; i--) {
if (getRow(i) != null) {
_lastrow = i;
@ -1763,7 +1763,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
short externSheetIndex = _book.checkExternSheet(sheetIndex);
String sheetName = _workbook.getSheetName(sheetIndex);
FormulaShifter formulaShifter = FormulaShifter.createForColumnShift(
externSheetIndex, sheetName, startColumn, endColumn, n, SpreadsheetVersion.EXCEL97);
externSheetIndex, sheetName, startColumn, endColumn, n, _workbook.getSpreadsheetVersion());
updateFormulasForShift(formulaShifter);
// add logic for hyperlinks etc, like in shiftRows()
}
@ -1973,7 +1973,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
@Override
public void setColumnBreak(int column) {
validateColumn((short) column);
_sheet.getPageSettings().setColumnBreak((short) column, (short) 0, (short) SpreadsheetVersion.EXCEL97.getLastRowIndex());
_sheet.getPageSettings().setColumnBreak((short) column, (short) 0, (short) _workbook.getSpreadsheetVersion().getLastRowIndex());
}
/**
@ -2003,7 +2003,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @param row the index of the row to validate, zero-based
*/
protected void validateRow(int row) {
int maxrow = SpreadsheetVersion.EXCEL97.getLastRowIndex();
final int maxrow = _workbook.getSpreadsheetVersion().getLastRowIndex();
if (row > maxrow) throw new IllegalArgumentException("Maximum row number is " + maxrow);
if (row < 0) throw new IllegalArgumentException("Minumum row number is 0");
}
@ -2014,7 +2014,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @param column the index of the column to validate, zero-based
*/
protected void validateColumn(int column) {
int maxcol = SpreadsheetVersion.EXCEL97.getLastColumnIndex();
final int maxcol = _workbook.getSpreadsheetVersion().getLastColumnIndex();
if (column > maxcol) throw new IllegalArgumentException("Maximum column number is " + maxcol);
if (column < 0) throw new IllegalArgumentException("Minimum column number is 0");
}
@ -2534,8 +2534,8 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
private void setRepeatingRowsAndColumns(
CellRangeAddress rowDef, CellRangeAddress colDef) {
int sheetIndex = _workbook.getSheetIndex(this);
int maxRowIndex = SpreadsheetVersion.EXCEL97.getLastRowIndex();
int maxColIndex = SpreadsheetVersion.EXCEL97.getLastColumnIndex();
final int maxRowIndex = _workbook.getSpreadsheetVersion().getLastRowIndex();
final int maxColIndex = _workbook.getSpreadsheetVersion().getLastColumnIndex();
int col1 = -1;
int col2 = -1;
@ -2620,8 +2620,8 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
return null;
}
int maxRowIndex = SpreadsheetVersion.EXCEL97.getLastRowIndex();
int maxColIndex = SpreadsheetVersion.EXCEL97.getLastColumnIndex();
final int maxRowIndex = _workbook.getSpreadsheetVersion().getLastRowIndex();
final int maxColIndex = _workbook.getSpreadsheetVersion().getLastColumnIndex();
for (Ptg ptg : nameDefinition) {

View File

@ -174,7 +174,7 @@ public final class HSSFSheetConditionalFormatting implements SheetConditionalFor
if (regions == null) {
throw new IllegalArgumentException("regions must not be null");
}
for(CellRangeAddress range : regions) range.validate(SpreadsheetVersion.EXCEL97);
for(CellRangeAddress range : regions) range.validate(_sheet._workbook.getSpreadsheetVersion());
if (cfRules == null) {
throw new IllegalArgumentException("cfRules must not be null");

View File

@ -129,7 +129,7 @@ import org.apache.poi.util.POILogger;
* @see org.apache.poi.hssf.usermodel.HSSFSheet
*/
@SuppressWarnings("WeakerAccess")
public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook {
public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook {
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 100_000;
@ -156,6 +156,11 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
public final static int INITIAL_CAPACITY = Configurator.getIntValue("HSSFWorkbook.SheetInitialCapacity",3);
/**
* SpreadsheetVersion used by this workbook, EXCEL97 in every case where we need to read or write to disk
*/
public final SpreadsheetVersion spreadsheetVersion;
/**
* this is the reference to the low level Workbook object
*/
@ -220,8 +225,19 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
this(InternalWorkbook.createWorkbook());
}
protected HSSFWorkbook(final SpreadsheetVersion spreadsheetVersion) {
this(InternalWorkbook.createWorkbook(), spreadsheetVersion);
if(spreadsheetVersion == null)
throw new IllegalArgumentException("SpreadsheetVersion must be non-null");
}
private HSSFWorkbook(InternalWorkbook book) {
this(book, SpreadsheetVersion.EXCEL97);
}
private HSSFWorkbook(InternalWorkbook book, final SpreadsheetVersion spreadsheetVersion) {
super((DirectoryNode)null);
this.spreadsheetVersion = spreadsheetVersion;
workbook = book;
_sheets = new ArrayList<>(INITIAL_CAPACITY);
names = new ArrayList<>(INITIAL_CAPACITY);
@ -324,6 +340,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
throws IOException
{
super(directory);
spreadsheetVersion = SpreadsheetVersion.EXCEL97;
String workbookName = getWorkbookDirEntryName(directory);
this.preserveNodes = preserveNodes;
@ -2228,7 +2245,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
*/
@Override
public SpreadsheetVersion getSpreadsheetVersion() {
return SpreadsheetVersion.EXCEL97;
return this.spreadsheetVersion;
}
@Override