Bug 51448 - Avoid exception when evaluating workbooks with more than 256 sheets

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1142181 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2011-07-02 09:05:35 +00:00
parent 3fc8c3b1b7
commit 16bee58c25
4 changed files with 172 additions and 8 deletions

View File

@ -34,6 +34,7 @@
<changes>
<release version="3.8-beta4" date="2011-??-??">
<action dev="poi-developers" type="fix">51448 - Avoid exception when evaluating workbooks with more than 256 sheets </action>
<action dev="poi-developers" type="fix">51458 - Correct BitField wrapping when setting large values</action>
<action dev="poi-developers" type="add">51460 - Improve HSSF performance when loading very long rows, by switching the CellValue array to an iterator</action>
<action dev="poi-developers" type="fix">51444 - Prevent corrupted output when saving files created by LibreOffice 3.3 </action>

View File

@ -28,7 +28,7 @@ final class PlainCellCache {
public static final class Loc {
private final int _bookSheetColumn;
private final long _bookSheetColumn;
private final int _rowIndex;
@ -37,18 +37,19 @@ final class PlainCellCache {
_rowIndex = rowIndex;
}
public static int toBookSheetColumn(int bookIndex, int sheetIndex, int columnIndex) {
return ((bookIndex & 0x00FF) << 24) + ((sheetIndex & 0x00FF) << 16)
+ ((columnIndex & 0xFFFF) << 0);
public static long toBookSheetColumn(int bookIndex, int sheetIndex, int columnIndex) {
return ((bookIndex & 0xFFFFl) << 48) +
((sheetIndex & 0xFFFFl) << 32) +
((columnIndex & 0xFFFFl) << 0);
}
public Loc(int bookSheetColumn, int rowIndex) {
public Loc(long bookSheetColumn, int rowIndex) {
_bookSheetColumn = bookSheetColumn;
_rowIndex = rowIndex;
}
public int hashCode() {
return _bookSheetColumn + 17 * _rowIndex;
return (int)(_bookSheetColumn ^ (_bookSheetColumn >>> 32)) + 17 * _rowIndex;
}
public boolean equals(Object obj) {
@ -60,9 +61,18 @@ final class PlainCellCache {
public int getRowIndex() {
return _rowIndex;
}
public int getColumnIndex() {
return _bookSheetColumn & 0x000FFFF;
return (int)(_bookSheetColumn & 0x000FFFF);
}
public int getSheetIndex() {
return (int)((_bookSheetColumn >> 32) & 0xFFFF);
}
public int getBookIndex() {
return (int)((_bookSheetColumn >> 48) & 0xFFFF);
}
}
private Map<Loc, PlainValueCellCacheEntry> _plainValueEntriesByLoc;

View File

@ -46,7 +46,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.ss.formula.IEvaluationListener.ICacheEntry;
import org.apache.poi.ss.formula.PlainCellCache.Loc;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.*;
/**
* Tests {@link org.apache.poi.ss.formula.EvaluationCache}. Makes sure that where possible (previously calculated) cached
@ -696,4 +696,82 @@ public class TestEvaluationCache extends TestCase {
ps.println('"' + log[i] + "\",");
}
}
private static void testPlainValueCache(Workbook wb, int numberOfSheets) {
Row row;
Cell cell;
//create summary sheet
Sheet summary = wb.createSheet("summary");
wb.setActiveSheet(wb.getSheetIndex(summary));
//formula referring all sheets created below
row = summary.createRow(0);
Cell summaryCell = row.createCell(0);
summaryCell.setCellFormula("SUM(A2:A" + (numberOfSheets + 2) + ")");
//create sheets with cells having (different) numbers
// and add a row to summary
for (int i = 1; i < numberOfSheets; i++) {
Sheet sheet = wb.createSheet("new" + i);
row = sheet.createRow(0);
cell = row.createCell(0);
cell.setCellValue(i);
row = summary.createRow(i);
cell = row.createCell(0);
cell.setCellFormula("new" + i + "!A1");
}
//calculate
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateFormulaCell(summaryCell);
}
public void testPlainValueCache() {
Workbook wb = new HSSFWorkbook();
int numberOfSheets = 4098; // Bug 51448 reported that Evaluation Cache got messed up after 256 sheets
Row row;
Cell cell;
//create summary sheet
Sheet summary = wb.createSheet("summary");
wb.setActiveSheet(wb.getSheetIndex(summary));
//formula referring all sheets created below
row = summary.createRow(0);
Cell summaryCell = row.createCell(0);
summaryCell.setCellFormula("SUM(A2:A" + (numberOfSheets + 2) + ")");
//create sheets with cells having (different) numbers
// and add a row to summary
for (int i = 1; i < numberOfSheets; i++) {
Sheet sheet = wb.createSheet("new" + i);
row = sheet.createRow(0);
cell = row.createCell(0);
cell.setCellValue(i);
row = summary.createRow(i);
cell = row.createCell(0);
cell.setCellFormula("new" + i + "!A1");
}
//calculate
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateFormulaCell(summaryCell);
assertEquals(8394753.0, summaryCell.getNumericCellValue());
}
}

View File

@ -0,0 +1,75 @@
/*
* ====================================================================
* 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.formula;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.model.HSSFFormulaParser;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.ss.formula.IEvaluationListener.ICacheEntry;
import org.apache.poi.ss.formula.PlainCellCache.Loc;
import org.apache.poi.ss.formula.eval.*;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.usermodel.CellValue;
import java.io.PrintStream;
import java.util.*;
/**
* @author Yegor Kozlov
*/
public class TestPlainCellCache extends TestCase {
/**
*
*/
public void testLoc(){
PlainCellCache cache = new PlainCellCache();
for (int bookIndex = 0; bookIndex < 0x1000; bookIndex += 0x100) {
for (int sheetIndex = 0; sheetIndex < 0x1000; sheetIndex += 0x100) {
for (int rowIndex = 0; rowIndex < 0x100000; rowIndex += 0x1000) {
for (int columnIndex = 0; columnIndex < 0x4000; columnIndex += 0x100) {
Loc loc = new Loc(bookIndex, sheetIndex, rowIndex, columnIndex);
assertEquals(bookIndex, loc.getBookIndex());
assertEquals(sheetIndex, loc.getSheetIndex());
assertEquals(rowIndex, loc.getRowIndex());
assertEquals(columnIndex, loc.getColumnIndex());
Loc sameLoc = new Loc(bookIndex, sheetIndex, rowIndex, columnIndex);
assertEquals(loc.hashCode(), sameLoc.hashCode());
assertTrue(loc.equals(sameLoc));
assertNull(cache.get(loc));
PlainValueCellCacheEntry entry = new PlainValueCellCacheEntry(new NumberEval(0));
cache.put(loc, entry);
assertSame(entry, cache.get(loc));
cache.remove(loc);
assertNull(cache.get(loc));
cache.put(loc, entry);
}
cache.clear();
}
}
}
}
}