sc: take the implicit intersection of an external range
=@'file:///source.ods'#Sheet1.B:B showed the first cell of the column and not the one in the formula's own row. ScSingleValue leaves an external range on the stack unchanged, and Interpret ends with it on the matrix path, so the result became the top left element of a matrix over the whole range. A sheet local range takes a position dependent implicit intersection in both places, with DoubleRefToPosSingleRef. An external range now does the same through PopExternalDoubleRefPushSingleRef, which reads the position from ScCompiler::ExternalDoubleRefToPosSingleRef. Where the position does not intersect the range nothing changes, so an unresolved link keeps its own error instead of turning into a #VALUE!, as testExternalRefUnresolved expects. The external and the sheet local case still differ there (TODO). Follow-up of commit: I3baf826177fbeeb4616f407d653f1457a2fbfa8f (sc: don't build a JumpMatrix for an entire external column) Change-Id: I68cc88b5bb533cfcd19537be6705e8a64834af64 Signed-off-by: Balazs Varga <balazs.varga@collabora.com> Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12614 Tested-by: Jenkins CPCI <releng@collaboraoffice.com> Reviewed-by: Miklos Vajna <vmiklos@collabora.com> (cherry picked from commit 596a329b2f0e888047069b442f4da507a0c668f8) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211105 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
6e785d7b0b
commit
54378c2b97
@@ -524,6 +524,11 @@ public:
|
||||
static bool DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, ScAddress& rAdr,
|
||||
const ScAddress& rFormulaPos);
|
||||
|
||||
/** The same for a range in another document, where the shape of the range
|
||||
decides and not whether the formula position lies within it. */
|
||||
static bool ExternalDoubleRefToPosSingleRef(const ScRange& rRange, ScAddress& rAdr,
|
||||
const ScAddress& rFormulaPos);
|
||||
|
||||
bool HasUnhandledPossibleImplicitIntersections() const { return !mUnhandledPossibleImplicitIntersections.empty(); }
|
||||
|
||||
SC_DLLPUBLIC static OUString SanitizeDefinedName(const OUString& rStr, const ScDocument& rDoc);
|
||||
|
||||
@@ -657,6 +657,19 @@ CPPUNIT_TEST_FIXTURE(ScFiltersTest2, testExternalRefWholeColumnXLOOKUP)
|
||||
CPPUNIT_ASSERT_EQUAL(u"#N/A"_ustr, pDoc->GetString(ScAddress(1, 9, 0)));
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(ScFiltersTest2, testExternalRefWholeColumnResult)
|
||||
{
|
||||
createScDoc("xlsx/external-ref-wholecol-xlookup.xlsx");
|
||||
ScDocument* pDoc = getScDoc();
|
||||
|
||||
// Excel's cached results are applied on import, recalculate to run the formulas.
|
||||
pDoc->CalcAll();
|
||||
|
||||
// D2:D6 are @ on an entire external column, each row takes its own.
|
||||
for (SCROW nRow = 1; nRow <= 5; ++nRow)
|
||||
CPPUNIT_ASSERT_EQUAL(9.0 + nRow, pDoc->GetValue(ScAddress(3, nRow, 0)));
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(ScFiltersTest2, testExternalRefCacheODS)
|
||||
{
|
||||
createScDoc("ods/external-ref-cache.ods");
|
||||
|
||||
@@ -2001,6 +2001,10 @@ void TestFormula2::testExtRefImplicitIntersection(ScDocument* pDoc, ScDocument&
|
||||
rExtDoc.SetValue(ScAddress(1, nRow, 0), (nRow + 1.0) * 10);
|
||||
}
|
||||
|
||||
// @ takes the same intersection, here where its result feeds an operator.
|
||||
pDoc->SetString(ScAddress(0, 1, 0), u"=@'file:///extdata.fake'#Data.A1:A5*1"_ustr);
|
||||
CPPUNIT_ASSERT_EQUAL(2.0, pDoc->GetValue(ScAddress(0, 1, 0)));
|
||||
|
||||
// A scalar Value parameter takes an implicit intersection, row 3 looks up A3.
|
||||
pDoc->SetString(ScAddress(0, 2, 0), u"=XLOOKUP('file:///extdata.fake'#Data.A1:A5;"
|
||||
u"'file:///extdata.fake'#Data.A1:A5;"
|
||||
|
||||
@@ -468,6 +468,10 @@ private:
|
||||
bool PopDoubleRefOrSingleRef( ScAddress& rAdr );
|
||||
void PopDoubleRefPushMatrix();
|
||||
void PopRefListPushMatrixOrRef();
|
||||
/** Pop an external double reference and push the single reference its
|
||||
implicit intersection yields. Leaves the stack alone and returns false
|
||||
when the formula position does not intersect the range. */
|
||||
bool PopExternalDoubleRefPushSingleRef();
|
||||
// If MatrixFormula: convert svDoubleRef to svMatrix, create JumpMatrix.
|
||||
// Else convert area reference parameters marked as ForceArray to array.
|
||||
// Returns true if JumpMatrix created.
|
||||
|
||||
@@ -7151,6 +7151,29 @@ bool ScCompiler::DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, ScAddr
|
||||
return bOk;
|
||||
}
|
||||
|
||||
bool ScCompiler::ExternalDoubleRefToPosSingleRef(const ScRange& rRange, ScAddress& rAdr,
|
||||
const ScAddress& rFormulaPos)
|
||||
{
|
||||
SCCOL nCol = rFormulaPos.Col();
|
||||
SCROW nRow = rFormulaPos.Row();
|
||||
if (rRange.aStart == rRange.aEnd)
|
||||
{
|
||||
nCol = rRange.aStart.Col();
|
||||
nRow = rRange.aStart.Row();
|
||||
}
|
||||
else if (rRange.aStart.Col() == rRange.aEnd.Col() && rRange.aStart.Row() <= nRow
|
||||
&& nRow <= rRange.aEnd.Row())
|
||||
nCol = rRange.aStart.Col();
|
||||
else if (rRange.aStart.Row() == rRange.aEnd.Row() && rRange.aStart.Col() <= nCol
|
||||
&& nCol <= rRange.aEnd.Col())
|
||||
nRow = rRange.aStart.Row();
|
||||
else
|
||||
return false;
|
||||
|
||||
rAdr.Set(nCol, nRow, rRange.aStart.Tab());
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lcl_GetColRowDeltas(const ScRange& rRange, SCCOL& rXDelta, SCROW& rYDelta)
|
||||
{
|
||||
rXDelta = rRange.aEnd.Col() - rRange.aStart.Col();
|
||||
|
||||
@@ -1605,6 +1605,9 @@ void ScInterpreter::ScSingleValue()
|
||||
PushDouble(GetCellValue(aAdr, aCell));
|
||||
return;
|
||||
}
|
||||
// Same intersection as for a sheet local range above.
|
||||
if (eType == svExternalDoubleRef && PopExternalDoubleRefPushSingleRef())
|
||||
return;
|
||||
if (eType == svMatrix)
|
||||
{
|
||||
ScMatrixRef pMat = GetMatrix();
|
||||
|
||||
@@ -1467,6 +1467,25 @@ bool ScInterpreter::PopDoubleRefOrSingleRef( ScAddress& rAdr )
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScInterpreter::PopExternalDoubleRefPushSingleRef()
|
||||
{
|
||||
const ScExternalDoubleRefToken* pToken
|
||||
= static_cast<const ScExternalDoubleRefToken*>(pStack[sp - 1]);
|
||||
ScComplexRefData aData( pToken->GetDoubleRef());
|
||||
const ScRange aRange = aData.toAbs(mrDoc, aPos);
|
||||
ScAddress aAdr;
|
||||
if (!ScCompiler::ExternalDoubleRefToPosSingleRef( aRange, aAdr, aPos))
|
||||
return false;
|
||||
|
||||
ScSingleRefData aRefData;
|
||||
aRefData.InitAddress( aAdr);
|
||||
FormulaTokenRef xNew = new ScExternalSingleRefToken(
|
||||
pToken->GetFileId(), pToken->GetTableName(), aRefData);
|
||||
Pop();
|
||||
PushTempTokenWithoutError( xNew.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScInterpreter::PopDoubleRefPushMatrix()
|
||||
{
|
||||
if ( GetStackType() == svDoubleRef )
|
||||
@@ -1693,32 +1712,14 @@ bool ScInterpreter::ConvertMatrixParameters()
|
||||
auto pEDRToken = static_cast<const ScExternalDoubleRefToken*>(p);
|
||||
ScComplexRefData aData( pEDRToken->GetDoubleRef());
|
||||
const ScRange aRange = aData.toAbs(mrDoc, aPos);
|
||||
// Resolve from the shape of the range.
|
||||
// DoubleRefToPosSingleRefScalarCase() asks instead
|
||||
// whether the formula position lies within the range.
|
||||
SCCOL nCol = aPos.Col();
|
||||
SCROW nRow = aPos.Row();
|
||||
bool bOk = true;
|
||||
if (aRange.aStart == aRange.aEnd)
|
||||
{
|
||||
nCol = aRange.aStart.Col();
|
||||
nRow = aRange.aStart.Row();
|
||||
}
|
||||
else if (aRange.aStart.Col() == aRange.aEnd.Col() &&
|
||||
aRange.aStart.Row() <= nRow && nRow <= aRange.aEnd.Row())
|
||||
nCol = aRange.aStart.Col();
|
||||
else if (aRange.aStart.Row() == aRange.aEnd.Row() &&
|
||||
aRange.aStart.Col() <= nCol && nCol <= aRange.aEnd.Col())
|
||||
nRow = aRange.aStart.Row();
|
||||
else
|
||||
bOk = false;
|
||||
if (!bOk)
|
||||
ScAddress aAdr;
|
||||
if (!ScCompiler::ExternalDoubleRefToPosSingleRef( aRange, aAdr, aPos))
|
||||
SetError( FormulaError::NoValue);
|
||||
else
|
||||
{
|
||||
ScSingleRefData aRefData;
|
||||
// Absolute, an external table reference must be.
|
||||
aRefData.InitAddress( nCol, nRow, aRange.aStart.Tab());
|
||||
aRefData.InitAddress( aAdr);
|
||||
formula::FormulaToken* pNew = new ScExternalSingleRefToken(
|
||||
pEDRToken->GetFileId(), pEDRToken->GetTableName(), aRefData);
|
||||
pNew->IncRef();
|
||||
@@ -4584,6 +4585,11 @@ StackVar ScInterpreter::Interpret()
|
||||
PopRefListPushMatrixOrRef();
|
||||
pCur = pStack[ sp-1 ];
|
||||
}
|
||||
// An external range takes the same implicit intersection as
|
||||
// the svDoubleRef case below, which nothing else does for it.
|
||||
if (!bMatrixFormula && pCur->GetType() == svExternalDoubleRef
|
||||
&& PopExternalDoubleRefPushSingleRef())
|
||||
pCur = pStack[ sp-1 ];
|
||||
switch( pCur->GetType() )
|
||||
{
|
||||
case svEmptyCell:
|
||||
|
||||
Reference in New Issue
Block a user