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
This commit is contained in:
Yegor Kozlov 2013-03-16 12:33:08 +00:00
parent 0f299e5b5f
commit 4d2be80806
13 changed files with 526 additions and 586 deletions

View File

@ -43,6 +43,7 @@ import org.apache.poi.ss.usermodel.ErrorConstants;
* </p>
*
* @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 <code>null</code> 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<len; i++) {
char ch = value.charAt(i);
switch(ch) {
case '?':
case '?': //Any single character
hasWildCard = true;
// match exactly one character
sb.append('.');
continue;
case '*':
case '*': //Zero or more characters
hasWildCard = true;
// match one or more occurrences of any character
sb.append(".*");

View File

@ -29,10 +29,14 @@ import org.apache.poi.ss.formula.eval.StringEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.formula.TwoDEval;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Common functionality used by VLOOKUP, HLOOKUP, LOOKUP and MATCH
*
* @author Josh Micich
* @author Cedric Walter at innoveo.com
*/
final class LookupUtils {
@ -167,6 +171,14 @@ final class LookupUtils {
return EQUAL;
}
public static final CompareResult valueOf(boolean matches) {
if(matches) {
return EQUAL ;
}
return LESS_THAN;
}
public boolean isTypeMismatch() {
return _isTypeMismatch;
}
@ -243,16 +255,37 @@ final class LookupUtils {
protected abstract String getValueAsString();
}
private static final class StringLookupComparer extends LookupValueComparerBase {
private String _value;
protected StringLookupComparer(StringEval se) {
private static final class StringLookupComparer extends LookupValueComparerBase {
private String _value;
private final Pattern _wildCardPattern;
private boolean _matchExact;
private boolean _isMatchFunction;
protected StringLookupComparer(StringEval se, boolean matchExact, boolean isMatchFunction) {
super(se);
_value = se.getStringValue();
_wildCardPattern = Countif.StringMatcher.getWildCardPattern(_value);
_matchExact = matchExact;
_isMatchFunction = isMatchFunction;
}
protected CompareResult compareSameType(ValueEval other) {
StringEval se = (StringEval) other;
return CompareResult.valueOf(_value.compareToIgnoreCase(se.getStringValue()));
StringEval se = (StringEval) other;
String stringValue = se.getStringValue();
if (_wildCardPattern != null) {
Matcher matcher = _wildCardPattern.matcher(stringValue);
boolean matches = matcher.matches();
if (_isMatchFunction ||
!_matchExact) {
return CompareResult.valueOf(matches);
}
}
return CompareResult.valueOf(_value.compareToIgnoreCase(stringValue));
}
protected String getValueAsString() {
return _value;
@ -423,7 +456,7 @@ final class LookupUtils {
}
public static int lookupIndexOfValue(ValueEval lookupValue, ValueVector vector, boolean isRangeLookup) throws EvaluationException {
LookupValueComparer lookupComparer = createLookupComparer(lookupValue);
LookupValueComparer lookupComparer = createLookupComparer(lookupValue, isRangeLookup, false);
int result;
if(isRangeLookup) {
result = performBinarySearch(vector, lookupComparer);
@ -439,7 +472,7 @@ final class LookupUtils {
/**
* Finds first (lowest index) exact occurrence of specified value.
* @param lookupValue the value to be found in column or row vector
* @param lookupComparer the value to be found in column or row vector
* @param vector the values to be searched. For VLOOKUP this is the first column of the
* tableArray. For HLOOKUP this is the first row of the tableArray.
* @return zero based index into the vector, -1 if value cannot be found
@ -581,7 +614,7 @@ final class LookupUtils {
return maxIx - 1;
}
public static LookupValueComparer createLookupComparer(ValueEval lookupValue) {
public static LookupValueComparer createLookupComparer(ValueEval lookupValue, boolean matchExact, boolean isMatchFunction) {
if (lookupValue == BlankEval.instance) {
// blank eval translates to zero
@ -590,7 +623,8 @@ final class LookupUtils {
return new NumberLookupComparer(NumberEval.ZERO);
}
if (lookupValue instanceof StringEval) {
return new StringLookupComparer((StringEval) lookupValue);
//TODO eventually here return a WildcardStringLookupComparer
return new StringLookupComparer((StringEval) lookupValue, matchExact, isMatchFunction);
}
if (lookupValue instanceof NumberEval) {
return new NumberLookupComparer((NumberEval) lookupValue);

View File

@ -62,6 +62,7 @@ import org.apache.poi.ss.formula.TwoDEval;
*
*
* @author Josh Micich
* @author Cedric Walter at innoveo.com
*/
public final class Match extends Var2or3ArgFunction {
@ -232,14 +233,7 @@ public final class Match extends Var2or3ArgFunction {
}
private static LookupValueComparer createLookupComparer(ValueEval lookupValue, boolean matchExact) {
if (matchExact && lookupValue instanceof StringEval) {
String stringValue = ((StringEval) lookupValue).getStringValue();
if(isLookupValueWild(stringValue)) {
throw new RuntimeException("Wildcard lookup values '" + stringValue + "' not supported yet");
}
}
return LookupUtils.createLookupComparer(lookupValue);
return LookupUtils.createLookupComparer(lookupValue, matchExact, true);
}
private static boolean isLookupValueWild(String stringValue) {

View File

@ -38,6 +38,7 @@ import org.apache.poi.ss.formula.TwoDEval;
* the lookup_value. If FALSE, only exact matches will be considered<br/>
*
* @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) {

View File

@ -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 = "<end>";
/** Used to indicate that the test on the current row should be ignored */
public static final String SKIP_CURRENT_TEST_CASE_MARKER = "<skip>";
}
// 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 "<blank>";
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<maxRows; rowIndex++) {
HSSFRow r = sheet.getRow(rowIndex);
String newMarkerValue = getMarkerColumnValue(r);
if(r == null) {
continue;
}
if(SS.TEST_CASES_END_MARKER.equalsIgnoreCase(newMarkerValue)) {
// normal exit point
return result;
}
if(SS.SKIP_CURRENT_TEST_CASE_MARKER.equalsIgnoreCase(newMarkerValue)) {
// currently disabled test case row
continue;
}
if(newMarkerValue != null) {
currentGroupComment = newMarkerValue;
}
HSSFCell c = r.getCell(SS.COLUMN_INDEX_EVALUATION);
if (c == null || c.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {
continue;
}
CellValue actualValue = evaluator.evaluate(c);
HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT);
String rowComment = getRowCommentColumnValue(r);
String msgPrefix = formatTestCaseDetails(sheetName, r.getRowNum(), c, currentGroupComment, rowComment);
try {
confirmExpectedResult(msgPrefix, expectedValueCell, actualValue);
_evaluationSuccessCount ++;
if(result != Result.SOME_EVALUATIONS_FAILED) {
result = Result.ALL_EVALUATIONS_SUCCEEDED;
}
} catch (RuntimeException e) {
_evaluationFailureCount ++;
printShortStackTrace(System.err, e);
result = Result.SOME_EVALUATIONS_FAILED;
} catch (AssertionFailedError e) {
_evaluationFailureCount ++;
printShortStackTrace(System.err, e);
result = Result.SOME_EVALUATIONS_FAILED;
}
}
throw new RuntimeException("Missing end marker '" + SS.TEST_CASES_END_MARKER
+ "' on sheet '" + sheetName + "'");
}
private static String formatTestCaseDetails(String sheetName, int rowIndex, HSSFCell c, String currentGroupComment,
String rowComment) {
StringBuffer sb = new StringBuffer();
CellReference cr = new CellReference(sheetName, rowIndex, c.getColumnIndex(), false, false);
sb.append(cr.formatAsString());
sb.append(" {=").append(c.getCellFormula()).append("}");
if(currentGroupComment != null) {
sb.append(" '");
sb.append(currentGroupComment);
if(rowComment != null) {
sb.append(" - ");
sb.append(rowComment);
}
sb.append("' ");
} else {
if(rowComment != null) {
sb.append(" '");
sb.append(rowComment);
sb.append("' ");
}
}
return sb.toString();
}
/**
* Asserts that the 'read me' comment page exists, and has this class' name in one of the
* cells. This back-link is to make it easy to find this class if a reader encounters the
* spreadsheet first.
*/
private void confirmReadMeSheet(HSSFWorkbook workbook) {
String firstSheetName = workbook.getSheetName(0);
if(!firstSheetName.equalsIgnoreCase(SS.README_SHEET_NAME)) {
throw new RuntimeException("First sheet's name was '" + firstSheetName + "' but expected '" + SS.README_SHEET_NAME + "'");
}
HSSFSheet sheet = workbook.getSheetAt(0);
String specifiedClassName = sheet.getRow(2).getCell(0).getRichStringCellValue().getString();
assertEquals("Test class name in spreadsheet comment", getClass().getName(), specifiedClassName);
}
/**
* Useful to keep output concise when expecting many failures to be reported by this test case
*/
private static void printShortStackTrace(PrintStream ps, Throwable e) {
StackTraceElement[] stes = e.getStackTrace();
int startIx = 0;
// skip any top frames inside junit.framework.Assert
while(startIx<stes.length) {
if(!stes[startIx].getClassName().equals(Assert.class.getName())) {
break;
}
startIx++;
}
// skip bottom frames (part of junit framework)
int endIx = startIx+1;
while(endIx < stes.length) {
if(stes[endIx].getClassName().equals(TestCase.class.getName())) {
break;
}
endIx++;
}
if(startIx >= 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<endIx; i++) {
ps.println("\tat " + stes[i].toString());
}
}
private static String getRowCommentColumnValue(HSSFRow r) {
return getCellTextValue(r, SS.COLUMN_ROW_COMMENT, "row comment");
}
private static String getMarkerColumnValue(HSSFRow r) {
return getCellTextValue(r, SS.COLUMN_INDEX_MARKER, "marker");
}
/**
* @return <code>null</code> 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) + ")");
}
}

View File

@ -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 {

View File

@ -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.<p/>
*
* @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 "<blank>";
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<maxRows; rowIndex++) {
HSSFRow r = sheet.getRow(rowIndex);
if(r == null) {
continue;
}
HSSFCell c = r.getCell(SS.COLUMN_INDEX_EVALUATION);
if (c == null || c.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {
continue;
}
HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT);
String msgPrefix = formatTestCaseDetails(sheetName, r.getRowNum(), c);
try {
CellValue actualValue = evaluator.evaluate(c);
confirmExpectedResult(msgPrefix, expectedValueCell, actualValue);
_evaluationSuccessCount ++;
if(result != Result.SOME_EVALUATIONS_FAILED) {
result = Result.ALL_EVALUATIONS_SUCCEEDED;
}
} catch (RuntimeException e) {
_evaluationFailureCount ++;
printShortStackTrace(System.err, e, msgPrefix);
result = Result.SOME_EVALUATIONS_FAILED;
} catch (AssertionFailedError e) {
_evaluationFailureCount ++;
printShortStackTrace(System.err, e, msgPrefix);
result = Result.SOME_EVALUATIONS_FAILED;
}
}
}
private static String formatTestCaseDetails(String sheetName, int rowIndex, HSSFCell c) {
StringBuffer sb = new StringBuffer();
CellReference cr = new CellReference(sheetName, rowIndex, c.getColumnIndex(), false, false);
sb.append(cr.formatAsString());
sb.append(" [formula: ").append(c.getCellFormula()).append(" ]");
return sb.toString();
}
/**
* Useful to keep output concise when expecting many failures to be reported by this test case
*/
private static void printShortStackTrace(PrintStream ps, Throwable e, String msgPrefix) {
System.err.println("Problem with " + msgPrefix);
StackTraceElement[] stes = e.getStackTrace();
int startIx = 0;
// skip any top frames inside junit.framework.Assert
while(startIx<stes.length) {
if(!stes[startIx].getClassName().equals(Assert.class.getName())) {
break;
}
startIx++;
}
// skip bottom frames (part of junit framework)
int endIx = startIx+1;
while(endIx < stes.length) {
if(stes[endIx].getClassName().equals(TestCase.class.getName())) {
break;
}
endIx++;
}
if(startIx >= 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<endIx; i++) {
ps.println("\tat " + stes[i].toString());
}
}
}
public final class TestIndexFunctionFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet {
@Override
protected String getFilename() {
return "IndexFunctionTestCaseData.xls";
}
}

View File

@ -17,21 +17,7 @@
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 lookup functions (VLOOKUP, HLOOKUP, LOOKUP, MATCH) as loaded from a test data spreadsheet.<p/>
@ -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 {
public final class TestLookupFunctionsFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet {
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 = "<end>";
/** Used to indicate that the test on the current row should be ignored */
public static final String SKIP_CURRENT_TEST_CASE_MARKER = "<skip>";
}
// 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 "<blank>";
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<maxRows; rowIndex++) {
HSSFRow r = sheet.getRow(rowIndex);
String newMarkerValue = getMarkerColumnValue(r);
if(r == null) {
continue;
}
if(SS.TEST_CASES_END_MARKER.equalsIgnoreCase(newMarkerValue)) {
// normal exit point
return result;
}
if(SS.SKIP_CURRENT_TEST_CASE_MARKER.equalsIgnoreCase(newMarkerValue)) {
// currently disabled test case row
continue;
}
if(newMarkerValue != null) {
currentGroupComment = newMarkerValue;
}
HSSFCell c = r.getCell(SS.COLUMN_INDEX_EVALUATION);
if (c == null || c.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {
continue;
}
CellValue actualValue = evaluator.evaluate(c);
HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT);
String rowComment = getRowCommentColumnValue(r);
String msgPrefix = formatTestCaseDetails(sheetName, r.getRowNum(), c, currentGroupComment, rowComment);
try {
confirmExpectedResult(msgPrefix, expectedValueCell, actualValue);
_evaluationSuccessCount ++;
if(result != Result.SOME_EVALUATIONS_FAILED) {
result = Result.ALL_EVALUATIONS_SUCCEEDED;
}
} catch (RuntimeException e) {
_evaluationFailureCount ++;
printShortStackTrace(System.err, e);
result = Result.SOME_EVALUATIONS_FAILED;
} catch (AssertionFailedError e) {
_evaluationFailureCount ++;
printShortStackTrace(System.err, e);
result = Result.SOME_EVALUATIONS_FAILED;
}
}
throw new RuntimeException("Missing end marker '" + SS.TEST_CASES_END_MARKER
+ "' on sheet '" + sheetName + "'");
}
private static String formatTestCaseDetails(String sheetName, int rowIndex, HSSFCell c, String currentGroupComment,
String rowComment) {
StringBuffer sb = new StringBuffer();
CellReference cr = new CellReference(sheetName, rowIndex, c.getColumnIndex(), false, false);
sb.append(cr.formatAsString());
sb.append(" {=").append(c.getCellFormula()).append("}");
if(currentGroupComment != null) {
sb.append(" '");
sb.append(currentGroupComment);
if(rowComment != null) {
sb.append(" - ");
sb.append(rowComment);
}
sb.append("' ");
} else {
if(rowComment != null) {
sb.append(" '");
sb.append(rowComment);
sb.append("' ");
}
}
return sb.toString();
}
/**
* Asserts that the 'read me' comment page exists, and has this class' name in one of the
* cells. This back-link is to make it easy to find this class if a reader encounters the
* spreadsheet first.
*/
private void confirmReadMeSheet(HSSFWorkbook workbook) {
String firstSheetName = workbook.getSheetName(0);
if(!firstSheetName.equalsIgnoreCase(SS.README_SHEET_NAME)) {
throw new RuntimeException("First sheet's name was '" + firstSheetName + "' but expected '" + SS.README_SHEET_NAME + "'");
}
HSSFSheet sheet = workbook.getSheetAt(0);
String specifiedClassName = sheet.getRow(2).getCell(0).getRichStringCellValue().getString();
assertEquals("Test class name in spreadsheet comment", getClass().getName(), specifiedClassName);
}
/**
* Useful to keep output concise when expecting many failures to be reported by this test case
*/
private static void printShortStackTrace(PrintStream ps, Throwable e) {
StackTraceElement[] stes = e.getStackTrace();
int startIx = 0;
// skip any top frames inside junit.framework.Assert
while(startIx<stes.length) {
if(!stes[startIx].getClassName().equals(Assert.class.getName())) {
break;
}
startIx++;
}
// skip bottom frames (part of junit framework)
int endIx = startIx+1;
while(endIx < stes.length) {
if(stes[endIx].getClassName().equals(TestCase.class.getName())) {
break;
}
endIx++;
}
if(startIx >= 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<endIx; i++) {
ps.println("\tat " + stes[i].toString());
}
}
private static String getRowCommentColumnValue(HSSFRow r) {
return getCellTextValue(r, SS.COLUMN_ROW_COMMENT, "row comment");
}
private static String getMarkerColumnValue(HSSFRow r) {
return getCellTextValue(r, SS.COLUMN_INDEX_MARKER, "marker");
}
/**
* @return <code>null</code> 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) + ")");
}
@Override
protected String getFilename() {
return "LookupFunctionsTestCaseData.xls";
}
}

View File

@ -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));

View File

@ -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.<p/>
* 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
* (<tt>TestXxxx</tt>) of the target (<tt>Xxxx</tt>) 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";
}
}

Binary file not shown.