From b36ecef07a4933c0943b27ea937f31e1df2e77cd Mon Sep 17 00:00:00 2001 From: Balazs Varga Date: Mon, 12 Feb 2024 21:36:30 +0100 Subject: [PATCH] tdf#128425 Add Excel2021 function XMATCH to Calc https://issues.oasis-open.org/browse/OFFICE-4155 Change-Id: I7791ab873cf62ce882feba75dc0d722bc5990f9a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163260 Tested-by: Jenkins Reviewed-by: Balazs Varga --- formula/inc/core_resource.hrc | 6 + include/formula/compiler.hxx | 2 +- include/formula/opcode.hxx | 2 + sc/README.md | 1 + sc/inc/helpids.h | 1 + sc/inc/scfuncs.hrc | 14 + sc/qa/extras/scfunctionlistobj.cxx | 2 +- .../functions/spreadsheet/fods/xmatch.fods | 3753 +++++++++++++++++ sc/qa/unit/ucalc.cxx | 1 + sc/source/core/data/funcdesc.cxx | 1 + sc/source/core/data/queryiter.cxx | 39 +- sc/source/core/inc/interpre.hxx | 3 +- sc/source/core/tool/interpr1.cxx | 180 +- sc/source/core/tool/interpr4.cxx | 1 + sc/source/core/tool/parclass.cxx | 3 +- sc/source/core/tool/token.cxx | 1 + sc/source/filter/excel/xlformula.cxx | 3 +- sc/source/filter/oox/formulabase.cxx | 3 +- 18 files changed, 3988 insertions(+), 28 deletions(-) create mode 100644 sc/qa/unit/data/functions/spreadsheet/fods/xmatch.fods diff --git a/formula/inc/core_resource.hrc b/formula/inc/core_resource.hrc index 835d928ea4a8..60460e93d38b 100644 --- a/formula/inc/core_resource.hrc +++ b/formula/inc/core_resource.hrc @@ -266,6 +266,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF[] = { "INDIRECT" , SC_OPCODE_INDIRECT }, { "ADDRESS" , SC_OPCODE_ADDRESS }, { "MATCH" , SC_OPCODE_MATCH }, + { "COM.MICROSOFT.XMATCH" , SC_OPCODE_X_MATCH }, { "COUNTBLANK" , SC_OPCODE_COUNT_EMPTY_CELLS }, { "COUNTIF" , SC_OPCODE_COUNT_IF }, { "SUMIF" , SC_OPCODE_SUM_IF }, @@ -714,6 +715,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML[] = { "INDIRECT" , SC_OPCODE_INDIRECT }, { "ADDRESS" , SC_OPCODE_ADDRESS }, { "MATCH" , SC_OPCODE_MATCH }, + { "_xlfn.XMATCH" , SC_OPCODE_X_MATCH }, { "COUNTBLANK" , SC_OPCODE_COUNT_EMPTY_CELLS }, { "COUNTIF" , SC_OPCODE_COUNT_IF }, { "SUMIF" , SC_OPCODE_SUM_IF }, @@ -1165,6 +1167,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF[] = { "INDIRECT" , SC_OPCODE_INDIRECT }, { "ADDRESS" , SC_OPCODE_ADDRESS }, { "MATCH" , SC_OPCODE_MATCH }, + { "XMATCH" , SC_OPCODE_X_MATCH }, { "COUNTBLANK" , SC_OPCODE_COUNT_EMPTY_CELLS }, { "COUNTIF" , SC_OPCODE_COUNT_IF }, { "SUMIF" , SC_OPCODE_SUM_IF }, @@ -1617,6 +1620,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH_API[] = { "INDIRECT" , SC_OPCODE_INDIRECT }, { "ADDRESS" , SC_OPCODE_ADDRESS }, { "MATCH" , SC_OPCODE_MATCH }, + { "XMATCH" , SC_OPCODE_X_MATCH }, { "COUNTBLANK" , SC_OPCODE_COUNT_EMPTY_CELLS }, { "COUNTIF" , SC_OPCODE_COUNT_IF }, { "SUMIF" , SC_OPCODE_SUM_IF }, @@ -2067,6 +2071,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH[] = { "INDIRECT" , SC_OPCODE_INDIRECT }, { "ADDRESS" , SC_OPCODE_ADDRESS }, { "MATCH" , SC_OPCODE_MATCH }, + { "XMATCH" , SC_OPCODE_X_MATCH }, { "COUNTBLANK" , SC_OPCODE_COUNT_EMPTY_CELLS }, { "COUNTIF" , SC_OPCODE_COUNT_IF }, { "SUMIF" , SC_OPCODE_SUM_IF }, @@ -2498,6 +2503,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES[] = { NC_("RID_STRLIST_FUNCTION_NAMES", "INDIRECT") , SC_OPCODE_INDIRECT }, { NC_("RID_STRLIST_FUNCTION_NAMES", "ADDRESS") , SC_OPCODE_ADDRESS }, { NC_("RID_STRLIST_FUNCTION_NAMES", "MATCH") , SC_OPCODE_MATCH }, + { NC_("RID_STRLIST_FUNCTION_NAMES", "XMATCH") , SC_OPCODE_X_MATCH }, { NC_("RID_STRLIST_FUNCTION_NAMES", "COUNTBLANK") , SC_OPCODE_COUNT_EMPTY_CELLS }, { NC_("RID_STRLIST_FUNCTION_NAMES", "COUNTIF") , SC_OPCODE_COUNT_IF }, { NC_("RID_STRLIST_FUNCTION_NAMES", "SUMIF") , SC_OPCODE_SUM_IF }, diff --git a/include/formula/compiler.hxx b/include/formula/compiler.hxx index 479008dfb1a2..f3187ae95baa 100644 --- a/include/formula/compiler.hxx +++ b/include/formula/compiler.hxx @@ -399,7 +399,7 @@ #define SC_OPCODE_ISPMT 386 #define SC_OPCODE_HYPERLINK 387 #define SC_OPCODE_X_LOOKUP 388 -// free: 389 +#define SC_OPCODE_X_MATCH 389 #define SC_OPCODE_GET_PIVOT_DATA 390 #define SC_OPCODE_EUROCONVERT 391 #define SC_OPCODE_NUMBERVALUE 392 diff --git a/include/formula/opcode.hxx b/include/formula/opcode.hxx index ad056a9b0b50..3c8053ffb1e3 100644 --- a/include/formula/opcode.hxx +++ b/include/formula/opcode.hxx @@ -307,6 +307,7 @@ enum OpCode : sal_uInt16 ocIndirect = SC_OPCODE_INDIRECT, ocAddress = SC_OPCODE_ADDRESS, ocMatch = SC_OPCODE_MATCH, + ocXMatch = SC_OPCODE_X_MATCH, ocCountEmptyCells = SC_OPCODE_COUNT_EMPTY_CELLS, ocCountIf = SC_OPCODE_COUNT_IF, ocSumIf = SC_OPCODE_SUM_IF, @@ -790,6 +791,7 @@ inline std::string OpCodeEnumToString(OpCode eCode) case ocIndirect: return "Indirect"; case ocAddress: return "Address"; case ocMatch: return "Match"; + case ocXMatch: return "XMatch"; case ocCountEmptyCells: return "CountEmptyCells"; case ocCountIf: return "CountIf"; case ocSumIf: return "SumIf"; diff --git a/sc/README.md b/sc/README.md index a778661d46f9..9740a6f4fa12 100644 --- a/sc/README.md +++ b/sc/README.md @@ -74,6 +74,7 @@ https://docs.oasis-open.org/office/OpenDocument/v1.3/os/part4-formula/OpenDocume * HLOOKUP * LOOKUP * MATCH + * XMATCH * VLOOKUP * XLOOKUP * Mathematical Functions diff --git a/sc/inc/helpids.h b/sc/inc/helpids.h index 3dfc8892aef6..bf42e63ddce1 100644 --- a/sc/inc/helpids.h +++ b/sc/inc/helpids.h @@ -594,5 +594,6 @@ inline constexpr OUString HID_FUNC_FOURIER = u"SC_HID_FUNC_FOURIER"_ustr; inline constexpr OUString HID_FUNC_RAND_NV = u"SC_HID_FUNC_RAND_NV"_ustr; inline constexpr OUString HID_FUNC_RANDBETWEEN_NV = u"SC_HID_FUNC_RANDBETWEEN_NV"_ustr; inline constexpr OUString HID_FUNC_XLOOKUP_MS = u"SC_HID_FUNC_XLOOKUP_MS"_ustr; +inline constexpr OUString HID_FUNC_XMATCH_MS = u"SC_HID_FUNC_XMATCH_MS"_ustr; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/scfuncs.hrc b/sc/inc/scfuncs.hrc index 279ba37b6b92..42e6f2000a70 100644 --- a/sc/inc/scfuncs.hrc +++ b/sc/inc/scfuncs.hrc @@ -3443,6 +3443,20 @@ const TranslateId SC_OPCODE_MATCH_ARY[] = NC_("SC_OPCODE_MATCH", "Type can take the value 1 (first column array ascending), 0 (exact match or wildcard or regular expression match) or -1 (first column array descending) and determines the criteria to be used for comparison purposes.") }; +// -=*# Resource for function XMATCH #*=- +const TranslateId SC_OPCODE_X_MATCH_ARY[] = +{ + NC_("SC_OPCODE_X_MATCH", "Defines a relative position in an array or range of cells after comparing values."), + NC_("SC_OPCODE_X_MATCH", "Search criterion"), + NC_("SC_OPCODE_X_MATCH", "The value to be used for comparison."), + NC_("SC_OPCODE_X_MATCH", "Search Array"), + NC_("SC_OPCODE_X_MATCH", "The array or range to search."), + NC_("SC_OPCODE_X_MATCH", "Match Mode"), + NC_("SC_OPCODE_X_MATCH", "0, -1, 1 or 2 "), // TODO : add explanation of values + NC_("SC_OPCODE_X_MATCH", "Search Mode"), + NC_("SC_OPCODE_X_MATCH", "1, -1, 2 or -2 ") // TODO : add explanation of values +}; + // -=*# Resource for function OFFSET #*=- const TranslateId SC_OPCODE_OFFSET_ARY[] = { diff --git a/sc/qa/extras/scfunctionlistobj.cxx b/sc/qa/extras/scfunctionlistobj.cxx index 03cd76733f9f..875708cb35d0 100644 --- a/sc/qa/extras/scfunctionlistobj.cxx +++ b/sc/qa/extras/scfunctionlistobj.cxx @@ -78,7 +78,7 @@ public: ScFunctionListObj::ScFunctionListObj() : UnoApiTest("/sc/qa/extras/testdocuments") , XElementAccess(cppu::UnoType>::get()) - , XIndexAccess(396) + , XIndexAccess(397) , XNameAccess("IF") , XServiceInfo("stardiv.StarCalc.ScFunctionListObj", "com.sun.star.sheet.FunctionDescriptions") { diff --git a/sc/qa/unit/data/functions/spreadsheet/fods/xmatch.fods b/sc/qa/unit/data/functions/spreadsheet/fods/xmatch.fods new file mode 100644 index 000000000000..ff328a91275f --- /dev/null +++ b/sc/qa/unit/data/functions/spreadsheet/fods/xmatch.fods @@ -0,0 +1,3753 @@ + + + + 2024-01-16T18:30:06.278000000PT2H40M39S47LibreOfficeDev/24.8.0.0.alpha0$Windows_X86_64 LibreOffice_project/7bc98a8f230280ef37ba925eecd9b656bf1022302024-02-15T18:47:52.944000000 + + + 0 + 0 + 47322 + 15804 + + + view1 + + + 2 + 7 + 2 + 0 + 0 + 0 + 0 + 0 + 140 + 60 + true + false + false + false + false + + + 2 + 21 + 2 + 0 + 0 + 0 + 0 + 0 + 140 + 60 + true + false + false + false + false + + + Sheet1 + 1471 + 0 + 140 + 60 + false + true + true + false + true + 12632256 + true + 1 + true + true + false + false + false + 1270 + 1270 + 1 + 1 + true + false + false + false + false + + + + + true + true + true + 0 + true + true + false + true + false + + + en + US + + + + + + 12632256 + true + true + 0 + false + false + true + true + false + 3 + false + Microsoft Print to PDF + false + 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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEgBDT01QQVRfRFVQTEVYX01PREUTAER1cGxleE1vZGU6OlVua25vd24MAFBSSU5URVJfTkFNRRYATWljcm9zb2Z0IFByaW50IHRvIFBERgsARFJJVkVSX05BTUUWAE1pY3Jvc29mdCBQcmludCBUbyBQREY= + false + 1270 + 1270 + 1 + 1 + true + false + false + true + true + true + true + 7 + true + + + Sheet1 + + + Sheet2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kč + + + + - + + + Kč + + + + \ + + + + + \- + + + + + + % + + + \ + + + + + + \ + + - + + + + + \ + + - + + + + + + + + + + £ + + + + + - + £ + + + + + + + + $ + + + + + + $ + + ( + + ) + + + $ + + - + + + + + + + + + + + + + . + + . + + + + + + + + ( + + ) + + + + € + + + + + € + - + + + + + $ + + + + + ($ + + ) + + + + + + + + ( + + ) + + + - + + + + + + + + + + ⌀ + + + + + € + + + - + + € + + + - € + + + + + + + + + + + + + + + + € + + + + + + € + + + - + + + + + - + + - + + + + £ + + + + - + £ + + + + + + - + + + + + + + + + + + + + + + + - + + + + + + - + + + + + + + + + + + + € + + + + + + + ( + € + + + ) + + + + + + + + + + $ + + + + + + + + ( + + ) + + + + + + Kč + + + + - + + + Kč + + + + $ + + + + + - + $ + + + + + + + + + + + - + + + + + + - + + + + + + + + + + + + Kč + + + + - + + + Kč + + + + Yes + + + Yes + + + No + + + + + + + + + + + + + EUR + + + + - + + + EUR + + + + + € + + + - + + € + + + + \ + + + + \- + + + + + \ + + + + + + \ + + - + + + + + \ + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + ( + + ) + + + + - + + + + + + + + + + £ + + + + - + £ + + + + + + + + + + + + + EUR + + + + - + + + EUR + + + + + € + + + + - + + € + + + + + - + + - + + + + $ + + + + + + $ + + ( + + ) + + + $ + + - + + + + + + + + + + $ + + + + + $( + + ) + + + $- + + + + + + + + + + + + + + + + £ + + + + + - + £ + + + + + + On + + + On + + + Off + + + + + $ + + + + + + ($ + + ) + + + + + € + + + - + + € + + + + + + + $ + + + + + ($ + + ) + + + + $ + + + + + + ($ + + ) + + + + + + + + + + + + + ( + + ) + + + + - + + + + + + + + + + + + + + Kč + + + - + + + Kč + + + + + + + True + + + True + + + False + + + + + + € + + + - + + € + + + - + + € + + + + + + + + + + + + + + + + + - + + + + + - + + + + + + + + + + + / + + + + £ + + + + + - + £ + + + + + £ + + + + + - + £ + + + + + $ + + + + + $( + + ) + + + $- + + + + + + + + + + + + + + + + - + + + + + - + + + + + + + + + + + + WAHR + + + WAHR + + + FALSCH + + + + + + . + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + + + + + + ( + + ) + + + - + + + + + + + + + + + + + : + + + + + : + + + + £ + + + + + - + £ + + + + + + \ + + + + + \- + + + + + € + + + + + € + - + + + + + + + € + + + + - + + + € + + + + + + + + + + + € + + + + - + + € + + + + + + Kč + + + - + + + Kč + + + + + : + + : + + + + \ + + + + \- + + + + + + + + + + + + + + + + + - + + + € + + + + + + + / + + / + + + + + + + / + + / + + + + € + + + + + + - + € + + + + + + + Kč + + + - + + Kč + + + + + Kč + + + + - + + Kč + + + + + Kč + + + - + + Kč + + + + + Kč + + + + - + + Kč + + + + + + + - + + + + + + + + + - + + + + + + + + - + + + + + + + + + - + + + + + + + + + + - + + + + + + + - + + + + + + + + + + + + Kč + + + - + + + Kč + + + + - Kč + + + + + + + + + + + + + + + - + + + + + + + - + + + + + + + + + + + + + + Kč + + + - + + + Kč + + + + - + + Kč + + + + + + + + + + ¥€ + + + + + + + + + ( + € + + + + + ) + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + + € + + + + - + + + € + + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + + + $ + + + + + ( + $ + + ) + + + + + % + + + + % + + + + - + + % + + + + + + + + - + + + + + + - + + - + + + + £ + + + + + - + £ + + + + + £ + + + + + - + £ + + + + + + % + + + + - + + % + + + + + DM + + + - + + DM + + + + + DM + + + + - + + DM + + + + + DM + + + - + + DM + + + + + DM + + + + - + + DM + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + DM + + + - + + + DM + + + + + - DM + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + + + DM + + + - + + + DM + + + + + - + + DM + + + + + + + + + + + Ouch! - + + - Error detected! + + + + - Result=0 - No Errordetection + + + + / + + / + + + + + / + + / + + + + + + € + + + + - + + + € + + + + + . + + . + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + € + + + - + + + € + + + + + - € + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + + + € + + + - + + + € + + + + + - + + € + + + + + + + + + + + $ + + + + $ + + + + + - + $ + + + + + + . + + . + + + + + . + + + + + + + . + + + + + + + + + + : + + + + + + + : + + : + + + + + + + . + + . + + + + : + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + $ + + + + $ + + + + -$ + + + + + $ + + + + + -$ + + + + + $ + + + + -$ + + + + + $ + + + + + -$ + + + + + -$ + + + + $ + + + + -$ + + + + $- + + + + + - + + + + + + + - + + + + - + + + + + -$ + + + + $ + + + + -$ + + + + $- + + + + + + - + + + + + + + - + + + + - + + + + + + + / + + / + + + + + - + + - + + + + + - + + + + + - + + + + + : + + + + + + + : + + : + + + + + + + : + + + + + : + + : + + + + + / + + / + + + + : + + + + + + + + + ( + + ) + + + + + + + + + ( + + ) + + + + + + + + + ( + + + ) + + + + + + + + + + ( + + + ) + + + + + + + + + ( + + + ) + + + + + + + + + + ( + + + ) + + + + + + + + ( + + ) + + + + + + + + ( + + ) + + + + + + + + + + ( + + ) + + + + - + + + + + + + + + + + + + + + + + + ( + + ) + + + + + - + + + + + + + + + + + + + + + + ( + + ) + + + + - + + + + + + + + + + + + + + + + + + + + ( + + ) + + + + + - + + + + + + + + + + + + + + + + + + + - + + + + + : + + : + + + + + : + + + + + + + + % + + + + + + + . + + + + + + + . + + + + + + + + + + : + + + + + + + : + + : + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + € + + + - + + € + + + + + € + + + + - + + € + + + + + € + + + - + + € + + + + + € + + + + - + + € + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ??? + + + + + Page 1 + + + + + + + + ???(???) + + + 0000.00.00, 00:00:00 + + + + + + Page 1/ 99 + + + + + + + + + + + + + + + XMATCH Function + + + + + + + + + + + + Result + + + IGAZ + + + + + + + + + Sheet + + + Result + + + Description + + + + + 2 + + + IGAZ + + + Simple XMATCH formulas with local references and values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Function + + + Expected + + + Correct + + + FunctionString + + + Comment + + + XL-test + + + Pear + + + Product + + + + + + 2 + + + 2 + + + IGAZ + + + =XMATCH(4;{5.4.3.2.1}) + + + + Ban? + + + Apple + + + + lundi + + + + + + 2 + + + 2 + + + IGAZ + + + =XMATCH(4;{5.4.3.2.1};0;-1) + + + + 14 + + + Grape + + + + mardi + + + + + + 3 + + + 3 + + + IGAZ + + + =XMATCH($G$1;H$2:H$6;0;-1) + + + + And* + + + Pear + + + + mercredi + + + + + + + 3 + + + 3 + + + IGAZ + + + =XMATCH($G$1;H$2:H$6;0;1) + + + + 13 + + + Banana + + + + jeudi + + + + + + 4 + + + 4 + + + IGAZ + + + =XMATCH($G$2;H$2:H$6;1) + + + + 21 + + + Cherry + + + + vendredi + + + + + + 5 + + + 5 + + + IGAZ + + + =XMATCH(G3;H12:H19;1) + + + + samedi + + + + + + 5,5 + + + 5,5 + + + IGAZ + + + =INDEX(H13:J19;XMATCH(G10;G13:G19);XMATCH(H10;H12:J12)) + + + + dimanche + + + + + + 6 + + + 6 + + + IGAZ + + + =XMATCH(G3;H12:H19;-1) + + + + Sales Rep + + + Month + + + + lundi + + + + + + 5 + + + 5 + + + IGAZ + + + =XMATCH(G4;G12:G19;2) + + + + Andrew + + + Feb + + + + mardi + + + + + + 5 + + + 5 + + + IGAZ + + + =XMATCH(G4;G12:G19;2;-1) + + + + + + 5 + + + 5 + + + IGAZ + + + =XMATCH(G5;H13:H19;0;-2) + + + + Sales Rep + + + Jan + + + Feb + + + Mar + + + + + + 6 + + + 6 + + + IGAZ + + + =XMATCH(G6;J13:J19;0;2) + + + + Michael + + + 42 + + + 6 + + + 0 + + + + + + 1 + + + 1 + + + IGAZ + + + {=XMATCH(K2:K10;K2:K10;)} + + + + Jan + + + 35 + + + 12 + + + 2 + + + + + + 2 + + + 2 + + + IGAZ + + + {=XMATCH(K2:K10;K2:K10;)} + + + + Nancy + + + 25 + + + 11 + + + 3 + + + + + + 3 + + + 3 + + + IGAZ + + + {=XMATCH(K2:K10;K2:K10;)} + + + + Andrew + + + 15 + + + 5,5 + + + 5 + + + + + + 4 + + + 4 + + + IGAZ + + + {=XMATCH(K2:K10;K2:K10;)} + + + + Anne + + + 13 + + + 3 + + + 9 + + + + + + 5 + + + 5 + + + IGAZ + + + {=XMATCH(K2:K10;K2:K10;)} + + + + Nancy + + + 12 + + + 7 + + + 21 + + + + + + 6 + + + 6 + + + IGAZ + + + {=XMATCH(K2:K10;K2:K10;)} + + + + Maria + + + 9 + + + 8 + + + 35 + + + + + + 7 + + + 7 + + + IGAZ + + + {=XMATCH(K2:K10;K2:K10;)} + + + + + + 1 + + + 1 + + + IGAZ + + + {=XMATCH(K2:K10;K2:K10;)} + + + + + + 2 + + + 2 + + + IGAZ + + + {=XMATCH(K2:K10;K2:K10;)} + + + + + + + + + #N/A + + + #N/A + + + IGAZ + + + =XMATCH(G27;$G$23:$N$23) + + + + + + + 2 + + + 4 + + + 8 + + + B + + + D + + + H + + + + + + 1 + + + 1 + + + IGAZ + + + =XMATCH(G28;$G$23:$N$23) + + + + + + #N/A + + + #N/A + + + IGAZ + + + =XMATCH(G29;$G$23:$N$23) + + + + + + 2 + + + 2 + + + IGAZ + + + =XMATCH(G30;$G$23:$N$23) + + + + Lookup + + + + + + 5 + + + 5 + + + IGAZ + + + =XMATCH(G31;$G$23:$N$23) + + + + 1 + + + + + + #N/A + + + #N/A + + + IGAZ + + + =XMATCH(G32;$G$23:$N$23) + + + + 2 + + + + + + #N/A + + + #N/A + + + IGAZ + + + =XMATCH(G33;$G$23:$N$23) + + + + 3 + + + + + + 4 + + + 4 + + + IGAZ + + + =XMATCH(G34;$G$23:$N$23) + + + + 4 + + + + + + + + + + + + + B + + + + + + + + + 6 + + + + + + + + + 7 + + + + + + + + + 8 + + + + + + + + + 9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx index efd836c89190..99bc7b231b5b 100644 --- a/sc/qa/unit/ucalc.cxx +++ b/sc/qa/unit/ucalc.cxx @@ -2924,6 +2924,7 @@ CPPUNIT_TEST_FIXTURE(Test, testFunctionLists) "STYLE", "VLOOKUP", "XLOOKUP", + "XMATCH", nullptr }; diff --git a/sc/source/core/data/funcdesc.cxx b/sc/source/core/data/funcdesc.cxx index a9c123d89768..1c704c9933d4 100644 --- a/sc/source/core/data/funcdesc.cxx +++ b/sc/source/core/data/funcdesc.cxx @@ -676,6 +676,7 @@ ScFunctionList::ScFunctionList( bool bEnglishFunctionNames ) { SC_OPCODE_ISPMT, ENTRY(SC_OPCODE_ISPMT_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_ISPMT, 4, { 0, 0, 0, 0 }, 0 }, { SC_OPCODE_HYPERLINK, ENTRY(SC_OPCODE_HYPERLINK_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_HYPERLINK, 2, { 0, 1 }, 0 }, { SC_OPCODE_X_LOOKUP, ENTRY(SC_OPCODE_X_LOOKUP_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_XLOOKUP_MS, 6, { 0, 0, 0, 1, 1, 1 }, 0 }, + { SC_OPCODE_X_MATCH, ENTRY(SC_OPCODE_X_MATCH_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_XMATCH_MS, 4, { 0, 0, 1, 1 }, 0 }, { SC_OPCODE_GET_PIVOT_DATA, ENTRY(SC_OPCODE_GET_PIVOT_DATA_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_GETPIVOTDATA, VAR_ARGS+2, { 0, 0, 1 }, 0 }, { SC_OPCODE_EUROCONVERT, ENTRY(SC_OPCODE_EUROCONVERT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_EUROCONVERT, 5, { 0, 0, 0, 1, 1 }, 0 }, { SC_OPCODE_NUMBERVALUE, ENTRY(SC_OPCODE_NUMBERVALUE_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_NUMBERVALUE, 3, { 0, 1, 1 }, 0 }, diff --git a/sc/source/core/data/queryiter.cxx b/sc/source/core/data/queryiter.cxx index 8abaa2b93fdd..d931b9429925 100644 --- a/sc/source/core/data/queryiter.cxx +++ b/sc/source/core/data/queryiter.cxx @@ -206,8 +206,8 @@ void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery() if ( aCell.isEmpty()) return; - // XLookUp: Forward/asc/backward/desc search for best fit value, except if we have an exact match - if (nSearchOpCode == SC_OPCODE_X_LOOKUP && + // XLookUp/XMatch: Forward/asc/backward/desc search for best fit value, except if we have an exact match + if ((nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH) && (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL) && (nBestFitCol != nCol || nBestFitRow != nRow)) { @@ -315,8 +315,8 @@ void ScQueryCellIteratorBase< accessType, queryType >::InitPos() // non-matching position using SC_LESS and the start position // is the one after it. lastRow = nRow; - // BinarySearch() looks for the first match for XLOOKUP - if (nSearchOpCode != SC_OPCODE_X_LOOKUP) + // BinarySearch() looks for the first match for XLOOKUP/XMATCH + if (nSearchOpCode != SC_OPCODE_X_LOOKUP && nSearchOpCode != SC_OPCODE_X_MATCH) { ScQueryOp saveOp = op; op = SC_LESS; @@ -342,7 +342,8 @@ void ScQueryCellIteratorBase< accessType, queryType >::InitPos() if( BinarySearch( nCol )) lastRow = nRow; } - AccessBase::InitPosFinish(beforeRow, lastRow, (nSearchOpCode == SC_OPCODE_X_LOOKUP)); + AccessBase::InitPosFinish(beforeRow, lastRow, + (nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH)); } } @@ -546,9 +547,9 @@ bool ScQueryCellIteratorBase< accessType, queryType >::BinarySearch( SCCOL col, { if (fLastInRangeValue <= nCellVal) { - if (nSearchOpCode == SC_OPCODE_X_LOOKUP && - nSortedBinarySearch != nSearchbDesc && - fLastInRangeValue == nCellVal && aIndexer.getLowIndex() != i) + if ((nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH) && + nSortedBinarySearch != nSearchbDesc && fLastInRangeValue == nCellVal && + aIndexer.getLowIndex() != i) bDone = true; else { @@ -596,9 +597,9 @@ bool ScQueryCellIteratorBase< accessType, queryType >::BinarySearch( SCCOL col, aCellStr); if (nTmp <= 0) { - if (nSearchOpCode == SC_OPCODE_X_LOOKUP && - nSortedBinarySearch != nSearchbDesc && - nTmp == 0 && aIndexer.getLowIndex() != i) + if ((nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH) && + nSortedBinarySearch != nSearchbDesc && nTmp == 0 && + aIndexer.getLowIndex() != i) bDone = true; else { @@ -672,8 +673,8 @@ bool ScQueryCellIteratorBase< accessType, queryType >::BinarySearch( SCCOL col, { found = i; nLastInRange = i; - if (nSearchOpCode == SC_OPCODE_X_LOOKUP && (nSortedBinarySearch == nSearchbAscd && - (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_EQUAL))) + if ((nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH) && + (nSortedBinarySearch == nSearchbAscd && (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_EQUAL))) bDone = true; else // But keep searching to find the last matching one. nLo = nMid + 1; @@ -753,8 +754,9 @@ bool ScQueryCellIterator< accessType >::FindEqualOrSortedLastInRange( SCCOL& nFo nFoundCol = rDoc.MaxCol()+1; nFoundRow = rDoc.MaxRow()+1; - if (nSearchOpCode == SC_OPCODE_X_LOOKUP && nSortedBinarySearch == nBinarySearchDisabled) - SetStopOnMismatch( false ); // assume not sorted keys for XLookup + if ((nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH) && + nSortedBinarySearch == nBinarySearchDisabled) + SetStopOnMismatch( false ); // assume not sorted keys for XLookup/XMatch else SetStopOnMismatch( true ); // assume sorted keys @@ -767,8 +769,8 @@ bool ScQueryCellIterator< accessType >::FindEqualOrSortedLastInRange( SCCOL& nFo (bLiteral || maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByValue) && (maParam.GetEntry(0).eOp == SC_LESS_EQUAL || maParam.GetEntry(0).eOp == SC_GREATER_EQUAL); - // assume not sorted properly if we are using XLookup with forward or backward search - if (bBinary && nSearchOpCode == SC_OPCODE_X_LOOKUP && + // assume not sorted properly if we are using XLookup/XMatch with forward or backward search + if (bBinary && (nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH) && nSortedBinarySearch == nBinarySearchDisabled) bBinary = false; @@ -862,7 +864,8 @@ bool ScQueryCellIterator< accessType >::FindEqualOrSortedLastInRange( SCCOL& nFo } } } - if ( IsEqualConditionFulfilled() && nSearchOpCode != SC_OPCODE_X_LOOKUP ) + if (IsEqualConditionFulfilled() && (nSearchOpCode != SC_OPCODE_X_LOOKUP && + nSearchOpCode != SC_OPCODE_X_MATCH)) { // Position on last equal entry, except for XLOOKUP, // which looking for the first equal entry diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx index 57c69d6a3d02..75f70c78d647 100644 --- a/sc/source/core/inc/interpre.hxx +++ b/sc/source/core/inc/interpre.hxx @@ -541,7 +541,7 @@ private: inline void TreatDoubleError( double& rVal ); // Lookup using ScLookupCache, @returns true if found and result address bool LookupQueryWithCache( ScAddress & o_rResultPos, const ScQueryParam & rParam, - const ScComplexRefData* refData, sal_Int8 nSearchMode, sal_Int16 nOpCode ) const; + const ScComplexRefData* refData, sal_Int8 nSearchMode, sal_uInt16 nOpCode ) const; void ScIfJump(); void ScIfError( bool bNAonly ); @@ -671,6 +671,7 @@ private: void ScRow(); void ScSheet(); void ScMatch(); + void ScXMatch(); void IterateParametersIf( ScIterFuncIf ); void ScCountIf(); void ScSumIf(); diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx index fd7bab714805..c33e74ba9c3e 100644 --- a/sc/source/core/tool/interpr1.cxx +++ b/sc/source/core/tool/interpr1.cxx @@ -5075,6 +5075,178 @@ void ScInterpreter::ScMatch() PushIllegalParameter(); } +void ScInterpreter::ScXMatch() +{ + sal_uInt8 nParamCount = GetByte(); + if (!MustHaveParamCount(nParamCount, 2, 4)) + return; + + VectorSearchArguments vsa; + vsa.nSearchOpCode = SC_OPCODE_X_MATCH; + + // get search mode + if (nParamCount == 4) + { + sal_Int16 k = GetInt16(); + if (k >= -2 && k <= 2 && k != 0) + vsa.eSearchMode = static_cast(k); + else + { + PushIllegalParameter(); + return; + } + } + else + vsa.eSearchMode = searchfwd; + + // get match mode + if (nParamCount >= 3) + { + sal_Int16 k = GetInt16(); + if (k >= -1 && k <= 2) + vsa.eMatchMode = static_cast(k); + else + { + PushIllegalParameter(); + return; + } + } + else + vsa.eMatchMode = exactorNA; + + // get vector to be searched + switch (GetStackType()) + { + case svSingleRef: + { + PopSingleRef(vsa.nCol1, vsa.nRow1, vsa.nTab1); + vsa.nCol2 = vsa.nCol1; + vsa.nRow2 = vsa.nRow1; + vsa.pMatSrc = nullptr; + } + break; + case svDoubleRef: + { + vsa.pMatSrc = nullptr; + SCTAB nTab2 = 0; + PopDoubleRef(vsa.nCol1, vsa.nRow1, vsa.nTab1, vsa.nCol2, vsa.nRow2, nTab2); + if (vsa.nTab1 != nTab2 || (vsa.nCol1 != vsa.nCol2 && vsa.nRow1 != vsa.nRow2)) + { + PushIllegalParameter(); + return; + } + } + break; + case svMatrix: + case svExternalDoubleRef: + { + if (GetStackType() == svMatrix) + vsa.pMatSrc = PopMatrix(); + else + PopExternalDoubleRef(vsa.pMatSrc); + + if (!vsa.pMatSrc) + { + PushIllegalParameter(); + return; + } + } + break; + default: + PushIllegalParameter(); + return; + } + + // get search value + if (nGlobalError == FormulaError::NONE) + { + switch (GetStackType()) + { + case svDouble: + { + vsa.isStringSearch = false; + vsa.fSearchVal = GetDouble(); + } + break; + case svString: + { + vsa.isStringSearch = true; + vsa.sSearchStr = GetString(); + } + break; + case svDoubleRef: + case svSingleRef: + { + ScAddress aAdr; + if (!PopDoubleRefOrSingleRef(aAdr)) + { + PushInt(0); + return; + } + ScRefCellValue aCell(mrDoc, aAdr); + if (aCell.hasNumeric()) + { + vsa.isStringSearch = false; + vsa.fSearchVal = GetCellValue(aAdr, aCell); + } + else + { + vsa.isStringSearch = true; + GetCellString(vsa.sSearchStr, aCell); + } + } + break; + case svExternalSingleRef: + { + ScExternalRefCache::TokenRef pToken; + PopExternalSingleRef(pToken); + if (nGlobalError != FormulaError::NONE) + { + PushError(nGlobalError); + return; + } + if (pToken->GetType() == svDouble) + { + vsa.isStringSearch = false; + vsa.fSearchVal = pToken->GetDouble(); + } + else + { + vsa.isStringSearch = true; + vsa.sSearchStr = pToken->GetString(); + } + } + break; + case svExternalDoubleRef: + case svMatrix: + { + ScMatValType nType = GetDoubleOrStringFromMatrix( + vsa.fSearchVal, vsa.sSearchStr); + vsa.isStringSearch = ScMatrix::IsNonValueType(nType); + } + break; + default: + { + PushIllegalParameter(); + return; + } + } + + // execute search + if (SearchVectorForValue(vsa)) + PushDouble(vsa.nIndex); + else + { + if (vsa.isResultNA) + PushNA(); + else + return; // error occurred and has already been pushed + } + } + else + PushIllegalParameter(); +} + namespace { bool isCellContentEmpty( const ScRefCellValue& rCell ) @@ -10741,8 +10913,8 @@ bool ScInterpreter::SearchVectorForValue( VectorSearchArguments& vsa ) break; case wildcard : - // this mode can only used with XLOOKUP - if ( vsa.nSearchOpCode == SC_OPCODE_X_LOOKUP ) + // this mode can only used with XLOOKUP/XMATCH + if ( vsa.nSearchOpCode == SC_OPCODE_X_LOOKUP || vsa.nSearchOpCode == SC_OPCODE_X_MATCH ) { rEntry.eOp = SC_EQUAL; if ( vsa.isStringSearch ) @@ -10833,7 +11005,7 @@ bool ScInterpreter::SearchVectorForValue( VectorSearchArguments& vsa ) static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument& rDoc, ScInterpreterContext& rContext, const ScQueryParam & rParam, const ScQueryEntry & rEntry, const ScFormulaCell* cell, - const ScComplexRefData* refData, sal_Int8 nSearchMode, sal_Int16 nOpCode ) + const ScComplexRefData* refData, sal_Int8 nSearchMode, sal_uInt16 nOpCode ) { if (rEntry.eOp != SC_EQUAL) { @@ -10952,7 +11124,7 @@ static SCROW lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache& rCache, bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos, const ScQueryParam & rParam, const ScComplexRefData* refData, - sal_Int8 nSearchMode, sal_Int16 nOpCode ) const + sal_Int8 nSearchMode, sal_uInt16 nOpCode ) const { bool bFound = false; const ScQueryEntry& rEntry = rParam.GetEntry(0); diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx index 60190bac72b3..2069bedbb763 100644 --- a/sc/source/core/tool/interpr4.cxx +++ b/sc/source/core/tool/interpr4.cxx @@ -4317,6 +4317,7 @@ StackVar ScInterpreter::Interpret() case ocIndirect : ScIndirect(); break; case ocAddress : ScAddressFunc(); break; case ocMatch : ScMatch(); break; + case ocXMatch : ScXMatch(); break; case ocCountEmptyCells : ScCountEmptyCells(); break; case ocCountIf : ScCountIf(); break; case ocSumIf : ScSumIf(); break; diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx index e6a5cda2f19f..448731491154 100644 --- a/sc/source/core/tool/parclass.cxx +++ b/sc/source/core/tool/parclass.cxx @@ -264,7 +264,8 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] = { ocVarS, {{ Reference }, 1, Value }}, { ocWhitespace, {{ Bounds }, 0, Bounds }}, { ocWorkday_MS, {{ Value, Value, Value, Reference }, 0, Value }}, - { ocXLookup, {{ Value, ReferenceOrForceArray, ReferenceOrForceArray, Value, Value, Value }, 0, Value }}, + { ocXLookup, {{ Value, ReferenceOrForceArray, ReferenceOrForceArray, Value, Value, Value }, 0, Reference }}, + { ocXMatch, {{ Value, ReferenceOrForceArray, Value, Value }, 0, Value }}, { ocXor, {{ Reference }, 1, Value }}, { ocZTest, {{ Reference, Value, Value }, 0, Value }}, { ocZTest_MS, {{ Reference, Value, Value }, 0, Value }}, diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx index 114bdae1497a..f57efce3414c 100644 --- a/sc/source/core/tool/token.cxx +++ b/sc/source/core/tool/token.cxx @@ -1388,6 +1388,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r ) case ocCount2: case ocVLookup: case ocXLookup: + case ocXMatch: case ocSLN: case ocIRR: case ocMIRR: diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx index f0e5b09a0e4b..3968e33e794d 100644 --- a/sc/source/filter/excel/xlformula.cxx +++ b/sc/source/filter/excel/xlformula.cxx @@ -601,7 +601,8 @@ const XclFunctionInfo saFuncTable_2016[] = */ const XclFunctionInfo saFuncTable_2021[] = { - EXC_FUNCENTRY_V_VR( ocXLookup, 3, 6, 0, "XLOOKUP" ) + EXC_FUNCENTRY_V_VR( ocXLookup, 3, 6, 0, "XLOOKUP" ), + EXC_FUNCENTRY_V_VR( ocXMatch, 2, 4, 0, "XMATCH" ) }; diff --git a/sc/source/filter/oox/formulabase.cxx b/sc/source/filter/oox/formulabase.cxx index 6ee06ff6b89b..6f4037712d1d 100644 --- a/sc/source/filter/oox/formulabase.cxx +++ b/sc/source/filter/oox/formulabase.cxx @@ -875,7 +875,8 @@ const FunctionData saFuncTable2016[] = /* FIXME: BIFF?? function identifiers available? Where to obtain? */ const FunctionData saFuncTable2021[] = { - { "COM.MICROSOFT.XLOOKUP", "XLOOKUP", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW } + { "COM.MICROSOFT.XLOOKUP", "XLOOKUP", NOID, NOID, 3, 6, R, { VR, VA, VR }, FuncFlags::MACROCALL_NEW }, + { "COM.MICROSOFT.XMATCH", "XMATCH", NOID, NOID, 2, 4, V, { VR, VA }, FuncFlags::MACROCALL_NEW } };