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 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 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 zcmeHw33!}Id1kfFmaq1GkKL9fAF?c4hwnozS(fi(Y|njo+EU9_MwXmzX%1j!1kB`u zkco#(GB5!WAP^uxAc15<7?WfPn;nvDNWw#4vn0F8lVtNO1hUU=b_3e?{i^E!tE&H& zZ6N!QCu-^M?)v_!?|bX3uf96|s_H-dg9TsxxnExS7bQjeQ zUX7?>bx9TR{{0mr1*D&%swyCT7j8{GBY9EG%iXGgTnr*dqqy4DO7*!o26)t_dL)<3 z@vY~y;ilSJe3KX{)irHCzJhXUxJisEwd^KRYE+ZNK!H~**9Ft^XicTm?rCw<+%yLB zGChy4z}h$76zRcfkIdIsm4NW z`be%#ETZF-Usa1y%1ZsusjPhJw;Nf~Yc2gB?X4O5ccUMsO{uJA!S^by&jx%I{r`%& zRr5~Ke>we^D$qYrf&SqN^tV@_f20EaLlx*hTY>(j3iL{ND)Ilg@R?PnpLbNCYdq!g zHJ)<%dn)kx-**k^wtXWmPGnI@D%=mKgm>}qaUo}NA*J6 z`2tJ-V|5l^*P=fv_!s(gs72Ek#q=z`LR>%(@GQ3R1Nst6kMybQ%Ti1KWAzBWuC)Bo zss;EzX!LC8FE3cIboNq|%iDy$+{OogT`s!3YSF_=Dwh>Det;7Ibo@U?6RQqJ`dMY^ zL41xV#V@7tMP6Dz{F+jmlwv{fW&9PtHYE5l(j`6>$X679w&q`>$uT{U8^t&DoF5;( z0PtC-)V)4kq2)`lVZRX44WFYv9UTS4hZR{qbq%8bh|YrG%ixLVHok`wR!1x3%cBEN zg5CsY5T9xn{ac*IK2sIw6eW6g_`EK?SiO#nqStx#g}?vO2cAlARZn?zMyOJA(Y{yX z8)dL_gl#HbzYx->^pbMBbK~JD1ns9YxE!Bwn3PaSa z?)PQ0Po+a9bQrh5gzbFHBrGp_m)n9)w{ugeubyP@uCi2CfoY|NFzJk@O%N)FUg{QL z`Wge6s|h<~AU~O*O}fC%3wUOg7#clj&>vCV?(qm}XUl z;MDK_{#5Llp9-ARX2hryC&jq`=P5PiC8#${o_Xe(DO=Oi4lQe54GFYa4h_8J^;G&N z0{|_c*GFDbBDsidF5dgz_eKCLsP69WDGR`Ya%mP6X<9rdBeYgrT6uf54bgRFqgI%C zPb=jKwnqurBCi_FTrN}vtRrDDBDk^R|LAl>;hWf3TSpIDHieK|m&+}@xx>8~*5REZ%Jv61MhZlQg}w_>bfGi%-QAD**lg~nUVSOb zy)ASWMp)=B%@(>88kqBC3*DuaH?_MFQ`cf!6}((n3!9ouMWD)=?D3eqTnMxt@ayEC|6A6pdS^*oq9D=kJZA*KK3yfjAmki3C*a&YQd#hEkra(c`6+`gngjK4uRFg z(}D%S8*m6B+X5gBMI*8;07Dbm7656GBCm3zJ^;El>Qs3>JBXWcE6ARwhps$9ca(sI z3bB56$`kZN32fi?CK7sYH^HIXP0)9{2@c+G(WSE^>s)LcwvZ&%GKtG43mQ3LoZ zU?P5;lg7w(t(v3iStFs%W)1_%dO^s#qnvf)G_2XeRbag^WZhZLdc!oVb^DlzdQr%_ ztDJSqG_2W@RzSTtWWD2h`c$CnwrNAHQ(Sr)Q}<%Eh_KMiYIj>$pot0({Z*FKq?gXN)^ z1S{G&jhJbyXhq06!HTv_!+IJkS{brVu%d0#uy(EJ)svt6^S?<}&Z&jx~381SdO2A zIhdraS1M|!b_Bljj;Z=eZBxi-ear|J5;9sBFmhU=kWo|22sRKhY7Q7xEcZERT%)X^ z#6w1#14b1~d5+o;GeY@>Rd#E@$SqT)PJHOINo6`mZH$>FlxBOt)NMhO`d=^hCoxUq z(ExgDz4^iGm}K^P9H~`P)v6Z*<3XsKOL^v~T6W(tn_+7Dd^{q@Nc&KJWME<>KR!G* zdObbiGpSlSHCyR5J_Bk()(ebdoe$R!aFj#OM5dhf5TOLkO2qT*Y4=qLgCX*2y}=-4w*F!-By$ zZdN@2kNi-SX6s}P(rgIQ)J&z3W&+ENiqc0MP)|lF+9vCfqAf^KKQ*T;g)eJ~+Qz3L zho87Mk{`{Fj}@QHUMM^mVnYxrJ-pkWAlG-6v1yPQ;$)>0eL zMaJCAk+Ct@`97a_YUNaoIs&d^V(GfeLRg&ou?C7fSYGqc4aXed(L#Q3c=U1>UG<>A zs^_D?jsg~|4q;seRCdu>$n#XEn;L#EfK$C7A+`2>q9fLc`kJZk_r3DxKu3mME<^ag z5YEB`hV+3N&JH&HXeJqAk~Pd6?ccY zmaaoqx(-?CIz%tKf3g<(YVyU_)0`8;?e;lU&4$Om2V9D% z9@fnyxmPyYqO`Z9?^SPcCKa9s+y(f%04A3t1LpzjN%?jP*`fEEfcfl!VusaYVhC{z zR)ouFO*#FrQ(ecJX3pQTVo$06oUT)AKe-s8KUV+z&06)1^s`ER@b45QszE*~eqKHQ zgL~o9-wQuIsczi3fsaY`(o0k7rOqMncvZa$K12AM0$mLu{3u2YYU(BR62en2AuagAU#QNZDb@MvtDqw`ua|HQf&I`+>ctmdREVHDUjhOC zu@l!za)H)~v>-e1_yhJmrKv7Md~@o=3@&`Cq_$S#qq)*8GP;iB&XHUh(Sg@ z`JYIuSBCVq^m6q!r%+S8ix)rNjb-TXcJ7h>W>tfGDfQu04QgSkW*OMqG@nVO)n^d+ zligM7pVj^aSCO~RBTb7^<7nx#rGa5gP7M!Xz4|Dun>{{$$(AOrT@%dNCkx|?luC#P zMV=qNdTpeT9WRu|OWCoZ?A<5NoH=pV+3ZA#HZBUv=8Kmnu0lrkvHZwHp@dWylB1W# zt`@QrqZ6fx{79JUq>~ETyjB zbyDtSF9_rIkWdz*GmOy%zXMd+cJpb&d{9k#2TZZJeY?E3%e$s@%J(jL-y!ci<$agD z@0RyHd~cVu?UJ@#(zZ+5c1hbVY1<`jyQFQGwC$3%V|xg9hk<23cZLymCK$odn;QY# z$fdFIE7`{i1L$;?sN#vMqa}C|k6avkyo7#IXk|C&k7W|N6bA8P9@Ydv7U@JE9<|XAwMv8^m>3-% zmwJ+Yd>B?&EU$IQP!=t^1Tn0lv`gY2 z7`w{4Rcg%+4}th(8w#v=f1x-=TiD9-&tDoq%e=nt*xlQEGq{fPu+w#})16K-d*T#n1N~N};vEo&XP}_zkMnxl~ zHtJ7T&wfkmFOFS3f2|1bUAj^zjLQr!x~BGpjvKrH*>*u5dm+tyEx?&wfj=t6&`^-0p(n)=q7HT8nFajNISp`&=PcRCNB3ge~iTm}EE zV~7JI`BEwS_?2N8@dV|J2cY)ix#gw{NJ((?;H(80hG#dL265xMo=!Se+#eb)UZp>N zQbe&q*gOEGK32exQj`x@T^b&=QV<>1J~pa(OU6x!W=-9rm6J_Y>j2R6ur%Z?UmP4R zK@{WX$J`ERNN1gGn6f$LTlrI`?4OhlwS#y7aqDF5x%n40pg1ABSBbNMTML zi5wQHCjA3_Y!)q7p(r(>MBGdwCQc`g@9yn^{3x`?>IH)>778P%A7k+1T?IvC>3&qKvFst-(=E{$x-yX_p~&nd zlom{6jGTl)307}1k93jO5eX&Tn;RJ(FJLfhOi(h#I<9kqA{#C|hTIg;`vodg_>HjV zahi#A%km=|k<+oOR!A>JqZKR_M%Z>C8uhg?Pfy4G;KuCOH8yE&u-yHnM~AN+gthE1 zj1C_BXSzwHK?~y$x(7cXy+#==pk&9c4v(WF*vdl6U%OTudtw-+#KxC3bhtFD zSQwuujur;nvd2b^t9X1&T#xkN+>&KSZ-c1@!kvyGscV9O{K2o|3orPW;W-l7Ya!t(hrNZUm zQO?<*X*V8H4_mSibhLK1c0Giqo$*i#^lb24*@rz&onB~%7ot5=N((v^xQd0#g(uKFb2tni3$xB{LA%E0 zYNP;*ES3P@45Um0f?{XYUn>1l4C9|LwT||{8?5Uic_h6`Bi*=0iW|B`c%78KZeg=#R&ErK(PZuyAoh^F{<@pHk?r?Q5tqN|-t2t;Ic{qL zN6xzOs0C^PlZkkJq1%2AkG+g%crV8=kiviMhehbuT|kjmPl;VA^@&T&Ef$g@3|*$a zoJR5SRfS^3i#pP?f$1s~q4q>I84iOg*Z_ptk>cPf$rM{?^s|yhSD|jjw{vE{fyH6mFQaO3o zZ*PLFzsW1-D(rgCsA`I5M(gp1nP(L0m(yRv6m7jsLR?NSQDj>9!*rmxn->1**o z_n4Eu4xBzQFODxhdutp&4JUjR4KZsv8|DW5U0oiBXQ9z@)6ZrzT^;j753Q^rr?=Lf zN!^gMv*S0~)MH4aUkGW{2{b+w4CCns{=N)6GAxUy*JSwd_)szA*N{MC;~mE1PVr#lyT# z84aVjGTN#H+8qfr?r06MtPNE;wbLuP!(EATDA@~;2uT~>S-*M;5ky#{k);O zWwQ|4W@6G3b6EPJ)3FBT6hghjl(M`>HzJsX>SwxHuw*B`7vl9YSZKP(4Ct z5u)TOE7_!*g%xNRd&#|Ob8m5uJgafxX?=8Wwjkb}t5XLdm%RY}V%v-wFtthb;drzg zD{?B;OdD*-&B`&*gkHoA+^Uyby){Fsz6zTzFmCIlm>PV|03fuORiRO0f%98LDkR-u%t6Zv0VRygp((7u2}Yr z8K#2jv{^#+F2zm{Iv_Wt{5YS(`2vEO51Rcz8gCL-qb%;fF&fsZC zEV2d%58Eb6IVKuu5Ilzro(6;Gpx|NscJaU`hj?}p0c&FhgD0&KniTWvmmW)kewk&$ z93aMxbAo{3jKa9a+|#AkEu}ZxV7(wQXCr1W)*k7YY(t4ICs^5DX!DvarH8h{Vx@U; zp1_(HmLN+)m6OaNxsDN=7aMTg%W1K51Yo}rJ4cJ1)MD8rLF{BpZjOjWbL5KUB!v|_ z*N9zw!xcN%=!F)@woLMX7_>m>%Vov`7A-SwK?a-2yK^oxtP~|xkr|aO_YfvCjQcG! zYtDOEcodky!Yo~QnMA%B2PEeYT)qokzN`&;i2+&97uNL3I#i8>9AnW2<^!aLPUX)x z0vM02at3SAbT+^qA)ZD!U$U_%7RQ7$(lT7+FxJPWMOQ- zr9Q(Y4#OoL!zF@nnJ`@9Fa!g`aEUNn<}+OCFkI>}Tq+EkgyB+$As85jONHTbpW!lx z;j)M!%XXP$c{|FMnO^3w1Pfu=0xHdQh0k)iu++L*E-YJ8WVl=qwi|}a9fn}wFx_O%N4@1B}LN#3$ny?)(XLhcGp%ZW@n|a%m~Ys5lek1wm&2iT+0xj z8C~NuS!Hy`wuMcl`d&x>zx8R+H+hASyz5<#asjWcE^mzQdb2pvbCkLE_p( ziE9-kt~Cgzyps;2~1K2-s*yIHYP+H3;cgwfOcV=&W+!iV$8>k+`#uAsvqc zJ3I7hgg8C)A~%|Nzd&D)jJwb=p+o5FJ?QHdpkuRNW$5cs^JD1iQS;BL*FuOEW}$;C z#)z*2ozTJ6fzG{z7W#U0*N$V}MQ3C^pl3ua)OAw;7^P0YO&ahvt?MQN#-_qVz%sgP z62Q+u1dF-}fcx=RBboy*eE$gp6TAeN)-ZP}ax}$^HDR!jGbtMI^x|pIsre=W{2CLB zZbIw2Ie-q`6FQdmJ7&_X03F*HD?@Mgpf`KaxlN!SzEW!#yd3D<8%YfldNZIO7qHFH z^$GPB&^@ad2GC;r^Jir?i0B@KM@CFa?1`jZJ0n|@IAWZ_i9Kc{20p>Hi z69RFA0G>n&0^T5iUyl$0Zwvq<0|dO$1H4fIFt%bQYFNf&8wD^6kRmn;;Ey9j&25Y- zBDezHb1lP%M~oW!MuGl@h%fE>jR+C?rT{v!Lg<@3=$jOvZ>j)&7r|~4 z=sygmv}^7S?Z=?6Rf0{C4KHv;}Sga~+R02mn{;H@6utqOp*Mu6v`>)|xN+lg_j zASPEGauIKh5Q8mHy&JKJ7;GKH;2$C0Du~|`aUHbML$BV(I~cw2-RY=P<{h(*L;>mUaI2r-&__<~=IxDhdV6EVB6ZqEie zAmZ&F;_V8Ew^u^kMXhcZ#2+>?wnvEdJ>vXj+#^mfb8v!dgmb&#{6NHmILVPX*{ybQ zqANQ&row{*=at~Bcfq0w)0CGw?u&kyBJSg0A9rxzI%Yg!>5fl!_ zT_}J?DA<7QKsiV#2d9H_(1XHX>FURy_88pE9e3Tp3n9G3V}L0_+GB7IK$pfrDGhiK zLh05}(45nGbrVXrfx^=0c1ok$lZ%AlZTn(K=*R^Jk^4nFsQb`DEDyzEzj^8v^&4nR z?DY=yR`ouFKPG?gM^oIS-h>H(H<>^EjcW;V$WA(#{pm@d^(N2`CD8g3XonMM)MGkW z6v)C1pGot{b4cT};*fScfp#K+hKHYJX-_54IQbFEIFmp-i;@rH-6=GGM(tbz4NV-< z`tdF#(C$j0-JL+YS7`o>+I>RvXVmUbpgoX4doY3aPy+4Y1lq-b#_mcEG+j%&rIw(Z z#afcnwZzPI$QV(BmQKA%Y$UL63u=$3xH)Api?Vv0Zzxa1el3grGM<&?5+dhzNQ;1eiFy z0aC1kUI#(1hoCn?02Yw)3&=Kgz`4g*2jCSUI20l16$C&;1cy8Xhlt>igW!;Z;E;#l zP=o+1AjNjQ!NNfRUJ-)62*Dvi07OL4=OO4Lf<6a9pM#(;LO`oPLh!a-X(V*yfN^5V%TlT(9Zv!uQ2=(t01G>QGNfbOif2-f0BgVcHSp!I`AAes zU=2DKC(};ARq$!lU@xIM^c0Q;$bfWe10JucmcFAvmk!_^CA_24!8_`~I|_Jb)h7+S zqY*stMrYZ=16Kzg*c*6z5XW!*PXOL})pv}Ft5(=nLe-f$-DAXdtO7O}TOJc^&j1aJ z=$NSP*A2E~5jHS~e2WcS9c*APv%7V8Tzx#i2CT$(++yo?upKA1F*&2R6U266I&3FAY$pWUZy0PRB5Yvp zU;|eN8`v9c`><%q#|9I`={0>PiLF+4;klYSNpvSGpp(hilY-7p#-0>(pEBr9M(DsE z2qr;ig#cFv9oQRmd#Nc;b3ltns>^BwSl=mtbc*#9!JVoAPNrK=3Ai7ISeo1^0r#5- zv9_Lyz=5p;4qP2@U~j@8(9z2oNnwY#oT;Zy@f$19~4KI>hj&aP!!n*>|2GWA&XUxbv03NjrI7z}czK z^B%bK5jgM#fPZhmfvp1${0+EWI6=z?2Ys^UT>z}0=3OAP3zeWrJ9I&y**VS&9<&P) zH1LM_-!{;|)`1582HJkbfrIPo0oB%wyQnpb&vd=-BEGvS;gbm+?ms&t^N*|=cSZPg zh^T(YpaL@o6}TEy9g0I27ZvI+yJ2?&QQX_Pn^^9yghl2M?iMU|*79x-3z#`wGca?y zX5cCz&P;O1)bIT1naO(*!_G|ZQXE3LcrjDS0=^G;tpd%Q;C%#rUj#Z~dh$MCj;AN@ z17=FslMP^q@3waVUI^hO-AOP-h=Z8@io-YU>hBY83w*Xz?+2!+R1K*63F>|WivIq7 zV6guFeh&eXJAE%CbP#}pL2v-4kop<{BDl1^2ed}I({TE_|79RNKnM>Q2%*aryL!OW z%L5)Nu!Ur+7w~dWfoX(_!&SGOFf1ji2VGR+Xbq|diR!`WP(A3OdeB1!whk)ra!`S( zLDhrPT76W|-P9J<_fUiizt9{O*+WG3&~(Ti@{m2`Ap>g%8F)F!z|EY=xJ?vq6*uw;-kUOb#PXfFgOkip-9aNk)(6;@s*!Cr0+7$J% zE&>`CtkK;YSQiQFqJc&0=00C*-4{I!NbO)iLI(pl2nH@ATZBCx_g1|Di^ll(1pZcI z@y`Sg-L;^)3wee^$L+y@+%5@blM*`y&3{hN(ABtUZFfh7&{i1lpwp z+JMmfiJ`#+S|Ncplt8H13akp>}%$U$M!W1pgaNPrvsGt0OdVE z;0U3%`w3nSATTw6_T%(SYqB^e7y~K*wC?y4Kt*-i%mQB`s7nSE4fc{_u$MdpNDe8s zhlzv^0&p-04&X#YjFqfxW(&$Cv*avzP1E)YZ1qV*Sfm86{6a)^J=YpRANau8$ zt(WJ5ivh9=hndK7n`&y=W9PJm!IM^T=2vX3mjNZRR)gg-v0QerTz0TrjhkQ`EM_YVmj1mIv0 z?82$B7Qtmf07Os(eM#RHB8W@k3WZ;BgkN!lU-5(^CHi!~t91KOaHW%qyGosg2l#a9!q^4X6%}t=qOQ3P)J=DQ`(84a*f&>~K zqL#%&AFzzJB!RXxfwnAx#<~|`S&=|nnLt~WKwF(ayCZ?NCZN#?!tmgj_+(Xwxci0n zqC-i0diCzv6w>P5vy90V_!&&i=yX-Wb6*syf`t81=zF!pS8tH55*!C3?!<8cp-;=t z&oOWAU%gCT+I_&9gWd<+Z8%g*PVO=x){+d|xo%3#)?Bl@RBz4AMKLM)ZiKias1_QK z=Rb=3JJX=0z6~15dTZ_mZp~foZ_S;h(mB1QxK*iOYcA(pYvF#t)NB~#j@*p2VeE6% z;;&ckQS8@ocpW6ZoyqPHlpeg^ znhUA2;S~}c1S+Qy)Hw+3=3I?HZ_Z_E8c?06{a%9r5?*g`pZ4f*uq@kHK zICxSYEgC!x4j#5l#HP2n>RrHx5vGdtE@1XcXydH)jL8?xWOfBSdKa+8!+rM|q>T3j zYyIH)YlMeIY4v0F1e-0ldQULB)x^U+YZ>(*Lc|049-cWB@XQvYAs$wX*^x%baW**h z;&%^F|4^>84GwY#H*jYKoMs=f-s3mNV>w4yjvAJ89F}tp%Vl6r1fM#9ubh=ZLrWR5r<`nUBoWSa2v9= zb+Y7X2EF(-8*OO|9hP7rEHj8hmP>t>dLwd1oXsLBsyvubtK62X1@Pe%wY4D0%yHB*m7PjoG$?lIqS{BVGg)a zxEJGKodfXo;`1qYAg9pxOQ zU{_0)sD5%>oxo86DLI0J$8k+LN5Oc9aI_fjDC3w?Uw=h&1P71f`U)KN=4Q)rO%g}s z`im;3UXRg7A2t-Xs2_oe>_(ornfN(e&%@-;BO(;g?W-Y7)n z#JA^kr*1upjbSoFVtnUX1I?`2XU6@JmRVCGvnFB2r&+78ZO~_iUb5?tnjL1%nptqr zMYDqpi3KHqivOw0s&AP-xHWuvg3fJ)@j(|G68UlqA)6>FlM1%eM)TDd(3KfPadSU8 zca;BDiboHybIvKE2}}uXz{T(Ns{9~G7GTAxAbM~P&lb^_*XgOC@7?i_@dOYp>z&vz z_hY}H)baO!zfS7Q|sr0P&zUd%rqi!iIX} zfqSUqsO$QB`Gn2Mo>l!X~6~iXfzM!X~6~ z!X~8ANrW_NCY+q%L`+EIL`+EIL`+CqoIqnQCyd9*nUKcGnUKbM5z;t06Vf<26Vf<2 z6Vf<26Vf<26Vlcs(AEYtx}T&88`=i%rNoudDj53ireWBJtbzy18D3m`P219(xv-j6 zr9*QNqCM%*JcMXT?2}f54l9_{M|tRpmm|?Vjtk;@9L&Y{=6Lt!diUn>p0YgIZDKk~ zrzY$G=m0`)6M(KZ+7qk-s8Qw^w`!C^I;W3ATT`W0*55zY2TQaD6n zSul@aM?A=*4!JqjQ%39q>3eRDYcQ15C#>mvZjNg({B!TQIj+GS_hdFV$H*b>=@%dA zU@nI_#`Mq)=Bt@Go|T)OW1wP=W%ZGsZOVZa^{Qu^H~~wnoNbD8Tm|qWJ)L8% zMRu?o)Bcarp2(7ZI(G9h)k-c92L;uWm!)J9x_?hQ-Ri7>#4x_ zo@80+M*|I#<@lbPWhzn6vesdzpFf2NZLUx|dv5;bh`jioE06K? zh>5z!X-#lc>mfmh*frQ-WIF}2~J0#sIlkN^L-5rw7-dp3zTO;W>T%f!)Ub;1s&fZ(=rCTfM*rGAr zS})yNNymGf!g>MKtFLM`YT&b&dH#m^`=DGCAStsf2Jz|a1 zHA=dyNyh`v+r6U*+L-mU>qVZu$2`aBnj~GLNv99}QfjB}AWNXiGT!6qlZD<2-3N+g48FoJbgt zvjicH$q_HUoX>{{0jU6z0 zyKvdlMo{%VyX@&VG2pX5Ww1C0mu((+%Hv2Z10v-J-|<|V-;eSA#yRrRU<#7>rdIpX(nz(D&H2wR2!eOQO1?bnLw6q7VnCJyD3A{N5;JM`~KaeB9%3 zM~4<5!~us6Ekvjvp*l1t?wlJ1an7w?tx55}nk~4VN<-`%e`z6U!V15%5KUNdO0yxi zFvkh2rdowXz*y8Iqr=`BEUB*IG?n?Kg?Pq_4|weym45)gT@l5nP5#VLw9biSF4C%l zlfq-0x&9(8{N!40fuIQ&X~k&d>8D;D#hE|qh(p!*_Y=%)lJoeS58murDPq%eJn>^S z_!K?~BQ{3W6F2%-5$r_Gi5yMPF1)e}oyd$1N$%k`R}^si#4z&|s3!_?h(!gOsR3i= zmakqbq#xJ$!*|Ytl<}3vwt8%CO^{>r_;(4+*gQ~$UIC<~gzI|XB181@YXf>+52qc7 zp6hy$=Sg2HdPR@sYnR`&KyS|S8>`}^Bl`WvpmyO6RjilV#N`G5Y(cWT5{~FcNx9{v z*V1TYbnfgNSY&1`jYY;=_L*wNWYa(IKLZ0Hb0X8LXb`}Q*))q@$jlbL%y8@FG_1u` z9~ab6eLmm$!dE{A)kE|S#HW;a9gJ3{SqEbwhwEU-xtJx6 zzN=EZ4u&qp9LNurRCeMa{5kIsev8W{0v_&NeiIl?B>caGha%NHhoN`lofM>2UE2{4Lc& zGQHMvi~!-a(yrH9juCD;H!nvYn&h?oy{Y28xOC=pbcVEEEc2s4M{Vn~^$`$lbIA;C zjk(ZM0#8MuSvXJvZ!5&q5fHs`36RyBo)URHO2z(n%!5PDXQF#-zh57P*nYn;3bFluE()>z z{^2M@y}cZT*nYn=3bFnExhS*F1wre#OZ1tfcP*A~YX!YXWU+0q?;Q5Sp*koe8vE3AEh_v^@#5y$Q5^3AFt}^L2V4 zfyS;3Z8A(n*L;jI*e^($R*Gu+rtSQt5z)9SD*?Rnwx@@$`GNZdv#s zWqvkJcOaJL;?9HpahP=m3Qz7_hYnOf{`3hJ>jc=(8er=HjD1TDY#o3xL|~kz)xcPh zG_Z97490AMG1rYo6m#7OQSrv)@JB6v-Uw~gHbeB9r9Pbim4+MIHNSPP{q&_dUXP_sIfMZlceMUxcDpOlD&Hbp|Y zm@+=$k6Th>^>Rt270TIVEtI}S3vCji0O97;F0Q1|W>08yB(zzCe#EG^*%R7qgt8r> z3)7;SMHEE4qV$SOZND2LQ53vE1pKlQko+?a6hMEd1#A!j5a72I zLk~$?MGB-^DYRG~d2c-u54O+X7!1vl2iwOg-s-Sqd!t!y6_ya;vgDccmL*S~&tO-! zX33N1W0q|WOEy=oE+E3P40R#B&TXO#+hgWw@fn=hp`~o|r0}$OtBdV{6iBTkMKsQn z-(!t$7b&(^#=?TaOy)UM05Bai~Am83|IuS2BR z5l08|5_iftWfte9!^sP(hrR>d(ncY5S}C2LlunUio48I-N~a^G3sSUG=oBe?wG@)s zuyx^=&~Ck9>jDIt9~-tVxRAJE>k>G&Ve0~%*zt?0rWOI zA|J&TaYy8DcZew4obB*L@x**#M9UAEFMQ6SFydu1ZYC+xn}UY>ALyahdX)#sSaCO_d~d(!d)(( zg9%P;)#t;@u|4c*HtTMuUNzW+GvD=L@w}xc+7l*;c!GR9NyK9^GBTyZo+vmtNO@{J z5fIIR01eO)%>iW&KPg9pW8d`=@|DoY;Frh9$Ae$6gT(&;nM4B)4jLXDuhASuf85s% z&`ovd6G-rI!kmFp4hhG<>!ai=L6Y``$H~XD8hqH1QNL#(frA5y2gqAU-2o&(w2*KX zd__n&8D1YLUkQ>-f$*I9cnSpkAof2RNZ{Z=;@R?q#FZm>mW!1mljsml$ls$l*@(ZL z_%g=R-0*~RPR!p=V4IQtV~o}KMXS zmQyHKthT>Vsa$2Mi6^7SQ%zt9Ij=$rtsfkmas?|1)nQ!F6W{^9sU7+Zba)0F-dCeK zckJAifU1Y6dZs}ob+X5+lRbiJ(x3td2NhTuRLA6@D=m*NY6&Q(I`z5e;;(R6Q)gEK zrd|b1z0+WlE=;drvR#;74-+^zn83riE{g)`6fvFWzl*y{7> zbnEDm7H!9;kx3R3qC27wMWT;IQ zX^$a=$F%>Jfdm!~B=CwL9gQFz5gh>{o7SU%gE|u*aOOigN=Qc?NIaa~LgLZvF(iF7 zJ0EF*r-=0rIRh61W}i8foi#@52jF<5H9Cws*+`)4^!o#i?SRm8CV^AQWs6bmV zI7A9L5urFPD1hqQ&DvmaDEf^+A(*s@&s(QLc)Yq*2oG1+3LyZb#+~0a065zt-cza& zk5p%f@z^5SovUlq^Yz%$jm^t1V#xbi^xJ+2N1wfbLH3*Q?e*r^GyZb8-N(vDg8ZKP z_I{a}$9I4}jG0BDW6H9CUY~qNh0`k#ImepArEokjmjsosKwPQbg_^ejPgZ3mB7Kd* zb(mh~d%hBp5cI1FA20loQh)hhlsfv=f9t7B8JZ;&4J1lrsL8lHTW$)KJ?xzr$jMUyIn zuJt1%t_}=o)OSeZ>cEi3)qx?6n*c%@S386>t_}=oD-&p|5@=i<7^b}=fwm@rwl<*A zQPKm2s{>m=)%Upajt_uz$ga&`X915*#M#;-(zwik!u=#P_%kp*xECFS(A8IIBIJ)fleD(pgV4Y7^cWvqs0n>KdZ1k(fE$<1d`l zqD-`h(L;$kwEXz>6)X;v;Z!pp)7IzebMhsfOXt`N=&i=%_^QN}g_MWHGfJVmjB$MbQwhHm4sIFdL`jGlXtKZ2?qs>SwT2yPgoE{V*3U(&aqaB92a^hI@-N$GY?VssS#>lowTlb%`t(oFZm%;?WXTz&7&? z3cD1x%tU!cX}k%UN04a~rSid1LKHy>Sgc41I8^KwC5Rd71(DVa=PJ93ley*jWX!|C z9OZ$(gsePFf>Iui61zotVrqKLBn^lXcn%qJbV4L>S`{6I$(Gza$q|};O2BH!n@FHJ zv4_EqG})T%qSwnLZP9a~q+YtAOR+=MlxF+D3!At=;lL&`fo z3yi>5dF36S3`UON;FNcoCwcL?hRab(RG$TAOVpeNCY9N)a${xIsOqw^kd{1HX8O=C zU1s*kFP2&Pay1AJg-eChhkV&m(aY4hw3$4hW~YM45;UIF>SjQG2THF+v)-Mn(dZ~H z9C3a1xp#i!pUzyF|G}S3t1TNp{w1!fdp|A?BzZrFiyhsgxYD>T z;9~PJh>HWkAHl`J9~Ufefc7q2l=R<_hyxv_&En#BrXMH_7KaCPm;!(%8^= z_EdggcyxTMbR~QEiGjih?&WZ){re{0^MjvD@%!W}TUI=u{ryxm=`*hx4a{g@Mgub% zn9;zD24*xcqk$O>%xGXn12Yu6$IPeC%0b8moSsS1PF``4)zo;mqaFl6QiZE z;&@?D;vThe$;5u21I7f#i3#$!XRP>W=}Mt6UJ`7SIrEy)z>EfFG%%xq84b*6U`7Ko z8ko_*j0R>jFr$GP4g71;0LSqh>T=A@=kc|;IKJgGeh!H_*5`6#4ud&n=lGqAw>j?T z7@vpi@tHrz`CM?#1=bwvb3TBZ8Mvm1i=??In$PsP=$XT6uKUR1T8FC<*Lqw{xSDZo zz_k(ACR|*D$c4>YaJAyvimMIRHeB0r@m!w{T%EYOaPeH8ow#`J&Td>hSBHB%_Tk!( zi_4-9;^I=V9IhT*y|@nH>ce#yS3j;JxQ^mFhU+-46Sz*|I)&>rt~0pK;<^*pIb7#) zUBGo0uDfyFgNySb_u;x9*8{j7#PtxahjCrR^$4y!u1mNEa1G)r;2Oep8P^qD!?=C` z*Q2;ba9zbUifatlHC*4Gnwl!&vxJNDEfcsN!}U0>CvfrpYw-D6T%3n_3fI%Pp20PV zi}$}}E`FbR{oB&OIatydEGG-gAH`=;JqfG0^%vGCrmV)QkSv?#yHYpgUGFo~U*mV$ znGM|o=|x!gCBZ=}zr7dE?Tq9T$E~LRtFP*qwWzrhs8v@{6Pfp8(87>lB4r%)Z%pc2 tCBB;=KZTpsxNcSc50V+3CtkJiI%BB)#88kDxIH{LW8${+FC}c`|3Ab?9nt^* literal 41472 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 z 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 diff --git a/test-data/spreadsheet/MatchFunctionTestCaseData.xls b/test-data/spreadsheet/MatchFunctionTestCaseData.xls new file mode 100644 index 0000000000000000000000000000000000000000..0b98a177136c381aae62af94f4dbed751c2f8359 GIT binary patch literal 40448 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