From 9539c626a9d7c5450ef17202f7f237e6cf79f01a Mon Sep 17 00:00:00 2001 From: Javen O'Neal Date: Thu, 7 Jul 2016 22:22:10 +0000 Subject: [PATCH] bug 59814: clear evaluation workbook and evaluation sheet caches. Patch from Greg Woolsey. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1751836 13f79535-47bb-0310-9956-ffa450edef68 --- .../hssf/usermodel/HSSFEvaluationSheet.java | 4 ++ .../usermodel/HSSFEvaluationWorkbook.java | 4 ++ .../poi/ss/formula/EvaluationSheet.java | 8 +++ .../poi/ss/formula/EvaluationWorkbook.java | 7 +++ .../poi/ss/formula/WorkbookEvaluator.java | 1 + .../eval/forked/ForkedEvaluationSheet.java | 8 +++ .../eval/forked/ForkedEvaluationWorkbook.java | 8 +++ .../xssf/streaming/SXSSFEvaluationSheet.java | 4 ++ .../usermodel/BaseXSSFEvaluationWorkbook.java | 4 ++ .../xssf/usermodel/XSSFEvaluationSheet.java | 4 ++ .../usermodel/XSSFEvaluationWorkbook.java | 5 ++ .../ss/formula/TestStructuredReferences.java | 52 +++++++++++++++++- .../spreadsheet/StructuredReferences.xlsx | Bin 10185 -> 10459 bytes 13 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationSheet.java index 3497539be..0af2c842e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationSheet.java @@ -45,4 +45,8 @@ final class HSSFEvaluationSheet implements EvaluationSheet { } return new HSSFEvaluationCell(cell, this); } + + public void clearAllCachedResultValues() { + // nothing to do + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java index 81eef9005..61984497e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java @@ -64,6 +64,10 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E _iBook = book.getWorkbook(); } + public void clearAllCachedResultValues() { + // nothing to do + } + @Override public HSSFName createName() { return _uBook.createName(); diff --git a/src/java/org/apache/poi/ss/formula/EvaluationSheet.java b/src/java/org/apache/poi/ss/formula/EvaluationSheet.java index 5dec3aacd..3cc292dc5 100644 --- a/src/java/org/apache/poi/ss/formula/EvaluationSheet.java +++ b/src/java/org/apache/poi/ss/formula/EvaluationSheet.java @@ -30,4 +30,12 @@ public interface EvaluationSheet { * @return null if there is no cell at the specified coordinates */ EvaluationCell getCell(int rowIndex, int columnIndex); + + /** + * Propagated from {@link EvaluationWorkbook#clearAllCachedResultValues()} to clear locally cached data. + * + * @see WorkbookEvaluator#clearAllCachedResultValues() + * @see EvaluationWorkbook#clearAllCachedResultValues() + */ + public void clearAllCachedResultValues(); } diff --git a/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java b/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java index b0a3c7606..fc4cfb32b 100644 --- a/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java +++ b/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java @@ -73,6 +73,13 @@ public interface EvaluationWorkbook { String resolveNameXText(NameXPtg ptg); Ptg[] getFormulaTokens(EvaluationCell cell); UDFFinder getUDFFinder(); + + /** + * Propagated from {@link WorkbookEvaluator#clearAllCachedResultValues()} to clear locally cached data. + * Implementations must call the same method on all referenced {@link EvaluationSheet} instances, as well as clearing local caches. + * @see WorkbookEvaluator#clearAllCachedResultValues() + */ + public void clearAllCachedResultValues(); class ExternalSheet { private final String _workbookName; diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java index d1e7d8b22..4ed054467 100644 --- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java @@ -208,6 +208,7 @@ public final class WorkbookEvaluator { public void clearAllCachedResultValues() { _cache.clear(); _sheetIndexesBySheet.clear(); + _workbook.clearAllCachedResultValues(); } /** diff --git a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationSheet.java b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationSheet.java index 886919fe6..fcf00a488 100644 --- a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationSheet.java +++ b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationSheet.java @@ -101,6 +101,14 @@ final class ForkedEvaluationSheet implements EvaluationSheet { return mewb.getSheetIndex(_masterSheet); } + /* (non-Javadoc) + * leave the map alone, if it needs resetting, reusing this class is probably a bad idea. + * @see org.apache.poi.ss.formula.EvaluationSheet#clearAllCachedResultValues() + */ + public void clearAllCachedResultValues() { + _masterSheet.clearAllCachedResultValues(); + } + private static final class RowColKey implements Comparable{ private final int _rowIndex; private final int _columnIndex; diff --git a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java index b936a5476..5e1da9bdc 100644 --- a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java +++ b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java @@ -136,4 +136,12 @@ final class ForkedEvaluationWorkbook implements EvaluationWorkbook { public UDFFinder getUDFFinder(){ return _masterBook.getUDFFinder(); } + + /* (non-Javadoc) + * leave the map alone, if it needs resetting, reusing this class is probably a bad idea. + * @see org.apache.poi.ss.formula.EvaluationSheet#clearAllCachedResultValues() + */ + public void clearAllCachedResultValues() { + _masterBook.clearAllCachedResultValues(); + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFEvaluationSheet.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFEvaluationSheet.java index c5d809cad..f2c11953f 100644 --- a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFEvaluationSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFEvaluationSheet.java @@ -47,4 +47,8 @@ final class SXSSFEvaluationSheet implements EvaluationSheet { } return new SXSSFEvaluationCell(cell, this); } + + public void clearAllCachedResultValues() { + // nothing to do + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java index 44b182b4d..8f0eac330 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java @@ -56,6 +56,10 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork _uBook = book; } + public void clearAllCachedResultValues() { + _tableCache = null; + } + private int convertFromExternalSheetIndex(int externSheetIndex) { return externSheetIndex; } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationSheet.java index 0286740e0..78aa8f86c 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationSheet.java @@ -41,6 +41,10 @@ final class XSSFEvaluationSheet implements EvaluationSheet { return _xs; } + public void clearAllCachedResultValues() { + _cellCache = null; + } + public EvaluationCell getCell(int rowIndex, int columnIndex) { // cache for performance: ~30% speedup due to caching if (_cellCache == null) { diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java index 1e849b673..60f99676c 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java @@ -40,6 +40,11 @@ public final class XSSFEvaluationWorkbook extends BaseXSSFEvaluationWorkbook { super(book); } + public void clearAllCachedResultValues() { + super.clearAllCachedResultValues(); + _sheetCache = null; + } + @Override public int getSheetIndex(EvaluationSheet evalSheet) { XSSFSheet sheet = ((XSSFEvaluationSheet)evalSheet).getXSSFSheet(); diff --git a/src/ooxml/testcases/org/apache/poi/ss/formula/TestStructuredReferences.java b/src/ooxml/testcases/org/apache/poi/ss/formula/TestStructuredReferences.java index fd598a479..4e176257f 100644 --- a/src/ooxml/testcases/org/apache/poi/ss/formula/TestStructuredReferences.java +++ b/src/ooxml/testcases/org/apache/poi/ss/formula/TestStructuredReferences.java @@ -18,17 +18,22 @@ package org.apache.poi.ss.formula; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.CellValue; import org.apache.poi.ss.usermodel.FormulaEvaluator; +import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Table; +import org.apache.poi.ss.util.AreaReference; +import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFTable; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Test; @@ -63,8 +68,40 @@ public class TestStructuredReferences { try { final FormulaEvaluator eval = new XSSFFormulaEvaluator(wb); - confirm(eval, wb.getSheet("Table").getRow(5).getCell(0), 49); - confirm(eval, wb.getSheet("Formulas").getRow(0).getCell(0), 209); + final XSSFSheet tableSheet = wb.getSheet("Table"); + final XSSFSheet formulaSheet = wb.getSheet("Formulas"); + + confirm(eval, tableSheet.getRow(5).getCell(0), 49); + confirm(eval, formulaSheet.getRow(0).getCell(0), 209); + confirm(eval, formulaSheet.getRow(1).getCell(0), "one"); + + // test changing a table value, to see if the caches are properly cleared + // Issue 59814 + + // this test passes before the fix for 59814 + tableSheet.getRow(1).getCell(1).setCellValue("ONEA"); + confirm(eval, formulaSheet.getRow(1).getCell(0), "ONEA"); + + // test adding a row to a table, issue 59814 + Row newRow = tableSheet.getRow(7); + if (newRow == null) newRow = tableSheet.createRow(7); + newRow.createCell(0, CellType.FORMULA).setCellFormula("\\_Prime.1[[#This Row],[@Number]]*\\_Prime.1[[#This Row],[@Number]]"); + newRow.createCell(1, CellType.STRING).setCellValue("thirteen"); + newRow.createCell(2, CellType.NUMERIC).setCellValue(13); + + // update Table + final XSSFTable table = wb.getTable("\\_Prime.1"); + final AreaReference newArea = new AreaReference(table.getStartCellReference(), new CellReference(table.getEndRowIndex() + 1, table.getEndColIndex())); + String newAreaStr = newArea.formatAsString(); + table.getCTTable().setRef(newAreaStr); + table.getCTTable().getAutoFilter().setRef(newAreaStr); + table.updateHeaders(); + table.updateReferences(); + + // these fail before the fix for 59814 + confirm(eval, tableSheet.getRow(7).getCell(0), 13*13); + confirm(eval, formulaSheet.getRow(0).getCell(0), 209 + 13*13); + } finally { wb.close(); } @@ -78,4 +115,13 @@ public class TestStructuredReferences { } assertEquals(expectedResult, cv.getNumberValue(), 0.0); } + + private static void confirm(FormulaEvaluator fe, Cell cell, String expectedResult) { + fe.clearAllCachedResultValues(); + CellValue cv = fe.evaluate(cell); + if (cv.getCellType() != CellType.STRING) { + fail("expected String cell type but got " + cv.formatAsString()); + } + assertEquals(expectedResult, cv.getStringValue()); + } } diff --git a/test-data/spreadsheet/StructuredReferences.xlsx b/test-data/spreadsheet/StructuredReferences.xlsx index 54cf75234eebdcb10af2db597f9e49b8ccc8db53..90a470a93f0934c29c73970759323864b66d3073 100644 GIT binary patch delta 4523 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 delta 4224 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