/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace formula; using ::std::pair; // ScRangeData ScRangeData::ScRangeData( ScDocument& rDok, const OUString& rName, const OUString& rSymbol, const ScAddress& rAddress, Type nType, const FormulaGrammar::Grammar eGrammar ) : aName ( rName ), aUpperName ( ScGlobal::getCharClassPtr()->uppercase( rName ) ), aPos ( rAddress ), eType ( nType ), rDoc ( rDok ), eTempGrammar( eGrammar ), nIndex ( 0 ), bModified ( false ) { if (!rSymbol.isEmpty()) { // Let the compiler set an error on unknown names for a subsequent // CompileUnresolvedXML(). const bool bImporting = rDoc.IsImportingXML(); CompileRangeData( rSymbol, bImporting); if (bImporting) rDoc.CheckLinkFormulaNeedingCheck( *pCode); } else { // #i63513#/#i65690# don't leave pCode as NULL. // Copy ctor default-constructs pCode if it was NULL, so it's initialized here, too, // to ensure same behavior if unnecessary copying is left out. pCode.reset( new ScTokenArray(rDoc) ); pCode->SetFromRangeName(true); } } ScRangeData::ScRangeData( ScDocument& rDok, const OUString& rName, const ScTokenArray& rArr, const ScAddress& rAddress, Type nType ) : aName ( rName ), aUpperName ( ScGlobal::getCharClassPtr()->uppercase( rName ) ), pCode ( new ScTokenArray( rArr ) ), aPos ( rAddress ), eType ( nType ), rDoc ( rDok ), eTempGrammar( FormulaGrammar::GRAM_UNSPECIFIED ), nIndex ( 0 ), bModified ( false ) { pCode->SetFromRangeName(true); InitCode(); } ScRangeData::ScRangeData( ScDocument& rDok, const OUString& rName, const ScAddress& rTarget ) : aName ( rName ), aUpperName ( ScGlobal::getCharClassPtr()->uppercase( rName ) ), pCode ( new ScTokenArray(rDok) ), aPos ( rTarget ), eType ( Type::Name ), rDoc ( rDok ), eTempGrammar( FormulaGrammar::GRAM_UNSPECIFIED ), nIndex ( 0 ), bModified ( false ) { ScSingleRefData aRefData; aRefData.InitAddress( rTarget ); aRefData.SetFlag3D( true ); pCode->AddSingleReference( aRefData ); pCode->SetFromRangeName(true); ScCompiler aComp( rDoc, aPos, *pCode, rDoc.GetGrammar() ); aComp.CompileTokenArray(); if ( pCode->GetCodeError() == FormulaError::NONE ) eType |= Type::AbsPos; } ScRangeData::ScRangeData(const ScRangeData& rScRangeData, ScDocument* pDocument, const ScAddress* pPos) : aName (rScRangeData.aName), aUpperName (rScRangeData.aUpperName), pCode (rScRangeData.pCode ? rScRangeData.pCode->Clone().release() : new ScTokenArray(*pDocument)), // make real copy (not copy-ctor) aPos (pPos ? *pPos : rScRangeData.aPos), eType (rScRangeData.eType), rDoc (pDocument ? *pDocument : rScRangeData.rDoc), eTempGrammar(rScRangeData.eTempGrammar), nIndex (rScRangeData.nIndex), bModified (rScRangeData.bModified) { pCode->SetFromRangeName(true); } ScRangeData::~ScRangeData() { } void ScRangeData::CompileRangeData( const OUString& rSymbol, bool bSetError ) { if (eTempGrammar == FormulaGrammar::GRAM_UNSPECIFIED) { OSL_FAIL( "ScRangeData::CompileRangeData: unspecified grammar"); // Anything is almost as bad as this, but we might have the best choice // if not loading documents. eTempGrammar = FormulaGrammar::GRAM_NATIVE; } ScCompiler aComp( rDoc, aPos, eTempGrammar ); if (bSetError) aComp.SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_NO_BREAK); pCode = aComp.CompileString( rSymbol ); pCode->SetFromRangeName(true); if( pCode->GetCodeError() != FormulaError::NONE ) return; FormulaTokenArrayPlainIterator aIter(*pCode); FormulaToken* p = aIter.GetNextReference(); if( p ) { // first token is a reference /* FIXME: wouldn't that need a check if it's exactly one reference? */ if( p->GetType() == svSingleRef ) eType = eType | Type::AbsPos; else eType = eType | Type::AbsArea; } // For manual input set an error for an incomplete formula. if (!rDoc.IsImportingXML()) { aComp.CompileTokenArray(); pCode->DelRPN(); } } void ScRangeData::CompileUnresolvedXML( sc::CompileFormulaContext& rCxt ) { if (pCode->GetCodeError() == FormulaError::NoName) { // Reconstruct the symbol/formula and then recompile. OUString aSymbol; rCxt.setGrammar(eTempGrammar); ScCompiler aComp(rCxt, aPos, *pCode); aComp.CreateStringFromTokenArray( aSymbol); // Don't let the compiler set an error for unknown names on final // compile, errors are handled by the interpreter thereafter. CompileRangeData( aSymbol, false); rCxt.getDoc().CheckLinkFormulaNeedingCheck( *pCode); } } #if DEBUG_FORMULA_COMPILER void ScRangeData::Dump() const { cout << "-- ScRangeData" << endl; cout << " name: " << aName << endl; cout << " ref position: (col=" << aPos.Col() << ", row=" << aPos.Row() << ", sheet=" << aPos.Tab() << ")" << endl; if (pCode) pCode->Dump(); } #endif void ScRangeData::GuessPosition() { // set a position that allows "absoluting" of all relative references // in CalcAbsIfRel without errors OSL_ENSURE(aPos == ScAddress(), "position will go lost now"); SCCOL nMinCol = 0; SCROW nMinRow = 0; SCTAB nMinTab = 0; formula::FormulaToken* t; formula::FormulaTokenArrayPlainIterator aIter(*pCode); while ( ( t = aIter.GetNextReference() ) != nullptr ) { ScSingleRefData& rRef1 = *t->GetSingleRef(); if ( rRef1.IsColRel() && rRef1.Col() < nMinCol ) nMinCol = rRef1.Col(); if ( rRef1.IsRowRel() && rRef1.Row() < nMinRow ) nMinRow = rRef1.Row(); if ( rRef1.IsTabRel() && rRef1.Tab() < nMinTab ) nMinTab = rRef1.Tab(); if ( t->GetType() == svDoubleRef ) { ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2; if ( rRef2.IsColRel() && rRef2.Col() < nMinCol ) nMinCol = rRef2.Col(); if ( rRef2.IsRowRel() && rRef2.Row() < nMinRow ) nMinRow = rRef2.Row(); if ( rRef2.IsTabRel() && rRef2.Tab() < nMinTab ) nMinTab = rRef2.Tab(); } } aPos = ScAddress( static_cast(-nMinCol), static_cast(-nMinRow), static_cast(-nMinTab) ); } void ScRangeData::GetSymbol( OUString& rSymbol, const FormulaGrammar::Grammar eGrammar ) const { ScCompiler aComp(rDoc, aPos, *pCode, eGrammar); aComp.CreateStringFromTokenArray( rSymbol ); } void ScRangeData::GetSymbol( OUString& rSymbol, const ScAddress& rPos, const FormulaGrammar::Grammar eGrammar ) const { OUString aStr; ScCompiler aComp(rDoc, rPos, *pCode, eGrammar); aComp.CreateStringFromTokenArray( aStr ); rSymbol = aStr; } void ScRangeData::UpdateSymbol( OUStringBuffer& rBuffer, const ScAddress& rPos ) { std::unique_ptr pTemp( pCode->Clone() ); ScCompiler aComp(rDoc, rPos, *pTemp, formula::FormulaGrammar::GRAM_DEFAULT); aComp.MoveRelWrap(); aComp.CreateStringFromTokenArray( rBuffer ); } void ScRangeData::UpdateReference( sc::RefUpdateContext& rCxt, SCTAB nLocalTab ) { sc::RefUpdateResult aRes = pCode->AdjustReferenceInName(rCxt, aPos); bModified = aRes.mbReferenceModified; if (aRes.mbReferenceModified) rCxt.maUpdatedNames.setUpdatedName(nLocalTab, nIndex); } void ScRangeData::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest ) { bool bChanged = false; formula::FormulaToken* t; formula::FormulaTokenArrayPlainIterator aIter(*pCode); while ( ( t = aIter.GetNextReference() ) != nullptr ) { if( t->GetType() != svIndex ) { SingleDoubleRefModifier aMod( *t ); ScComplexRefData& rRef = aMod.Ref(); if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() && (!rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel()) && ( t->GetType() == svSingleRef || (!rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel() && (!rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel())))) { ScRange aAbs = rRef.toAbs(rDoc, aPos); if (ScRefUpdate::UpdateTranspose(rDoc, rSource, rDest, aAbs) != UR_NOTHING) { rRef.SetRange(rDoc.GetSheetLimits(), aAbs, aPos); bChanged = true; } } } } bModified = bChanged; } void ScRangeData::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) { bool bChanged = false; formula::FormulaToken* t; formula::FormulaTokenArrayPlainIterator aIter(*pCode); while ( ( t = aIter.GetNextReference() ) != nullptr ) { if( t->GetType() != svIndex ) { SingleDoubleRefModifier aMod( *t ); ScComplexRefData& rRef = aMod.Ref(); if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() && (!rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel()) && ( t->GetType() == svSingleRef || (!rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel() && (!rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel())))) { ScRange aAbs = rRef.toAbs(rDoc, aPos); if (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING) { rRef.SetRange(rDoc.GetSheetLimits(), aAbs, aPos); bChanged = true; } } } } bModified = bChanged; // has to be evaluated immediately afterwards } bool ScRangeData::operator== (const ScRangeData& rData) const // for Undo { if ( nIndex != rData.nIndex || aName != rData.aName || aPos != rData.aPos || eType != rData.eType ) return false; sal_uInt16 nLen = pCode->GetLen(); if ( nLen != rData.pCode->GetLen() ) return false; FormulaToken** ppThis = pCode->GetArray(); FormulaToken** ppOther = rData.pCode->GetArray(); for ( sal_uInt16 i=0; iIsReference(rRange, aPos); return false; } bool ScRangeData::IsReference( ScRange& rRange, const ScAddress& rPos ) const { if ( (eType & ( Type::AbsArea | Type::RefArea | Type::AbsPos ) ) && pCode ) return pCode->IsReference(rRange, rPos); return false; } bool ScRangeData::IsValidReference( ScRange& rRange ) const { if ( (eType & ( Type::AbsArea | Type::RefArea | Type::AbsPos ) ) && pCode ) return pCode->IsValidReference(rRange, aPos); return false; } void ScRangeData::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt, SCTAB nLocalTab ) { sc::RefUpdateResult aRes = pCode->AdjustReferenceOnInsertedTab(rCxt, aPos); if (aRes.mbReferenceModified) rCxt.maUpdatedNames.setUpdatedName(nLocalTab, nIndex); if (rCxt.mnInsertPos <= aPos.Tab()) aPos.IncTab(rCxt.mnSheets); } void ScRangeData::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt, SCTAB nLocalTab ) { sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aPos); if (aRes.mbReferenceModified) rCxt.maUpdatedNames.setUpdatedName(nLocalTab, nIndex); if (rCxt.mnDeletePos <= aPos.Tab()) aPos.IncTab(-rCxt.mnSheets); } void ScRangeData::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nLocalTab ) { sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aPos); if (aRes.mbReferenceModified) rCxt.maUpdatedNames.setUpdatedName(nLocalTab, nIndex); aPos.SetTab(rCxt.getNewTab(aPos.Tab())); } void ScRangeData::MakeValidName( const ScDocument& rDoc, OUString& rName ) { // strip leading invalid characters sal_Int32 nPos = 0; sal_Int32 nLen = rName.getLength(); while ( nPos < nLen && !ScCompiler::IsCharFlagAllConventions( rName, nPos, ScCharFlags::Name) ) ++nPos; if ( nPos>0 ) rName = rName.copy(nPos); // if the first character is an invalid start character, precede with '_' if ( !rName.isEmpty() && !ScCompiler::IsCharFlagAllConventions( rName, 0, ScCharFlags::CharName ) ) rName = "_" + rName; // replace invalid with '_' nLen = rName.getLength(); for (nPos=0; nPos( nConv ) ); // Don't check Parse on VALID, any partial only VALID may result in // #REF! during compile later! while (aRange.Parse(rName, rDoc, details) != ScRefFlags::ZERO || aAddr.Parse(rName, rDoc, details) != ScRefFlags::ZERO) { // Range Parse is partially valid also with invalid sheet name, // Address Parse ditto, during compile name would generate a #REF! if ( rName.indexOf( '.' ) != -1 ) rName = rName.replaceFirst( ".", "_" ); else rName = "_" + rName; } } } ScRangeData::IsNameValidType ScRangeData::IsNameValid( const OUString& rName, const ScDocument& rDoc ) { /* XXX If changed, sc/source/filter/ftools/ftools.cxx * ScfTools::ConvertToScDefinedName needs to be changed too. */ char const a('.'); if (rName.indexOf(a) != -1) return IsNameValidType::NAME_INVALID_BAD_STRING; sal_Int32 nPos = 0; sal_Int32 nLen = rName.getLength(); if ( !nLen || !ScCompiler::IsCharFlagAllConventions( rName, nPos++, ScCharFlags::CharName ) ) return IsNameValidType::NAME_INVALID_BAD_STRING; while ( nPos < nLen ) { if ( !ScCompiler::IsCharFlagAllConventions( rName, nPos++, ScCharFlags::Name ) ) return IsNameValidType::NAME_INVALID_BAD_STRING; } ScAddress aAddr; ScRange aRange; for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; ) { ScAddress::Details details( static_cast( nConv ) ); // Don't check Parse on VALID, any partial only VALID may result in // #REF! during compile later! if (aRange.Parse(rName, rDoc, details) != ScRefFlags::ZERO || aAddr.Parse(rName, rDoc, details) != ScRefFlags::ZERO ) { return IsNameValidType::NAME_INVALID_CELL_REF; } } return IsNameValidType::NAME_VALID; } FormulaError ScRangeData::GetErrCode() const { return pCode ? pCode->GetCodeError() : FormulaError::NONE; } bool ScRangeData::HasReferences() const { return pCode->HasReferences(); } sal_uInt32 ScRangeData::GetUnoType() const { sal_uInt32 nUnoType = 0; if ( HasType(Type::Criteria) ) nUnoType |= css::sheet::NamedRangeFlag::FILTER_CRITERIA; if ( HasType(Type::PrintArea) ) nUnoType |= css::sheet::NamedRangeFlag::PRINT_AREA; if ( HasType(Type::ColHeader) ) nUnoType |= css::sheet::NamedRangeFlag::COLUMN_HEADER; if ( HasType(Type::RowHeader) ) nUnoType |= css::sheet::NamedRangeFlag::ROW_HEADER; return nUnoType; } void ScRangeData::ValidateTabRefs() { // try to make sure all relative references and the reference position // are within existing tables, so they can be represented as text // (if the range of used tables is more than the existing tables, // the result may still contain invalid tables, because the relative // references aren't changed so formulas stay the same) // find range of used tables SCTAB nMinTab = aPos.Tab(); SCTAB nMaxTab = nMinTab; formula::FormulaToken* t; formula::FormulaTokenArrayPlainIterator aIter(*pCode); while ( ( t = aIter.GetNextReference() ) != nullptr ) { ScSingleRefData& rRef1 = *t->GetSingleRef(); ScAddress aAbs = rRef1.toAbs(rDoc, aPos); if ( rRef1.IsTabRel() && !rRef1.IsTabDeleted() ) { if (aAbs.Tab() < nMinTab) nMinTab = aAbs.Tab(); if (aAbs.Tab() > nMaxTab) nMaxTab = aAbs.Tab(); } if ( t->GetType() == svDoubleRef ) { ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2; aAbs = rRef2.toAbs(rDoc, aPos); if ( rRef2.IsTabRel() && !rRef2.IsTabDeleted() ) { if (aAbs.Tab() < nMinTab) nMinTab = aAbs.Tab(); if (aAbs.Tab() > nMaxTab) nMaxTab = aAbs.Tab(); } } } SCTAB nTabCount = rDoc.GetTableCount(); if ( nMaxTab < nTabCount || nMinTab <= 0 ) return; // move position and relative tab refs // The formulas that use the name are not changed by this SCTAB nMove = nMinTab; ScAddress aOldPos = aPos; aPos.SetTab( aPos.Tab() - nMove ); aIter.Reset(); while ( ( t = aIter.GetNextReference() ) != nullptr ) { switch (t->GetType()) { case svSingleRef: { ScSingleRefData& rRef = *t->GetSingleRef(); if (!rRef.IsTabDeleted()) { ScAddress aAbs = rRef.toAbs(rDoc, aOldPos); rRef.SetAddress(rDoc.GetSheetLimits(), aAbs, aPos); } } break; case svDoubleRef: { ScComplexRefData& rRef = *t->GetDoubleRef(); if (!rRef.Ref1.IsTabDeleted()) { ScAddress aAbs = rRef.Ref1.toAbs(rDoc, aOldPos); rRef.Ref1.SetAddress(rDoc.GetSheetLimits(), aAbs, aPos); } if (!rRef.Ref2.IsTabDeleted()) { ScAddress aAbs = rRef.Ref2.toAbs(rDoc, aOldPos); rRef.Ref2.SetAddress(rDoc.GetSheetLimits(), aAbs, aPos); } } break; default: ; } } } void ScRangeData::SetCode( const ScTokenArray& rArr ) { pCode.reset(new ScTokenArray( rArr )); pCode->SetFromRangeName(true); InitCode(); } void ScRangeData::InitCode() { if( pCode->GetCodeError() == FormulaError::NONE ) { FormulaToken* p = FormulaTokenArrayPlainIterator(*pCode).GetNextReference(); if( p ) // exact one reference at first { if( p->GetType() == svSingleRef ) eType = eType | Type::AbsPos; else eType = eType | Type::AbsArea; } } } extern "C" int ScRangeData_QsortNameCompare( const void* p1, const void* p2 ) { return static_cast(ScGlobal::GetCollator()->compareString( (*static_cast(p1))->GetName(), (*static_cast(p2))->GetName() )); } namespace { /** * Predicate to check if the name references the specified range. */ class MatchByRange { const ScRange& mrRange; public: explicit MatchByRange(const ScRange& rRange) : mrRange(rRange) {} bool operator() (std::pair> const& r) const { return r.second->IsRangeAtBlock(mrRange); } }; } ScRangeName::ScRangeName() {} ScRangeName::ScRangeName(const ScRangeName& r) { for (auto const& it : r.m_Data) { m_Data.insert(std::make_pair(it.first, std::make_unique(*it.second))); } // std::map was cloned, so each collection needs its own index to data. maIndexToData.resize( r.maIndexToData.size(), nullptr); for (auto const& itr : m_Data) { size_t nPos = itr.second->GetIndex() - 1; if (nPos >= maIndexToData.size()) { OSL_FAIL( "ScRangeName copy-ctor: maIndexToData size doesn't fit"); maIndexToData.resize(nPos+1, nullptr); } maIndexToData[nPos] = itr.second.get(); } } const ScRangeData* ScRangeName::findByRange(const ScRange& rRange) const { DataType::const_iterator itr = std::find_if( m_Data.begin(), m_Data.end(), MatchByRange(rRange)); return itr == m_Data.end() ? nullptr : itr->second.get(); } ScRangeData* ScRangeName::findByUpperName(const OUString& rName) { DataType::iterator itr = m_Data.find(rName); return itr == m_Data.end() ? nullptr : itr->second.get(); } const ScRangeData* ScRangeName::findByUpperName(const OUString& rName) const { DataType::const_iterator itr = m_Data.find(rName); return itr == m_Data.end() ? nullptr : itr->second.get(); } ScRangeData* ScRangeName::findByIndex(sal_uInt16 i) const { if (!i) // index should never be zero. return nullptr; size_t nPos = i - 1; return nPos < maIndexToData.size() ? maIndexToData[nPos] : nullptr; } void ScRangeName::UpdateReference(sc::RefUpdateContext& rCxt, SCTAB nLocalTab ) { if (rCxt.meMode == URM_COPY) // Copying cells does not modify named expressions. return; for (auto const& itr : m_Data) { itr.second->UpdateReference(rCxt, nLocalTab); } } void ScRangeName::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt, SCTAB nLocalTab ) { for (auto const& itr : m_Data) { itr.second->UpdateInsertTab(rCxt, nLocalTab); } } void ScRangeName::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt, SCTAB nLocalTab ) { for (auto const& itr : m_Data) { itr.second->UpdateDeleteTab(rCxt, nLocalTab); } } void ScRangeName::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nLocalTab ) { for (auto const& itr : m_Data) { itr.second->UpdateMoveTab(rCxt, nLocalTab); } } void ScRangeName::UpdateTranspose(const ScRange& rSource, const ScAddress& rDest) { for (auto const& itr : m_Data) { itr.second->UpdateTranspose(rSource, rDest); } } void ScRangeName::UpdateGrow(const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY) { for (auto const& itr : m_Data) { itr.second->UpdateGrow(rArea, nGrowX, nGrowY); } } void ScRangeName::CompileUnresolvedXML( sc::CompileFormulaContext& rCxt ) { for (auto const& itr : m_Data) { itr.second->CompileUnresolvedXML(rCxt); } } void ScRangeName::CopyUsedNames( const SCTAB nLocalTab, const SCTAB nOldTab, const SCTAB nNewTab, const ScDocument& rOldDoc, ScDocument& rNewDoc, const bool bGlobalNamesToLocal ) const { for (auto const& itr : m_Data) { SCTAB nSheet = (nLocalTab < 0) ? nLocalTab : nOldTab; sal_uInt16 nIndex = itr.second->GetIndex(); ScAddress aOldPos( itr.second->GetPos()); aOldPos.SetTab( nOldTab); ScAddress aNewPos( aOldPos); aNewPos.SetTab( nNewTab); ScRangeData* pRangeData = nullptr; rOldDoc.CopyAdjustRangeName( nSheet, nIndex, pRangeData, rNewDoc, aNewPos, aOldPos, bGlobalNamesToLocal, false); } } ScRangeName::const_iterator ScRangeName::begin() const { return m_Data.begin(); } ScRangeName::const_iterator ScRangeName::end() const { return m_Data.end(); } ScRangeName::iterator ScRangeName::begin() { return m_Data.begin(); } ScRangeName::iterator ScRangeName::end() { return m_Data.end(); } size_t ScRangeName::size() const { return m_Data.size(); } bool ScRangeName::empty() const { return m_Data.empty(); } bool ScRangeName::insert( ScRangeData* p, bool bReuseFreeIndex ) { if (!p) return false; if (!p->GetIndex()) { // Assign a new index. An index must be unique and is never 0. if (bReuseFreeIndex) { IndexDataType::iterator itr = std::find( maIndexToData.begin(), maIndexToData.end(), static_cast(nullptr)); if (itr != maIndexToData.end()) { // Empty slot exists. Re-use it. size_t nPos = std::distance(maIndexToData.begin(), itr); p->SetIndex(nPos + 1); } else // No empty slot. Append it to the end. p->SetIndex(maIndexToData.size() + 1); } else { p->SetIndex(maIndexToData.size() + 1); } } OUString aName(p->GetUpperName()); erase(aName); // ptr_map won't insert it if a duplicate name exists. pair r = m_Data.insert(std::make_pair(aName, std::unique_ptr(p))); if (r.second) { // Data inserted. Store its index for mapping. size_t nPos = p->GetIndex() - 1; if (nPos >= maIndexToData.size()) maIndexToData.resize(nPos+1, nullptr); maIndexToData[nPos] = p; } return r.second; } void ScRangeName::erase(const ScRangeData& r) { erase(r.GetUpperName()); } void ScRangeName::erase(const OUString& rName) { DataType::iterator itr = m_Data.find(rName); if (itr != m_Data.end()) erase(itr); } void ScRangeName::erase(const iterator& itr) { sal_uInt16 nIndex = itr->second->GetIndex(); m_Data.erase(itr); OSL_ENSURE( 0 < nIndex && nIndex <= maIndexToData.size(), "ScRangeName::erase: bad index"); if (0 < nIndex && nIndex <= maIndexToData.size()) maIndexToData[nIndex-1] = nullptr; } void ScRangeName::clear() { m_Data.clear(); maIndexToData.clear(); } bool ScRangeName::operator== (const ScRangeName& r) const { return std::equal(m_Data.begin(), m_Data.end(), r.m_Data.begin(), r.m_Data.end(), [](const DataType::value_type& lhs, const DataType::value_type& rhs) { return (lhs.first == rhs.first) && (*lhs.second == *rhs.second); }); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */