]> source.dussan.org Git - poi.git/commitdiff
Bug 54673 - [PATCH] Simple wildcard support in HLOOKUP, VOOLKUP, MATCH, COUNTIF
authorYegor Kozlov <yegor@apache.org>
Sat, 16 Mar 2013 12:33:08 +0000 (12:33 +0000)
committerYegor Kozlov <yegor@apache.org>
Sat, 16 Mar 2013 12:33:08 +0000 (12:33 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1457243 13f79535-47bb-0310-9956-ffa450edef68

13 files changed:
src/java/org/apache/poi/ss/formula/functions/Countif.java
src/java/org/apache/poi/ss/formula/functions/LookupUtils.java
src/java/org/apache/poi/ss/formula/functions/Match.java
src/java/org/apache/poi/ss/formula/functions/Vlookup.java
src/testcases/org/apache/poi/ss/formula/functions/BaseTestFunctionsFromSpreadsheet.java [new file with mode: 0644]
src/testcases/org/apache/poi/ss/formula/functions/TestCountFuncs.java
src/testcases/org/apache/poi/ss/formula/functions/TestIndexFunctionFromSpreadsheet.java
src/testcases/org/apache/poi/ss/formula/functions/TestLookupFunctionsFromSpreadsheet.java
src/testcases/org/apache/poi/ss/formula/functions/TestMatch.java
src/testcases/org/apache/poi/ss/formula/functions/TestMatchFunctionsFromSpreadsheet.java [new file with mode: 0644]
test-data/spreadsheet/IndexFunctionTestCaseData.xls
test-data/spreadsheet/LookupFunctionsTestCaseData.xls
test-data/spreadsheet/MatchFunctionTestCaseData.xls [new file with mode: 0644]

index ef524acde3028ccc23c5200b993f45b005012340..9a8a0341049d4ce4b6df66e5896df2d0b360e68e 100644 (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(".*");
index 62bc71b120024fbcf804eccc1918d8294437cfea..f2cc607d04c9dda54b5eec0ec19aa17e5151e82d 100644 (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);
index 8c89ab04046e02ed1537c212f8eaef5d9abcb5b6..bbcf0e2e6b54db2302c8c91d59c959ec31b4370a 100644 (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) {
index 539be0816360a8306936051df896c6e62c87e558..3ece2f7f6d0f478a145f674fb1320335c4778515 100644 (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) {
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 (file)
index 0000000..920593c
--- /dev/null
@@ -0,0 +1,360 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula.functions;\r
+\r
+import java.io.PrintStream;\r
+\r
+import junit.framework.Assert;\r
+import junit.framework.AssertionFailedError;\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.hssf.HSSFTestDataSamples;\r
+import org.apache.poi.ss.formula.eval.ErrorEval;\r
+import org.apache.poi.hssf.usermodel.HSSFCell;\r
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;\r
+import org.apache.poi.hssf.usermodel.HSSFRow;\r
+import org.apache.poi.hssf.usermodel.HSSFSheet;\r
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
+import org.apache.poi.hssf.util.CellReference;\r
+import org.apache.poi.ss.usermodel.CellValue;\r
+\r
+/**\r
+ * @author Josh Micich\r
+ * @author Cedric Walter at innoveo.com\r
+ */\r
+public abstract class BaseTestFunctionsFromSpreadsheet extends TestCase {\r
+\r
+    private static final class Result {\r
+        public static final int SOME_EVALUATIONS_FAILED = -1;\r
+        public static final int ALL_EVALUATIONS_SUCCEEDED = +1;\r
+        public static final int NO_EVALUATIONS_FOUND = 0;\r
+    }\r
+\r
+    /**\r
+     * This class defines constants for navigating around the test data spreadsheet used for these tests.\r
+     */\r
+    private static final class SS {\r
+\r
+        /** Name of the test spreadsheet (found in the standard test data folder) */\r
+\r
+\r
+        /** Name of the first sheet in the spreadsheet (contains comments) */\r
+        public final static String README_SHEET_NAME = "Read Me";\r
+\r
+        /** Row (zero-based) in each sheet where the evaluation cases start.   */\r
+        public static final int START_TEST_CASES_ROW_INDEX = 4; // Row '5'\r
+        /**  Index of the column that contains the function names */\r
+        public static final int COLUMN_INDEX_MARKER = 0; // Column 'A'\r
+        public static final int COLUMN_INDEX_EVALUATION = 1; // Column 'B'\r
+        public static final int COLUMN_INDEX_EXPECTED_RESULT = 2; // Column 'C'\r
+        public static final int COLUMN_ROW_COMMENT = 3; // Column 'D'\r
+\r
+        /** Used to indicate when there are no more test cases on the current sheet   */\r
+        public static final String TEST_CASES_END_MARKER = "<end>";\r
+        /** Used to indicate that the test on the current row should be ignored */\r
+        public static final String SKIP_CURRENT_TEST_CASE_MARKER = "<skip>";\r
+\r
+    }\r
+\r
+    // Note - multiple failures are aggregated before ending.\r
+    // If one or more functions fail, a single AssertionFailedError is thrown at the end\r
+    private int _sheetFailureCount;\r
+    private int _sheetSuccessCount;\r
+    private int _evaluationFailureCount;\r
+    private int _evaluationSuccessCount;\r
+\r
+\r
+\r
+    private static void confirmExpectedResult(String msg, HSSFCell expected, CellValue actual) {\r
+        if (expected == null) {\r
+            throw new AssertionFailedError(msg + " - Bad setup data expected value is null");\r
+        }\r
+        if(actual == null) {\r
+            throw new AssertionFailedError(msg + " - actual value was null");\r
+        }\r
+        if(expected.getCellType() == HSSFCell.CELL_TYPE_ERROR) {\r
+            confirmErrorResult(msg, expected.getErrorCellValue(), actual);\r
+            return;\r
+        }\r
+        if(actual.getCellType() == HSSFCell.CELL_TYPE_ERROR) {\r
+            throw unexpectedError(msg, expected, actual.getErrorValue());\r
+        }\r
+        if(actual.getCellType() != expected.getCellType()) {\r
+            throw wrongTypeError(msg, expected, actual);\r
+        }\r
+\r
+\r
+        switch (expected.getCellType()) {\r
+            case HSSFCell.CELL_TYPE_BOOLEAN:\r
+                assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue());\r
+                break;\r
+            case HSSFCell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation\r
+                throw new IllegalStateException("Cannot expect formula as result of formula evaluation: " + msg);\r
+            case HSSFCell.CELL_TYPE_NUMERIC:\r
+                assertEquals(expected.getNumericCellValue(), actual.getNumberValue(), 0.0);\r
+                break;\r
+            case HSSFCell.CELL_TYPE_STRING:\r
+                assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue());\r
+                break;\r
+        }\r
+    }\r
+\r
+\r
+    private static AssertionFailedError wrongTypeError(String msgPrefix, HSSFCell expectedCell, CellValue actualValue) {\r
+        return new AssertionFailedError(msgPrefix + " Result type mismatch. Evaluated result was "\r
+                + actualValue.formatAsString()\r
+                + " but the expected result was "\r
+                + formatValue(expectedCell)\r
+        );\r
+    }\r
+    private static AssertionFailedError unexpectedError(String msgPrefix, HSSFCell expected, int actualErrorCode) {\r
+        return new AssertionFailedError(msgPrefix + " Error code ("\r
+                + ErrorEval.getText(actualErrorCode)\r
+                + ") was evaluated, but the expected result was "\r
+                + formatValue(expected)\r
+        );\r
+    }\r
+\r
+\r
+    private static void confirmErrorResult(String msgPrefix, int expectedErrorCode, CellValue actual) {\r
+        if(actual.getCellType() != HSSFCell.CELL_TYPE_ERROR) {\r
+            throw new AssertionFailedError(msgPrefix + " Expected cell error ("\r
+                    + ErrorEval.getText(expectedErrorCode) + ") but actual value was "\r
+                    + actual.formatAsString());\r
+        }\r
+        if(expectedErrorCode != actual.getErrorValue()) {\r
+            throw new AssertionFailedError(msgPrefix + " Expected cell error code ("\r
+                    + ErrorEval.getText(expectedErrorCode)\r
+                    + ") but actual error code was ("\r
+                    + ErrorEval.getText(actual.getErrorValue())\r
+                    + ")");\r
+        }\r
+    }\r
+\r
+\r
+    private static String formatValue(HSSFCell expecedCell) {\r
+        switch (expecedCell.getCellType()) {\r
+            case HSSFCell.CELL_TYPE_BLANK: return "<blank>";\r
+            case HSSFCell.CELL_TYPE_BOOLEAN: return String.valueOf(expecedCell.getBooleanCellValue());\r
+            case HSSFCell.CELL_TYPE_NUMERIC: return String.valueOf(expecedCell.getNumericCellValue());\r
+            case HSSFCell.CELL_TYPE_STRING: return expecedCell.getRichStringCellValue().getString();\r
+        }\r
+        throw new RuntimeException("Unexpected cell type of expected value (" + expecedCell.getCellType() + ")");\r
+    }\r
+\r
+\r
+    protected void setUp() {\r
+        _sheetFailureCount = 0;\r
+        _sheetSuccessCount = 0;\r
+        _evaluationFailureCount = 0;\r
+        _evaluationSuccessCount = 0;\r
+    }\r
+\r
+    public void testFunctionsFromTestSpreadsheet() {\r
+        HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook(this.getFilename());\r
+\r
+        confirmReadMeSheet(workbook);\r
+        int nSheets = workbook.getNumberOfSheets();\r
+        for(int i=1; i< nSheets; i++) {\r
+            int sheetResult = processTestSheet(workbook, i, workbook.getSheetName(i));\r
+            switch(sheetResult) {\r
+                case Result.ALL_EVALUATIONS_SUCCEEDED: _sheetSuccessCount ++; break;\r
+                case Result.SOME_EVALUATIONS_FAILED: _sheetFailureCount ++; break;\r
+            }\r
+        }\r
+\r
+        // confirm results\r
+        String successMsg = "There were "\r
+                + _sheetSuccessCount + " successful sheets(s) and "\r
+                + _evaluationSuccessCount + " function(s) without error";\r
+        if(_sheetFailureCount > 0) {\r
+            String msg = _sheetFailureCount + " sheets(s) failed with "\r
+                    + _evaluationFailureCount + " evaluation(s).  " + successMsg;\r
+            throw new AssertionFailedError(msg);\r
+        }\r
+        if(false) { // normally no output for successful tests\r
+            System.out.println(getClass().getName() + ": " + successMsg);\r
+        }\r
+    }\r
+\r
+    protected abstract String getFilename();\r
+\r
+    private int processTestSheet(HSSFWorkbook workbook, int sheetIndex, String sheetName) {\r
+        HSSFSheet sheet = workbook.getSheetAt(sheetIndex);\r
+        HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook);\r
+        int maxRows = sheet.getLastRowNum()+1;\r
+        int result = Result.NO_EVALUATIONS_FOUND; // so far\r
+\r
+        String currentGroupComment = null;\r
+        for(int rowIndex=SS.START_TEST_CASES_ROW_INDEX; rowIndex<maxRows; rowIndex++) {\r
+            HSSFRow r = sheet.getRow(rowIndex);\r
+            String newMarkerValue = getMarkerColumnValue(r);\r
+            if(r == null) {\r
+                continue;\r
+            }\r
+            if(SS.TEST_CASES_END_MARKER.equalsIgnoreCase(newMarkerValue)) {\r
+                // normal exit point\r
+                return result;\r
+            }\r
+            if(SS.SKIP_CURRENT_TEST_CASE_MARKER.equalsIgnoreCase(newMarkerValue)) {\r
+                // currently disabled test case row\r
+                continue;\r
+            }\r
+            if(newMarkerValue != null) {\r
+                currentGroupComment = newMarkerValue;\r
+            }\r
+            HSSFCell c = r.getCell(SS.COLUMN_INDEX_EVALUATION);\r
+            if (c == null || c.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {\r
+                continue;\r
+            }\r
+            CellValue actualValue = evaluator.evaluate(c);\r
+            HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT);\r
+            String rowComment = getRowCommentColumnValue(r);\r
+\r
+            String msgPrefix = formatTestCaseDetails(sheetName, r.getRowNum(), c, currentGroupComment, rowComment);\r
+            try {\r
+                confirmExpectedResult(msgPrefix, expectedValueCell, actualValue);\r
+                _evaluationSuccessCount ++;\r
+                if(result != Result.SOME_EVALUATIONS_FAILED) {\r
+                    result = Result.ALL_EVALUATIONS_SUCCEEDED;\r
+                }\r
+            } catch (RuntimeException e) {\r
+                _evaluationFailureCount ++;\r
+                printShortStackTrace(System.err, e);\r
+                result = Result.SOME_EVALUATIONS_FAILED;\r
+            } catch (AssertionFailedError e) {\r
+                _evaluationFailureCount ++;\r
+                printShortStackTrace(System.err, e);\r
+                result = Result.SOME_EVALUATIONS_FAILED;\r
+            }\r
+\r
+        }\r
+        throw new RuntimeException("Missing end marker '" + SS.TEST_CASES_END_MARKER\r
+                + "' on sheet '" + sheetName + "'");\r
+\r
+    }\r
+\r
+\r
+    private static String formatTestCaseDetails(String sheetName, int rowIndex, HSSFCell c, String currentGroupComment,\r
+                                                String rowComment) {\r
+\r
+        StringBuffer sb = new StringBuffer();\r
+        CellReference cr = new CellReference(sheetName, rowIndex, c.getColumnIndex(), false, false);\r
+        sb.append(cr.formatAsString());\r
+        sb.append(" {=").append(c.getCellFormula()).append("}");\r
+\r
+        if(currentGroupComment != null) {\r
+            sb.append(" '");\r
+            sb.append(currentGroupComment);\r
+            if(rowComment != null) {\r
+                sb.append(" - ");\r
+                sb.append(rowComment);\r
+            }\r
+            sb.append("' ");\r
+        } else {\r
+            if(rowComment != null) {\r
+                sb.append(" '");\r
+                sb.append(rowComment);\r
+                sb.append("' ");\r
+            }\r
+        }\r
+\r
+        return sb.toString();\r
+    }\r
+\r
+    /**\r
+     * Asserts that the 'read me' comment page exists, and has this class' name in one of the\r
+     * cells.  This back-link is to make it easy to find this class if a reader encounters the\r
+     * spreadsheet first.\r
+     */\r
+    private void confirmReadMeSheet(HSSFWorkbook workbook) {\r
+        String firstSheetName = workbook.getSheetName(0);\r
+        if(!firstSheetName.equalsIgnoreCase(SS.README_SHEET_NAME)) {\r
+            throw new RuntimeException("First sheet's name was '" + firstSheetName + "' but expected '" + SS.README_SHEET_NAME + "'");\r
+        }\r
+        HSSFSheet sheet = workbook.getSheetAt(0);\r
+        String specifiedClassName = sheet.getRow(2).getCell(0).getRichStringCellValue().getString();\r
+        assertEquals("Test class name in spreadsheet comment", getClass().getName(), specifiedClassName);\r
+    }\r
+\r
+\r
+    /**\r
+     * Useful to keep output concise when expecting many failures to be reported by this test case\r
+     */\r
+    private static void printShortStackTrace(PrintStream ps, Throwable e) {\r
+        StackTraceElement[] stes = e.getStackTrace();\r
+\r
+        int startIx = 0;\r
+        // skip any top frames inside junit.framework.Assert\r
+        while(startIx<stes.length) {\r
+            if(!stes[startIx].getClassName().equals(Assert.class.getName())) {\r
+                break;\r
+            }\r
+            startIx++;\r
+        }\r
+        // skip bottom frames (part of junit framework)\r
+        int endIx = startIx+1;\r
+        while(endIx < stes.length) {\r
+            if(stes[endIx].getClassName().equals(TestCase.class.getName())) {\r
+                break;\r
+            }\r
+            endIx++;\r
+        }\r
+        if(startIx >= endIx) {\r
+            // something went wrong. just print the whole stack trace\r
+            e.printStackTrace(ps);\r
+        }\r
+        endIx -= 4; // skip 4 frames of reflection invocation\r
+        ps.println(e.toString());\r
+        for(int i=startIx; i<endIx; i++) {\r
+            ps.println("\tat " + stes[i].toString());\r
+        }\r
+\r
+    }\r
+\r
+    private static String getRowCommentColumnValue(HSSFRow r) {\r
+        return getCellTextValue(r, SS.COLUMN_ROW_COMMENT, "row comment");\r
+    }\r
+\r
+    private static String getMarkerColumnValue(HSSFRow r) {\r
+        return getCellTextValue(r, SS.COLUMN_INDEX_MARKER, "marker");\r
+    }\r
+\r
+    /**\r
+     * @return <code>null</code> if cell is missing, empty or blank\r
+     */\r
+    private static String getCellTextValue(HSSFRow r, int colIndex, String columnName) {\r
+        if(r == null) {\r
+            return null;\r
+        }\r
+        HSSFCell cell = r.getCell(colIndex);\r
+        if(cell == null) {\r
+            return null;\r
+        }\r
+        if(cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {\r
+            return null;\r
+        }\r
+        if(cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {\r
+            return cell.getRichStringCellValue().getString();\r
+        }\r
+\r
+        throw new RuntimeException("Bad cell type for '" + columnName + "' column: ("\r
+                + cell.getCellType() + ") row (" + (r.getRowNum() +1) + ")");\r
+    }\r
+\r
+}\r
index 2d340ad8f16e6f9047d220eb91b0dea70b48a3e2..02992fc5db0820bbe0eefe71fef3239dd1116795 100644 (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 {
 
index c2978435a131d7122447b97c047faee1d7c36449..6d900e1ef4709b9760c657c1cc9c35fec9d70312 100644 (file)
 
 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";
+    }
+}
\ No newline at end of file
index 6054d08208661b72adbcdb34ee09eab7c704e04b..09810a8dc734bde8e2f2e31c90c6763efe1f7c35 100644 (file)
 
 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 {
-
-       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();
-               }
+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";
+    }
 }
index f9fdab6a791c1ddb2d6ab960b7e0df224c4dc55c..ee6e45c1fa594ca2f77cf5dafd3d4b620114a798 100644 (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));
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 (file)
index 0000000..7fcb1fa
--- /dev/null
@@ -0,0 +1,40 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula.functions;\r
+\r
+\r
+\r
+/**\r
+ * Tests lookup functions (VLOOKUP, HLOOKUP, LOOKUP, MATCH) as loaded from a test data spreadsheet.<p/>\r
+ * These tests have been separated from the common function and operator tests because the lookup\r
+ * functions have more complex test cases and test data setup.\r
+ *\r
+ * Tests for bug fixes and specific/tricky behaviour can be found in the corresponding test class\r
+ * (<tt>TestXxxx</tt>) of the target (<tt>Xxxx</tt>) implementor, where execution can be observed\r
+ *  more easily.\r
+ *\r
+ * @author Josh Micich\r
+ * @author Cedric Walter at innoveo.com\r
+ */\r
+public final class TestMatchFunctionsFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet {\r
+\r
+    @Override\r
+    protected String getFilename() {\r
+        return "MatchFunctionTestCaseData.xls";\r
+    }\r
+}\r
index d1de304576e0ea8c954502fe4861abf258a70cfc..64df3ecd86d51bf1d5c3157d7978219ca85aa7c6 100644 (file)
Binary files a/test-data/spreadsheet/IndexFunctionTestCaseData.xls and b/test-data/spreadsheet/IndexFunctionTestCaseData.xls differ
index ea7fad9f3df61e28629bfb11ff343d3b092fee7e..56b6f12a00bdeff9cccf0b6282209ca4cf1a8b54 100644 (file)
Binary files a/test-data/spreadsheet/LookupFunctionsTestCaseData.xls and b/test-data/spreadsheet/LookupFunctionsTestCaseData.xls differ
diff --git a/test-data/spreadsheet/MatchFunctionTestCaseData.xls b/test-data/spreadsheet/MatchFunctionTestCaseData.xls
new file mode 100644 (file)
index 0000000..0b98a17
Binary files /dev/null and b/test-data/spreadsheet/MatchFunctionTestCaseData.xls differ