summaryrefslogtreecommitdiff
path: root/sc
diff options
context:
space:
mode:
authorEike Rathke <erack@redhat.com>2016-03-30 22:11:47 +0200
committerEike Rathke <erack@redhat.com>2016-03-31 21:38:47 +0200
commitd2a2d436aa10839c83a5471c9d37e2d69011825a (patch)
treee9c666ba85302b987cccc9b2b0e2b28710d79c90 /sc
parente3e0ac4d3d3d7f701618358ccbb3d667683c6b20 (diff)
copy and adjust nested named expressions during sheet-copy
Change-Id: Idec3d7f2282bb2521c1f46e4488bf85a47b19ea4
Diffstat (limited to 'sc')
-rw-r--r--sc/inc/refupdatecontext.hxx1
-rw-r--r--sc/source/core/data/formulacell.cxx266
-rw-r--r--sc/source/core/data/refupdatecontext.cxx6
3 files changed, 251 insertions, 22 deletions
diff --git a/sc/inc/refupdatecontext.hxx b/sc/inc/refupdatecontext.hxx
index 272c3a23e4a2..2c47563dd34c 100644
--- a/sc/inc/refupdatecontext.hxx
+++ b/sc/inc/refupdatecontext.hxx
@@ -35,6 +35,7 @@ public:
void setUpdatedName(SCTAB nTab, sal_uInt16 nIndex);
bool isNameUpdated(SCTAB nTab, sal_uInt16 nIndex) const;
NameIndicesType getUpdatedNames(SCTAB nTab) const;
+ bool isEmpty(SCTAB nTab) const;
private:
typedef std::unordered_map<SCTAB, NameIndicesType> UpdatedNamesType;
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index a8f829195e1c..c3db7a97121c 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -420,9 +420,104 @@ bool lcl_isReference(const FormulaToken& rToken)
rToken.GetType() == svDoubleRef;
}
+enum MightReferenceSheet
+{
+ UNKNOWN,
+ NONE,
+ CODE,
+ NAME
+};
+
+MightReferenceSheet mightRangeNameReferenceSheet( ScRangeData* pData, SCTAB nRefTab)
+{
+ ScTokenArray* pCode = pData->GetCode();
+ if (!pCode)
+ return MightReferenceSheet::NONE;
+
+ for (const FormulaToken* p = pCode->First(); p; p = pCode->Next())
+ {
+ if (p->GetOpCode() == ocName)
+ return MightReferenceSheet::NAME;
+ }
+
+ return pCode->ReferencesSheet( nRefTab, pData->GetPos().Tab()) ?
+ MightReferenceSheet::CODE : MightReferenceSheet::NONE;
+}
+
+// See also lcl_FindRangeNamesInUse() below.
+/** Recursively find all named expressions that directly or indirectly (nested)
+ reference a given sheet.
+
+ @param pToken
+ A token of type svIndex with OpCode ocName.
+ */
+bool findRangeNamesReferencingSheet( sc::UpdatedRangeNames& rIndexes, const FormulaToken* pToken,
+ const ScDocument* pDoc, SCTAB nGlobalRefTab, SCTAB nLocalRefTab,
+ SCTAB nOldTokenTab, SCTAB nOldTokenTabReplacement, int nRecursion)
+{
+ const sal_uInt16 nTokenIndex = pToken->GetIndex();
+ SCTAB nTokenTab = pToken->GetSheet();
+ if (nTokenTab < -1)
+ {
+ SAL_WARN("sc.core", "findRangeNamesReferencingSheet - nTokenTab < -1 : " <<
+ nTokenTab << ", nTokenIndex " << nTokenIndex << " Fix the creator!");
+#if OSL_DEBUG_LEVEL > 0
+ const ScRangeData* pData = pDoc->FindRangeNameByIndexAndSheet( nTokenIndex, nTokenTab);
+ SAL_WARN_IF( pData, "sc.core", "findRangeNamesReferencingSheet - named expression is: " << pData->GetName());
+#endif
+ nTokenTab = -1;
+ }
+ SCTAB nRefTab = nGlobalRefTab;
+ if (nTokenTab == nOldTokenTab)
+ {
+ nTokenTab = nOldTokenTabReplacement;
+ nRefTab = nLocalRefTab;
+ }
+ else if (nTokenTab == nOldTokenTabReplacement)
+ {
+ nRefTab = nLocalRefTab;
+ }
+
+ if (rIndexes.isNameUpdated( nTokenTab, nTokenIndex))
+ return true;
+
+ ScRangeData* pData = pDoc->FindRangeNameByIndexAndSheet( nTokenIndex, nTokenTab);
+ if (!pData)
+ return false;
+
+ ScTokenArray* pCode = pData->GetCode();
+ if (!pCode)
+ return false;
+
+ bool bRef = false;
+ if (nRecursion < 126) // whatever.. 42*3
+ {
+ for (const FormulaToken* p = pCode->First(); p; p = pCode->Next())
+ {
+ if (p->GetOpCode() == ocName)
+ {
+ bRef |= findRangeNamesReferencingSheet( rIndexes, p, pDoc, nGlobalRefTab, nLocalRefTab,
+ nOldTokenTab, nOldTokenTabReplacement, nRecursion+1);
+ }
+ }
+ }
+
+ if (!bRef)
+ {
+ SCTAB nPosTab = pData->GetPos().Tab();
+ if (nPosTab == nOldTokenTab)
+ nPosTab = nOldTokenTabReplacement;
+ bRef = pCode->ReferencesSheet( nRefTab, nPosTab);
+ }
+ if (bRef)
+ rIndexes.setUpdatedName( nTokenTab, nTokenIndex);
+
+ return bRef;
+}
+
ScRangeData* copyRangeName( const ScRangeData* pOldRangeData, ScDocument& rNewDoc, const ScDocument* pOldDoc,
const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal,
- SCTAB nOldSheet, SCTAB & nNewSheet)
+ SCTAB nOldSheet, SCTAB & nNewSheet, bool bSameDoc)
{
ScAddress aRangePos( pOldRangeData->GetPos());
if (nOldSheet < 0 && !bGlobalNamesToLocal)
@@ -437,7 +532,6 @@ ScRangeData* copyRangeName( const ScRangeData* pOldRangeData, ScDocument& rNewDo
ScRangeData* pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc, &aRangePos);
pRangeData->SetIndex(0); // needed for insert to assign a new index
ScTokenArray* pRangeNameToken = pRangeData->GetCode();
- bool bSameDoc = (rNewDoc.GetPool() == const_cast<ScDocument*>(pOldDoc)->GetPool());
if (bSameDoc && nNewSheet >= 0)
{
if (bGlobalNamesToLocal && nOldSheet < 0)
@@ -464,6 +558,80 @@ ScRangeData* copyRangeName( const ScRangeData* pOldRangeData, ScDocument& rNewDo
return bInserted ? pRangeData : nullptr;
}
+struct SheetIndex
+{
+ SCTAB mnSheet;
+ sal_uInt16 mnIndex;
+
+ SheetIndex( SCTAB nSheet, sal_uInt16 nIndex ) : mnSheet(nSheet < -1 ? -1 : nSheet), mnIndex(nIndex) {}
+ bool operator<( const SheetIndex& r ) const
+ {
+ // Ascending order sheet, index
+ if (mnSheet < r.mnSheet)
+ return true;
+ if (mnSheet == r.mnSheet)
+ return mnIndex < r.mnIndex;
+ return false;
+ }
+};
+typedef std::map< SheetIndex, SheetIndex > SheetIndexMap;
+
+ScRangeData* copyRangeNames( SheetIndexMap& rSheetIndexMap, std::vector<ScRangeData*>& rRangeDataVec,
+ const sc::UpdatedRangeNames& rReferencingNames, SCTAB nTab,
+ const ScRangeData* pOldRangeData, ScDocument& rNewDoc, const ScDocument* pOldDoc,
+ const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal,
+ SCTAB nOldSheet, SCTAB & nNewSheet, bool bSameDoc)
+{
+ ScRangeData* pRangeData = nullptr;
+ const ScRangeName* pOldRangeName = (nTab < 0 ? pOldDoc->GetRangeName() : pOldDoc->GetRangeName(nTab));
+ if (pOldRangeName)
+ {
+ const ScRangeName* pNewRangeName = (nNewSheet < 0 ? rNewDoc.GetRangeName() : rNewDoc.GetRangeName(nNewSheet));
+ const SCTAB nFixSheet = nNewSheet;
+ sc::UpdatedRangeNames::NameIndicesType aSet( rReferencingNames.getUpdatedNames(nTab));
+ for (auto const & rIndex : aSet)
+ {
+ const ScRangeData* pCopyData = pOldRangeName->findByIndex(rIndex);
+ if (pCopyData)
+ {
+ // Match the original pOldRangeData to adapt the current
+ // token's values later. For that no check for an already
+ // copied name is needed as we only enter here if there was
+ // none.
+ if (pCopyData == pOldRangeData)
+ {
+ pRangeData = copyRangeName( pCopyData, rNewDoc, pOldDoc, rNewPos, rOldPos,
+ bGlobalNamesToLocal, nOldSheet, nNewSheet, bSameDoc);
+ rRangeDataVec.push_back(pRangeData);
+ rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()),
+ SheetIndex( nNewSheet, pRangeData->GetIndex())));
+ }
+ else
+ {
+ // First check if the name is already available as copy.
+ const ScRangeData* pFoundData = pNewRangeName->findByUpperName( pCopyData->GetUpperName());
+ if (pFoundData)
+ {
+ // Just add the resulting sheet/index mapping.
+ rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()),
+ SheetIndex( nFixSheet, pFoundData->GetIndex())));
+ }
+ else
+ {
+ SCTAB nTmpSheet = nFixSheet; // don't let the original get adapted
+ ScRangeData* pTmpData = copyRangeName( pCopyData, rNewDoc, pOldDoc, rNewPos, rOldPos,
+ bGlobalNamesToLocal, nOldSheet, nTmpSheet, bSameDoc);
+ rRangeDataVec.push_back(pTmpData);
+ rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()),
+ SheetIndex( nTmpSheet, pTmpData->GetIndex())));
+ }
+ }
+ }
+ }
+ }
+ return pRangeData;
+}
+
void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc,
const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal)
{
@@ -482,15 +650,22 @@ void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const S
int nOldIndex = pToken->GetIndex();
ScRangeData* pOldRangeData = nullptr;
+ // XXX bGlobalNamesToLocal is also a synonym for copied sheet.
+ bool bInsertingBefore = (bGlobalNamesToLocal && bSameDoc && rNewPos.Tab() <= rOldPos.Tab());
+
+ // The Tab where an old local name is to be found or that a global name
+ // references. May differ below from nOldSheet if a sheet was inserted
+ // before the old position. Global names and local names other than on the
+ // old sheet or new sheet are already updated, local names on the old sheet
+ // or inserted sheet will be updated later. Confusing stuff. Watch out.
+ SCTAB nOldTab = (nOldSheet < 0 ? rOldPos.Tab() : nOldSheet);
+ if (bInsertingBefore)
+ // Sheet was already inserted before old position.
+ ++nOldTab;
+
// Search the name of the RangeName.
if (nOldSheet >= 0)
{
- SCTAB nOldTab = nOldSheet;
- // XXX bGlobalNamesToLocal is also a synonym for copied sheet.
- if (bGlobalNamesToLocal && bSameDoc && rNewPos.Tab() <= rOldPos.Tab())
- // Sheet was already inserted before old position.
- ++nOldTab;
-
const ScRangeName* pRangeName = pOldDoc->GetRangeName(nOldTab);
pOldRangeData = pRangeName ? pRangeName->findByIndex(nOldIndex) : nullptr;
if (!pOldRangeData)
@@ -526,24 +701,70 @@ void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const S
// If no range name was found copy it.
if (!pRangeData)
{
- if (nOldSheet < 0 && bGlobalNamesToLocal)
+ bool bCopyGlobalName = (nOldSheet < 0 && bGlobalNamesToLocal);
+ MightReferenceSheet eMightReference = mightRangeNameReferenceSheet( pOldRangeData, nOldTab);
+ if (bCopyGlobalName && eMightReference == MightReferenceSheet::NONE)
+ return;
+
+ if (eMightReference == MightReferenceSheet::NAME)
{
- // Copy only global names to local that reference the old sheet.
- SCTAB nOldTab = rOldPos.Tab();
- if (bSameDoc && rNewPos.Tab() <= nOldTab)
+ // Name these to clarify what is passed where.
+ const SCTAB nGlobalRefTab = nOldTab;
+ const SCTAB nLocalRefTab = (bInsertingBefore ? nOldTab-1 : nOldTab);
+ const SCTAB nOldTokenTab = (nOldSheet < 0 ? (bInsertingBefore ? nOldTab-1 : nOldTab) : nOldSheet);
+ const SCTAB nOldTokenTabReplacement = nOldTab;
+ sc::UpdatedRangeNames aReferencingNames;
+ findRangeNamesReferencingSheet( aReferencingNames, pToken, pOldDoc,
+ nGlobalRefTab, nLocalRefTab, nOldTokenTab, nOldTokenTabReplacement, 0);
+ if (bCopyGlobalName && aReferencingNames.isEmpty(-1) && aReferencingNames.isEmpty(nOldTokenTabReplacement))
+ return;
+
+ SheetIndexMap aSheetIndexMap;
+ std::vector<ScRangeData*> aRangeDataVec;
+ if (!aReferencingNames.isEmpty(nOldTokenTabReplacement))
{
- // Sheet was inserted before old position, references were
- // already updated but rOldPos points to the old position,
- // adjust to look for references.
- ++nOldTab;
+ const SCTAB nTmpSheet = (nOldSheet < 0 ? nOldTab : nOldSheet);
+ pRangeData = copyRangeNames( aSheetIndexMap, aRangeDataVec, aReferencingNames, nOldTab,
+ pOldRangeData, rNewDoc, pOldDoc, rNewPos, rOldPos,
+ bGlobalNamesToLocal, nTmpSheet, nNewSheet, bSameDoc);
+ }
+ if (bGlobalNamesToLocal && !aReferencingNames.isEmpty(-1))
+ {
+ const SCTAB nTmpSheet = -1;
+ ScRangeData* pTmpData = copyRangeNames( aSheetIndexMap, aRangeDataVec, aReferencingNames, -1,
+ pOldRangeData, rNewDoc, pOldDoc, rNewPos, rOldPos,
+ bGlobalNamesToLocal, nTmpSheet, nNewSheet, bSameDoc);
+ if (!pRangeData)
+ pRangeData = pTmpData;
}
- if (!pOldRangeData->GetCode()->ReferencesSheet( nOldTab, pOldRangeData->GetPos().Tab()))
- return;
- }
- // Also may modify nNewSheet to be set below at the end.
- pRangeData = copyRangeName( pOldRangeData, rNewDoc, pOldDoc, rNewPos, rOldPos, bGlobalNamesToLocal,
- nOldSheet, nNewSheet);
+ // Adjust copied nested names to new sheet/index.
+ for (auto & iRD : aRangeDataVec)
+ {
+ ScTokenArray* pCode = iRD->GetCode();
+ if (pCode)
+ {
+ for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
+ {
+ if (p->GetOpCode() == ocName)
+ {
+ auto it = aSheetIndexMap.find( SheetIndex( p->GetSheet(), p->GetIndex()));
+ if (it != aSheetIndexMap.end())
+ {
+ p->SetSheet( it->second.mnSheet);
+ p->SetIndex( it->second.mnIndex);
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // Also may modify nNewSheet to be set below at the end.
+ pRangeData = copyRangeName( pOldRangeData, rNewDoc, pOldDoc, rNewPos, rOldPos, bGlobalNamesToLocal,
+ nOldSheet, nNewSheet, bSameDoc);
+ }
if (!pRangeData)
{
@@ -3752,6 +3973,7 @@ void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY
StartListeningTo( pDocument ); // Listener as previous
}
+// See also findRangeNamesReferencingSheet() above.
static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, ScTokenArray* pCode, const ScDocument* pDoc,
int nRecursion)
{
diff --git a/sc/source/core/data/refupdatecontext.cxx b/sc/source/core/data/refupdatecontext.cxx
index 79a3792b41a5..c065b35e9210 100644
--- a/sc/source/core/data/refupdatecontext.cxx
+++ b/sc/source/core/data/refupdatecontext.cxx
@@ -56,6 +56,12 @@ UpdatedRangeNames::NameIndicesType UpdatedRangeNames::getUpdatedNames(SCTAB nTab
return it->second;
}
+bool UpdatedRangeNames::isEmpty(SCTAB nTab) const
+{
+ UpdatedNamesType::const_iterator it = maUpdatedNames.find(nTab);
+ return it == maUpdatedNames.end();
+}
+
RefUpdateContext::RefUpdateContext(ScDocument& rDoc) :
mrDoc(rDoc), meMode(URM_INSDEL), mnColDelta(0), mnRowDelta(0), mnTabDelta(0) {}