copy and adjust nested named expressions during sheet-copy

Change-Id: Idec3d7f2282bb2521c1f46e4488bf85a47b19ea4
This commit is contained in:
Eike Rathke 2016-03-30 22:11:47 +02:00
parent e3e0ac4d3d
commit d2a2d436aa
3 changed files with 254 additions and 25 deletions

View file

@ -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;

View file

@ -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)
{
// Copy only global names to local that reference the old sheet.
SCTAB nOldTab = rOldPos.Tab();
if (bSameDoc && rNewPos.Tab() <= nOldTab)
{
// Sheet was inserted before old position, references were
// already updated but rOldPos points to the old position,
// adjust to look for references.
++nOldTab;
}
if (!pOldRangeData->GetCode()->ReferencesSheet( nOldTab, pOldRangeData->GetPos().Tab()))
return;
}
bool bCopyGlobalName = (nOldSheet < 0 && bGlobalNamesToLocal);
MightReferenceSheet eMightReference = mightRangeNameReferenceSheet( pOldRangeData, nOldTab);
if (bCopyGlobalName && eMightReference == MightReferenceSheet::NONE)
return;
// Also may modify nNewSheet to be set below at the end.
pRangeData = copyRangeName( pOldRangeData, rNewDoc, pOldDoc, rNewPos, rOldPos, bGlobalNamesToLocal,
nOldSheet, nNewSheet);
if (eMightReference == MightReferenceSheet::NAME)
{
// 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))
{
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;
}
// 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)
{

View file

@ -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) {}