From 4d2be808066991482c00b74e1789c1cfcd3701d0 Mon Sep 17 00:00:00 2001
From: Yegor Kozlov
Date: Sat, 16 Mar 2013 12:33:08 +0000
Subject: [PATCH] Bug 54673 - [PATCH] Simple wildcard support in HLOOKUP,
VOOLKUP, MATCH, COUNTIF
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1457243 13f79535-47bb-0310-9956-ffa450edef68
---
.../poi/ss/formula/functions/Countif.java | 9 +-
.../poi/ss/formula/functions/LookupUtils.java | 52 ++-
.../poi/ss/formula/functions/Match.java | 10 +-
.../poi/ss/formula/functions/Vlookup.java | 15 +-
.../BaseTestFunctionsFromSpreadsheet.java | 360 ++++++++++++++++++
.../ss/formula/functions/TestCountFuncs.java | 1 +
.../TestIndexFunctionFromSpreadsheet.java | 232 +----------
.../TestLookupFunctionsFromSpreadsheet.java | 337 +---------------
.../poi/ss/formula/functions/TestMatch.java | 56 ++-
.../TestMatchFunctionsFromSpreadsheet.java | 40 ++
.../spreadsheet/IndexFunctionTestCaseData.xls | Bin 27136 -> 45568 bytes
.../LookupFunctionsTestCaseData.xls | Bin 41472 -> 68096 bytes
.../spreadsheet/MatchFunctionTestCaseData.xls | Bin 0 -> 40448 bytes
13 files changed, 526 insertions(+), 586 deletions(-)
create mode 100644 src/testcases/org/apache/poi/ss/formula/functions/BaseTestFunctionsFromSpreadsheet.java
create mode 100644 src/testcases/org/apache/poi/ss/formula/functions/TestMatchFunctionsFromSpreadsheet.java
create mode 100644 test-data/spreadsheet/MatchFunctionTestCaseData.xls
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 ef524acde3..9a8a034104 100644
--- a/src/java/org/apache/poi/ss/formula/functions/Countif.java
+++ b/src/java/org/apache/poi/ss/formula/functions/Countif.java
@@ -43,6 +43,7 @@ import org.apache.poi.ss.usermodel.ErrorConstants;
*
*
* @author Josh Micich
+ * @author Cedric Walter at innoveo.com
*/
public final class Countif extends Fixed2ArgFunction {
@@ -309,7 +310,7 @@ public final class Countif extends Fixed2ArgFunction {
return false;
}
}
- private static final class StringMatcher extends MatcherBase {
+ public static final class StringMatcher extends MatcherBase {
private final String _value;
private final Pattern _pattern;
@@ -378,19 +379,19 @@ public final class Countif extends Fixed2ArgFunction {
* Translates Excel countif wildcard strings into java regex strings
* @return null
if the specified value contains no special wildcard characters.
*/
- private static Pattern getWildCardPattern(String value) {
+ public static Pattern getWildCardPattern(String value) {
int len = value.length();
StringBuffer sb = new StringBuffer(len);
boolean hasWildCard = false;
for(int i=0; i
*
* @author Josh Micich
+ * @author Cedric Walter at innoveo.com
*/
public final class Vlookup extends Var3or4ArgFunction {
private static final ValueEval DEFAULT_ARG3 = BoolEval.TRUE;
@@ -47,16 +48,16 @@ public final class Vlookup extends Var3or4ArgFunction {
return evaluate(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, DEFAULT_ARG3);
}
- public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
- ValueEval arg2, ValueEval arg3) {
+ public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval lookup_value, ValueEval table_array,
+ ValueEval col_index, ValueEval range_lookup) {
try {
// Evaluation order:
- // arg0 lookup_value, arg1 table_array, arg3 range_lookup, find lookup value, arg2 col_index, fetch result
- ValueEval lookupValue = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
- TwoDEval tableArray = LookupUtils.resolveTableArrayArg(arg1);
- boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcRowIndex, srcColumnIndex);
+ // lookup_value , table_array, range_lookup, find lookup value, col_index, fetch result
+ ValueEval lookupValue = OperandResolver.getSingleValue(lookup_value, srcRowIndex, srcColumnIndex);
+ TwoDEval tableArray = LookupUtils.resolveTableArrayArg(table_array);
+ boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(range_lookup, srcRowIndex, srcColumnIndex);
int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup);
- int colIndex = LookupUtils.resolveRowOrColIndexArg(arg2, srcRowIndex, srcColumnIndex);
+ int colIndex = LookupUtils.resolveRowOrColIndexArg(col_index, srcRowIndex, srcColumnIndex);
ValueVector resultCol = createResultColumnVector(tableArray, colIndex);
return resultCol.getItem(rowIndex);
} catch (EvaluationException e) {
diff --git a/src/testcases/org/apache/poi/ss/formula/functions/BaseTestFunctionsFromSpreadsheet.java b/src/testcases/org/apache/poi/ss/formula/functions/BaseTestFunctionsFromSpreadsheet.java
new file mode 100644
index 0000000000..920593c83f
--- /dev/null
+++ b/src/testcases/org/apache/poi/ss/formula/functions/BaseTestFunctionsFromSpreadsheet.java
@@ -0,0 +1,360 @@
+/* ====================================================================
+ 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.functions;
+
+import java.io.PrintStream;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.ss.usermodel.CellValue;
+
+/**
+ * @author Josh Micich
+ * @author Cedric Walter at innoveo.com
+ */
+public abstract class BaseTestFunctionsFromSpreadsheet extends TestCase {
+
+ private static final class Result {
+ public static final int SOME_EVALUATIONS_FAILED = -1;
+ public static final int ALL_EVALUATIONS_SUCCEEDED = +1;
+ public static final int NO_EVALUATIONS_FOUND = 0;
+ }
+
+ /**
+ * This class defines constants for navigating around the test data spreadsheet used for these tests.
+ */
+ private static final class SS {
+
+ /** Name of the test spreadsheet (found in the standard test data folder) */
+
+
+ /** Name of the first sheet in the spreadsheet (contains comments) */
+ public final static String README_SHEET_NAME = "Read Me";
+
+ /** Row (zero-based) in each sheet where the evaluation cases start. */
+ public static final int START_TEST_CASES_ROW_INDEX = 4; // Row '5'
+ /** Index of the column that contains the function names */
+ public static final int COLUMN_INDEX_MARKER = 0; // Column 'A'
+ public static final int COLUMN_INDEX_EVALUATION = 1; // Column 'B'
+ public static final int COLUMN_INDEX_EXPECTED_RESULT = 2; // Column 'C'
+ public static final int COLUMN_ROW_COMMENT = 3; // Column 'D'
+
+ /** Used to indicate when there are no more test cases on the current sheet */
+ public static final String TEST_CASES_END_MARKER = "";
+ /** Used to indicate that the test on the current row should be ignored */
+ public static final String SKIP_CURRENT_TEST_CASE_MARKER = "";
+
+ }
+
+ // Note - multiple failures are aggregated before ending.
+ // If one or more functions fail, a single AssertionFailedError is thrown at the end
+ private int _sheetFailureCount;
+ private int _sheetSuccessCount;
+ private int _evaluationFailureCount;
+ private int _evaluationSuccessCount;
+
+
+
+ private static void confirmExpectedResult(String msg, HSSFCell expected, CellValue actual) {
+ if (expected == null) {
+ throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
+ }
+ if(actual == null) {
+ throw new AssertionFailedError(msg + " - actual value was null");
+ }
+ if(expected.getCellType() == HSSFCell.CELL_TYPE_ERROR) {
+ confirmErrorResult(msg, expected.getErrorCellValue(), actual);
+ return;
+ }
+ if(actual.getCellType() == HSSFCell.CELL_TYPE_ERROR) {
+ throw unexpectedError(msg, expected, actual.getErrorValue());
+ }
+ if(actual.getCellType() != expected.getCellType()) {
+ throw wrongTypeError(msg, expected, actual);
+ }
+
+
+ switch (expected.getCellType()) {
+ case HSSFCell.CELL_TYPE_BOOLEAN:
+ assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue());
+ break;
+ case HSSFCell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation
+ throw new IllegalStateException("Cannot expect formula as result of formula evaluation: " + msg);
+ case HSSFCell.CELL_TYPE_NUMERIC:
+ assertEquals(expected.getNumericCellValue(), actual.getNumberValue(), 0.0);
+ break;
+ case HSSFCell.CELL_TYPE_STRING:
+ assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue());
+ break;
+ }
+ }
+
+
+ private static AssertionFailedError wrongTypeError(String msgPrefix, HSSFCell expectedCell, CellValue actualValue) {
+ return new AssertionFailedError(msgPrefix + " Result type mismatch. Evaluated result was "
+ + actualValue.formatAsString()
+ + " but the expected result was "
+ + formatValue(expectedCell)
+ );
+ }
+ private static AssertionFailedError unexpectedError(String msgPrefix, HSSFCell expected, int actualErrorCode) {
+ return new AssertionFailedError(msgPrefix + " Error code ("
+ + ErrorEval.getText(actualErrorCode)
+ + ") was evaluated, but the expected result was "
+ + formatValue(expected)
+ );
+ }
+
+
+ private static void confirmErrorResult(String msgPrefix, int expectedErrorCode, CellValue actual) {
+ if(actual.getCellType() != HSSFCell.CELL_TYPE_ERROR) {
+ throw new AssertionFailedError(msgPrefix + " Expected cell error ("
+ + ErrorEval.getText(expectedErrorCode) + ") but actual value was "
+ + actual.formatAsString());
+ }
+ if(expectedErrorCode != actual.getErrorValue()) {
+ throw new AssertionFailedError(msgPrefix + " Expected cell error code ("
+ + ErrorEval.getText(expectedErrorCode)
+ + ") but actual error code was ("
+ + ErrorEval.getText(actual.getErrorValue())
+ + ")");
+ }
+ }
+
+
+ private static String formatValue(HSSFCell expecedCell) {
+ switch (expecedCell.getCellType()) {
+ case HSSFCell.CELL_TYPE_BLANK: return "";
+ case HSSFCell.CELL_TYPE_BOOLEAN: return String.valueOf(expecedCell.getBooleanCellValue());
+ case HSSFCell.CELL_TYPE_NUMERIC: return String.valueOf(expecedCell.getNumericCellValue());
+ case HSSFCell.CELL_TYPE_STRING: return expecedCell.getRichStringCellValue().getString();
+ }
+ throw new RuntimeException("Unexpected cell type of expected value (" + expecedCell.getCellType() + ")");
+ }
+
+
+ protected void setUp() {
+ _sheetFailureCount = 0;
+ _sheetSuccessCount = 0;
+ _evaluationFailureCount = 0;
+ _evaluationSuccessCount = 0;
+ }
+
+ public void testFunctionsFromTestSpreadsheet() {
+ HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook(this.getFilename());
+
+ confirmReadMeSheet(workbook);
+ int nSheets = workbook.getNumberOfSheets();
+ for(int i=1; i< nSheets; i++) {
+ int sheetResult = processTestSheet(workbook, i, workbook.getSheetName(i));
+ switch(sheetResult) {
+ case Result.ALL_EVALUATIONS_SUCCEEDED: _sheetSuccessCount ++; break;
+ case Result.SOME_EVALUATIONS_FAILED: _sheetFailureCount ++; break;
+ }
+ }
+
+ // confirm results
+ String successMsg = "There were "
+ + _sheetSuccessCount + " successful sheets(s) and "
+ + _evaluationSuccessCount + " function(s) without error";
+ if(_sheetFailureCount > 0) {
+ String msg = _sheetFailureCount + " sheets(s) failed with "
+ + _evaluationFailureCount + " evaluation(s). " + successMsg;
+ throw new AssertionFailedError(msg);
+ }
+ if(false) { // normally no output for successful tests
+ System.out.println(getClass().getName() + ": " + successMsg);
+ }
+ }
+
+ protected abstract String getFilename();
+
+ private int processTestSheet(HSSFWorkbook workbook, int sheetIndex, String sheetName) {
+ HSSFSheet sheet = workbook.getSheetAt(sheetIndex);
+ HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook);
+ int maxRows = sheet.getLastRowNum()+1;
+ int result = Result.NO_EVALUATIONS_FOUND; // so far
+
+ String currentGroupComment = null;
+ for(int rowIndex=SS.START_TEST_CASES_ROW_INDEX; rowIndex= endIx) {
+ // something went wrong. just print the whole stack trace
+ e.printStackTrace(ps);
+ }
+ endIx -= 4; // skip 4 frames of reflection invocation
+ ps.println(e.toString());
+ for(int i=startIx; inull if cell is missing, empty or blank
+ */
+ private static String getCellTextValue(HSSFRow r, int colIndex, String columnName) {
+ if(r == null) {
+ return null;
+ }
+ HSSFCell cell = r.getCell(colIndex);
+ if(cell == null) {
+ return null;
+ }
+ if(cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
+ return null;
+ }
+ if(cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
+ return cell.getRichStringCellValue().getString();
+ }
+
+ throw new RuntimeException("Bad cell type for '" + columnName + "' column: ("
+ + cell.getCellType() + ") row (" + (r.getRowNum() +1) + ")");
+ }
+
+}
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 2d340ad8f1..02992fc5db 100644
--- a/src/testcases/org/apache/poi/ss/formula/functions/TestCountFuncs.java
+++ b/src/testcases/org/apache/poi/ss/formula/functions/TestCountFuncs.java
@@ -43,6 +43,7 @@ import org.apache.poi.ss.util.CellReference;
* Test cases for COUNT(), COUNTA() COUNTIF(), COUNTBLANK()
*
* @author Josh Micich
+ * @author Cedric Walter at innoveo.com
*/
public final class TestCountFuncs extends TestCase {
diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestIndexFunctionFromSpreadsheet.java b/src/testcases/org/apache/poi/ss/formula/functions/TestIndexFunctionFromSpreadsheet.java
index c2978435a1..6d900e1ef4 100644
--- a/src/testcases/org/apache/poi/ss/formula/functions/TestIndexFunctionFromSpreadsheet.java
+++ b/src/testcases/org/apache/poi/ss/formula/functions/TestIndexFunctionFromSpreadsheet.java
@@ -17,234 +17,16 @@
package org.apache.poi.ss.formula.functions;
-import java.io.PrintStream;
-
-import junit.framework.Assert;
-import junit.framework.AssertionFailedError;
-import junit.framework.TestCase;
-
-import org.apache.poi.hssf.HSSFTestDataSamples;
-import org.apache.poi.ss.formula.eval.ErrorEval;
-import org.apache.poi.hssf.usermodel.HSSFCell;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
-import org.apache.poi.hssf.usermodel.HSSFRow;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.util.CellReference;
-import org.apache.poi.ss.usermodel.CellValue;
-
/**
* Tests INDEX() as loaded from a test data spreadsheet.
*
* @author Josh Micich
+ * @author Cedric Walter at innoveo.com
*/
-public final class TestIndexFunctionFromSpreadsheet extends TestCase {
-
- private static final class Result {
- public static final int SOME_EVALUATIONS_FAILED = -1;
- public static final int ALL_EVALUATIONS_SUCCEEDED = +1;
- public static final int NO_EVALUATIONS_FOUND = 0;
- }
-
- /**
- * This class defines constants for navigating around the test data spreadsheet used for these tests.
- */
- private static final class SS {
-
- /** Name of the test spreadsheet (found in the standard test data folder) */
- public final static String FILENAME = "IndexFunctionTestCaseData.xls";
-
- public static final int COLUMN_INDEX_EVALUATION = 2; // Column 'C'
- public static final int COLUMN_INDEX_EXPECTED_RESULT = 3; // Column 'D'
-
- }
-
- // Note - multiple failures are aggregated before ending.
- // If one or more functions fail, a single AssertionFailedError is thrown at the end
- private int _evaluationFailureCount;
- private int _evaluationSuccessCount;
-
-
-
- private static void confirmExpectedResult(String msg, HSSFCell expected, CellValue actual) {
- if (expected == null) {
- throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
- }
- if(actual == null) {
- throw new AssertionFailedError(msg + " - actual value was null");
- }
- if(expected.getCellType() == HSSFCell.CELL_TYPE_ERROR) {
- confirmErrorResult(msg, expected.getErrorCellValue(), actual);
- return;
- }
- if(actual.getCellType() == HSSFCell.CELL_TYPE_ERROR) {
- throw unexpectedError(msg, expected, actual.getErrorValue());
- }
- if(actual.getCellType() != expected.getCellType()) {
- throw wrongTypeError(msg, expected, actual);
- }
-
-
- switch (expected.getCellType()) {
- case HSSFCell.CELL_TYPE_BOOLEAN:
- assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue());
- break;
- case HSSFCell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation
- throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg);
- case HSSFCell.CELL_TYPE_NUMERIC:
- assertEquals(msg, expected.getNumericCellValue(), actual.getNumberValue(), 0.0);
- break;
- case HSSFCell.CELL_TYPE_STRING:
- assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue());
- break;
- }
- }
-
-
- private static AssertionFailedError wrongTypeError(String msgPrefix, HSSFCell expectedCell, CellValue actualValue) {
- return new AssertionFailedError(msgPrefix + " Result type mismatch. Evaluated result was "
- + actualValue.formatAsString()
- + " but the expected result was "
- + formatValue(expectedCell)
- );
- }
- private static AssertionFailedError unexpectedError(String msgPrefix, HSSFCell expected, int actualErrorCode) {
- return new AssertionFailedError(msgPrefix + " Error code ("
- + ErrorEval.getText(actualErrorCode)
- + ") was evaluated, but the expected result was "
- + formatValue(expected)
- );
- }
-
-
- private static void confirmErrorResult(String msgPrefix, int expectedErrorCode, CellValue actual) {
- if(actual.getCellType() != HSSFCell.CELL_TYPE_ERROR) {
- throw new AssertionFailedError(msgPrefix + " Expected cell error ("
- + ErrorEval.getText(expectedErrorCode) + ") but actual value was "
- + actual.formatAsString());
- }
- if(expectedErrorCode != actual.getErrorValue()) {
- throw new AssertionFailedError(msgPrefix + " Expected cell error code ("
- + ErrorEval.getText(expectedErrorCode)
- + ") but actual error code was ("
- + ErrorEval.getText(actual.getErrorValue())
- + ")");
- }
- }
-
-
- private static String formatValue(HSSFCell expecedCell) {
- switch (expecedCell.getCellType()) {
- case HSSFCell.CELL_TYPE_BLANK: return "";
- case HSSFCell.CELL_TYPE_BOOLEAN: return String.valueOf(expecedCell.getBooleanCellValue());
- case HSSFCell.CELL_TYPE_NUMERIC: return String.valueOf(expecedCell.getNumericCellValue());
- case HSSFCell.CELL_TYPE_STRING: return expecedCell.getRichStringCellValue().getString();
- }
- throw new RuntimeException("Unexpected cell type of expected value (" + expecedCell.getCellType() + ")");
- }
-
-
- protected void setUp() {
- _evaluationFailureCount = 0;
- _evaluationSuccessCount = 0;
- }
-
- public void testFunctionsFromTestSpreadsheet() {
- HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook(SS.FILENAME);
-
- processTestSheet(workbook, workbook.getSheetName(0));
-
- // confirm results
- String successMsg = "There were "
- + _evaluationSuccessCount + " function(s) without error";
- if(_evaluationFailureCount > 0) {
- String msg = _evaluationFailureCount + " evaluation(s) failed. " + successMsg;
- throw new AssertionFailedError(msg);
- }
- if(false) { // normally no output for successful tests
- System.out.println(getClass().getName() + ": " + successMsg);
- }
- }
-
- private void processTestSheet(HSSFWorkbook workbook, String sheetName) {
- HSSFSheet sheet = workbook.getSheetAt(0);
- HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook);
- int maxRows = sheet.getLastRowNum()+1;
- int result = Result.NO_EVALUATIONS_FOUND; // so far
-
- for(int rowIndex=0; rowIndex= endIx) {
- // something went wrong. just print the whole stack trace
- e.printStackTrace(ps);
- }
- endIx -= 4; // skip 4 frames of reflection invocation
- ps.println(e.toString());
- for(int i=startIx; i
@@ -43,323 +29,12 @@ import org.apache.poi.ss.usermodel.CellValue;
* more easily.
*
* @author Josh Micich
+ * @author Cedric Walter at innoveo.com
*/
-public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
-
- private static final class Result {
- public static final int SOME_EVALUATIONS_FAILED = -1;
- public static final int ALL_EVALUATIONS_SUCCEEDED = +1;
- public static final int NO_EVALUATIONS_FOUND = 0;
- }
-
- /**
- * This class defines constants for navigating around the test data spreadsheet used for these tests.
- */
- private static final class SS {
-
- /** Name of the test spreadsheet (found in the standard test data folder) */
- public final static String FILENAME = "LookupFunctionsTestCaseData.xls";
-
- /** Name of the first sheet in the spreadsheet (contains comments) */
- public final static String README_SHEET_NAME = "Read Me";
-
-
- /** Row (zero-based) in each sheet where the evaluation cases start. */
- public static final int START_TEST_CASES_ROW_INDEX = 4; // Row '5'
- /** Index of the column that contains the function names */
- public static final int COLUMN_INDEX_MARKER = 0; // Column 'A'
- public static final int COLUMN_INDEX_EVALUATION = 1; // Column 'B'
- public static final int COLUMN_INDEX_EXPECTED_RESULT = 2; // Column 'C'
- public static final int COLUMN_ROW_COMMENT = 3; // Column 'D'
-
- /** Used to indicate when there are no more test cases on the current sheet */
- public static final String TEST_CASES_END_MARKER = "";
- /** Used to indicate that the test on the current row should be ignored */
- public static final String SKIP_CURRENT_TEST_CASE_MARKER = "";
-
- }
-
- // Note - multiple failures are aggregated before ending.
- // If one or more functions fail, a single AssertionFailedError is thrown at the end
- private int _sheetFailureCount;
- private int _sheetSuccessCount;
- private int _evaluationFailureCount;
- private int _evaluationSuccessCount;
-
-
-
- private static void confirmExpectedResult(String msg, HSSFCell expected, CellValue actual) {
- if (expected == null) {
- throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
- }
- if(actual == null) {
- throw new AssertionFailedError(msg + " - actual value was null");
- }
- if(expected.getCellType() == HSSFCell.CELL_TYPE_ERROR) {
- confirmErrorResult(msg, expected.getErrorCellValue(), actual);
- return;
- }
- if(actual.getCellType() == HSSFCell.CELL_TYPE_ERROR) {
- throw unexpectedError(msg, expected, actual.getErrorValue());
- }
- if(actual.getCellType() != expected.getCellType()) {
- throw wrongTypeError(msg, expected, actual);
- }
-
-
- switch (expected.getCellType()) {
- case HSSFCell.CELL_TYPE_BOOLEAN:
- assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue());
- break;
- case HSSFCell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation
- throw new IllegalStateException("Cannot expect formula as result of formula evaluation: " + msg);
- case HSSFCell.CELL_TYPE_NUMERIC:
- assertEquals(expected.getNumericCellValue(), actual.getNumberValue(), 0.0);
- break;
- case HSSFCell.CELL_TYPE_STRING:
- assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue());
- break;
- }
- }
-
-
- private static AssertionFailedError wrongTypeError(String msgPrefix, HSSFCell expectedCell, CellValue actualValue) {
- return new AssertionFailedError(msgPrefix + " Result type mismatch. Evaluated result was "
- + actualValue.formatAsString()
- + " but the expected result was "
- + formatValue(expectedCell)
- );
- }
- private static AssertionFailedError unexpectedError(String msgPrefix, HSSFCell expected, int actualErrorCode) {
- return new AssertionFailedError(msgPrefix + " Error code ("
- + ErrorEval.getText(actualErrorCode)
- + ") was evaluated, but the expected result was "
- + formatValue(expected)
- );
- }
-
-
- private static void confirmErrorResult(String msgPrefix, int expectedErrorCode, CellValue actual) {
- if(actual.getCellType() != HSSFCell.CELL_TYPE_ERROR) {
- throw new AssertionFailedError(msgPrefix + " Expected cell error ("
- + ErrorEval.getText(expectedErrorCode) + ") but actual value was "
- + actual.formatAsString());
- }
- if(expectedErrorCode != actual.getErrorValue()) {
- throw new AssertionFailedError(msgPrefix + " Expected cell error code ("
- + ErrorEval.getText(expectedErrorCode)
- + ") but actual error code was ("
- + ErrorEval.getText(actual.getErrorValue())
- + ")");
- }
- }
-
-
- private static String formatValue(HSSFCell expecedCell) {
- switch (expecedCell.getCellType()) {
- case HSSFCell.CELL_TYPE_BLANK: return "";
- case HSSFCell.CELL_TYPE_BOOLEAN: return String.valueOf(expecedCell.getBooleanCellValue());
- case HSSFCell.CELL_TYPE_NUMERIC: return String.valueOf(expecedCell.getNumericCellValue());
- case HSSFCell.CELL_TYPE_STRING: return expecedCell.getRichStringCellValue().getString();
- }
- throw new RuntimeException("Unexpected cell type of expected value (" + expecedCell.getCellType() + ")");
- }
-
-
- protected void setUp() {
- _sheetFailureCount = 0;
- _sheetSuccessCount = 0;
- _evaluationFailureCount = 0;
- _evaluationSuccessCount = 0;
- }
-
- public void testFunctionsFromTestSpreadsheet() {
- HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook(SS.FILENAME);
-
- confirmReadMeSheet(workbook);
- int nSheets = workbook.getNumberOfSheets();
- for(int i=1; i< nSheets; i++) {
- int sheetResult = processTestSheet(workbook, i, workbook.getSheetName(i));
- switch(sheetResult) {
- case Result.ALL_EVALUATIONS_SUCCEEDED: _sheetSuccessCount ++; break;
- case Result.SOME_EVALUATIONS_FAILED: _sheetFailureCount ++; break;
- }
- }
-
- // confirm results
- String successMsg = "There were "
- + _sheetSuccessCount + " successful sheets(s) and "
- + _evaluationSuccessCount + " function(s) without error";
- if(_sheetFailureCount > 0) {
- String msg = _sheetFailureCount + " sheets(s) failed with "
- + _evaluationFailureCount + " evaluation(s). " + successMsg;
- throw new AssertionFailedError(msg);
- }
- if(false) { // normally no output for successful tests
- System.out.println(getClass().getName() + ": " + successMsg);
- }
- }
-
- private int processTestSheet(HSSFWorkbook workbook, int sheetIndex, String sheetName) {
- HSSFSheet sheet = workbook.getSheetAt(sheetIndex);
- HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook);
- int maxRows = sheet.getLastRowNum()+1;
- int result = Result.NO_EVALUATIONS_FOUND; // so far
-
- String currentGroupComment = null;
- for(int rowIndex=SS.START_TEST_CASES_ROW_INDEX; rowIndex= endIx) {
- // something went wrong. just print the whole stack trace
- e.printStackTrace(ps);
- }
- endIx -= 4; // skip 4 frames of reflection invocation
- ps.println(e.toString());
- for(int i=startIx; inull if cell is missing, empty or blank
- */
- private static String getCellTextValue(HSSFRow r, int colIndex, String columnName) {
- if(r == null) {
- return null;
- }
- HSSFCell cell = r.getCell(colIndex);
- if(cell == null) {
- return null;
- }
- if(cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
- return null;
- }
- if(cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
- return cell.getRichStringCellValue().getString();
- }
+public final class TestLookupFunctionsFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet {
- throw new RuntimeException("Bad cell type for '" + columnName + "' column: ("
- + cell.getCellType() + ") row (" + (r.getRowNum() +1) + ")");
- }
+ @Override
+ protected String getFilename() {
+ return "LookupFunctionsTestCaseData.xls";
+ }
}
diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestMatch.java b/src/testcases/org/apache/poi/ss/formula/functions/TestMatch.java
index f9fdab6a79..ee6e45c1fa 100644
--- a/src/testcases/org/apache/poi/ss/formula/functions/TestMatch.java
+++ b/src/testcases/org/apache/poi/ss/formula/functions/TestMatch.java
@@ -19,6 +19,8 @@ package org.apache.poi.ss.formula.functions;
import junit.framework.TestCase;
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.formula.eval.AreaEval;
import org.apache.poi.ss.formula.eval.BoolEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
@@ -26,11 +28,13 @@ import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.NumericValueEval;
import org.apache.poi.ss.formula.eval.StringEval;
import org.apache.poi.ss.formula.eval.ValueEval;
+import org.apache.poi.ss.usermodel.CellValue;
/**
* Test cases for MATCH()
*
* @author Josh Micich
+ * @author Cedric Walter at innoveo.com
*/
public final class TestMatch extends TestCase {
/** less than or equal to */
@@ -93,7 +97,7 @@ public final class TestMatch extends TestCase {
}
public void testSimpleString() {
-
+ // Arrange
ValueEval[] values = {
new StringEval("Albert"),
new StringEval("Charles"),
@@ -109,10 +113,52 @@ public final class TestMatch extends TestCase {
confirmInt(3, invokeMatch(new StringEval("eD"), ae, MATCH_LARGEST_LTE));
confirmInt(3, invokeMatch(new StringEval("Ed"), ae, MATCH_EXACT));
confirmInt(3, invokeMatch(new StringEval("ed"), ae, MATCH_EXACT));
- confirmInt(4, invokeMatch(new StringEval("Hugh"), ae, MATCH_LARGEST_LTE));
+
assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Hugh"), ae, MATCH_EXACT));
}
+ public void testSimpleWildcardValuesString() {
+ // Arrange
+ ValueEval[] values = {
+ new StringEval("Albert"),
+ new StringEval("Charles"),
+ new StringEval("Ed"),
+ new StringEval("Greg"),
+ new StringEval("Ian"),
+ };
+
+ AreaEval ae = EvalFactory.createAreaEval("A1:A5", values);
+
+ // Note String comparisons are case insensitive
+ confirmInt(3, invokeMatch(new StringEval("e*"), ae, MATCH_EXACT));
+ confirmInt(3, invokeMatch(new StringEval("*d"), ae, MATCH_EXACT));
+
+ confirmInt(1, invokeMatch(new StringEval("Al*"), ae, MATCH_EXACT));
+ confirmInt(2, invokeMatch(new StringEval("Char*"), ae, MATCH_EXACT));
+
+ confirmInt(4, invokeMatch(new StringEval("*eg"), ae, MATCH_EXACT));
+ confirmInt(4, invokeMatch(new StringEval("G?eg"), ae, MATCH_EXACT));
+ confirmInt(4, invokeMatch(new StringEval("??eg"), ae, MATCH_EXACT));
+ confirmInt(4, invokeMatch(new StringEval("G*?eg"), ae, MATCH_EXACT));
+ confirmInt(4, invokeMatch(new StringEval("Hugh"), ae, MATCH_LARGEST_LTE));
+
+ confirmInt(5, invokeMatch(new StringEval("*Ian*"), ae, MATCH_EXACT));
+ confirmInt(5, invokeMatch(new StringEval("*Ian*"), ae, MATCH_LARGEST_LTE));
+ }
+
+ public void testTildeString() {
+
+ ValueEval[] values = {
+ new StringEval("what?"),
+ new StringEval("all*")
+ };
+
+ AreaEval ae = EvalFactory.createAreaEval("A1:A2", values);
+
+ confirmInt(1, invokeMatch(new StringEval("what~?"), ae, MATCH_EXACT));
+ confirmInt(2, invokeMatch(new StringEval("all~*"), ae, MATCH_EXACT));
+ }
+
public void testSimpleBoolean() {
ValueEval[] values = {
@@ -159,11 +205,17 @@ public final class TestMatch extends TestCase {
confirmInt(3, invokeMatch(new NumberEval(5), ae, MATCH_EXACT));
confirmInt(8, invokeMatch(new StringEval("CHARLES"), ae, MATCH_EXACT));
+ //wildcard values
+ confirmInt(8, invokeMatch(new StringEval("CHAR*"), ae, MATCH_EXACT));
+ confirmInt(8, invokeMatch(new StringEval("*CHARLES"), ae, MATCH_EXACT));
confirmInt(4, invokeMatch(new StringEval("Ben"), ae, MATCH_LARGEST_LTE));
confirmInt(13, invokeMatch(new StringEval("ED"), ae, MATCH_LARGEST_LTE));
+ confirmInt(13, invokeMatch(new StringEval("ED*"), ae, MATCH_LARGEST_LTE));
+ confirmInt(13, invokeMatch(new StringEval("*ED"), ae, MATCH_LARGEST_LTE));
confirmInt(9, invokeMatch(new StringEval("ED"), ae, MATCH_EXACT));
+ confirmInt(9, invokeMatch(new StringEval("ED*"), ae, MATCH_EXACT));
confirmInt(13, invokeMatch(new StringEval("Hugh"), ae, MATCH_LARGEST_LTE));
assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Hugh"), ae, MATCH_EXACT));
diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestMatchFunctionsFromSpreadsheet.java b/src/testcases/org/apache/poi/ss/formula/functions/TestMatchFunctionsFromSpreadsheet.java
new file mode 100644
index 0000000000..7fcb1fa13b
--- /dev/null
+++ b/src/testcases/org/apache/poi/ss/formula/functions/TestMatchFunctionsFromSpreadsheet.java
@@ -0,0 +1,40 @@
+/* ====================================================================
+ 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.functions;
+
+
+
+/**
+ * Tests lookup functions (VLOOKUP, HLOOKUP, LOOKUP, MATCH) as loaded from a test data spreadsheet.
+ * These tests have been separated from the common function and operator tests because the lookup
+ * functions have more complex test cases and test data setup.
+ *
+ * Tests for bug fixes and specific/tricky behaviour can be found in the corresponding test class
+ * (TestXxxx) of the target (Xxxx) implementor, where execution can be observed
+ * more easily.
+ *
+ * @author Josh Micich
+ * @author Cedric Walter at innoveo.com
+ */
+public final class TestMatchFunctionsFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet {
+
+ @Override
+ protected String getFilename() {
+ return "MatchFunctionTestCaseData.xls";
+ }
+}
diff --git a/test-data/spreadsheet/IndexFunctionTestCaseData.xls b/test-data/spreadsheet/IndexFunctionTestCaseData.xls
index d1de304576e0ea8c954502fe4861abf258a70cfc..64df3ecd86d51bf1d5c3157d7978219ca85aa7c6 100644
GIT binary patch
literal 45568
zcmeHw33yz^m2S1HrM+Ou3$_7cx8>cIgcsQu4A$0?Wg}Ta3%1!rTWU#-TWYx%FD$Xy
z*U1EvkOZ8`!tf>u2}yuJ@?H|0`7#Of#w71$V8{fr4rH=T0wkG)OhE7dPu+XF?$uRW
zddZuY_r1RTRd?5^s{foib?Ve|t8f30Z_N7shd*=n50vNHrlzR3N2jV{gC4;5-FCc8
zA$@d|QTy{Ud@Lb8?9}B2rH24oxf%P3b~fHItf;awakghdvp69}`^St8qPd7mTipOTzkkoSw#Y<0ORRg3pN
zeBKsCs|~0<1+|jzFXX*k-e=-Hpq`d8-xkVxwMe~;{6Y0D7AsKoC4mMNQE?Sh`&1I|
z|Cu=gkmCzfz#k>Y)YH~w?rCxaRP|}(2&k#2$x)!@pN5Mnx*^|$4egE^{Co8zL>e-a>(7Ys`6LOWPQ5EV!
zH#1!WH$M^0U|Ca>Wx83dMSEnK+T|U-c41+iE%|*l7nY>dZ$?A%soze9Nav_4^k3Ua
zdHNrtAC?WMkcQxUfk&TDT1kZ;bnD-te{vl9$H$>RHV*xvap<2KhyICi=pPt|e$P1c
z@#Gl~{|Uhy?6S1r>nvTB>yyr-rxVs;%64C!l#8^3?CwT{v9&NW!vZKap)D}
z(AW6VrvuZ^pUkl7PWx(mKEtLzuiEi-G3H<*=S(*pu$o?)rHAkpQ4jsspvtA`m~(1*
z9Q|nfpJmgZSJ&X{**4B>o9@W@sHHax=ftd8^QO;(o*xwY96LYowVt*9i!o2})AL+A
zzemn9ZTj<=1gdS09_HC}PkxZWA;b@TS$>9*fLfE6&SHrvSVxdJ(>gzoLQ3rS+yT
z(g?lKA|w9|!MW-NCm-nz>YLyB^usp>E7grII@1c&85m!t;2S5n9Go$%t(l3LK*87o
z%rdD0da^nR4isck99Z#56`(37Re)pfqzaTwwg4MtQXJ?`CRKobW>N*{ASP9So?}u4
z=u#$CfWBr@1?Yrw7O3;|2X@9OGKtn(+7_bej_Zp;?a)LLYltkW($WTL;UViGbv8odpvE$=y0<
zO$;P=>tH~fNOf|z&N&kU$=y0w7aEcv;vP$M-`TJ+ck5u?I1!NCt+QxiAh}xyBl|=o
z$=y1kiGk#9oh1_k$=y0wYM;J3O66{yr4s|m-8#!A29mpVmQM^Mck8T}7)b8cSvfI~
z+^w_90pWbU5l69MrKH{N!w21wQr##CplId4LNY#*zs
zIq0v%(NpgqW6poNKIUzd&5kO3JHFkaM~?*@p~jlW`7V$X(Q(XU^k*jw5N~ha;;4tZ
z9J8LZZESS(lmlYBpvND7d`xdQX?AZX4cKzoz1^gZZLAA=)o9Xe
zp-gvU3uV&Ax*%*Z88LI2;jSi>R9}l*w
z(u}&>lAytV`r+V>di@=3rx5+NqWweeP+0
zHVeFLd@OBc4mPHxooe~kD_`(ubC#ElkEN~6!N#<-Q!S@Xe%qhT*@v*dJIoOz%wqfJ9A9%Mvn?+tWK9;sB2OHDUHf;RLix2s;S?p!wV`&vR*qD~K
zVdG~$^fiArAuk&rOIw$NjcI8cHXeWOWq&qHyli|dtvUxA)6zC<{QWb3>BnXUZtY|3
z?Tnjso^kWs(UNh;N~PLv`l3JR(kv(}$_u*018Q0kZX30?`hzaZg2H~hpvyg=rp+kz
z(0|+O54t=H3TyF#uJ(YM7NXR#zK{5WuE>JIHoTyf9#GRBl)B^-U-buFnFWObDOx`GE#;Ukfw6`bgjL;05lM51cbPMb%^R+2@SFMVvumV}r9Je&_Coq@DY&E3Fc|IKA8Lrk0UUkGN
zQFW{YC8|6nN=9dlDdFaIraF(eMjH>0#3RFzbRu~q)D=CPJ|5iehAP-JTBaJ+VH`+g
z+Yd|oCm_fX=)4Q!9ELq`sb#l58{0Cksi+$^8MY@9Fx0JX+`!qRg=#yLmhhM=_eX3j
zFtf2{V!n?(zUFA8FE%_7!dk&2=#(;OwiyD^_G1uq00QbYf|vk0%;Ft4YB-u=2_(~w
z@cx3CSY#OB@O@tJSw7&}|BVAaTJty0a1KxKjV3sJpcj0$54iS&DAfQ!V!wOjDcCkZQ;9NCu{maW`4PT-0X=G&=%YWTR)hKlfwoM&qW<
zAKCrOFhPP}YKaZ+kM>#p{}PWd3*eVpq@nkt(feE;V2WdnjxVyk4>xIwp|~09e8{hM
zMKkGS1n$M-m;(#pn0En~HBG{%=vefMM*JyuY3%x*z*dfB@Gz-k3f*y|Th@RA%U!Va
zq&NsdS=tZaYj&|Z6(=PDu`7{|9uMwwV-!q7e<4O1LMhbG_PpHdv0WPXX|j%1$849T
zW424vvHJ_As|ytF1hi$+(&C3aOwR;#o9JL{o0~459@5q3BxEJgFIV3@8OX;Qk1EKUPS%wej@BP(8;)YPuNWRwFvtW;s`?s!w3n45rhK>NrV(a8X<#l2;nfo5mefg
zDindAw^Eery$ZS5`Y1V3kGBl1W8Li++_OMxOXV7Z*fmq^~
z!-H}TYHu~K4+3svyHh=-ar1vDmZF@YRJ1oS+(!jqtC70F=>p%hWa1E*`X=>?0ij}t
z1{3jU2stZ4`m0U}C8McKJe`8C=^BisLa7n(=}Qepqv;U1rz5f9ROnzN8B1hRp>#9_
zY9tj+h58f8P&|>?pBV}DXNG$@1y5ZVx~!$G?UL^HicqutMSq4Os7GsES3@(le0N2L
zqM<~8C_NYr4FNxNZDu%@w!q_&R0;yddIv)p@=1Gyx)7$jstYA49QIxb)Qfp`Xk~})
z@hg|qZ499zNbip&hcFE9J0h$o{OVrJ91lh@XVf*^7fr?bY%a)->d|7Hildf8SYU*r
z2P5%}tTRjPs9N4)rG!Ln6#3RPGQ&4TUnp=~x`XvkB6eJ%`FKiVpW}E2rZ66B#s?BWI|t
z3OoMjP-qtR%<_v;`(q=_YS$!GC*sEjh7(Chw+ijnpNWfw^k$Mt2p2yBj;YMZNFqu3
z_eBRI2Vqdj)qrlJ0uDiYmR^9?8yQ9(w!df;2~tIN2?{)rOu*`(S134zjPb;ws?e7)
z`O_w4wH3;0(-sR6m51t~5NKsMk%r-=(I|wA+G@43XY18aAsGzwJQRbaC8MkdwAUYr
z#WTq$D$zx)ThUZ#0DUJi3@&JcKCLw}LU(32k!GW9Sk+3wDEdJ>QuPrmjU9uGhO|9a
z+xAl7YD1JFrXibCHS|~-3L!&qqtT>vkQ5tC#9-g1%qVUqyR?om0*#hVKu-4RIA`@p@hSHJ!Q9vg|uZdxl8v;dmvD-l5hDsg9C(ztfM^Xn|-X6@ygp11m=&
zskFFOXebtmuhgBx>UH&GjYdyJ<7_I++*U^D0Exdclo(+PR)yiilLJ+e5wM6>jU-}K
zsZ^EaE2JF^ZJvq0%~m={>x9On`}y&P#)562eAw^K#C~c+G
z@pvhhJXjO(ysP3u6Bo4_EGVfYoXYq;0TW>Bx(A5=d1$9xj4QPoU)Ww=4&fcUy35t7X?-((Jm<_0E`8CP
zZpPcpi1FRF3Y({qBwveQK>TfUp}f4Q6N_u#lzpFAseG)X4q&g)nhW8ZG+EKOz+alJ6Xnky0
zHMMoLcD8kQG&HJ?#?Ej{i|QVZVaCgE&F$etZ}*PQ?j5QtnTe{#&i0NSU7hxPT@$hB
zz$BW}AKWKi*EbSVO_)o8iqANfkIn7vgGVsqh{W5HF-$mQwWQkHdfG8rPDWyB<)(LZ
zb=2+XLNQJ5klFJ3E$b|}a0c_c!)jOCu13`!8Hpx4W7kDhUA1bB_QfJyM@A6o?rOv2
zD2^LIQPtMe1S}`93lrFbvFM@p#7JgDR{%K^$<{<)RBfoG_xQ8g
zu!XK`X>Gvbm^v6vr&MQGT~}jk<1SXcF_MfQ!8Y4a_XsC-SV$@hzPY_2lS(Is!rfE@
zId(*b2cl|Y)t2)QZ>rj|v1d!wro-#2vF1Y~Vrk#kniz>egs|GMWlL+lYEOdOrI`q3
z$Vaww!KB(RmCv5_V2t)|PbNm#rkP}KG>nP88XV~zjIX<3eN}H{AF_zLI)@_3wA2;r
zrC6_v+#@;KWJa9oY3v)oX4P;ynTTT_7^~p8J2SQ2V{KY*PezWUlxV&U%LSi|&5?M2
z8Y^)Xj$#MG1BK!v*dRzv2+qlz6S93Ukxsbrsw<^_ZsBykaiGMu8roXgI(k~$!i^}|
z9&Qm!#b-}@L(688cD6JQ_a=BRNOgBLZ2@U#Vju2@W7RDdhj?3kT|+}xk9_Xx>Jdm7
zd$Z14e*^{Eu!)hx)`i+xzq8H3*qvxcSC>BA+`gx^r70GVc4HOIwyoZ@Vcq7c!$VYN
zYh)Ofp}SZZ%b>)-j&yV=g&k6uX+~D5JHkEd*7r2kH8h6XcJHd()z;BQ!$o1IH|p}^
z*|yUa$7Vy%-__Y(*U`AEVJ`upnRaaZz?2i2fk9h-u{~Y56WWSL+am)}RNW`Nt*_6M
zsB731jrN&dht=tB2TOh10DjS%%F6mGh>aUp@*Yj4B+E#aNmrr^Hoe{!&D1Xi)6PwW
zlQA@#6+yk(Mcan!u?X+vE|e~X{@^%NX4o;z?$Qar8g&wOp;5!J{{ANJSdl@)U?SEV
zRb3t3x`hU!jfZ=qx@*!1Aa6r^BRV&}Fn>062X@ht{Sg%I984T?r;W)BZ;J}RN}@>_
z2;fNIrs4C`Y#8qAId<8(yJD01Gn>T0n^}sK{
z;lk5nQwb~f-gF`nPucZza#=$Z{3iTP22K*5Frodn)`XK2-mVXZJtT%d7D(>bV*!t+
z)}Vr!5$tqeV6)WefQUmFVWYqvBfY&agiH$B9Fwsv*%wW6zbhWIVcI)8qsfD4q*RZz
zdoM>LDSd5XA3C7MFmi=4e)UFCHCB>1ofuLxmQdMz+W)F7A)FZ+I$}|q+ils=wWH~t
zPW0*xn>Y5<<8#yc>YiqaZ=!Wzgu*^SQja>)#cgBOBT`4*?#>Z3f((kBBy?=A*A6lq
z?T=swxiSTc+Sy_H1H_uc?HEGfW02Gwrh|#ZWF6BHVm;_n=yZ(-Qj!F4l(dQ^*rs*3Gc^qFr({kZ=R3U$l(+J_Wt#4
zFV)Wd+USbe*M8{Eoik=!_4wTPET6LHqebE9?9TFebKbS!LsMGst({i3y!LO48*cmE
zv>OV4zkI{NPd@+IpYD0St$n@v<%z;a-g(|H-u1)cw`+d(ldu2sJ8$lK{U5GB^skT3
z3Jq<&=izl9`tknew;tQ|iARQ?ea~+{@tOZP`0*#NDZh71Rq=BJCx@ng{OHlaZ;Z_7
z`PjcSeYNG9bFcr_`~$ClczW;s-@W;lqp{u(-+FHRBW1q{H!u5W%d6EteeQ-!AARBN
zRae|~{U3%@qPdxgYFV3!iaK`GRQ(t=D
ztolWp>Xy{LcYAQjcN*UMyLo-*-S?}5XD(m3bjkHC^RM{G)^OdEuYUAznit*q%-+9x
z^og0TZhYsV%br^K_I+g^`1s4o36{^pa9Us=)h?`u2zzxm1ky7N<8KlqdX
z^1E+bap4D^`NGUS+t+qa`^Rscd~p8Pel`2iwX>f-KYaJ`->&`cTXS2MEq`qH4}N;I
z_?y3a<(0So>50QtKmAx)eR=t!-xNN3wEnA~>G;XdFTd{9FMjsH$4{L4hxdPD`oNFg
zbK~#-?Q0L+_v-r}d1~g?`QNMgi?^QoM$cQ(2@=da$fMxa3y8
zUHp}Ek2ekW{5ctGUY5KD@*2o%Ag_VE2J#xnYap+Iyaw_b$ZH_4fxHIt8pvy4j0P@J
z(*k3N|KAdLmS`$YIO#j}1vuNVr@%f#WPkqepCuY9)@O-$wul^5SvFpPa}LMXXX8A!
z^3>F9oTm?-T9%FT%=uIJ_g-4guMB9{NGEP>OH8EDTFOiMOJY^;6DOB{(jKjxs<9{uZQ^Kf_XA#2*N-LlEM%8vR}*YzonR
zHLxx*4KeyR9h;6A4=d@|48(XCQOC|ejQlAcP1O{yIHcaP#VewC=zXSmaYEn;A}EAj
zCgLHRhNv~gP-Wpm?m1j!`^3u)vB@Jh}>T#bXCH#Y5+H
z#4DkAC2sLjgeVd5aH~bcLpFg3))FXaiB}@x@zkX*LGfleF^V_aiBUYF>6{dgXH6*{
zeF^;}1*Yx6k2vB5DPGVLuc&}dR5AvrGbEx2Fa54{78Id)h?T7Pdm-(Fb&Nvsz^;x_
zC_eF0$0!sZT}h$n%uJ!+$Q+@jQmCnhP*V+|kcfil2G8jCB7vhHB;0&ZSw~om1BZ@u
zG7NJS17;n0#1nE!9ofqWR8}i+H5!gCRHOryaBz1H9G>ZXGCbXYn=Wvr0*6F_)6(&m
zQpIW+Ov)`C;74a#y?+Ma>I=?Q6pW{hZNWg-NYdSjL?yBVm+~>xf=h30DiMb0;FcM<
zWtN2Rz;F6M*SOD6GWvE?Z}8y`!Yl)UXP+q<5kMCNEdxgaLBKmz)np*RX*qS{f1}t4
zbY@!Jl!4=hAfTp1K&>1Z<`@Y4UlkhxbU_H#5`m+PAk=CE)XG7aYasA3hBgA|f>2gV
zz0Q{ye%>T{Gt#EV+NDCZ!YZ6R2LML%nBwjgnK#5O_>Dg9av^}`X;#KP$4V4dT}iV^D^
z!J;oF7IH}GGSM|hRuc=Os)M!Ajdd-t777+UIkAvKO3xK6j>x**bRD602kTr5i~ZtU
z!O+$Rx*#m54HOjPLGXq6ca#_L%iu3T++PP4;{5eY^+D_P9@QqVouJh_Fa5m*XBS&Q
z+HxP-3Ln}^AKEG(+G-!#8XwwPA6kVEt_a=>hsOU>^76RAhjyV4?P4F=HXmAzhen$Yi4F3J!yL+J
zDQ1sA-{)W<-eL)?IK0FfEX?k@L`<69l_M>(2}E{oM_`(p2@F$nfXfM7?go}gBmYS%
zJ848V1DMku0@G$qVA!hzyp+I8-N4e3mkKaDG9^Yf19+|lOv9HBZMkw7y#u_Az{`wr
zbD03@VHk-9+yV=Zea4g%{l$S>PB{GJ+nkn)BU~=9wq}vdf*{aq*7(kjX9AuXF>8W>*TZtyyF2$;H;Ske6s_09^uY2;LbDPkf`C%cR9V*m8EMJ
zIIBm)CpmC|O(IJ>k|HaO({?BpPt`bhuim
z_^~_O+A{o1f#CWgIEfG}Tcj~COgdN>SXi8XT_6~G`UScmERdRUV3p>q
zy+9Dq0uI832Ev7cpr^T@3&Ly9P`-;L#wS;BVB!F5)()*eYm*k-2E6b#%RJ0x+$P{`
zM!EoG)9~7itzN-V+yt&QfNKa`;|7*Ctr1{*{{`7JL@j|hlDvoqq-=%TUWlJc?v+=Y
z^QkAE=KD;UaOz2CJtm}kW$Fn*P;>XDW1SDJ-iOxULks)R8hvP3rj6md-G|ofL)+m)
z+v!8Q#D~`6Lu>P)wfoR6^`Uk6&^mo+T|TsKAKGO;wB0_mJw7xzvoSiw;4p@El@IM|
zA6k!x#vVd@F%22E;`ESO8gi}Mki`kqiXqbpu!kUyxbxN;n0>dLyY5~5Kz?b>}U+MxzHUrqU
zOuBXx7;fDG?jUf74{(PI7}*5E?Ls&{n7|kt9Nvo`K6cH(K++G82ufXZ1K%xO>_jC^9BAo??
zb&LadIpHoh;4U|$L!!WCmyqmNR|$2obj@XoW4~#SXeiF2r3YONaAVyfz4B_oXRqX(
z9yzSiTx)R7U}9ZkVD_DO7s^rY2_RwH;orbMs=bkpU0gd=ZT
z&I|V&-L&5T1zm&Ue#vS)^3mOiFwVPS1}qqBtcM7K>s$xQ4w+U}|Cl1HF&VSIhU~NfOj^_*w;*c&!0-tpNqP0mV*eRv#VE1+|}0
z``s!KeeV}Y)Js%=92O|mvtLxeG#%qzey#6Hi0EdB-tA%-C&Rd#q3Ap=3~ikwhry8b
z&gwjF=)4jS7qxXB7iB?tE`~#7IAkbmNEm5r2i;(k5Nj_-z1W!aH>T>KN;c*JbwFBS
zUUOAlu=9WA}`u
zq%KRXy3qe=!#p5Lf}bPn44&e{LkaW3X+uG2LqVVm3vS!3#-m)_H9(ErFd26ZL?s!Q
zN|3_>q?8$_285^gH5CNU>xMt*h8G1LbSVfqEC|B0cD8A*R&?ZA2?s?9sEr%`kfDS_
zh7v#*(AR3dh^N3_CwjKeH0a%CeXwM1?F`K17pd#5mRMFx=G)}G8t+PdzeeZaGi4cP
z4m<`*-gn4*7#I}&9nyZwr2Q%t$A?n=7_xu4rWOxUh3NCuQkq5hsDgnXh3Mrt;B;1*
zO3+Y_YOVDC_u3b6x@s-X>6{AGW7}XR9(&Fw#cx-az+evGeWtn;angJ6+K$^|e9e?8
z#xY^oj$a?d}w!jXmqm2JSx4(Q0XzM
z^q5Db#~hV%wT3Kc5nlS8tF=l65G#(`Dz!@8Y=GWG(3?D=vI0Gonc(wha$E?L8ep8K@Ns19pc7d-h2IL#lvjjiIoHR_bIE!0s}@?j+cq9c3Oe4|8_Ze;2{-GQg<*y97*66OkrRb7dG|?vf5eC+6|aud3x6F805%Pt3a;CrV_rE9q)j2+asVgf0Z0knBU?
zvCbP1xNN3X@T}y!5y`)>v;ckxKNZLwvMNT==Pr
zwjTTI1J4F(k$x=Hl_(B
zkV6Bx*8c@BJomzhs&mTje=pYm6>mPq_5T(u{COsy>;HBH?gs8b;Li@Z?&tqk#1XiD
zPa|;M&wT*?Nbyz#?g!k9!2N-LNAT_kfTZ+#ex~(b81{c+jQv~$CftkQ-huT;QHtA9
z^7kxKI{VLUBDDPkk>$719=VPHIUapUIYJk
zHNbT|m#H6;?V~hX)0wEta{Qv*}
literal 27136
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
diff --git a/test-data/spreadsheet/LookupFunctionsTestCaseData.xls b/test-data/spreadsheet/LookupFunctionsTestCaseData.xls
index ea7fad9f3df61e28629bfb11ff343d3b092fee7e..56b6f12a00bdeff9cccf0b6282209ca4cf1a8b54 100644
GIT binary patch
literal 68096
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
literal 41472
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
z
zKY=mIs$i#8o;CxF=Ki#8wQ(e$LvvuSB2#_Yk=%(+uW0g#q}hpqqs1g2_&C=_Sopt$
z-zQ=}ip|69{bKBG<_i4Ijvx5+S^WNXe8~grFp|Ae_qI>}Yy9Rh2Hb)H>5BT~1E0a~
zvHIfK_1Mq&`-X>O6ZpNl@onZ?_=(cuk$`{54)OXe`b
z|2d5GKUuA@fnZiX{4tw%NoP@y=xL@
z5Qn{&ID~lDWxrX^{F%ESjw$`e5@$f`Tiw9_AJ-&KT+>kh0`e0dpA)BwXQ3|h+Fe=0
zvV{E<>A4wq89rmcl7kSQpAS*mxv*B(Bx0s`mU&pd0qNYNCB7R#k1+XwCgaaO`)*@t
zA=~ZbH*VI&UTjSR9D{j2SvnJo~|mRe#R{DGx|IhP;9w
ze7u12-+z~;i%yVov3$m{o!eui=6WnA|0@!at8V+mBb*$(`Fa@9`m!pcdJB;p6=
z?7WwG31a*t;Tu=~1Ber^OQ1
zR?{oMN|_b~EF`B_087T{6@Udgy#gF}rX>Rmfaw*$SUkM~?AldX+8OB&?6-B5`$IxH
zN_LP2}ii|B00COdhKv}sR2p#+Tjwc1|-#MhbK2{KvKPSVCU6>qa_!#
zHGl+>@LZyOr@Nz>b1k;uQed4UOOvlLsGqVyxNddubq{(A*o(F?X@7$
z1zF_|xT-cJ)oW*UZAhxu&YIegRIi=2wIQiqJL_sgQoVLMN=Ud~ddM^sL+dd19VQh2
z^qHcoosUK)ul2~$D3cfS{^MB2G$gS0k?c+l!4ur
zj53AYE1xMY?D#1w*&~lUGKHz$-rgxp1!0yc!W1>P?^IJk*wj_*1;}1?T@YO-Z=7y{
zP17yVIo$#@KUD_SHY+fs*s05s&B&&sA`8Ii4;Qd)>}!7)Y-7>2O;-sCtCJx_m2HQ>
z>VtfDEl7&J)sghnf+RZKMC+CIvug-b#F^Thx1xaSW4*mMmscXTw5K%ZpI;w~R?4=L
zD#~_z+o6jSu@X~Lo5zJABoAdPG>_tce<*+?+S|96)I(iPu_tXC!zqFi5<3Ka;R|1w
z(%XYD+uI2PZu!{W9)wM;Gu}`M)>&`)Ix9CW28SD~#vsfx6&!9XQ$g6&A?RW-