summaryrefslogtreecommitdiffstats
path: root/sc/source/core/tool/rangenam.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/tool/rangenam.cxx')
-rw-r--r--sc/source/core/tool/rangenam.cxx899
1 files changed, 899 insertions, 0 deletions
diff --git a/sc/source/core/tool/rangenam.cxx b/sc/source/core/tool/rangenam.cxx
new file mode 100644
index 000000000..b5578ca26
--- /dev/null
+++ b/sc/source/core/tool/rangenam.cxx
@@ -0,0 +1,899 @@
+/* -*- 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 <string.h>
+#include <memory>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/charclass.hxx>
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <osl/diagnose.h>
+
+#include <token.hxx>
+#include <tokenarray.hxx>
+#include <rangenam.hxx>
+#include <rangeutl.hxx>
+#include <global.hxx>
+#include <compiler.hxx>
+#include <refupdat.hxx>
+#include <document.hxx>
+#include <refupdatecontext.hxx>
+#include <tokenstringcontext.hxx>
+
+#include <formula/errorcodes.hxx>
+
+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::getCharClass().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::getCharClass().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::getCharClass().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<SCCOL>(-nMinCol), static_cast<SCROW>(-nMinRow), static_cast<SCTAB>(-nMinTab) );
+}
+
+OUString ScRangeData::GetSymbol( const FormulaGrammar::Grammar eGrammar ) const
+{
+ ScCompiler aComp(rDoc, aPos, *pCode, eGrammar);
+ OUString symbol;
+ aComp.CreateStringFromTokenArray( symbol );
+ return symbol;
+}
+
+OUString ScRangeData::GetSymbol( const ScAddress& rPos, const FormulaGrammar::Grammar eGrammar ) const
+{
+ OUString aStr;
+ ScCompiler aComp(rDoc, rPos, *pCode, eGrammar);
+ aComp.CreateStringFromTokenArray( aStr );
+ return aStr;
+}
+
+void ScRangeData::UpdateSymbol( OUStringBuffer& rBuffer, const ScAddress& rPos )
+{
+ ScTokenArray aTemp( pCode->CloneValue() );
+ ScCompiler aComp(rDoc, rPos, aTemp, 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();
+ // Update only absolute references
+ 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);
+ // Check if the absolute reference of this range is pointing to the transposed source
+ 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; i<nLen; i++ )
+ if ( ppThis[i] != ppOther[i] && !(*ppThis[i] == *ppOther[i]) )
+ return false;
+
+ return true;
+}
+
+bool ScRangeData::IsRangeAtBlock( const ScRange& rBlock ) const
+{
+ bool bRet = false;
+ ScRange aRange;
+ if ( IsReference(aRange) )
+ bRet = ( rBlock == aRange );
+ return bRet;
+}
+
+bool ScRangeData::IsReference( ScRange& rRange ) const
+{
+ if ( (eType & ( Type::AbsArea | Type::RefArea | Type::AbsPos )) && pCode )
+ return pCode->IsReference(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);
+
+ ScRangeUpdater::UpdateDeleteTab( aPos, rCxt);
+}
+
+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<nLen; nPos++)
+ {
+ if ( !ScCompiler::IsCharFlagAllConventions( rName, nPos, ScCharFlags::Name) )
+ rName = rName.replaceAt( nPos, 1, u"_" );
+ }
+
+ // Ensure that the proposed name is not a reference under any convention,
+ // same as in IsNameValid()
+ ScAddress aAddr;
+ ScRange aRange;
+ for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; )
+ {
+ ScAddress::Details details( static_cast<FormulaGrammar::AddressConvention>( 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<FormulaGrammar::AddressConvention>( 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;
+}
+
+bool ScRangeData::HasPossibleAddressConflict() const
+{
+ // Similar to part of IsNameValid(), but only check if the name is a valid address.
+ ScAddress aAddr;
+ for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; )
+ {
+ ScAddress::Details details( static_cast<FormulaGrammar::AddressConvention>( nConv ) );
+ // Don't check Parse on VALID, any partial only VALID may result in
+ // #REF! during compile later!
+ if(aAddr.Parse(aUpperName, rDoc, details) != ScRefFlags::ZERO)
+ return true;
+ }
+ return false;
+}
+
+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<int>(ScGlobal::GetCollator().compareString(
+ (*static_cast<const ScRangeData* const *>(p1))->GetName(),
+ (*static_cast<const ScRangeData* const *>(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<OUString const, std::unique_ptr<ScRangeData>> const& r) const
+ {
+ return r.second->IsRangeAtBlock(mrRange);
+ }
+};
+
+}
+
+ScRangeName::ScRangeName()
+ : mHasPossibleAddressConflict(false)
+ , mHasPossibleAddressConflictDirty(false)
+{
+}
+
+ScRangeName::ScRangeName(const ScRangeName& r)
+ : mHasPossibleAddressConflict( r.mHasPossibleAddressConflict )
+ , mHasPossibleAddressConflictDirty( r.mHasPossibleAddressConflictDirty )
+{
+ for (auto const& it : r.m_Data)
+ {
+ m_Data.insert(std::make_pair(it.first, std::make_unique<ScRangeData>(*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);
+ }
+}
+
+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<ScRangeData*>(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<DataType::iterator, bool> r =
+ m_Data.insert(std::make_pair(aName, std::unique_ptr<ScRangeData>(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;
+ mHasPossibleAddressConflictDirty = true;
+ }
+ return r.second;
+}
+
+void ScRangeName::erase(const ScRangeData& r)
+{
+ erase(r.GetUpperName());
+}
+
+void ScRangeName::erase(const OUString& rName)
+{
+ DataType::const_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;
+ if(mHasPossibleAddressConflict)
+ mHasPossibleAddressConflictDirty = true;
+}
+
+void ScRangeName::clear()
+{
+ m_Data.clear();
+ maIndexToData.clear();
+ mHasPossibleAddressConflict = false;
+ mHasPossibleAddressConflictDirty = false;
+}
+
+void ScRangeName::checkHasPossibleAddressConflict() const
+{
+ mHasPossibleAddressConflict = false;
+ mHasPossibleAddressConflictDirty = false;
+ for (auto const& itr : m_Data)
+ {
+ if( itr.second->HasPossibleAddressConflict())
+ {
+ mHasPossibleAddressConflict = true;
+ return;
+ }
+ }
+}
+
+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: */