tdf#127293 Add Excel2021 function XLOOKUP to Calc
https://issues.oasis-open.org/browse/OFFICE-4154 What is working already: xlookup with normal forward, backward search in columns/rows. Binary search in rows with real binary search algorithm, in columns only works with linear search yet. Linear forward backward wildcard/regex search in columns/rows. Looking for the first smaller or greater value with linear and binary search ALso all the combination of all these options. Except XLOOKUP not supperted wildcard/regex search with binary search. TODO in next patches: - add the binary search option for searching in columns. - Evaluate Formula calculation not working in general. Co-authored-by: Balazs Varga <balazs.varga.extern@allotropia.de> Change-Id: I15fd4479b63ec13b093d269760d1bbb5957553e8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131905 Tested-by: Jenkins Tested-by: Gabor Kelemen <gabor.kelemen.extern@allotropia.de> Reviewed-by: Balazs Varga <balazs.varga.extern@allotropia.de>
This commit is contained in:
parent
8cccfc82a3
commit
f7039822c7
@ -275,6 +275,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF[] =
|
||||
{ "COUNTIFS" , SC_OPCODE_COUNT_IFS },
|
||||
{ "LOOKUP" , SC_OPCODE_LOOKUP },
|
||||
{ "VLOOKUP" , SC_OPCODE_V_LOOKUP },
|
||||
{ "COM.MICROSOFT.XLOOKUP" , SC_OPCODE_X_LOOKUP },
|
||||
{ "HLOOKUP" , SC_OPCODE_H_LOOKUP },
|
||||
{ "ORG.OPENOFFICE.MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||
@ -722,6 +723,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML[] =
|
||||
{ "COUNTIFS" , SC_OPCODE_COUNT_IFS },
|
||||
{ "LOOKUP" , SC_OPCODE_LOOKUP },
|
||||
{ "VLOOKUP" , SC_OPCODE_V_LOOKUP },
|
||||
{ "_xlfn.XLOOKUP" , SC_OPCODE_X_LOOKUP },
|
||||
{ "HLOOKUP" , SC_OPCODE_H_LOOKUP },
|
||||
{ "_xlfn.ORG.OPENOFFICE.MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||
@ -1172,6 +1174,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF[] =
|
||||
{ "COUNTIFS" , SC_OPCODE_COUNT_IFS },
|
||||
{ "LOOKUP" , SC_OPCODE_LOOKUP },
|
||||
{ "VLOOKUP" , SC_OPCODE_V_LOOKUP },
|
||||
{ "XLOOKUP" , SC_OPCODE_X_LOOKUP },
|
||||
{ "HLOOKUP" , SC_OPCODE_H_LOOKUP },
|
||||
{ "MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||
@ -1623,6 +1626,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_API[] =
|
||||
{ "COUNTIFS" , SC_OPCODE_COUNT_IFS },
|
||||
{ "LOOKUP" , SC_OPCODE_LOOKUP },
|
||||
{ "VLOOKUP" , SC_OPCODE_V_LOOKUP },
|
||||
{ "XLOOKUP" , SC_OPCODE_X_LOOKUP },
|
||||
{ "HLOOKUP" , SC_OPCODE_H_LOOKUP },
|
||||
{ "MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||
@ -2072,6 +2076,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH[] =
|
||||
{ "COUNTIFS" , SC_OPCODE_COUNT_IFS },
|
||||
{ "LOOKUP" , SC_OPCODE_LOOKUP },
|
||||
{ "VLOOKUP" , SC_OPCODE_V_LOOKUP },
|
||||
{ "XLOOKUP" , SC_OPCODE_X_LOOKUP },
|
||||
{ "HLOOKUP" , SC_OPCODE_H_LOOKUP },
|
||||
{ "MULTIRANGE" , SC_OPCODE_MULTI_AREA },
|
||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||
@ -2502,6 +2507,7 @@ const std::pair<TranslateId, int> RID_STRLIST_FUNCTION_NAMES[] =
|
||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "COUNTIFS") , SC_OPCODE_COUNT_IFS },
|
||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "LOOKUP") , SC_OPCODE_LOOKUP },
|
||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "VLOOKUP") , SC_OPCODE_V_LOOKUP },
|
||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "XLOOKUP") , SC_OPCODE_X_LOOKUP },
|
||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "HLOOKUP") , SC_OPCODE_H_LOOKUP },
|
||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "MULTIRANGE") , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "OFFSET") , SC_OPCODE_OFFSET },
|
||||
|
@ -398,7 +398,7 @@
|
||||
#define SC_OPCODE_CELL 385
|
||||
#define SC_OPCODE_ISPMT 386
|
||||
#define SC_OPCODE_HYPERLINK 387
|
||||
// free: 388
|
||||
#define SC_OPCODE_X_LOOKUP 388
|
||||
// free: 389
|
||||
#define SC_OPCODE_GET_PIVOT_DATA 390
|
||||
#define SC_OPCODE_EUROCONVERT 391
|
||||
|
@ -316,6 +316,7 @@ enum OpCode : sal_uInt16
|
||||
ocCountIfs = SC_OPCODE_COUNT_IFS,
|
||||
ocLookup = SC_OPCODE_LOOKUP,
|
||||
ocVLookup = SC_OPCODE_V_LOOKUP,
|
||||
ocXLookup = SC_OPCODE_X_LOOKUP,
|
||||
ocHLookup = SC_OPCODE_H_LOOKUP,
|
||||
ocMultiArea = SC_OPCODE_MULTI_AREA,
|
||||
ocOffset = SC_OPCODE_OFFSET,
|
||||
@ -798,6 +799,7 @@ inline std::string OpCodeEnumToString(OpCode eCode)
|
||||
case ocCountIfs: return "CountIfs";
|
||||
case ocLookup: return "Lookup";
|
||||
case ocVLookup: return "VLookup";
|
||||
case ocXLookup: return "XLookup";
|
||||
case ocHLookup: return "HLookup";
|
||||
case ocMultiArea: return "MultiArea";
|
||||
case ocOffset: return "Offset";
|
||||
|
@ -75,6 +75,7 @@ https://docs.oasis-open.org/office/OpenDocument/v1.3/os/part4-formula/OpenDocume
|
||||
* LOOKUP
|
||||
* MATCH
|
||||
* VLOOKUP
|
||||
* XLOOKUP
|
||||
* Mathematical Functions
|
||||
* SUMIF
|
||||
* SUMIFS
|
||||
|
@ -593,5 +593,6 @@ inline constexpr OUString HID_FUNC_REGEX = u"SC_HID_FUNC_REGEX"_ustr;
|
||||
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;
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
@ -62,7 +62,7 @@ class ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >
|
||||
{
|
||||
protected:
|
||||
ScQueryCellIteratorAccessSpecific( ScDocument& rDocument, ScInterpreterContext& rContext,
|
||||
const ScQueryParam& rParam );
|
||||
const ScQueryParam& rParam, bool bReverseSearch );
|
||||
// Initialize position for new column.
|
||||
void InitPos();
|
||||
// Increase position (next row).
|
||||
@ -71,12 +71,19 @@ protected:
|
||||
// should call IncPos().
|
||||
void IncBlock();
|
||||
|
||||
// Decrease position (prev row).
|
||||
void DecPos();
|
||||
// Prev mdds block. If access is not direct/linear, then
|
||||
// should call DecPos().
|
||||
void DecBlock();
|
||||
|
||||
// These members needs to be available already in the base class.
|
||||
typedef sc::CellStoreType::const_position_type PositionType;
|
||||
PositionType maCurPos;
|
||||
ScQueryParam maParam;
|
||||
ScDocument& rDoc;
|
||||
ScInterpreterContext& mrContext;
|
||||
bool mbReverseSearch;
|
||||
SCTAB nTab;
|
||||
SCCOL nCol;
|
||||
SCROW nRow;
|
||||
@ -98,19 +105,24 @@ public:
|
||||
bool IncPosImpl();
|
||||
protected:
|
||||
ScQueryCellIteratorAccessSpecific( ScDocument& rDocument, ScInterpreterContext& rContext,
|
||||
const ScQueryParam& rParam );
|
||||
const ScQueryParam& rParam, bool bReverseSearch );
|
||||
void InitPosStart();
|
||||
void InitPosFinish( SCROW beforeRow, SCROW lastRow );
|
||||
void InitPosFinish( SCROW beforeRow, SCROW lastRow, bool bFirstMatch );
|
||||
void IncPos() { IncPosImpl<false>(); }
|
||||
bool IncPosFast() { return IncPosImpl<true>(); }
|
||||
void IncBlock() { IncPos(); } // Cannot skip entire block, not linear.
|
||||
|
||||
// Initialize for backward search. (no need for SortedCache)
|
||||
static void DecPos() {};
|
||||
static void DecBlock() {};
|
||||
|
||||
// These members needs to be available already in the base class.
|
||||
typedef sc::CellStoreType::const_position_type PositionType;
|
||||
PositionType maCurPos;
|
||||
ScQueryParam maParam;
|
||||
ScDocument& rDoc;
|
||||
ScInterpreterContext& mrContext;
|
||||
bool mbReverseSearch;
|
||||
SCTAB nTab;
|
||||
SCCOL nCol;
|
||||
SCROW nRow;
|
||||
@ -163,17 +175,24 @@ protected:
|
||||
sal_uInt8 nTestEqualCondition;
|
||||
bool bAdvanceQuery;
|
||||
bool bIgnoreMismatchOnLeadingStrings;
|
||||
bool bSortedBinarySearch;
|
||||
bool bXLookUp;
|
||||
SCCOL nBestFitCol;
|
||||
SCROW nBestFitRow;
|
||||
|
||||
// Make base members directly visible here (templated bases need 'this->').
|
||||
using AccessBase::maCurPos;
|
||||
using AccessBase::maParam;
|
||||
using AccessBase::rDoc;
|
||||
using AccessBase::mrContext;
|
||||
using AccessBase::mbReverseSearch;
|
||||
using AccessBase::nTab;
|
||||
using AccessBase::nCol;
|
||||
using AccessBase::nRow;
|
||||
using AccessBase::IncPos;
|
||||
using AccessBase::IncBlock;
|
||||
using AccessBase::DecPos;
|
||||
using AccessBase::DecBlock;
|
||||
using typename AccessBase::BinarySearchCellType;
|
||||
using AccessBase::MakeBinarySearchIndexer;
|
||||
using TypeBase::HandleItemFound;
|
||||
@ -227,9 +246,14 @@ protected:
|
||||
bool IsEqualConditionFulfilled() const
|
||||
{ return nTestEqualCondition == nTestEqualConditionFulfilled; }
|
||||
|
||||
void HandleBestFitItemFound(SCCOL nBFitCol, SCROW nBFitRow)
|
||||
{
|
||||
nBestFitCol = nBFitCol;
|
||||
nBestFitRow = nBFitRow;
|
||||
}
|
||||
public:
|
||||
ScQueryCellIteratorBase(ScDocument& rDocument, ScInterpreterContext& rContext, SCTAB nTable,
|
||||
const ScQueryParam& aParam, bool bMod);
|
||||
const ScQueryParam& aParam, bool bMod, bool bReverse);
|
||||
// when !bMod, the QueryParam has to be filled
|
||||
// (bIsString)
|
||||
|
||||
@ -238,6 +262,12 @@ public:
|
||||
void SetAdvanceQueryParamEntryField( bool bVal )
|
||||
{ bAdvanceQuery = bVal; }
|
||||
void AdvanceQueryParamEntryField();
|
||||
|
||||
void SetSortedBinarySearchMode( bool bVal )
|
||||
{ bSortedBinarySearch = bVal; }
|
||||
|
||||
void SetXlookupMode( bool bVal )
|
||||
{ bXLookUp = bVal; }
|
||||
};
|
||||
|
||||
|
||||
@ -259,11 +289,13 @@ class ScQueryCellIterator
|
||||
using Base::maParam;
|
||||
using Base::rDoc;
|
||||
using Base::mrContext;
|
||||
using Base::mbReverseSearch;
|
||||
using Base::nTab;
|
||||
using Base::nCol;
|
||||
using Base::nRow;
|
||||
using Base::InitPos;
|
||||
using Base::IncPos;
|
||||
using Base::DecPos;
|
||||
using Base::bIgnoreMismatchOnLeadingStrings;
|
||||
using Base::SetStopOnMismatch;
|
||||
using Base::SetTestEqualCondition;
|
||||
@ -279,13 +311,17 @@ class ScQueryCellIterator
|
||||
using Base::nTestEqualConditionEnabled;
|
||||
using Base::PerformQuery;
|
||||
using Base::getThisResult;
|
||||
using Base::nBestFitCol;
|
||||
using Base::nBestFitRow;
|
||||
using Base::bSortedBinarySearch;
|
||||
using Base::bXLookUp;
|
||||
|
||||
bool GetThis();
|
||||
|
||||
public:
|
||||
ScQueryCellIterator(ScDocument& rDocument, ScInterpreterContext& rContext, SCTAB nTable,
|
||||
const ScQueryParam& aParam, bool bMod)
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod ) {}
|
||||
const ScQueryParam& aParam, bool bMod, bool bReverse)
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod, bReverse ) {}
|
||||
bool GetFirst();
|
||||
bool GetNext();
|
||||
SCCOL GetCol() const { return nCol; }
|
||||
@ -320,8 +356,8 @@ class ScQueryCellIteratorSortedCache
|
||||
typedef ScQueryCellIterator< ScQueryCellIteratorAccess::SortedCache > Base;
|
||||
public:
|
||||
ScQueryCellIteratorSortedCache(ScDocument& rDocument, ScInterpreterContext& rContext,
|
||||
SCTAB nTable, const ScQueryParam& aParam, bool bMod)
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod ) {}
|
||||
SCTAB nTable, const ScQueryParam& aParam, bool bMod, bool bReverse )
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod, bReverse ) {}
|
||||
// Returns true if this iterator can be used for the given query.
|
||||
static bool CanBeUsed(ScDocument& rDoc, const ScQueryParam& aParam,
|
||||
SCTAB nTab, const ScFormulaCell* cell, const ScComplexRefData* refData,
|
||||
@ -357,8 +393,8 @@ protected:
|
||||
|
||||
public:
|
||||
ScCountIfCellIterator(ScDocument& rDocument, ScInterpreterContext& rContext, SCTAB nTable,
|
||||
const ScQueryParam& aParam, bool bMod)
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod ) {}
|
||||
const ScQueryParam& aParam, bool bMod, bool bReverse)
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod, bReverse ) {}
|
||||
sal_uInt64 GetCount();
|
||||
};
|
||||
|
||||
@ -370,8 +406,8 @@ class ScCountIfCellIteratorSortedCache
|
||||
typedef ScCountIfCellIterator< ScQueryCellIteratorAccess::SortedCache > Base;
|
||||
public:
|
||||
ScCountIfCellIteratorSortedCache(ScDocument& rDocument, ScInterpreterContext& rContext,
|
||||
SCTAB nTable, const ScQueryParam& aParam, bool bMod)
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod ) {}
|
||||
SCTAB nTable, const ScQueryParam& aParam, bool bMod, bool bReverse)
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod, bReverse ) {}
|
||||
// Returns true if this iterator can be used for the given query.
|
||||
static bool CanBeUsed(ScDocument& rDoc, const ScQueryParam& aParam,
|
||||
SCTAB nTab, const ScFormulaCell* cell, const ScComplexRefData* refData,
|
||||
|
@ -3377,6 +3377,24 @@ const TranslateId SC_OPCODE_V_LOOKUP_ARY[] =
|
||||
NC_("SC_OPCODE_V_LOOKUP", "If the value is TRUE or not given, the search column of the array represents a series of ranges, and must be sorted in ascending order.")
|
||||
};
|
||||
|
||||
// -=*# Resource for function XLOOKUP #*=-
|
||||
const TranslateId SC_OPCODE_X_LOOKUP_ARY[] =
|
||||
{
|
||||
NC_("SC_OPCODE_X_LOOKUP", "Extended vertical search and reference to indicated cells."),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "Search criterion"),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "The value to be found in the first column."),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "Search Array"),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "The array or range to search."),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "Result Array"),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "The array or range to return."),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "Result if not found"),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "If given, return given text, otherwise return #N/A."),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "Match Mode"),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "0, -1, 1 or 2 "), // TODO : add explanation of values
|
||||
NC_("SC_OPCODE_X_LOOKUP", "Search Mode"),
|
||||
NC_("SC_OPCODE_X_LOOKUP", "1, -1, 2 or -2 ") // TODO : add explanation of values
|
||||
};
|
||||
|
||||
// -=*# Resource for function INDEX #*=-
|
||||
const TranslateId SC_OPCODE_INDEX_ARY[] =
|
||||
{
|
||||
|
@ -78,7 +78,7 @@ public:
|
||||
ScFunctionListObj::ScFunctionListObj()
|
||||
: UnoApiTest("/sc/qa/extras/testdocuments")
|
||||
, XElementAccess(cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get())
|
||||
, XIndexAccess(395)
|
||||
, XIndexAccess(396)
|
||||
, XNameAccess("IF")
|
||||
, XServiceInfo("stardiv.StarCalc.ScFunctionListObj", "com.sun.star.sheet.FunctionDescriptions")
|
||||
{
|
||||
|
4553
sc/qa/unit/data/functions/spreadsheet/fods/xlookup.fods
Normal file
4553
sc/qa/unit/data/functions/spreadsheet/fods/xlookup.fods
Normal file
File diff suppressed because one or more lines are too long
@ -2922,6 +2922,7 @@ CPPUNIT_TEST_FIXTURE(Test, testFunctionLists)
|
||||
"SHEETS",
|
||||
"STYLE",
|
||||
"VLOOKUP",
|
||||
"XLOOKUP",
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
@ -1972,8 +1972,8 @@ class TestQueryIterator
|
||||
typedef ScQueryCellIteratorBase< ScQueryCellIteratorAccess::Direct, ScQueryCellIteratorType::Generic > Base;
|
||||
public:
|
||||
TestQueryIterator( ScDocument& rDocument, ScInterpreterContext& rContext, SCTAB nTable,
|
||||
const ScQueryParam& aParam, bool bMod )
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod )
|
||||
const ScQueryParam& aParam, bool bMod, bool bReverse = false )
|
||||
: Base( rDocument, rContext, nTable, aParam, bMod, bReverse )
|
||||
{
|
||||
}
|
||||
using Base::BinarySearch; // make public
|
||||
|
@ -675,6 +675,7 @@ ScFunctionList::ScFunctionList( bool bEnglishFunctionNames )
|
||||
{ SC_OPCODE_CELL, ENTRY(SC_OPCODE_CELL_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ZELLE, 2, { 0, 1 }, 0 },
|
||||
{ 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_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 },
|
||||
|
@ -60,16 +60,20 @@
|
||||
|
||||
template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
|
||||
ScQueryCellIteratorBase< accessType, queryType >::ScQueryCellIteratorBase(ScDocument& rDocument,
|
||||
ScInterpreterContext& rContext, SCTAB nTable, const ScQueryParam& rParam, bool bMod )
|
||||
: AccessBase( rDocument, rContext, rParam )
|
||||
ScInterpreterContext& rContext, SCTAB nTable, const ScQueryParam& rParam, bool bMod, bool bReverse )
|
||||
: AccessBase( rDocument, rContext, rParam, bReverse )
|
||||
, nStopOnMismatch( nStopOnMismatchDisabled )
|
||||
, nTestEqualCondition( nTestEqualConditionDisabled )
|
||||
, bAdvanceQuery( false )
|
||||
, bIgnoreMismatchOnLeadingStrings( false )
|
||||
, bSortedBinarySearch( false )
|
||||
, bXLookUp( false )
|
||||
, nBestFitCol(SCCOL_MAX)
|
||||
, nBestFitRow(SCROW_MAX)
|
||||
{
|
||||
nTab = nTable;
|
||||
nCol = maParam.nCol1;
|
||||
nRow = maParam.nRow1;
|
||||
nCol = !bReverse ? maParam.nCol1 : maParam.nCol2;
|
||||
nRow = !bReverse ? maParam.nRow1 : maParam.nRow2;
|
||||
SCSIZE i;
|
||||
if (!bMod) // Or else it's already inserted
|
||||
return;
|
||||
@ -127,7 +131,7 @@ void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery()
|
||||
bool bNextColumn = maCurPos.first == pCol->maCells.end();
|
||||
if (!bNextColumn)
|
||||
{
|
||||
if (nRow > maParam.nRow2)
|
||||
if ((!mbReverseSearch && nRow > maParam.nRow2) || (mbReverseSearch && nRow < maParam.nRow1))
|
||||
bNextColumn = true;
|
||||
}
|
||||
|
||||
@ -135,9 +139,18 @@ void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery()
|
||||
{
|
||||
do
|
||||
{
|
||||
++nCol;
|
||||
if (nCol > maParam.nCol2 || nCol >= rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
|
||||
return;
|
||||
if (!mbReverseSearch)
|
||||
{
|
||||
++nCol;
|
||||
if (nCol > maParam.nCol2 || nCol >= rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
--nCol;
|
||||
if (nCol < maParam.nCol1 || nCol < static_cast<SCCOL>(0))
|
||||
return;
|
||||
}
|
||||
if ( bAdvanceQuery )
|
||||
{
|
||||
AdvanceQueryParamEntryField();
|
||||
@ -169,12 +182,12 @@ void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery()
|
||||
// ValidQuery().
|
||||
if(HandleItemFound())
|
||||
return;
|
||||
IncPos();
|
||||
!mbReverseSearch ? IncPos() : DecPos();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
IncBlock();
|
||||
!mbReverseSearch ? IncBlock() : DecBlock();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -182,7 +195,7 @@ void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery()
|
||||
ScRefCellValue aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
|
||||
|
||||
if (bAllStringIgnore && aCell.hasString())
|
||||
IncPos();
|
||||
!mbReverseSearch ? IncPos() : DecPos();
|
||||
else
|
||||
{
|
||||
if ( queryEvaluator.ValidQuery( nRow,
|
||||
@ -192,9 +205,54 @@ void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery()
|
||||
nTestEqualCondition |= nTestEqualConditionMatched;
|
||||
if ( aCell.isEmpty())
|
||||
return;
|
||||
if( HandleItemFound())
|
||||
|
||||
// XLookUp: Forward/backward search for best fit value, except if we have an exact match
|
||||
if (bXLookUp && !bSortedBinarySearch && (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL) &&
|
||||
(nBestFitCol != nCol || nBestFitRow != nRow))
|
||||
{
|
||||
bool bNumSearch = rItem.meType == ScQueryEntry::ByValue && aCell.hasNumeric();
|
||||
bool bStringSearch = rItem.meType == ScQueryEntry::ByString && aCell.hasString();
|
||||
if (bNumSearch || bStringSearch)
|
||||
{
|
||||
if (nTestEqualCondition == nTestEqualConditionFulfilled || (nBestFitCol == SCCOL_MAX && nBestFitRow == SCROW_MAX))
|
||||
HandleBestFitItemFound(nCol, nRow);
|
||||
else
|
||||
{
|
||||
ScAddress aBFAddr(nBestFitCol, nBestFitRow, nTab);
|
||||
ScRefCellValue aBFCell(rDoc, aBFAddr);
|
||||
ScQueryParam aParamTmp(maParam);
|
||||
ScQueryEntry& rEntryTmp = aParamTmp.GetEntry(0);
|
||||
|
||||
if (rEntry.eOp == SC_LESS_EQUAL)
|
||||
rEntryTmp.eOp = SC_GREATER;
|
||||
else if (rEntry.eOp == SC_GREATER_EQUAL)
|
||||
rEntryTmp.eOp = SC_LESS;
|
||||
|
||||
ScQueryEntry::Item& rItemTmp = rEntryTmp.GetQueryItem();
|
||||
if (bNumSearch)
|
||||
rItemTmp.mfVal = aBFCell.getValue();
|
||||
else if (bStringSearch)
|
||||
rItemTmp.maString = svl::SharedString(aBFCell.getString(&rDoc));
|
||||
|
||||
ScQueryEvaluator queryEvaluatorTmp(rDoc, *rDoc.maTabs[nTab], aParamTmp, &mrContext, nullptr);
|
||||
if (queryEvaluatorTmp.ValidQuery(nRow, (nCol == static_cast<SCCOL>(nFirstQueryField) ? &aCell : nullptr)))
|
||||
HandleBestFitItemFound(nCol, nRow);
|
||||
else
|
||||
{
|
||||
!mbReverseSearch ? IncPos() : DecPos();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
!mbReverseSearch ? IncPos() : DecPos();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (HandleItemFound())
|
||||
return;
|
||||
IncPos();
|
||||
!mbReverseSearch ? IncPos() : DecPos();
|
||||
continue;
|
||||
}
|
||||
else if ( nStopOnMismatch )
|
||||
@ -213,7 +271,7 @@ void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery()
|
||||
{
|
||||
if (aCell.hasString())
|
||||
{
|
||||
IncPos();
|
||||
!mbReverseSearch ? IncPos() : DecPos();
|
||||
bStop = false;
|
||||
}
|
||||
else
|
||||
@ -228,7 +286,7 @@ void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery()
|
||||
}
|
||||
}
|
||||
else
|
||||
IncPos();
|
||||
!mbReverseSearch ? IncPos() : DecPos();
|
||||
}
|
||||
bFirstStringIgnore = false;
|
||||
}
|
||||
@ -279,7 +337,8 @@ void ScQueryCellIteratorBase< accessType, queryType >::InitPos()
|
||||
if( BinarySearch( nCol ))
|
||||
lastRow = nRow;
|
||||
}
|
||||
AccessBase::InitPosFinish( beforeRow, lastRow );
|
||||
bool bFirstMatch = (bXLookUp && op != SC_EQUAL);
|
||||
AccessBase::InitPosFinish(beforeRow, lastRow, bFirstMatch);
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,11 +351,13 @@ void ScQueryCellIteratorBase< accessType, queryType >::AdvanceQueryParamEntryFie
|
||||
ScQueryEntry& rEntry = maParam.GetEntry( j );
|
||||
if ( rEntry.bDoQuery )
|
||||
{
|
||||
if ( rEntry.nField < rDoc.MaxCol() )
|
||||
if (!mbReverseSearch && rEntry.nField < rDoc.MaxCol())
|
||||
rEntry.nField++;
|
||||
else if (mbReverseSearch && rEntry.nField > static_cast<SCCOLROW>(0))
|
||||
rEntry.nField--;
|
||||
else
|
||||
{
|
||||
assert(!"AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL");
|
||||
assert(!"AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL || --rEntry.nField < 0");
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -670,14 +731,25 @@ bool ScQueryCellIterator< accessType >::FindEqualOrSortedLastInRange( SCCOL& nFo
|
||||
|
||||
nFoundCol = rDoc.MaxCol()+1;
|
||||
nFoundRow = rDoc.MaxRow()+1;
|
||||
SetStopOnMismatch( true ); // assume sorted keys
|
||||
|
||||
if (bXLookUp && !bSortedBinarySearch)
|
||||
SetStopOnMismatch( false ); // assume not sorted keys for XLookup
|
||||
else
|
||||
SetStopOnMismatch( true ); // assume sorted keys
|
||||
|
||||
SetTestEqualCondition( true );
|
||||
bIgnoreMismatchOnLeadingStrings = true;
|
||||
|
||||
bool bLiteral = maParam.eSearchType == utl::SearchParam::SearchType::Normal &&
|
||||
maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString;
|
||||
bool bBinary = maParam.bByRow &&
|
||||
(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 && bXLookUp && !bSortedBinarySearch)
|
||||
bBinary = false;
|
||||
|
||||
bool bFound = false;
|
||||
if (bBinary)
|
||||
{
|
||||
@ -768,9 +840,10 @@ bool ScQueryCellIterator< accessType >::FindEqualOrSortedLastInRange( SCCOL& nFo
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( IsEqualConditionFulfilled() )
|
||||
if ( IsEqualConditionFulfilled() && !bXLookUp )
|
||||
{
|
||||
// Position on last equal entry.
|
||||
// Position on last equal entry, except for XLOOKUP,
|
||||
// which looking for the first equal entry
|
||||
SCSIZE nEntries = maParam.GetEntryCount();
|
||||
for ( SCSIZE j = 0; j < nEntries; j++ )
|
||||
{
|
||||
@ -861,19 +934,27 @@ bool ScQueryCellIterator< accessType >::FindEqualOrSortedLastInRange( SCCOL& nFo
|
||||
|
||||
ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >
|
||||
::ScQueryCellIteratorAccessSpecific( ScDocument& rDocument,
|
||||
ScInterpreterContext& rContext, const ScQueryParam& rParam )
|
||||
ScInterpreterContext& rContext, const ScQueryParam& rParam, bool bReverseSearch )
|
||||
: maParam( rParam )
|
||||
, rDoc( rDocument )
|
||||
, mrContext( rContext )
|
||||
, mbReverseSearch( bReverseSearch )
|
||||
{
|
||||
// coverity[uninit_member] - this just contains data, subclass will initialize some of it
|
||||
}
|
||||
|
||||
void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::InitPos()
|
||||
{
|
||||
nRow = maParam.nRow1;
|
||||
if (maParam.bHasHeader && maParam.bByRow)
|
||||
++nRow;
|
||||
if (!mbReverseSearch)
|
||||
{
|
||||
nRow = maParam.nRow1;
|
||||
if (maParam.bHasHeader && maParam.bByRow)
|
||||
++nRow;
|
||||
}
|
||||
else
|
||||
{
|
||||
nRow = maParam.nRow2;
|
||||
}
|
||||
const ScColumn& rCol = rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol);
|
||||
maCurPos = rCol.maCells.position(nRow);
|
||||
}
|
||||
@ -891,6 +972,19 @@ void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::Inc
|
||||
IncBlock();
|
||||
}
|
||||
|
||||
void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::DecPos()
|
||||
{
|
||||
if (maCurPos.second > 0)
|
||||
{
|
||||
// Move within the same block.
|
||||
--maCurPos.second;
|
||||
--nRow;
|
||||
}
|
||||
else
|
||||
// Move to the prev block.
|
||||
DecBlock();
|
||||
}
|
||||
|
||||
void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::IncBlock()
|
||||
{
|
||||
++maCurPos.first;
|
||||
@ -899,6 +993,26 @@ void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::Inc
|
||||
nRow = maCurPos.first->position;
|
||||
}
|
||||
|
||||
void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::DecBlock()
|
||||
{
|
||||
// Set current position to the last possible row.
|
||||
const ScColumn& rCol = rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol);
|
||||
if (maCurPos.first != rCol.maCells.begin())
|
||||
{
|
||||
--maCurPos.first;
|
||||
maCurPos.second = maCurPos.first->size - 1;
|
||||
|
||||
nRow = maCurPos.first->position + maCurPos.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No rows, set to end. This will make PerformQuery() go to next column.
|
||||
nRow = maParam.nRow1 - 1;
|
||||
maCurPos.first = rCol.maCells.end();
|
||||
maCurPos.second = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class sequentially indexes non-empty cells in order, from the top of
|
||||
* the block where the start row position is, to the bottom of the block
|
||||
@ -1073,10 +1187,11 @@ ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::MakeBina
|
||||
|
||||
ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >
|
||||
::ScQueryCellIteratorAccessSpecific( ScDocument& rDocument,
|
||||
ScInterpreterContext& rContext, const ScQueryParam& rParam )
|
||||
ScInterpreterContext& rContext, const ScQueryParam& rParam, bool bReverseSearch )
|
||||
: maParam( rParam )
|
||||
, rDoc( rDocument )
|
||||
, mrContext( rContext )
|
||||
, mbReverseSearch( bReverseSearch )
|
||||
{
|
||||
// coverity[uninit_member] - this just contains data, subclass will initialize some of it
|
||||
}
|
||||
@ -1101,7 +1216,7 @@ void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >
|
||||
}
|
||||
|
||||
void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::InitPosFinish(
|
||||
SCROW beforeRow, SCROW lastRow )
|
||||
SCROW beforeRow, SCROW lastRow, bool bFirstMatch )
|
||||
{
|
||||
pColumn = &rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol);
|
||||
if(lastRow >= 0)
|
||||
@ -1110,7 +1225,10 @@ void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >
|
||||
sortedCachePosLast = sortedCache->indexForRow(lastRow);
|
||||
if(sortedCachePos <= sortedCachePosLast)
|
||||
{
|
||||
nRow = sortedCache->rowForIndex(sortedCachePos);
|
||||
if (!bFirstMatch)
|
||||
nRow = sortedCache->rowForIndex(sortedCachePos);
|
||||
else
|
||||
nRow = sortedCache->rowForIndex(sortedCachePosLast);
|
||||
maCurPos = pColumn->maCells.position(nRow);
|
||||
return;
|
||||
}
|
||||
@ -1322,7 +1440,10 @@ template< ScQueryCellIteratorAccess accessType >
|
||||
bool ScQueryCellIterator< accessType >::GetFirst()
|
||||
{
|
||||
assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
|
||||
nCol = maParam.nCol1;
|
||||
if (!mbReverseSearch)
|
||||
nCol = maParam.nCol1;
|
||||
else
|
||||
nCol = maParam.nCol2;
|
||||
InitPos();
|
||||
return GetThis();
|
||||
}
|
||||
@ -1330,7 +1451,10 @@ bool ScQueryCellIterator< accessType >::GetFirst()
|
||||
template< ScQueryCellIteratorAccess accessType >
|
||||
bool ScQueryCellIterator< accessType >::GetNext()
|
||||
{
|
||||
IncPos();
|
||||
if (!mbReverseSearch)
|
||||
IncPos();
|
||||
else
|
||||
DecPos();
|
||||
if ( nStopOnMismatch )
|
||||
nStopOnMismatch = nStopOnMismatchEnabled;
|
||||
if ( nTestEqualCondition )
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <token.hxx>
|
||||
#include <math.hxx>
|
||||
#include <kahan.hxx>
|
||||
#include <queryiter.hxx>
|
||||
#include "parclass.hxx"
|
||||
|
||||
#include <map>
|
||||
@ -55,6 +56,57 @@ struct ScInterpreterContext;
|
||||
class ScJumpMatrix;
|
||||
struct ScRefCellValue;
|
||||
|
||||
enum MatchMode{ exactorNA=0, exactorS=-1, exactorG=1, wildcard=2 };
|
||||
enum SearchMode{ searchfwd=1, searchrev=-1, searchbasc=2, searchbdesc=-2 };
|
||||
|
||||
struct VectorSearchArguments
|
||||
{
|
||||
// struct contains the contents of the function arguments
|
||||
// Struct owner, ScMatch or ScXLookup
|
||||
bool isXLookup = false;
|
||||
|
||||
// match mode (common, enum values are from XLOOKUP)
|
||||
// optional 5th argument to set match mode
|
||||
// 0 - Exact match. If none found, return #N/A. (MATCH value 0)
|
||||
// -1 - Exact match. If none found, return the next smaller item. (MATCH value 1)
|
||||
// 1 - Exact match. If none found, return the next larger item. (MATCH value -1)
|
||||
// 2 - A wildcard match where *, ?, and ~ have special meaning. (XLOOKUP only)
|
||||
// TODO : is this enum needed, or do we solely use rEntry.eOp ?
|
||||
MatchMode eMatchMode = exactorG;
|
||||
|
||||
// value to be searched for (common)
|
||||
SCCOL nCol1 = 0;
|
||||
SCROW nRow1 = 0;
|
||||
SCTAB nTab1 = 0;
|
||||
SCCOL nCol2 = 0;
|
||||
SCROW nRow2 = 0;
|
||||
SCTAB nTab2 = 0;
|
||||
ScMatrixRef pMatSrc;
|
||||
bool isStringSearch = true;
|
||||
double fSearchVal;
|
||||
svl::SharedString sSearchStr;
|
||||
bool bVLookup;
|
||||
|
||||
// search mode (only XLOOKUP has all 4 options, MATCH only uses searchfwd)
|
||||
// optional 6th argument to set search mode
|
||||
// 1 - Perform a search starting at the first item. This is the default.
|
||||
// -1 - Perform a reverse search starting at the last item.
|
||||
// 2 - Perform a binary search that relies on lookup_array being sorted in ascending order.
|
||||
// If not sorted, invalid results will be returned.
|
||||
// -2 - Perform a binary search that relies on lookup_array being sorted in descending order.
|
||||
// If not sorted, invalid results will be returned.
|
||||
//
|
||||
SearchMode eSearchMode = searchfwd;
|
||||
|
||||
// search variables
|
||||
SCSIZE nHitIndex = 0;
|
||||
SCSIZE nBestFit = SCSIZE_MAX;
|
||||
|
||||
// result
|
||||
int nIndex = -1;
|
||||
bool isResultNA = false;
|
||||
};
|
||||
|
||||
namespace sc {
|
||||
|
||||
struct CompareOptions;
|
||||
@ -234,6 +286,10 @@ private:
|
||||
bool IsTableOpInRange( const ScRange& );
|
||||
sal_uInt32 GetCellNumberFormat( const ScAddress& rPos, ScRefCellValue& rCell );
|
||||
double ConvertStringToValue( const OUString& );
|
||||
bool SearchVectorForValue( VectorSearchArguments& );
|
||||
bool SearchMatrixForValue( VectorSearchArguments&, ScQueryParam&, ScQueryEntry&, ScQueryEntry::Item& );
|
||||
bool SearchRangeForValue( VectorSearchArguments&, ScQueryParam&, ScQueryEntry& );
|
||||
|
||||
public:
|
||||
static double ScGetGCD(double fx, double fy);
|
||||
/** For matrix back calls into the current interpreter.
|
||||
@ -485,8 +541,8 @@ private:
|
||||
// Set error according to rVal, and set rVal to 0.0 if there was an error.
|
||||
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 ) const;
|
||||
bool LookupQueryWithCache( ScAddress & o_rResultPos, const ScQueryParam & rParam,
|
||||
const ScComplexRefData* refData, sal_Int8 nSearchMode, bool bXlookupMode ) const;
|
||||
|
||||
void ScIfJump();
|
||||
void ScIfError( bool bNAonly );
|
||||
@ -628,6 +684,7 @@ private:
|
||||
void ScLookup();
|
||||
void ScHLookup();
|
||||
void ScVLookup();
|
||||
void ScXLookup();
|
||||
void ScSubTotal();
|
||||
|
||||
// If upon call rMissingField==true then the database field parameter may be
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4326,6 +4326,7 @@ StackVar ScInterpreter::Interpret()
|
||||
case ocCountIfs : ScCountIfs(); break;
|
||||
case ocLookup : ScLookup(); break;
|
||||
case ocVLookup : ScVLookup(); break;
|
||||
case ocXLookup : ScXLookup(); break;
|
||||
case ocHLookup : ScHLookup(); break;
|
||||
case ocIndex : ScIndex(); break;
|
||||
case ocMultiArea : ScMultiArea(); break;
|
||||
|
@ -264,6 +264,7 @@ 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 }},
|
||||
{ ocXor, {{ Reference }, 1, Value }},
|
||||
{ ocZTest, {{ Reference, Value, Value }, 0, Value }},
|
||||
{ ocZTest_MS, {{ Reference, Value, Value }, 0, Value }},
|
||||
|
@ -1387,6 +1387,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
|
||||
case ocCount:
|
||||
case ocCount2:
|
||||
case ocVLookup:
|
||||
case ocXLookup:
|
||||
case ocSLN:
|
||||
case ocIRR:
|
||||
case ocMIRR:
|
||||
|
@ -593,6 +593,18 @@ const XclFunctionInfo saFuncTable_2016[] =
|
||||
EXC_FUNCENTRY_V_VR( ocMaxIfs_MS, 3, MX, 0, "MAXIFS" )
|
||||
};
|
||||
|
||||
|
||||
/** Functions new in Excel 2021.
|
||||
|
||||
|
||||
@See sc/source/filter/oox/formulabase.cxx saFuncTable2021 for V,VR,RO,...
|
||||
*/
|
||||
const XclFunctionInfo saFuncTable_2021[] =
|
||||
{
|
||||
EXC_FUNCENTRY_V_VR( ocXLookup, 3, 6, 0, "XLOOKUP" )
|
||||
};
|
||||
|
||||
|
||||
#define EXC_FUNCENTRY_ODF( opcode, minparam, maxparam, flags, asciiname ) \
|
||||
{ opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }, \
|
||||
{ opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }
|
||||
@ -669,6 +681,7 @@ XclFunctionProvider::XclFunctionProvider( const XclRoot& rRoot )
|
||||
(this->*pFillFunc)(saFuncTable_2010, std::end(saFuncTable_2010));
|
||||
(this->*pFillFunc)(saFuncTable_2013, std::end(saFuncTable_2013));
|
||||
(this->*pFillFunc)(saFuncTable_2016, std::end(saFuncTable_2016));
|
||||
(this->*pFillFunc)(saFuncTable_2021, std::end(saFuncTable_2021));
|
||||
(this->*pFillFunc)(saFuncTable_Odf, std::end(saFuncTable_Odf));
|
||||
(this->*pFillFunc)(saFuncTable_OOoLO, std::end(saFuncTable_OOoLO));
|
||||
}
|
||||
|
@ -866,6 +866,20 @@ const FunctionData saFuncTable2016[] =
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Functions new in Excel 2021.
|
||||
|
||||
|
||||
@See sc/source/filter/excel/xlformula.cxx saFuncTable_2021
|
||||
*/
|
||||
/* 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 }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Functions defined by OpenFormula, but not supported by Calc or by Excel. */
|
||||
const FunctionData saFuncTableOdf[] =
|
||||
{
|
||||
@ -1008,6 +1022,7 @@ FunctionProviderImpl::FunctionProviderImpl( bool bImportFilter )
|
||||
initFuncs(saFuncTable2010 , std::end(saFuncTable2010) , bImportFilter);
|
||||
initFuncs(saFuncTable2013 , std::end(saFuncTable2013) , bImportFilter);
|
||||
initFuncs(saFuncTable2016 , std::end(saFuncTable2016) , bImportFilter);
|
||||
initFuncs(saFuncTable2021 , std::end(saFuncTable2021 ), bImportFilter);
|
||||
initFuncs(saFuncTableOdf , std::end(saFuncTableOdf) , bImportFilter);
|
||||
initFuncs(saFuncTableOOoLO, std::end(saFuncTableOOoLO), bImportFilter);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user