Compare commits

...

6 Commits

19 changed files with 215 additions and 60 deletions

View File

@ -70,6 +70,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.RecordAggregate.RecordVisitor;
import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock; 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.formula.FormulaShifter;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.PaneInformation; import org.apache.poi.ss.util.PaneInformation;
@ -444,9 +445,12 @@ public final class InternalSheet {
* @return Sheet object with all values set to defaults * @return Sheet object with all values set to defaults
*/ */
public static InternalSheet createSheet() { 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(); _mergedCellsTable = new MergedCellsTable();
List<RecordBase> records = new ArrayList<RecordBase>(32); List<RecordBase> records = new ArrayList<RecordBase>(32);
@ -487,7 +491,7 @@ public final class InternalSheet {
_columnInfos = columns; _columnInfos = columns;
_dimensions = createDimensions(); _dimensions = createDimensions();
records.add(_dimensions); records.add(_dimensions);
_rowsAggregate = new RowRecordsAggregate(); _rowsAggregate = new RowRecordsAggregate(spreadsheetVersion);
records.add(_rowsAggregate); records.add(_rowsAggregate);
// 'Sheet View Settings' // 'Sheet View Settings'
records.add(windowTwo = createWindowTwo()); records.add(windowTwo = createWindowTwo());

View File

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

View File

@ -42,13 +42,7 @@ import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.ss.formula.eval.ErrorEval; import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.ptg.ExpPtg; import org.apache.poi.ss.formula.ptg.ExpPtg;
import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.FormulaError;
import org.apache.poi.ss.usermodel.Hyperlink;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.util.CellAddress; import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
@ -70,11 +64,6 @@ import org.apache.poi.util.LocaleUtil;
*/ */
public class HSSFCell implements Cell { public class HSSFCell implements Cell {
private static final String FILE_FORMAT_NAME = "BIFF8"; 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_UNCHANGED = -1;
public final static short ENCODING_COMPRESSED_UNICODE = 0; public final static short ENCODING_COMPRESSED_UNICODE = 0;
@ -105,7 +94,7 @@ public class HSSFCell implements Cell {
*/ */
protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col) protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col)
{ {
checkBounds(col); checkBounds(col, book);
_stringValue = null; _stringValue = null;
_book = book; _book = book;
_sheet = sheet; _sheet = sheet;
@ -150,7 +139,7 @@ public class HSSFCell implements Cell {
protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col, protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col,
CellType type) CellType type)
{ {
checkBounds(col); checkBounds(col, book);
_cellType = CellType._NONE; // Force 'setCellType' to create a first Record _cellType = CellType._NONE; // Force 'setCellType' to create a first Record
_stringValue = null; _stringValue = null;
_book = book; _book = book;
@ -570,8 +559,8 @@ public class HSSFCell implements Cell {
return; return;
} }
if(value.length() > SpreadsheetVersion.EXCEL97.getMaxTextLength()){ if(value.length() > _book.getSpreadsheetVersion().getMaxTextLength()){
throw new IllegalArgumentException("The maximum length of cell contents (text) is 32,767 characters"); throw new IllegalArgumentException("The maximum length of cell contents (text) is "+_book.getSpreadsheetVersion().getMaxTextLength()+" characters");
} }
if (_cellType == CellType.FORMULA) { if (_cellType == CellType.FORMULA) {
@ -977,8 +966,10 @@ public class HSSFCell implements Cell {
/** /**
* @throws RuntimeException if the bounds are exceeded. * @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) { if (cellIndex < 0 || cellIndex > LAST_COLUMN_NUMBER) {
final String LAST_COLUMN_NAME = book.getSpreadsheetVersion().getLastColumnName();
throw new IllegalArgumentException("Invalid column index (" + cellIndex throw new IllegalArgumentException("Invalid column index (" + cellIndex
+ "). Allowable column range for " + FILE_FORMAT_NAME + " is (0.." + "). Allowable column range for " + FILE_FORMAT_NAME + " is (0.."
+ LAST_COLUMN_NUMBER + ") or ('A'..'" + LAST_COLUMN_NAME + "')"); + LAST_COLUMN_NUMBER + ") or ('A'..'" + LAST_COLUMN_NAME + "')");

View File

@ -296,7 +296,7 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
@Override @Override
public SpreadsheetVersion getSpreadsheetVersion(){ 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) * @throws IllegalArgumentException if the name is invalid or the name already exists (case-insensitive)
*/ */
public void setNameName(String nameName){ public void setNameName(String nameName){
validateName(nameName); validateName(nameName, _book.getSpreadsheetVersion());
InternalWorkbook wb = _book.getWorkbook(); InternalWorkbook wb = _book.getWorkbook();
_definedNameRec.setNameText(nameName); _definedNameRec.setNameText(nameName);
@ -178,7 +178,7 @@ public final class HSSFName implements Name {
* *
* @param name * @param name
*/ */
private static void validateName(String name) { private static void validateName(String name, SpreadsheetVersion spreadsheetVersion) {
if (name.length() == 0) { if (name.length() == 0) {
throw new IllegalArgumentException("Name cannot be blank"); throw new IllegalArgumentException("Name cannot be blank");
@ -212,7 +212,7 @@ public final class HSSFName implements Name {
if (name.matches("[A-Za-z]+\\d+")) { if (name.matches("[A-Za-z]+\\d+")) {
String col = name.replaceAll("\\d", ""); String col = name.replaceAll("\\d", "");
String row = name.replaceAll("[A-Za-z]", ""); 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"); throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be $A$1-style cell reference");
} }
} }

View File

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

View File

@ -123,7 +123,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createSheet() * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createSheet()
*/ */
protected HSSFSheet(HSSFWorkbook workbook) { protected HSSFSheet(HSSFWorkbook workbook) {
_sheet = InternalSheet.createSheet(); _sheet = InternalSheet.createSheet(workbook.getSpreadsheetVersion());
_rows = new TreeMap<Integer, HSSFRow>(); _rows = new TreeMap<Integer, HSSFRow>();
this._workbook = workbook; this._workbook = workbook;
this._book = workbook.getWorkbook(); this._book = workbook.getWorkbook();
@ -715,7 +715,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
if (region.getNumberOfCells() < 2) { if (region.getNumberOfCells() < 2) {
throw new IllegalArgumentException("Merged region " + region.formatAsString() + " must contain 2 or more cells"); throw new IllegalArgumentException("Merged region " + region.formatAsString() + " must contain 2 or more cells");
} }
region.validate(SpreadsheetVersion.EXCEL97); region.validate(_workbook.getSpreadsheetVersion());
if (validate) { if (validate) {
// throw IllegalStateException if the argument CellRangeAddress intersects with // throw IllegalStateException if the argument CellRangeAddress intersects with
@ -1453,7 +1453,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
*/ */
@Override @Override
public void showInPane(int toprow, int leftcol) { 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); if (toprow > maxrow) throw new IllegalArgumentException("Maximum row number is " + maxrow);
showInPane((short)toprow, (short)leftcol); showInPane((short)toprow, (short)leftcol);
@ -1530,10 +1530,10 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* *
* @param row the row number * @param row the row number
*/ */
private static int clip(int row) { private int clip(int row) {
return Math.min( return Math.min(
Math.max(0, row), Math.max(0, row),
SpreadsheetVersion.EXCEL97.getLastRowIndex()); _workbook.getSpreadsheetVersion().getLastRowIndex());
} }
/** /**
@ -1677,7 +1677,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
} }
} }
if (endRow + n > _lastrow) { if (endRow + n > _lastrow) {
_lastrow = Math.min(endRow + n, SpreadsheetVersion.EXCEL97.getLastRowIndex()); _lastrow = Math.min(endRow + n, _workbook.getSpreadsheetVersion().getLastRowIndex());
} }
} else { } else {
// Rows are moving up // Rows are moving up
@ -1687,7 +1687,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
if (endRow == _lastrow) { if (endRow == _lastrow) {
// Need to walk backward to find the last non-blank row // Need to walk backward to find the last non-blank row
// NOTE: n is always negative here // 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--) { for (int i = endRow - 1; i > endRow + n; i--) {
if (getRow(i) != null) { if (getRow(i) != null) {
_lastrow = i; _lastrow = i;
@ -1703,7 +1703,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
String sheetName = _workbook.getSheetName(sheetIndex); String sheetName = _workbook.getSheetName(sheetIndex);
short externSheetIndex = _book.checkExternSheet(sheetIndex); short externSheetIndex = _book.checkExternSheet(sheetIndex);
FormulaShifter shifter = FormulaShifter.createForRowShift( FormulaShifter shifter = FormulaShifter.createForRowShift(
externSheetIndex, sheetName, startRow, endRow, n, SpreadsheetVersion.EXCEL97); externSheetIndex, sheetName, startRow, endRow, n, _workbook.getSpreadsheetVersion());
_sheet.updateFormulasAfterCellShift(shifter, externSheetIndex); _sheet.updateFormulasAfterCellShift(shifter, externSheetIndex);
int nSheets = _workbook.getNumberOfSheets(); int nSheets = _workbook.getNumberOfSheets();
@ -1923,7 +1923,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
@Override @Override
public void setColumnBreak(int column) { public void setColumnBreak(int column) {
validateColumn((short) 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());
} }
/** /**
@ -1953,7 +1953,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @param row the index of the row to validate, zero-based * @param row the index of the row to validate, zero-based
*/ */
protected void validateRow(int row) { 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 > maxrow) throw new IllegalArgumentException("Maximum row number is " + maxrow);
if (row < 0) throw new IllegalArgumentException("Minumum row number is 0"); if (row < 0) throw new IllegalArgumentException("Minumum row number is 0");
} }
@ -1964,7 +1964,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @param column the index of the column to validate, zero-based * @param column the index of the column to validate, zero-based
*/ */
protected void validateColumn(int column) { 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 > maxcol) throw new IllegalArgumentException("Maximum column number is " + maxcol);
if (column < 0) throw new IllegalArgumentException("Minimum column number is 0"); if (column < 0) throw new IllegalArgumentException("Minimum column number is 0");
} }
@ -2496,8 +2496,8 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
private void setRepeatingRowsAndColumns( private void setRepeatingRowsAndColumns(
CellRangeAddress rowDef, CellRangeAddress colDef) { CellRangeAddress rowDef, CellRangeAddress colDef) {
int sheetIndex = _workbook.getSheetIndex(this); int sheetIndex = _workbook.getSheetIndex(this);
int maxRowIndex = SpreadsheetVersion.EXCEL97.getLastRowIndex(); final int maxRowIndex = _workbook.getSpreadsheetVersion().getLastRowIndex();
int maxColIndex = SpreadsheetVersion.EXCEL97.getLastColumnIndex(); final int maxColIndex = _workbook.getSpreadsheetVersion().getLastColumnIndex();
int col1 = -1; int col1 = -1;
int col2 = -1; int col2 = -1;
@ -2582,8 +2582,8 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
return null; return null;
} }
int maxRowIndex = SpreadsheetVersion.EXCEL97.getLastRowIndex(); final int maxRowIndex = _workbook.getSpreadsheetVersion().getLastRowIndex();
int maxColIndex = SpreadsheetVersion.EXCEL97.getLastColumnIndex(); final int maxColIndex = _workbook.getSpreadsheetVersion().getLastColumnIndex();
for (Ptg ptg : nameDefinition) { for (Ptg ptg : nameDefinition) {

View File

@ -174,7 +174,7 @@ public final class HSSFSheetConditionalFormatting implements SheetConditionalFor
if (regions == null) { if (regions == null) {
throw new IllegalArgumentException("regions must not be 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) { if (cfRules == null) {
throw new IllegalArgumentException("cfRules must not be null"); throw new IllegalArgumentException("cfRules must not be null");

View File

@ -123,6 +123,9 @@ import org.apache.poi.util.Removal;
public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook { public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook {
private static final Pattern COMMA_PATTERN = Pattern.compile(","); private static final Pattern COMMA_PATTERN = Pattern.compile(",");
// org.apache.poi.hssf.usermodel.HSSFWorkbook.spreadsheetVersion
public final SpreadsheetVersion spreadsheetVersion;
/** /**
* The maximum number of cell styles in a .xls workbook. * The maximum number of cell styles in a .xls workbook.
* The 'official' limit is 4,000, but POI allows a slightly larger number. * The 'official' limit is 4,000, but POI allows a slightly larger number.
@ -207,8 +210,19 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
this(InternalWorkbook.createWorkbook()); this(InternalWorkbook.createWorkbook());
} }
public HSSFWorkbook(final SpreadsheetVersion spreadsheetVersion) {
this(InternalWorkbook.createWorkbook(), spreadsheetVersion);
if(spreadsheetVersion == null)
throw new IllegalArgumentException("SpreadsheetVersion must be non-null");
}
private HSSFWorkbook(InternalWorkbook book) { private HSSFWorkbook(InternalWorkbook book) {
this(book, SpreadsheetVersion.EXCEL97);
}
private HSSFWorkbook(InternalWorkbook book, final SpreadsheetVersion spreadsheetVersion) {
super((DirectoryNode)null); super((DirectoryNode)null);
this.spreadsheetVersion = spreadsheetVersion;
workbook = book; workbook = book;
_sheets = new ArrayList<HSSFSheet>(INITIAL_CAPACITY); _sheets = new ArrayList<HSSFSheet>(INITIAL_CAPACITY);
names = new ArrayList<HSSFName>(INITIAL_CAPACITY); names = new ArrayList<HSSFName>(INITIAL_CAPACITY);
@ -328,6 +342,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
throws IOException throws IOException
{ {
super(directory); super(directory);
spreadsheetVersion = SpreadsheetVersion.EXCEL97;
String workbookName = getWorkbookDirEntryName(directory); String workbookName = getWorkbookDirEntryName(directory);
this.preserveNodes = preserveNodes; this.preserveNodes = preserveNodes;
@ -1363,6 +1378,12 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
super.close(); super.close();
} }
protected void validateSpreadsheetVersionWritePossible() throws IllegalStateException {
if (this.spreadsheetVersion != SpreadsheetVersion.EXCEL97) {
throw new IllegalStateException("SpreadsheetVersion not EXCEL97, cannot write file meant only for in-memory calculations");
}
}
/** /**
* Write out this workbook to the currently open {@link File} via the * Write out this workbook to the currently open {@link File} via the
* writeable {@link POIFSFileSystem} it was opened as. * writeable {@link POIFSFileSystem} it was opened as.
@ -1375,6 +1396,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
*/ */
@Override @Override
public void write() throws IOException { public void write() throws IOException {
validateSpreadsheetVersionWritePossible();
validateInPlaceWritePossible(); validateInPlaceWritePossible();
final DirectoryNode dir = getDirectory(); final DirectoryNode dir = getDirectory();
@ -1408,6 +1430,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
*/ */
@Override @Override
public void write(File newFile) throws IOException { public void write(File newFile) throws IOException {
validateSpreadsheetVersionWritePossible();
POIFSFileSystem fs = POIFSFileSystem.create(newFile); POIFSFileSystem fs = POIFSFileSystem.create(newFile);
try { try {
write(fs); write(fs);
@ -1434,6 +1457,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
*/ */
@Override @Override
public void write(OutputStream stream) throws IOException { public void write(OutputStream stream) throws IOException {
validateSpreadsheetVersionWritePossible();
NPOIFSFileSystem fs = new NPOIFSFileSystem(); NPOIFSFileSystem fs = new NPOIFSFileSystem();
try { try {
write(fs); write(fs);
@ -1445,6 +1469,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
/** Writes the workbook out to a brand new, empty POIFS */ /** Writes the workbook out to a brand new, empty POIFS */
private void write(NPOIFSFileSystem fs) throws IOException { private void write(NPOIFSFileSystem fs) throws IOException {
validateSpreadsheetVersionWritePossible();
// For tracking what we've written out, used if we're // For tracking what we've written out, used if we're
// going to be preserving nodes // going to be preserving nodes
List<String> excepts = new ArrayList<String>(1); List<String> excepts = new ArrayList<String>(1);
@ -2269,6 +2294,27 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
return recalc != null && recalc.getEngineId() != 0; return recalc != null && recalc.getEngineId() != 0;
} }
/**
* Whether a call to {@link HSSFCell#setCellFormula(String)} will validate the formula or not.
*
* @param value true if the application will validate the formula is correct
* @since 3.17
*/
@Override
public void setCellFormulaValidation(final boolean value) {
// currently {@link HSSFCell#setCellFormula(String)} does no validation anyway, ignore
}
/**
* Whether a call to {@link HSSFCell#setCellFormula(String)} will validate the formula or not.
*
* @since 3.17
*/
@Override
public boolean getCellFormulaValidation() {
return false;
}
/** /**
* Changes an external referenced file to another file. * Changes an external referenced file to another file.
* A formula in Excel which references a cell in another file is saved in two parts: * A formula in Excel which references a cell in another file is saved in two parts:
@ -2304,6 +2350,6 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
*/ */
@Override @Override
public SpreadsheetVersion getSpreadsheetVersion() { public SpreadsheetVersion getSpreadsheetVersion() {
return SpreadsheetVersion.EXCEL97; return this.spreadsheetVersion;
} }
} }

View File

@ -35,15 +35,17 @@ import org.apache.poi.util.LittleEndianOutput;
*/ */
public class Formula { public class Formula {
private static final Formula EMPTY = new Formula(new byte[0], 0); private static final Formula EMPTY = new Formula(new byte[0], 0, new Ptg[0]);
/** immutable */ /** immutable */
private final byte[] _byteEncoding; private final byte[] _byteEncoding;
private final int _encodedTokenLen; private final int _encodedTokenLen;
private Ptg[] _cache;
private Formula(byte[] byteEncoding, int encodedTokenLen) { private Formula(byte[] byteEncoding, int encodedTokenLen, Ptg[] cache) {
_byteEncoding = byteEncoding.clone(); _byteEncoding = byteEncoding.clone();
_encodedTokenLen = encodedTokenLen; _encodedTokenLen = encodedTokenLen;
_cache = cache;
// if (false) { // set to true to eagerly check Ptg decoding // if (false) { // set to true to eagerly check Ptg decoding
// LittleEndianByteArrayInputStream in = new LittleEndianByteArrayInputStream(byteEncoding); // LittleEndianByteArrayInputStream in = new LittleEndianByteArrayInputStream(byteEncoding);
// Ptg.readTokens(encodedTokenLen, in); // Ptg.readTokens(encodedTokenLen, in);
@ -74,12 +76,14 @@ public class Formula {
public static Formula read(int encodedTokenLen, LittleEndianInput in, int totalEncodedLen) { public static Formula read(int encodedTokenLen, LittleEndianInput in, int totalEncodedLen) {
byte[] byteEncoding = new byte[totalEncodedLen]; byte[] byteEncoding = new byte[totalEncodedLen];
in.readFully(byteEncoding); in.readFully(byteEncoding);
return new Formula(byteEncoding, encodedTokenLen); return new Formula(byteEncoding, encodedTokenLen, null);
} }
public Ptg[] getTokens() { public Ptg[] getTokens() {
if(_cache != null)
return _cache;
LittleEndianInput in = new LittleEndianByteArrayInputStream(_byteEncoding); LittleEndianInput in = new LittleEndianByteArrayInputStream(_byteEncoding);
return Ptg.readTokens(_encodedTokenLen, in); return _cache = Ptg.readTokens(_encodedTokenLen, in);
} }
/** /**
* Writes The formula encoding is includes: * Writes The formula encoding is includes:
@ -140,7 +144,7 @@ public class Formula {
byte[] encodedData = new byte[totalSize]; byte[] encodedData = new byte[totalSize];
Ptg.serializePtgs(ptgs, encodedData, 0); Ptg.serializePtgs(ptgs, encodedData, 0);
int encodedTokenLen = Ptg.getEncodedSizeWithoutArrayData(ptgs); int encodedTokenLen = Ptg.getEncodedSizeWithoutArrayData(ptgs);
return new Formula(encodedData, encodedTokenLen); return new Formula(encodedData, encodedTokenLen, ptgs); // todo: copy ptgs? don't think we need to
} }
/** /**
* Gets the {@link Ptg} array from the supplied {@link Formula}. * Gets the {@link Ptg} array from the supplied {@link Formula}.

View File

@ -28,7 +28,7 @@ import org.apache.poi.ss.util.CellReference;
* Optimization - compacts many blank cell references used by a single formula. * Optimization - compacts many blank cell references used by a single formula.
*/ */
final class FormulaUsedBlankCellSet { final class FormulaUsedBlankCellSet {
public static final class BookSheetKey { public static final class BookSheetKey {// 253mb 10,567,793
private final int _bookIndex; private final int _bookIndex;
private final int _sheetIndex; private final int _sheetIndex;

View File

@ -44,7 +44,7 @@ public final class NamePtg extends OperandPtg implements WorkbookDependentFormul
/** Creates new NamePtg */ /** Creates new NamePtg */
public NamePtg(LittleEndianInput in) { public NamePtg(LittleEndianInput in) {
field_1_label_index = in.readShort(); field_1_label_index = in.readUShort();
field_2_zero = in.readShort(); field_2_zero = in.readShort();
} }

View File

@ -686,4 +686,19 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
* @throws IOException if the object can't be embedded * @throws IOException if the object can't be embedded
*/ */
int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException; int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException;
/**
* Whether a call to {@link Cell#setCellFormula(String)} will validate the formula or not.
*
* @param value true if the application will validate the formula is correct
* @since 3.17
*/
void setCellFormulaValidation(boolean value);
/**
* Whether a call to {@link Cell#setCellFormula(String)} will validate the formula or not.
*
* @since 3.17
*/
boolean getCellFormulaValidation();
} }

View File

@ -88,9 +88,9 @@ public class CellReference {
* digits or dot. (They can even end in dot). * digits or dot. (They can even end in dot).
*/ */
private static final Pattern NAMED_RANGE_NAME_PATTERN = Pattern.compile("[_A-Z][_.A-Z0-9]*", Pattern.CASE_INSENSITIVE); private static final Pattern NAMED_RANGE_NAME_PATTERN = Pattern.compile("[_A-Z][_.A-Z0-9]*", Pattern.CASE_INSENSITIVE);
//private static final String BIFF8_LAST_COLUMN = SpreadsheetVersion.EXCEL97.getLastColumnName(); //private static final String BIFF8_LAST_COLUMN = org.apache.poi.hssf.usermodel.HSSFWorkbook.spreadsheetVersion.getLastColumnName();
//private static final int BIFF8_LAST_COLUMN_TEXT_LEN = BIFF8_LAST_COLUMN.length(); //private static final int BIFF8_LAST_COLUMN_TEXT_LEN = BIFF8_LAST_COLUMN.length();
//private static final String BIFF8_LAST_ROW = String.valueOf(SpreadsheetVersion.EXCEL97.getMaxRows()); //private static final String BIFF8_LAST_ROW = String.valueOf(org.apache.poi.hssf.usermodel.HSSFWorkbook.spreadsheetVersion.getMaxRows());
//private static final int BIFF8_LAST_ROW_TEXT_LEN = BIFF8_LAST_ROW.length(); //private static final int BIFF8_LAST_ROW_TEXT_LEN = BIFF8_LAST_ROW.length();
// FIXME: _sheetName may be null, depending on the entry point. // FIXME: _sheetName may be null, depending on the entry point.

View File

@ -1363,6 +1363,27 @@ public class SXSSFWorkbook implements Workbook {
public int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException { public int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException {
return _wb.addOlePackage(oleData, label, fileName, command); return _wb.addOlePackage(oleData, label, fileName, command);
} }
/**
* Whether a call to {@link SXSSFCell#setCellFormula(String)} will validate the formula or not.
*
* @param value true if the application will validate the formula is correct
* @since 3.17
*/
@Override
public void setCellFormulaValidation(final boolean value) {
// currently {@link SXSSFCell#setCellFormula(String)} does no validation anyway, ignore
}
/**
* Whether a call to {@link SXSSFCell#setCellFormula(String)} will validate the formula or not.
*
* @since 3.17
*/
@Override
public boolean getCellFormulaValidation() {
return false;
}
//end of interface implementation //end of interface implementation
} }

View File

@ -561,9 +561,11 @@ public final class XSSFCell implements Cell {
return; return;
} }
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb); if(wb.getCellFormulaValidation()) {
//validate through the FormulaParser XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()), getRowIndex()); //validate through the FormulaParser
FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()), getRowIndex());
}
CTCellFormula f = CTCellFormula.Factory.newInstance(); CTCellFormula f = CTCellFormula.Factory.newInstance();
f.setStringValue(formula); f.setStringValue(formula);

View File

@ -402,7 +402,7 @@ public final class XSSFName implements Name {
if (name.matches("[A-Za-z]+\\d+")) { if (name.matches("[A-Za-z]+\\d+")) {
String col = name.replaceAll("\\d", ""); String col = name.replaceAll("\\d", "");
String row = name.replaceAll("[A-Za-z]", ""); String row = name.replaceAll("[A-Za-z]", "");
if (CellReference.cellReferenceIsWithinRange(col, row, SpreadsheetVersion.EXCEL97)) { if (CellReference.cellReferenceIsWithinRange(col, row, SpreadsheetVersion.EXCEL97)) { // todo: is EXCEL97 correct? sounds wrong...
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be $A$1-style cell reference"); throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be $A$1-style cell reference");
} }
} }

View File

@ -208,6 +208,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
*/ */
private MissingCellPolicy _missingCellPolicy = MissingCellPolicy.RETURN_NULL_AND_BLANK; private MissingCellPolicy _missingCellPolicy = MissingCellPolicy.RETURN_NULL_AND_BLANK;
/**
* Whether a call to {@link XSSFCell#setCellFormula(String)} will validate the formula or not.
*/
private boolean cellFormulaValidation = true;
/** /**
* array of pictures for this workbook * array of pictures for this workbook
*/ */
@ -2470,4 +2475,25 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
return oleId; return oleId;
} }
/**
* Whether a call to {@link XSSFCell#setCellFormula(String)} will validate the formula or not.
*
* @param value true if the application will validate the formula is correct
* @since 3.17
*/
@Override
public void setCellFormulaValidation(final boolean value) {
this.cellFormulaValidation = value;
}
/**
* Whether a call to {@link XSSFCell#setCellFormula(String)} will validate the formula or not.
*
* @since 3.17
*/
@Override
public boolean getCellFormulaValidation() {
return this.cellFormulaValidation;
}
} }

View File

@ -23,6 +23,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -30,6 +31,7 @@ import java.util.List;
import org.apache.poi.common.usermodel.HyperlinkType; import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.hssf.HSSFITestDataProvider; import org.apache.poi.hssf.HSSFITestDataProvider;
import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.FormulaParseException;
import org.apache.poi.ss.usermodel.BaseTestXCell; import org.apache.poi.ss.usermodel.BaseTestXCell;
import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
@ -179,6 +181,42 @@ public final class TestXSSFCell extends BaseTestXCell {
assertEquals(CellType.BLANK, cell.getCellTypeEnum()); assertEquals(CellType.BLANK, cell.getCellTypeEnum());
assertEquals(STCellType.N, ctCell.getT()); assertEquals(STCellType.N, ctCell.getT());
assertEquals("", cell.getStringCellValue()); assertEquals("", cell.getStringCellValue());
// check behavior with setCellFormulaValidation
final String invalidFormula = "A", validFormula = "A2";
FormulaParseException fpe = null;
// check that default is true
assertTrue(wb.getCellFormulaValidation());
// check that valid formula does not throw exception
try {
cell.setCellFormula(validFormula);
} catch(FormulaParseException e) {
fpe = e;
}
assertNull(fpe);
// check that invalid formula does throw exception
try {
cell.setCellFormula(invalidFormula);
} catch(FormulaParseException e) {
fpe = e;
}
assertNotNull(fpe);
fpe = null;
// set cell formula validation to false
wb.setCellFormulaValidation(false);
assertFalse(wb.getCellFormulaValidation());
// check that neither valid nor invalid formula throw an exception
try {
cell.setCellFormula(validFormula);
cell.setCellFormula(invalidFormula);
} catch(FormulaParseException e) {
fpe = e;
}
assertNull(fpe);
} finally { } finally {
wb.close(); wb.close();
} }