From 1d8365e7a3791b14e325cf81f82ccb7841d81d43 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Sat, 11 Feb 2012 13:16:33 +0000 Subject: [PATCH] fixed evaluation of blank cells in COUNTIF, see Bugzilla 51498 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1243054 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../poi/ss/formula/functions/Countif.java | 36 +++++- .../apache/poi/hssf/usermodel/TestBugs.java | 4 +- .../ss/formula/functions/TestCountFuncs.java | 114 ++++++++++++++++-- test-data/spreadsheet/51498.xls | Bin 13824 -> 24576 bytes test-data/spreadsheet/countifExamples.xls | Bin 26112 -> 38400 bytes 6 files changed, 139 insertions(+), 16 deletions(-) diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 86f2696b39..ea93e88316 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 51498 - fixed evaluation of blank cells in COUNTIF 52576 - support changing external file references in HSSFWorkbook 49896 - support external references in FormulaRenderer 52527 - avoid exception when matching shared formula records in HSSF diff --git a/src/java/org/apache/poi/ss/formula/functions/Countif.java b/src/java/org/apache/poi/ss/formula/functions/Countif.java index 173f6a1e77..ef524acde3 100644 --- a/src/java/org/apache/poi/ss/formula/functions/Countif.java +++ b/src/java/org/apache/poi/ss/formula/functions/Countif.java @@ -217,6 +217,14 @@ public final class Countif extends Fixed2ArgFunction { } else if((x instanceof NumberEval)) { NumberEval ne = (NumberEval) x; testValue = ne.getNumberValue(); + } else if((x instanceof BlankEval)) { + switch (getCode()) { + case CmpOp.NE: + // Excel counts blank values in range as not equal to any value. See Bugzilla 51498 + return true; + default: + return false; + } } else { return false; } @@ -258,7 +266,23 @@ public final class Countif extends Fixed2ArgFunction { } else if((x instanceof BoolEval)) { BoolEval be = (BoolEval) x; testValue = boolToInt(be.getBooleanValue()); - } else { + } else if((x instanceof BlankEval)) { + switch (getCode()) { + case CmpOp.NE: + // Excel counts blank values in range as not equal to any value. See Bugzilla 51498 + return true; + default: + return false; + } + } else if((x instanceof NumberEval)) { + switch (getCode()) { + case CmpOp.NE: + // not-equals comparison of a number to boolean always returnes false + return true; + default: + return false; + } + } else { return false; } return evaluate(testValue - _value); @@ -318,6 +342,10 @@ public final class Countif extends Fixed2ArgFunction { case CmpOp.NONE: case CmpOp.EQ: return _value.length() == 0; + case CmpOp.NE: + // pred '<>' matches empty string but not blank cell + // pred '<>ABC' matches blank and 'not ABC' + return _value.length() != 0; } // no other criteria matches a blank cell return false; @@ -342,7 +370,9 @@ public final class Countif extends Fixed2ArgFunction { if (_pattern != null) { return evaluate(_pattern.matcher(testedValue).matches()); } - return evaluate(testedValue.compareTo(_value)); + // String criteria in COUNTIF are case insensitive: + // for example, the string "apples" and the string "APPLES" will match the same cells. + return evaluate(testedValue.compareToIgnoreCase(_value)); } /** * Translates Excel countif wildcard strings into java regex strings @@ -394,7 +424,7 @@ public final class Countif extends Fixed2ArgFunction { sb.append(ch); } if (hasWildCard) { - return Pattern.compile(sb.toString()); + return Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE); } return null; } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 5499af4a3f..6f1fdcde2e 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -2220,7 +2220,9 @@ if(1==2) { public void test49896() { HSSFWorkbook wb = openSample("49896.xls"); HSSFCell cell = wb.getSheetAt(0).getRow(1).getCell(1); - assertEquals("VLOOKUP(A2,'[C:Documents and Settings/Yegor/My Documents/csco.xls]Sheet1'!$A$2:$B$3,2,FALSE)", + String PATH_SEPARATOR = System.getProperty("file.separator"); + assertEquals("VLOOKUP(A2,'[C:Documents and Settings" + PATH_SEPARATOR+"Yegor"+PATH_SEPARATOR + +"My Documents"+PATH_SEPARATOR+"csco.xls]Sheet1'!$A$2:$B$3,2,FALSE)", cell.getCellFormula()); } diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestCountFuncs.java b/src/testcases/org/apache/poi/ss/formula/functions/TestCountFuncs.java index a01a92aa00..40525480f5 100644 --- a/src/testcases/org/apache/poi/ss/formula/functions/TestCountFuncs.java +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestCountFuncs.java @@ -40,6 +40,7 @@ import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.CellValue; import org.apache.poi.ss.usermodel.FormulaEvaluator; +import org.apache.poi.ss.util.CellReference; /** * Test cases for COUNT(), COUNTA() COUNTIF(), COUNTBLANK() @@ -196,6 +197,27 @@ public final class TestCountFuncs extends TestCase { confirmCountIf(4, range, new StringEval("<>111")); } + /** + * String criteria in COUNTIF are case insensitive; + * for example, the string "apples" and the string "APPLES" will match the same cells. + */ + public void testCaseInsensitiveStringComparison() { + AreaEval range; + ValueEval[] values; + + values = new ValueEval[] { + new StringEval("no"), + new StringEval("NO"), + new StringEval("No"), + new StringEval("Yes") + }; + + range = EvalFactory.createAreaEval("A1:A4", values); + confirmCountIf(3, range, new StringEval("no")); + confirmCountIf(3, range, new StringEval("NO")); + confirmCountIf(3, range, new StringEval("No")); + } + /** * special case where the criteria argument is a cell reference */ @@ -365,27 +387,48 @@ public final class TestCountFuncs extends TestCase { * Bug #51498 - Check that CountIf behaves correctly for GTE, LTE * and NEQ cases */ - public void testCountifLTEGTE() throws Exception { + public void testCountifBug51498() throws Exception { final int REF_COL = 4; final int EVAL_COL = 3; - // Note - POI currently agrees with OpenOffice on certain blank cell cases, - // while Excel can differ. This is the list of checks to skip - List skipRowsPendingExcelVsOpenOffice = Arrays.asList( - new Integer[] {3}); - - HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook("51498.xls"); + HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook("51498.xls"); FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); HSSFSheet sheet = workbook.getSheetAt(0); - for (int i = 0; i < 8; i++) { - if (skipRowsPendingExcelVsOpenOffice.contains(i)) { - // Skip the check for now - continue; - } + + // numeric criteria + for (int i = 0; i < 8; i++) { CellValue expected = evaluator.evaluate(sheet.getRow(i).getCell(REF_COL)); CellValue actual = evaluator.evaluate(sheet.getRow(i).getCell(EVAL_COL)); assertEquals(expected.formatAsString(), actual.formatAsString()); } + + // boolean criteria + for (int i = 0; i < 8; i++) { + HSSFCell cellFmla = sheet.getRow(i).getCell(8); + HSSFCell cellRef = sheet.getRow(i).getCell(9); + + double expectedValue = cellRef.getNumericCellValue(); + double actualValue = evaluator.evaluate(cellFmla).getNumberValue(); + + assertEquals( + "Problem with a formula at " + + new CellReference(cellFmla).formatAsString() + "[" + cellFmla.getCellFormula()+"] ", + expectedValue, actualValue, 0.0001); + } + + // string criteria + for (int i = 1; i < 9; i++) { + HSSFCell cellFmla = sheet.getRow(i).getCell(13); + HSSFCell cellRef = sheet.getRow(i).getCell(14); + + double expectedValue = cellRef.getNumericCellValue(); + double actualValue = evaluator.evaluate(cellFmla).getNumberValue(); + + assertEquals( + "Problem with a formula at " + + new CellReference(cellFmla).formatAsString() + "[" + cellFmla.getCellFormula()+"] ", + expectedValue, actualValue, 0.0001); + } } public void testWildCards() { @@ -456,6 +499,53 @@ public final class TestCountFuncs extends TestCase { testCountFunctionFromSpreadsheet("countifExamples.xls", 1, 2, 3, "countif"); } + /** + * Two COUNTIF examples taken from + * http://office.microsoft.com/en-us/excel-help/countif-function-HP010069840.aspx?CTT=5&origin=HA010277524 + */ + public void testCountifExamples() { + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("countifExamples.xls"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + + HSSFSheet sheet1 = wb.getSheet("MSDN Example 1"); + for (int rowIx=7; rowIx<=12; rowIx++) { + HSSFRow row = sheet1.getRow(rowIx-1); + HSSFCell cellA = row.getCell(0); // cell containing a formula with COUNTIF + assertEquals(HSSFCell.CELL_TYPE_FORMULA, cellA.getCellType()); + HSSFCell cellC = row.getCell(2); // cell with a reference value + assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cellC.getCellType()); + + CellValue cv = fe.evaluate(cellA); + double actualValue = cv.getNumberValue(); + double expectedValue = cellC.getNumericCellValue(); + assertEquals( + "Problem with a formula at " + new CellReference(cellA).formatAsString() + + ": " + cellA.getCellFormula() + " :" + + "Expected = (" + expectedValue + ") Actual=(" + actualValue + ") ", + expectedValue, actualValue, 0.0001); + } + + HSSFSheet sheet2 = wb.getSheet("MSDN Example 2"); + for (int rowIx=9; rowIx<=14; rowIx++) { + HSSFRow row = sheet2.getRow(rowIx-1); + HSSFCell cellA = row.getCell(0); // cell containing a formula with COUNTIF + assertEquals(HSSFCell.CELL_TYPE_FORMULA, cellA.getCellType()); + HSSFCell cellC = row.getCell(2); // cell with a reference value + assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cellC.getCellType()); + + CellValue cv = fe.evaluate(cellA); + double actualValue = cv.getNumberValue(); + double expectedValue = cellC.getNumericCellValue(); + + assertEquals( + "Problem with a formula at " + + new CellReference(cellA).formatAsString() + "[" + cellA.getCellFormula()+"]: " + + "Expected = (" + expectedValue + ") Actual=(" + actualValue + ") ", + expectedValue, actualValue, 0.0001); + + } + } + public void testCountBlankFromSpreadsheet() { testCountFunctionFromSpreadsheet("countblankExamples.xls", 1, 3, 4, "countblank"); } diff --git a/test-data/spreadsheet/51498.xls b/test-data/spreadsheet/51498.xls index fc50d21d2c2f2b96ccad13c80b49f776080aae46..b5d2d659996940af01827b71fa51c1c685fe77dd 100644 GIT binary patch literal 24576 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 HpZ|XYLDjf9 delta 1186 zcmZuw%}Z2K6#w1#-n@D9<&5d5m7~)b3WfQRr6UuLi$KG`0ii{MDG~NDWvP8=kT0lB zxJVC((I)?Z$e4ZTGBB!*i&iGlRtW@&@OADTb!3?H_}z2P@7{OrJ?EZlFAF>Kx>+2Z zZ8>gK0r;5uAjlD()AP|U?$67BsRKWuVaZtG5!hg)aUT|NcYJDi;?C$}>ejAMzg9vE z+A&c=S?#t+QH>rc7@z17f667gF@{?hMkXuFd7WzgLtX(7ww)-jLk~Qav=#78lV%`f z>yFf=wyE?H+fcg9b}J1VN|)O%XGmxec!^S^OgV^!(~Y68G&fZjJ|>K74X z(otIrKEi%HV7)8%(|XtBB)vIvYI+h{iK1i}z{RjM-jM%waRenIs5qHOAty60PPE3t4Sd=8sV%;VU#OzB)|2$;Y$w&Wb&Uptblb*-eOW5PpzSxRaG^!sJ_RXWN_op7jAXhxyOi!1vqJ-OgF6~R^mlMdSoHAP Lv|qM9)ByYkWa++p diff --git a/test-data/spreadsheet/countifExamples.xls b/test-data/spreadsheet/countifExamples.xls index b15bd162a23056dd0d69e87184b8c1fdbed2dce0..6285423c0b3f5c0846b88ba499d33788bbdc75f6 100644 GIT binary patch literal 38400 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 PIX8Ls{R;JuddmMlS?Bci delta 4293 zcma)9Z%k8H6uwwVikp&Wctb`PSi2uhTk;qf{)T$GLDe?pm4>fk4NX9xW#mJ-C z^hQS@(AiZf5W2WacATF&-27;$Uyu(~MrPgW7v=z~Riw>b%aSAzp`G&3rZTrl1faW` z{?pY=TgxP|pU~#+=I4?25DGqtW-!WRB*$Er5-x&eCaAngL~>F(3vWV(pwy5pXfs_T zR@0TDgYFe=i6S5efnlXziMa^@vW8^7zLwq;7YaF4Hah7cL*oCDEQzEYheISvGmCxV zuT^qd=8`jL1lC%mY*1*G97!TcpI~8M4HEWcmF)CGDLE<~k4jEU>4Xj-TV7z=LJz9` z$9r&EDkhqs)*VLF`o0mXvA;fJqBSQ$s3f(dfz*@rBt+`NUehiiywU75Id_`vWTzR- zjy;RW-7Ed4X;P}&?D5nTE?8GEla5-`sXx`m0<9V#!vH!&l1%IbB^)V6!H|Ye)2tee zi?zUZEpS8&TxEem*2H;}H9c(70td9ffEIY72GVVX*-xEHAx0ufinnz^3{x1Yh3g7s znbz9ee3ntoI-tOPa7lcv)nPHoJt?Ru<4HR~HbYoLnpF`~y}d|Yhj+BHzc z`1pYyi}CTY9*glYZHi8hjE`&itP(HdXK&7UN^SgKJlbm+`SdkHz?SN{_|( z_!pl=2;<{|bgo?lf?E|it$HlR$M5u5jE@$lPLGU_emxfBV;7HwSvYdxawHb_*PQNW z<5n%Q?%%&3VASzZoD+39boe&8m`P{s}O5R)3Y@0VGdSUBL^o~HCZ}y9*GRZSWoA?r>AFP)_Su7 zeEWIPqM7diyZ>CAuA2N?(>3FQz2i=)9&q(k#iDSLOEtW>7w>kd^diu!1%7eal9`aF z$Ff%&t`Cn=!R?^-9KSgDlOn#C<1^eH9aQ8YkcWT%O_5*8^%<`J@c`xU$=obKJpHwT z54(MatGCZ6a#dch;mg503MXSmreL`CNjE)|@1XvCpW)mO`$67l==uDDf@Kx>4D`~V zBJVEni+$}N+QYvVd|?!i_J#KpotCUAHStLi;($1?1Xb`5`gF6TL#}z_-=;pE;xNYf^VCLkjJ2) z7rt{KaPGi&y-0tYzvelpB^D^-CrvAJ(wrnZv08cN!dt8)#P0`gHJw(wQjlpw?edd_G!IvaM!$@Y2Ajkj)8Auix^E)GBcx^V&j`E7mkYqsp_c|Ojt1V49Pm9}@yFP<9 zT0HPI547c_U=Bi)qe=DeHkrC#wxtTBk~EUH;lk{Pd*LRygcn#8h>ZZ~G6Rsmv1;V5AI?bg-#xMpFstQ{H4LhUJR-4WY)ZjlpfQKNzgv zP$w_hv7xR(F7bH0a&=>9^H$smlu>`NJ{T+|n0*vVk>XBKDS0@rrzw21V!!Y&3n(S6