From 1f15f25cb8689c56dfd619c477519a2073ca9d27 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Sun, 31 Jul 2016 17:19:27 +0000 Subject: [PATCH] Bug 59736: Incorrect evaluation of SUBTOTAL with composite interval git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1754674 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/ss/formula/LazyRefEval.java | 19 ++-- .../poi/ss/formula/functions/Subtotal.java | 30 +++++- .../usermodel/TestXSSFFormulaEvaluation.java | 24 ++++- .../ss/formula/functions/TestSubtotal.java | 97 ++++++++++++++++-- test-data/spreadsheet/59736.xlsx | Bin 0 -> 8752 bytes 5 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 test-data/spreadsheet/59736.xlsx diff --git a/src/java/org/apache/poi/ss/formula/LazyRefEval.java b/src/java/org/apache/poi/ss/formula/LazyRefEval.java index e0b5e19f93..c71b0e9779 100644 --- a/src/java/org/apache/poi/ss/formula/LazyRefEval.java +++ b/src/java/org/apache/poi/ss/formula/LazyRefEval.java @@ -27,7 +27,7 @@ import org.apache.poi.ss.util.CellReference; /** * Provides Lazy Evaluation to a 3D Reference */ -final class LazyRefEval extends RefEvalBase { +public final class LazyRefEval extends RefEvalBase { private final SheetRangeEvaluator _evaluator; public LazyRefEval(int rowIndex, int columnIndex, SheetRangeEvaluator sre) { @@ -47,14 +47,17 @@ final class LazyRefEval extends RefEvalBase { return new LazyAreaEval(area, _evaluator); } + public boolean isSubTotal() { + SheetRefEvaluator sheetEvaluator = _evaluator.getSheetEvaluator(getFirstSheetIndex()); + return sheetEvaluator.isSubTotal(getRow(), getColumn()); + } + public String toString() { CellReference cr = new CellReference(getRow(), getColumn()); - StringBuffer sb = new StringBuffer(); - sb.append(getClass().getName()).append("["); - sb.append(_evaluator.getSheetNameRange()); - sb.append('!'); - sb.append(cr.formatAsString()); - sb.append("]"); - return sb.toString(); + return getClass().getName() + "[" + + _evaluator.getSheetNameRange() + + '!' + + cr.formatAsString() + + "]"; } } diff --git a/src/java/org/apache/poi/ss/formula/functions/Subtotal.java b/src/java/org/apache/poi/ss/formula/functions/Subtotal.java index fca55c502e..f3b24cd1c4 100644 --- a/src/java/org/apache/poi/ss/formula/functions/Subtotal.java +++ b/src/java/org/apache/poi/ss/formula/functions/Subtotal.java @@ -19,6 +19,7 @@ package org.apache.poi.ss.formula.functions; import static org.apache.poi.ss.formula.functions.AggregateFunction.subtotalInstance; +import org.apache.poi.ss.formula.LazyRefEval; import org.apache.poi.ss.formula.eval.ErrorEval; import org.apache.poi.ss.formula.eval.EvaluationException; import org.apache.poi.ss.formula.eval.NotImplementedException; @@ -26,6 +27,11 @@ import org.apache.poi.ss.formula.eval.NotImplementedFunctionException; import org.apache.poi.ss.formula.eval.OperandResolver; import org.apache.poi.ss.formula.eval.ValueEval; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + /** * Implementation for the Excel function SUBTOTAL

* @@ -61,7 +67,6 @@ import org.apache.poi.ss.formula.eval.ValueEval; public class Subtotal implements Function { private static Function findFunction(int functionCode) throws EvaluationException { - Function func; switch (functionCode) { case 1: return subtotalInstance(AggregateFunction.AVERAGE); case 2: return Count.subtotalInstance(); @@ -87,7 +92,7 @@ public class Subtotal implements Function { return ErrorEval.VALUE_INVALID; } - Function innerFunc; + final Function innerFunc; try { ValueEval ve = OperandResolver.getSingleValue(args[0], srcRowIndex, srcColumnIndex); int functionCode = OperandResolver.coerceValueToInt(ve); @@ -96,9 +101,24 @@ public class Subtotal implements Function { return e.getErrorEval(); } - ValueEval[] innerArgs = new ValueEval[nInnerArgs]; - System.arraycopy(args, 1, innerArgs, 0, nInnerArgs); + // ignore the first arg, this is the function-type, we check for the length above + final List list = new ArrayList(Arrays.asList(args).subList(1, args.length)); + + Iterator it = list.iterator(); + + // See https://support.office.com/en-us/article/SUBTOTAL-function-7b027003-f060-4ade-9040-e478765b9939 + // "If there are other subtotals within ref1, ref2,... (or nested subtotals), these nested subtotals are ignored to avoid double counting." + // For array references it is handled in other evaluation steps, but we need to handle this here for references to subtotal-functions + while(it.hasNext()) { + ValueEval eval = it.next(); + if(eval instanceof LazyRefEval) { + LazyRefEval lazyRefEval = (LazyRefEval) eval; + if(lazyRefEval.isSubTotal()) { + it.remove(); + } + } + } - return innerFunc.evaluate(innerArgs, srcRowIndex, srcColumnIndex); + return innerFunc.evaluate(list.toArray(new ValueEval[list.size()]), srcRowIndex, srcColumnIndex); } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java index 566944d18d..9885fd8863 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java @@ -154,7 +154,9 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator { evaluator.evaluate(cXSL_cell); fail("Without a fix for #56752, shouldn't be able to evaluate a " + "reference to a non-provided linked workbook"); - } catch(Exception e) {} + } catch(Exception e) { + // expected here + } // Setup the environment Map evaluators = new HashMap(); @@ -196,7 +198,9 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator { try { cXSLX_nw_cell.setCellFormula("[alt.xlsx]Sheet1!$A$1"); fail("New workbook not linked, shouldn't be able to add"); - } catch (Exception e) {} + } catch (Exception e) { + // expected here + } // Link and re-try Workbook alt = new XSSFWorkbook(); @@ -651,4 +655,20 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator { private Cell getCell(Sheet sheet, int rowNo, int column) { return sheet.getRow(rowNo).getCell(column); } + + @Test + public void test59736() { + Workbook wb = XSSFTestDataSamples.openSampleWorkbook("59736.xlsx"); + FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); + Cell cell = wb.getSheetAt(0).getRow(0).getCell(0); + assertEquals(1, cell.getNumericCellValue(), 0.001); + + cell = wb.getSheetAt(0).getRow(1).getCell(0); + CellValue value = evaluator.evaluate(cell); + assertEquals(1, value.getNumberValue(), 0.001); + + cell = wb.getSheetAt(0).getRow(2).getCell(0); + value = evaluator.evaluate(cell); + assertEquals(1, value.getNumberValue(), 0.001); + } } diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestSubtotal.java b/src/testcases/org/apache/poi/ss/formula/functions/TestSubtotal.java index 7b63eaf799..f2750b591c 100644 --- a/src/testcases/org/apache/poi/ss/formula/functions/TestSubtotal.java +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestSubtotal.java @@ -20,9 +20,8 @@ package org.apache.poi.ss.formula.functions; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ss.formula.eval.AreaEval; -import org.apache.poi.ss.formula.eval.NumberEval; -import org.apache.poi.ss.formula.eval.ValueEval; +import org.apache.poi.ss.formula.FormulaParseException; +import org.apache.poi.ss.formula.eval.*; import junit.framework.TestCase; import org.apache.poi.ss.usermodel.*; @@ -75,7 +74,6 @@ public final class TestSubtotal extends TestCase { } public void testAvg(){ - Workbook wb = new HSSFWorkbook(); FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator(); @@ -95,16 +93,18 @@ public final class TestSubtotal extends TestCase { a6.setCellFormula("SUBTOTAL(1,B2:B6)*2 + 2"); Cell a7 = sh.createRow(7).createCell(1); a7.setCellFormula("SUBTOTAL(1,B2:B7)"); + Cell a8 = sh.createRow(8).createCell(1); + a8.setCellFormula("SUBTOTAL(1,B2,B3,B4,B5,B6,B7,B8)"); fe.evaluateAll(); assertEquals(2.0, a3.getNumericCellValue()); assertEquals(8.0, a6.getNumericCellValue()); assertEquals(3.0, a7.getNumericCellValue()); + assertEquals(3.0, a8.getNumericCellValue()); } public void testSum(){ - Workbook wb = new HSSFWorkbook(); FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator(); @@ -124,12 +124,15 @@ public final class TestSubtotal extends TestCase { a6.setCellFormula("SUBTOTAL(9,B2:B6)*2 + 2"); Cell a7 = sh.createRow(7).createCell(1); a7.setCellFormula("SUBTOTAL(9,B2:B7)"); + Cell a8 = sh.createRow(8).createCell(1); + a8.setCellFormula("SUBTOTAL(9,B2,B3,B4,B5,B6,B7,B8)"); fe.evaluateAll(); assertEquals(4.0, a3.getNumericCellValue()); assertEquals(26.0, a6.getNumericCellValue()); assertEquals(12.0, a7.getNumericCellValue()); + assertEquals(12.0, a8.getNumericCellValue()); } public void testCount(){ @@ -147,18 +150,21 @@ public final class TestSubtotal extends TestCase { a3.setCellFormula("SUBTOTAL(2,B2:B3)"); Cell a4 = sh.createRow(4).createCell(1); a4.setCellValue("POI"); // A4 is string and not counted - Cell a5 = sh.createRow(5).createCell(1); // A5 is blank and not counted + /*Cell a5 =*/ sh.createRow(5).createCell(1); // A5 is blank and not counted Cell a6 = sh.createRow(6).createCell(1); a6.setCellFormula("SUBTOTAL(2,B2:B6)*2 + 2"); Cell a7 = sh.createRow(7).createCell(1); a7.setCellFormula("SUBTOTAL(2,B2:B7)"); + Cell a8 = sh.createRow(8).createCell(1); + a8.setCellFormula("SUBTOTAL(2,B2,B3,B4,B5,B6,B7,B8)"); fe.evaluateAll(); assertEquals(2.0, a3.getNumericCellValue()); assertEquals(6.0, a6.getNumericCellValue()); assertEquals(2.0, a7.getNumericCellValue()); + assertEquals(2.0, a8.getNumericCellValue()); } public void testCounta(){ @@ -176,18 +182,21 @@ public final class TestSubtotal extends TestCase { a3.setCellFormula("SUBTOTAL(3,B2:B3)"); Cell a4 = sh.createRow(4).createCell(1); a4.setCellValue("POI"); // A4 is string and not counted - Cell a5 = sh.createRow(5).createCell(1); // A5 is blank and not counted + /*Cell a5 =*/ sh.createRow(5).createCell(1); // A5 is blank and not counted Cell a6 = sh.createRow(6).createCell(1); a6.setCellFormula("SUBTOTAL(3,B2:B6)*2 + 2"); Cell a7 = sh.createRow(7).createCell(1); a7.setCellFormula("SUBTOTAL(3,B2:B7)"); + Cell a8 = sh.createRow(8).createCell(1); + a8.setCellFormula("SUBTOTAL(3,B2,B3,B4,B5,B6,B7,B8)"); fe.evaluateAll(); assertEquals(2.0, a3.getNumericCellValue()); assertEquals(8.0, a6.getNumericCellValue()); assertEquals(3.0, a7.getNumericCellValue()); + assertEquals(3.0, a8.getNumericCellValue()); } public void testMax(){ @@ -211,12 +220,15 @@ public final class TestSubtotal extends TestCase { a6.setCellFormula("SUBTOTAL(4,B2:B6)*2 + 2"); Cell a7 = sh.createRow(7).createCell(1); a7.setCellFormula("SUBTOTAL(4,B2:B7)"); + Cell a8 = sh.createRow(8).createCell(1); + a8.setCellFormula("SUBTOTAL(4,B2,B3,B4,B5,B6,B7,B8)"); fe.evaluateAll(); assertEquals(3.0, a3.getNumericCellValue()); assertEquals(16.0, a6.getNumericCellValue()); assertEquals(7.0, a7.getNumericCellValue()); + assertEquals(7.0, a8.getNumericCellValue()); } public void testMin(){ @@ -240,12 +252,15 @@ public final class TestSubtotal extends TestCase { a6.setCellFormula("SUBTOTAL(5,B2:B6)*2 + 2"); Cell a7 = sh.createRow(7).createCell(1); a7.setCellFormula("SUBTOTAL(5,B2:B7)"); + Cell a8 = sh.createRow(8).createCell(1); + a8.setCellFormula("SUBTOTAL(5,B2,B3,B4,B5,B6,B7,B8)"); fe.evaluateAll(); assertEquals(1.0, a3.getNumericCellValue()); assertEquals(4.0, a6.getNumericCellValue()); assertEquals(1.0, a7.getNumericCellValue()); + assertEquals(1.0, a8.getNumericCellValue()); } public void testStdev(){ @@ -269,12 +284,15 @@ public final class TestSubtotal extends TestCase { a6.setCellFormula("SUBTOTAL(7,B2:B6)*2 + 2"); Cell a7 = sh.createRow(7).createCell(1); a7.setCellFormula("SUBTOTAL(7,B2:B7)"); + Cell a8 = sh.createRow(8).createCell(1); + a8.setCellFormula("SUBTOTAL(7,B2,B3,B4,B5,B6,B7,B8)"); fe.evaluateAll(); assertEquals(1.41421, a3.getNumericCellValue(), 0.0001); assertEquals(7.65685, a6.getNumericCellValue(), 0.0001); assertEquals(2.82842, a7.getNumericCellValue(), 0.0001); + assertEquals(2.82842, a8.getNumericCellValue(), 0.0001); } public void test50209(){ @@ -328,4 +346,69 @@ public final class TestSubtotal extends TestCase { confirmExpectedResult(evaluator, "SUBTOTAL(COUNT;B2:B8,C2:C8)", cellC2, 3.0); confirmExpectedResult(evaluator, "SUBTOTAL(COUNTA;B2:B8,C2:C8)", cellC3, 5.0); } + + public void testUnimplemented(){ + Workbook wb = new HSSFWorkbook(); + + FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator(); + + Sheet sh = wb.createSheet(); + Cell a3 = sh.createRow(3).createCell(1); + a3.setCellFormula("SUBTOTAL(8,B2:B3)"); + + try { + fe.evaluateAll(); + fail("Should catch an NotImplementedFunctionException here, adjust these tests if it was actually implemented"); + } catch (NotImplementedException e) { + // expected here + } + + a3.setCellFormula("SUBTOTAL(10,B2:B3)"); + + try { + fe.evaluateAll(); + fail("Should catch an NotImplementedFunctionException here, adjust these tests if it was actually implemented"); + } catch (NotImplementedException e) { + // expected here + } + + a3.setCellFormula("SUBTOTAL(11,B2:B3)"); + + try { + fe.evaluateAll(); + fail("Should catch an NotImplementedFunctionException here, adjust these tests if it was actually implemented"); + } catch (NotImplementedException e) { + // expected here + } + + a3.setCellFormula("SUBTOTAL(107,B2:B3)"); + + try { + fe.evaluateAll(); + fail("Should catch an NotImplementedFunctionException here, adjust these tests if it was actually implemented"); + } catch (NotImplementedException e) { + // expected here + } + + a3.setCellFormula("SUBTOTAL(0,B2:B3)"); + fe.evaluateAll(); + assertEquals(FormulaError.VALUE.getCode(), a3.getErrorCellValue()); + + try { + a3.setCellFormula("SUBTOTAL(9)"); + fail("Should catch an exception here"); + } catch (FormulaParseException e) { + // expected here + } + + try { + a3.setCellFormula("SUBTOTAL()"); + fail("Should catch an exception here"); + } catch (FormulaParseException e) { + // expected here + } + + Subtotal subtotal = new Subtotal(); + assertEquals(ErrorEval.VALUE_INVALID, subtotal.evaluate(new ValueEval[] {}, 0, 0)); + } } diff --git a/test-data/spreadsheet/59736.xlsx b/test-data/spreadsheet/59736.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1f29d54c502aa92dc8b17300df3f4dd455a23320 GIT binary patch literal 8752 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 literal 0 HcmV?d00001 -- 2.39.5