You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Countif.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.ss.formula.functions;
  16. import java.util.regex.Pattern;
  17. import org.apache.poi.ss.formula.ThreeDEval;
  18. import org.apache.poi.ss.formula.eval.BlankEval;
  19. import org.apache.poi.ss.formula.eval.BoolEval;
  20. import org.apache.poi.ss.formula.eval.ErrorEval;
  21. import org.apache.poi.ss.formula.eval.EvaluationException;
  22. import org.apache.poi.ss.formula.eval.NumberEval;
  23. import org.apache.poi.ss.formula.eval.OperandResolver;
  24. import org.apache.poi.ss.formula.eval.RefEval;
  25. import org.apache.poi.ss.formula.eval.StringEval;
  26. import org.apache.poi.ss.formula.eval.ValueEval;
  27. import org.apache.poi.ss.formula.functions.CountUtils.I_MatchPredicate;
  28. import org.apache.poi.ss.usermodel.FormulaError;
  29. /**
  30. * Implementation for the function COUNTIF
  31. * <p>
  32. * Syntax: COUNTIF ( range, criteria )
  33. * <table>
  34. * <caption>Parameter descriptions</caption>
  35. * <tr><th>range&nbsp;&nbsp;&nbsp;</th><td>is the range of cells to be counted based on the criteria</td></tr>
  36. * <tr><th>criteria</th><td>is used to determine which cells to count</td></tr>
  37. * </table>
  38. */
  39. public final class Countif extends Fixed2ArgFunction {
  40. private static final class CmpOp {
  41. public static final int NONE = 0;
  42. public static final int EQ = 1;
  43. public static final int NE = 2;
  44. public static final int LE = 3;
  45. public static final int LT = 4;
  46. public static final int GT = 5;
  47. public static final int GE = 6;
  48. public static final CmpOp OP_NONE = op("", NONE);
  49. public static final CmpOp OP_EQ = op("=", EQ);
  50. public static final CmpOp OP_NE = op("<>", NE);
  51. public static final CmpOp OP_LE = op("<=", LE);
  52. public static final CmpOp OP_LT = op("<", LT);
  53. public static final CmpOp OP_GT = op(">", GT);
  54. public static final CmpOp OP_GE = op(">=", GE);
  55. private final String _representation;
  56. private final int _code;
  57. private static CmpOp op(String rep, int code) {
  58. return new CmpOp(rep, code);
  59. }
  60. private CmpOp(String representation, int code) {
  61. _representation = representation;
  62. _code = code;
  63. }
  64. /**
  65. * @return number of characters used to represent this operator
  66. */
  67. public int getLength() {
  68. return _representation.length();
  69. }
  70. public int getCode() {
  71. return _code;
  72. }
  73. public static CmpOp getOperator(String value) {
  74. int len = value.length();
  75. if (len < 1) {
  76. return OP_NONE;
  77. }
  78. char firstChar = value.charAt(0);
  79. switch(firstChar) {
  80. case '=':
  81. return OP_EQ;
  82. case '>':
  83. if (len > 1) {
  84. switch(value.charAt(1)) {
  85. case '=':
  86. return OP_GE;
  87. }
  88. }
  89. return OP_GT;
  90. case '<':
  91. if (len > 1) {
  92. switch(value.charAt(1)) {
  93. case '=':
  94. return OP_LE;
  95. case '>':
  96. return OP_NE;
  97. }
  98. }
  99. return OP_LT;
  100. }
  101. return OP_NONE;
  102. }
  103. public boolean evaluate(boolean cmpResult) {
  104. switch (_code) {
  105. case NONE:
  106. case EQ:
  107. return cmpResult;
  108. case NE:
  109. return !cmpResult;
  110. }
  111. throw new RuntimeException("Cannot call boolean evaluate on non-equality operator '"
  112. + _representation + "'");
  113. }
  114. public boolean evaluate(int cmpResult) {
  115. switch (_code) {
  116. case NONE:
  117. case EQ:
  118. return cmpResult == 0;
  119. case NE: return cmpResult != 0;
  120. case LT: return cmpResult < 0;
  121. case LE: return cmpResult <= 0;
  122. case GT: return cmpResult > 0;
  123. case GE: return cmpResult >= 0;
  124. }
  125. throw new RuntimeException("Cannot call boolean evaluate on non-equality operator '"
  126. + _representation + "'");
  127. }
  128. @Override
  129. public String toString() {
  130. return getClass().getName() + " [" + _representation + "]";
  131. }
  132. public String getRepresentation() {
  133. return _representation;
  134. }
  135. }
  136. private static abstract class MatcherBase implements I_MatchPredicate {
  137. private final CmpOp _operator;
  138. MatcherBase(CmpOp operator) {
  139. _operator = operator;
  140. }
  141. protected final int getCode() {
  142. return _operator.getCode();
  143. }
  144. protected final boolean evaluate(int cmpResult) {
  145. return _operator.evaluate(cmpResult);
  146. }
  147. protected final boolean evaluate(boolean cmpResult) {
  148. return _operator.evaluate(cmpResult);
  149. }
  150. @Override
  151. public final String toString() {
  152. return getClass().getName() + " [" + _operator.getRepresentation() + getValueText() + "]";
  153. }
  154. protected abstract String getValueText();
  155. }
  156. private static final class NumberMatcher extends MatcherBase {
  157. private final double _value;
  158. public NumberMatcher(double value, CmpOp operator) {
  159. super(operator);
  160. _value = value;
  161. }
  162. @Override
  163. protected String getValueText() {
  164. return String.valueOf(_value);
  165. }
  166. @Override
  167. public boolean matches(ValueEval x) {
  168. double testValue;
  169. if(x instanceof StringEval) {
  170. // if the target(x) is a string, but parses as a number
  171. // it may still count as a match, only for the equality operator
  172. switch (getCode()) {
  173. case CmpOp.EQ:
  174. case CmpOp.NONE:
  175. break;
  176. case CmpOp.NE:
  177. // Always matches (inconsistent with above two cases).
  178. // for example '<>123' matches '123', '4', 'abc', etc
  179. return true;
  180. default:
  181. // never matches (also inconsistent with above three cases).
  182. // for example '>5' does not match '6',
  183. return false;
  184. }
  185. StringEval se = (StringEval)x;
  186. Double val = OperandResolver.parseDouble(se.getStringValue());
  187. if(val == null) {
  188. // x is text that is not a number
  189. return false;
  190. }
  191. return _value == val;
  192. } else if((x instanceof NumberEval)) {
  193. NumberEval ne = (NumberEval) x;
  194. testValue = ne.getNumberValue();
  195. } else if((x instanceof BlankEval)) {
  196. switch (getCode()) {
  197. case CmpOp.NE:
  198. // Excel counts blank values in range as not equal to any value. See Bugzilla 51498
  199. return true;
  200. default:
  201. return false;
  202. }
  203. } else {
  204. return false;
  205. }
  206. return evaluate(Double.compare(testValue, _value));
  207. }
  208. }
  209. private static final class BooleanMatcher extends MatcherBase {
  210. private final int _value;
  211. public BooleanMatcher(boolean value, CmpOp operator) {
  212. super(operator);
  213. _value = boolToInt(value);
  214. }
  215. @Override
  216. protected String getValueText() {
  217. return _value == 1 ? "TRUE" : "FALSE";
  218. }
  219. private static int boolToInt(boolean value) {
  220. return value ? 1 : 0;
  221. }
  222. @Override
  223. public boolean matches(ValueEval x) {
  224. int testValue;
  225. if(x instanceof StringEval) {
  226. // Note - Unlike with numbers, it seems that COUNTIF never matches
  227. // boolean values when the target(x) is a string
  228. return false;
  229. // uncomment to observe more intuitive behaviour
  230. // StringEval se = (StringEval)x;
  231. // Boolean val = parseBoolean(se.getStringValue());
  232. // if(val == null) {
  233. // // x is text that is not a boolean
  234. // return false;
  235. // }
  236. // testValue = boolToInt(val.booleanValue());
  237. } else if((x instanceof BoolEval)) {
  238. BoolEval be = (BoolEval) x;
  239. testValue = boolToInt(be.getBooleanValue());
  240. } else if((x instanceof BlankEval)) {
  241. switch (getCode()) {
  242. case CmpOp.NE:
  243. // Excel counts blank values in range as not equal to any value. See Bugzilla 51498
  244. return true;
  245. default:
  246. return false;
  247. }
  248. } else if((x instanceof NumberEval)) {
  249. switch (getCode()) {
  250. case CmpOp.NE:
  251. // not-equals comparison of a number to boolean always returnes false
  252. return true;
  253. default:
  254. return false;
  255. }
  256. } else {
  257. return false;
  258. }
  259. return evaluate(testValue - _value);
  260. }
  261. }
  262. public static final class ErrorMatcher extends MatcherBase {
  263. private final int _value;
  264. public ErrorMatcher(int errorCode, CmpOp operator) {
  265. super(operator);
  266. _value = errorCode;
  267. }
  268. @Override
  269. protected String getValueText() {
  270. return FormulaError.forInt(_value).getString();
  271. }
  272. @Override
  273. public boolean matches(ValueEval x) {
  274. if(x instanceof ErrorEval) {
  275. int testValue = ((ErrorEval)x).getErrorCode();
  276. return evaluate(testValue - _value);
  277. }
  278. return false;
  279. }
  280. public int getValue() {
  281. return _value;
  282. }
  283. }
  284. public static final class StringMatcher extends MatcherBase {
  285. private final String _value;
  286. private final Pattern _pattern;
  287. public StringMatcher(String value, CmpOp operator) {
  288. super(operator);
  289. _value = value;
  290. switch(operator.getCode()) {
  291. case CmpOp.NONE:
  292. case CmpOp.EQ:
  293. case CmpOp.NE:
  294. _pattern = getWildCardPattern(value);
  295. break;
  296. default:
  297. // pattern matching is never used for < > <= =>
  298. _pattern = null;
  299. }
  300. }
  301. @Override
  302. protected String getValueText() {
  303. if (_pattern == null) {
  304. return _value;
  305. }
  306. return _pattern.pattern();
  307. }
  308. @Override
  309. public boolean matches(ValueEval x) {
  310. if (x instanceof BlankEval) {
  311. switch(getCode()) {
  312. case CmpOp.NONE:
  313. case CmpOp.EQ:
  314. return _value.length() == 0;
  315. case CmpOp.NE:
  316. // pred '<>' matches empty string but not blank cell
  317. // pred '<>ABC' matches blank and 'not ABC'
  318. return _value.length() != 0;
  319. }
  320. // no other criteria matches a blank cell
  321. return false;
  322. }
  323. if(!(x instanceof StringEval)) {
  324. // must always be string
  325. // even if match str is wild, but contains only digits
  326. // e.g. '4*7', NumberEval(4567) does not match
  327. return false;
  328. }
  329. String testedValue = ((StringEval) x).getStringValue();
  330. if (testedValue.length() < 1 && _value.length() < 1) {
  331. // odd case: criteria '=' behaves differently to criteria ''
  332. switch(getCode()) {
  333. case CmpOp.NONE: return true;
  334. case CmpOp.EQ: return false;
  335. case CmpOp.NE: return true;
  336. }
  337. return false;
  338. }
  339. if (_pattern != null) {
  340. return evaluate(_pattern.matcher(testedValue).matches());
  341. }
  342. // String criteria in COUNTIF are case insensitive:
  343. // for example, the string "apples" and the string "APPLES" will match the same cells.
  344. return evaluate(testedValue.compareToIgnoreCase(_value));
  345. }
  346. /**
  347. * Translates Excel countif wildcard strings into java regex strings
  348. * @return {@code null} if the specified value contains no special wildcard characters.
  349. */
  350. public static Pattern getWildCardPattern(String value) {
  351. int len = value.length();
  352. StringBuilder sb = new StringBuilder(len);
  353. boolean hasWildCard = false;
  354. for(int i=0; i<len; i++) {
  355. char ch = value.charAt(i);
  356. switch(ch) {
  357. case '?': //Any single character
  358. hasWildCard = true;
  359. // match exactly one character
  360. sb.append('.');
  361. continue;
  362. case '*': //Zero or more characters
  363. hasWildCard = true;
  364. // match one or more occurrences of any character
  365. sb.append(".*");
  366. continue;
  367. case '~':
  368. if (i+1<len) {
  369. ch = value.charAt(i+1);
  370. switch (ch) {
  371. case '?':
  372. case '*':
  373. hasWildCard = true;
  374. sb.append('[').append(ch).append(']');
  375. i++; // Note - incrementing loop variable here
  376. continue;
  377. }
  378. }
  379. // else not '~?' or '~*'
  380. sb.append('~'); // just plain '~'
  381. continue;
  382. case '.':
  383. case '$':
  384. case '^':
  385. case '[':
  386. case ']':
  387. case '(':
  388. case ')':
  389. // escape literal characters that would have special meaning in regex
  390. sb.append("\\").append(ch);
  391. continue;
  392. }
  393. sb.append(ch);
  394. }
  395. if (hasWildCard) {
  396. return Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE);
  397. }
  398. return null;
  399. }
  400. }
  401. @Override
  402. public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
  403. I_MatchPredicate mp = createCriteriaPredicate(arg1, srcRowIndex, srcColumnIndex);
  404. if(mp == null) {
  405. // If the criteria arg is a reference to a blank cell, countif always returns zero.
  406. return NumberEval.ZERO;
  407. }
  408. double result = countMatchingCellsInArea(arg0, mp);
  409. return new NumberEval(result);
  410. }
  411. /**
  412. * @return the number of evaluated cells in the range that match the specified criteria
  413. */
  414. private double countMatchingCellsInArea(ValueEval rangeArg, I_MatchPredicate criteriaPredicate) {
  415. if (rangeArg instanceof RefEval) {
  416. return CountUtils.countMatchingCellsInRef((RefEval) rangeArg, criteriaPredicate);
  417. } else if (rangeArg instanceof ThreeDEval) {
  418. return CountUtils.countMatchingCellsInArea((ThreeDEval) rangeArg, criteriaPredicate);
  419. } else {
  420. throw new IllegalArgumentException("Bad range arg type (" + rangeArg.getClass().getName() + ")");
  421. }
  422. }
  423. /**
  424. * Creates a criteria predicate object for the supplied criteria arg
  425. * @return {@code null} if the arg evaluates to blank.
  426. */
  427. /* package */ static I_MatchPredicate createCriteriaPredicate(ValueEval arg, int srcRowIndex, int srcColumnIndex) {
  428. ValueEval evaluatedCriteriaArg = evaluateCriteriaArg(arg, srcRowIndex, srcColumnIndex);
  429. if(evaluatedCriteriaArg instanceof NumberEval) {
  430. return new NumberMatcher(((NumberEval)evaluatedCriteriaArg).getNumberValue(), CmpOp.OP_NONE);
  431. }
  432. if(evaluatedCriteriaArg instanceof BoolEval) {
  433. return new BooleanMatcher(((BoolEval)evaluatedCriteriaArg).getBooleanValue(), CmpOp.OP_NONE);
  434. }
  435. if(evaluatedCriteriaArg instanceof StringEval) {
  436. return createGeneralMatchPredicate((StringEval)evaluatedCriteriaArg);
  437. }
  438. if(evaluatedCriteriaArg instanceof ErrorEval) {
  439. return new ErrorMatcher(((ErrorEval)evaluatedCriteriaArg).getErrorCode(), CmpOp.OP_NONE);
  440. }
  441. if(evaluatedCriteriaArg == BlankEval.instance) {
  442. return null;
  443. }
  444. throw new RuntimeException("Unexpected type for criteria ("
  445. + evaluatedCriteriaArg.getClass().getName() + ")");
  446. }
  447. /**
  448. *
  449. * @return the de-referenced criteria arg (possibly {@link ErrorEval})
  450. */
  451. private static ValueEval evaluateCriteriaArg(ValueEval arg, int srcRowIndex, int srcColumnIndex) {
  452. try {
  453. return OperandResolver.getSingleValue(arg, srcRowIndex, srcColumnIndex);
  454. } catch (EvaluationException e) {
  455. return e.getErrorEval();
  456. }
  457. }
  458. /**
  459. * When the second argument is a string, many things are possible
  460. */
  461. private static I_MatchPredicate createGeneralMatchPredicate(StringEval stringEval) {
  462. String value = stringEval.getStringValue();
  463. CmpOp operator = CmpOp.getOperator(value);
  464. value = value.substring(operator.getLength());
  465. Boolean booleanVal = parseBoolean(value);
  466. if(booleanVal != null) {
  467. return new BooleanMatcher(booleanVal, operator);
  468. }
  469. Double doubleVal = OperandResolver.parseDouble(value);
  470. if(doubleVal != null) {
  471. return new NumberMatcher(doubleVal, operator);
  472. }
  473. ErrorEval ee = parseError(value);
  474. if (ee != null) {
  475. return new ErrorMatcher(ee.getErrorCode(), operator);
  476. }
  477. //else - just a plain string with no interpretation.
  478. return new StringMatcher(value, operator);
  479. }
  480. private static ErrorEval parseError(String value) {
  481. if (value.length() < 4 || value.charAt(0) != '#') {
  482. return null;
  483. }
  484. if (value.equals("#NULL!")) {
  485. return ErrorEval.NULL_INTERSECTION;
  486. }
  487. if (value.equals("#DIV/0!")) {
  488. return ErrorEval.DIV_ZERO;
  489. }
  490. if (value.equals("#VALUE!")) {
  491. return ErrorEval.VALUE_INVALID;
  492. }
  493. if (value.equals("#REF!")) {
  494. return ErrorEval.REF_INVALID;
  495. }
  496. if (value.equals("#NAME?")) {
  497. return ErrorEval.NAME_INVALID;
  498. }
  499. if (value.equals("#NUM!")) {
  500. return ErrorEval.NUM_ERROR;
  501. }
  502. if (value.equals("#N/A")) {
  503. return ErrorEval.NA;
  504. }
  505. return null;
  506. }
  507. /**
  508. * Boolean literals ('TRUE', 'FALSE') treated similarly but NOT same as numbers.
  509. * @return {@code null} to represent blank values
  510. */
  511. /* package */ static Boolean parseBoolean(String strRep) {
  512. if (strRep.length() < 1) {
  513. return null;
  514. }
  515. switch(strRep.charAt(0)) {
  516. case 't':
  517. case 'T':
  518. if("TRUE".equalsIgnoreCase(strRep)) {
  519. return Boolean.TRUE;
  520. }
  521. break;
  522. case 'f':
  523. case 'F':
  524. if("FALSE".equalsIgnoreCase(strRep)) {
  525. return Boolean.FALSE;
  526. }
  527. break;
  528. }
  529. return null;
  530. }
  531. }