summaryrefslogtreecommitdiffstats
path: root/sc/source/core/tool/token.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sc/source/core/tool/token.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/core/tool/token.cxx')
-rw-r--r--sc/source/core/tool/token.cxx5389
1 files changed, 5389 insertions, 0 deletions
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
new file mode 100644
index 000000000..2e1c641da
--- /dev/null
+++ b/sc/source/core/tool/token.cxx
@@ -0,0 +1,5389 @@
+/* -*- 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 <functional>
+
+#include <string.h>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <token.hxx>
+#include <tokenarray.hxx>
+#include <reftokenhelper.hxx>
+#include <clipparam.hxx>
+#include <compiler.hxx>
+#include <interpre.hxx>
+#include <formula/FormulaCompiler.hxx>
+#include <formula/compiler.hxx>
+#include <formula/opcode.hxx>
+#include <jumpmatrix.hxx>
+#include <rangeseq.hxx>
+#include <rangeutl.hxx>
+#include <externalrefmgr.hxx>
+#include <document.hxx>
+#include <refupdatecontext.hxx>
+#include <tokenstringcontext.hxx>
+#include <types.hxx>
+#include <addincol.hxx>
+#include <dbdata.hxx>
+#include <reordermap.hxx>
+#include <svl/sharedstring.hxx>
+#include <scmatrix.hxx>
+
+using ::std::vector;
+
+#include <com/sun/star/sheet/ComplexReference.hpp>
+#include <com/sun/star/sheet/ExternalReference.hpp>
+#include <com/sun/star/sheet/FormulaToken.hpp>
+#include <com/sun/star/sheet/ReferenceFlags.hpp>
+#include <com/sun/star/sheet/NameToken.hpp>
+#include <utility>
+#include <o3tl/safeint.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+using namespace formula;
+using namespace com::sun::star;
+
+namespace
+{
+ void lcl_SingleRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI )
+ {
+ rRef.InitFlags();
+
+ rRef.SetColRel( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 );
+ rRef.SetRowRel( ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE ) != 0 );
+ rRef.SetTabRel( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_RELATIVE ) != 0 );
+ rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED ) != 0 );
+ rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED ) != 0 );
+ rRef.SetTabDeleted( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_DELETED ) != 0 );
+ rRef.SetFlag3D( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D ) != 0 );
+ rRef.SetRelName( ( rAPI.Flags & sheet::ReferenceFlags::RELATIVE_NAME ) != 0 );
+
+ if (rRef.IsColRel())
+ rRef.SetRelCol(static_cast<SCCOL>(rAPI.RelativeColumn));
+ else
+ rRef.SetAbsCol(static_cast<SCCOL>(rAPI.Column));
+
+ if (rRef.IsRowRel())
+ rRef.SetRelRow(static_cast<SCROW>(rAPI.RelativeRow));
+ else
+ rRef.SetAbsRow(static_cast<SCROW>(rAPI.Row));
+
+ if (rRef.IsTabRel())
+ rRef.SetRelTab(static_cast<SCTAB>(rAPI.RelativeSheet));
+ else
+ rRef.SetAbsTab(static_cast<SCTAB>(rAPI.Sheet));
+ }
+
+ void lcl_ExternalRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI )
+ {
+ rRef.InitFlags();
+
+ rRef.SetColRel( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 );
+ rRef.SetRowRel( ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE ) != 0 );
+ rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED ) != 0 );
+ rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED ) != 0 );
+ rRef.SetTabDeleted( false ); // sheet must not be deleted for external refs
+ rRef.SetFlag3D( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D ) != 0 );
+ rRef.SetRelName( false );
+
+ if (rRef.IsColRel())
+ rRef.SetRelCol(static_cast<SCCOL>(rAPI.RelativeColumn));
+ else
+ rRef.SetAbsCol(static_cast<SCCOL>(rAPI.Column));
+
+ if (rRef.IsRowRel())
+ rRef.SetRelRow(static_cast<SCROW>(rAPI.RelativeRow));
+ else
+ rRef.SetAbsRow(static_cast<SCROW>(rAPI.Row));
+
+ // sheet index must be absolute for external refs
+ rRef.SetAbsTab(0);
+ }
+
+ struct TokenPointerRange
+ {
+ FormulaToken** mpStart;
+ FormulaToken** mpStop;
+
+ TokenPointerRange() : mpStart(nullptr), mpStop(nullptr) {}
+ TokenPointerRange( FormulaToken** p, sal_uInt16 n ) :
+ mpStart(p), mpStop( p + static_cast<size_t>(n)) {}
+ };
+ struct TokenPointers
+ {
+ TokenPointerRange maPointerRange[2];
+ bool mbSkipRelName;
+
+ TokenPointers( FormulaToken** pCode, sal_uInt16 nLen, FormulaToken** pRPN, sal_uInt16 nRPN,
+ bool bSkipRelName = true ) :
+ mbSkipRelName(bSkipRelName)
+ {
+ maPointerRange[0] = TokenPointerRange( pCode, nLen);
+ maPointerRange[1] = TokenPointerRange( pRPN, nRPN);
+ }
+
+ bool skipToken( size_t i, const FormulaToken* const * pp )
+ {
+ // Handle all code tokens, and tokens in RPN only if they have a
+ // reference count of 1, which means they are not referenced in the
+ // code array. Doing it the other way would skip code tokens that
+ // are held by flat copied token arrays and thus are shared. For
+ // flat copy arrays the caller has to know what it does and should
+ // discard all RPN, update only one array and regenerate all RPN.
+ if (i == 1)
+ {
+ if ((*pp)->GetRef() > 1)
+ return true;
+
+ if (mbSkipRelName)
+ {
+ // Skip (do not adjust) relative references resulting from
+ // named expressions. Resolved expressions are only in RPN.
+ switch ((*pp)->GetType())
+ {
+ case svSingleRef:
+ return (*pp)->GetSingleRef()->IsRelName();
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *(*pp)->GetDoubleRef();
+ return rRef.Ref1.IsRelName() || rRef.Ref2.IsRelName();
+ }
+ default:
+ ; // nothing
+ }
+ }
+ }
+
+ return false;
+ }
+
+ FormulaToken* getHandledToken( size_t i, FormulaToken* const * pp )
+ {
+ if (skipToken( i, pp))
+ return nullptr;
+
+ FormulaToken* p = *pp;
+ if (p->GetOpCode() == ocTableRef)
+ {
+ // Return the inner reference token if it is not in RPN.
+ ScTableRefToken* pTR = dynamic_cast<ScTableRefToken*>(p);
+ if (!pTR)
+ return p;
+ p = pTR->GetAreaRefRPN();
+ if (!p)
+ return pTR;
+ if (p->GetRef() > 1)
+ // Reference handled in RPN, but do not return nullptr so
+ // loops will process ocTableRef via pp instead of issuing
+ // a continue.
+ return pTR;
+ }
+ return p;
+ }
+ };
+
+} // namespace
+
+
+// --- class ScRawToken -----------------------------------------------------
+
+void ScRawToken::SetOpCode( OpCode e )
+{
+ eOp = e;
+ switch (eOp)
+ {
+ case ocIf:
+ eType = svJump;
+ nJump[ 0 ] = 3; // If, Else, Behind
+ break;
+ case ocIfError:
+ case ocIfNA:
+ eType = svJump;
+ nJump[ 0 ] = 2; // If, Behind
+ break;
+ case ocChoose:
+ eType = svJump;
+ nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
+ break;
+ case ocMissing:
+ eType = svMissing;
+ break;
+ case ocSep:
+ case ocOpen:
+ case ocClose:
+ case ocArrayRowSep:
+ case ocArrayColSep:
+ case ocArrayOpen:
+ case ocArrayClose:
+ case ocTableRefOpen:
+ case ocTableRefClose:
+ eType = svSep;
+ break;
+ case ocWhitespace:
+ eType = svByte;
+ whitespace.nCount = 1;
+ whitespace.cChar = 0x20;
+ break;
+ default:
+ eType = svByte;
+ sbyte.cByte = 0;
+ sbyte.eInForceArray = ParamClass::Unknown;
+ }
+}
+
+void ScRawToken::SetString( rtl_uString* pData, rtl_uString* pDataIgnoreCase )
+{
+ eOp = ocPush;
+ eType = svString;
+
+ sharedstring.mpData = pData;
+ sharedstring.mpDataIgnoreCase = pDataIgnoreCase;
+}
+
+void ScRawToken::SetSingleReference( const ScSingleRefData& rRef )
+{
+ eOp = ocPush;
+ eType = svSingleRef;
+ aRef.Ref1 =
+ aRef.Ref2 = rRef;
+}
+
+void ScRawToken::SetDoubleReference( const ScComplexRefData& rRef )
+{
+ eOp = ocPush;
+ eType = svDoubleRef;
+ aRef = rRef;
+}
+
+void ScRawToken::SetDouble(double rVal)
+{
+ eOp = ocPush;
+ eType = svDouble;
+ nValue = rVal;
+}
+
+void ScRawToken::SetErrorConstant( FormulaError nErr )
+{
+ eOp = ocPush;
+ eType = svError;
+ nError = nErr;
+}
+
+void ScRawToken::SetName(sal_Int16 nSheet, sal_uInt16 nIndex)
+{
+ eOp = ocName;
+ eType = svIndex;
+
+ name.nSheet = nSheet;
+ name.nIndex = nIndex;
+}
+
+void ScRawToken::SetExternalSingleRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef )
+{
+ eOp = ocPush;
+ eType = svExternalSingleRef;
+
+ extref.nFileId = nFileId;
+ extref.aRef.Ref1 =
+ extref.aRef.Ref2 = rRef;
+ maExternalName = rTabName;
+}
+
+void ScRawToken::SetExternalDoubleRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef )
+{
+ eOp = ocPush;
+ eType = svExternalDoubleRef;
+
+ extref.nFileId = nFileId;
+ extref.aRef = rRef;
+ maExternalName = rTabName;
+}
+
+void ScRawToken::SetExternalName( sal_uInt16 nFileId, const OUString& rName )
+{
+ eOp = ocPush;
+ eType = svExternalName;
+
+ extname.nFileId = nFileId;
+ maExternalName = rName;
+}
+
+void ScRawToken::SetExternal( const OUString& rStr )
+{
+ eOp = ocExternal;
+ eType = svExternal;
+ maExternalName = rStr;
+}
+
+bool ScRawToken::IsValidReference(const ScDocument& rDoc) const
+{
+ switch (eType)
+ {
+ case svSingleRef:
+ return aRef.Ref1.Valid(rDoc);
+ case svDoubleRef:
+ return aRef.Valid(rDoc);
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ return true;
+ default:
+ ; // nothing
+ }
+ return false;
+}
+
+FormulaToken* ScRawToken::CreateToken(ScSheetLimits& rLimits) const
+{
+#define IF_NOT_OPCODE_ERROR(o,c) SAL_WARN_IF((eOp!=o), "sc.core", #c "::ctor: OpCode " << static_cast<int>(eOp) << " lost, converted to " #o "; maybe inherit from FormulaToken instead!")
+ switch ( GetType() )
+ {
+ case svByte :
+ if (eOp == ocWhitespace)
+ return new FormulaSpaceToken( whitespace.nCount, whitespace.cChar );
+ else
+ return new FormulaByteToken( eOp, sbyte.cByte, sbyte.eInForceArray );
+ case svDouble :
+ IF_NOT_OPCODE_ERROR( ocPush, FormulaDoubleToken);
+ return new FormulaDoubleToken( nValue );
+ case svString :
+ {
+ svl::SharedString aSS(sharedstring.mpData, sharedstring.mpDataIgnoreCase);
+ if (eOp == ocPush)
+ return new FormulaStringToken(aSS);
+ else
+ return new FormulaStringOpToken(eOp, aSS);
+ }
+ case svSingleRef :
+ if (eOp == ocPush)
+ return new ScSingleRefToken(rLimits, aRef.Ref1 );
+ else
+ return new ScSingleRefToken(rLimits, aRef.Ref1, eOp );
+ case svDoubleRef :
+ if (eOp == ocPush)
+ return new ScDoubleRefToken(rLimits, aRef );
+ else
+ return new ScDoubleRefToken(rLimits, aRef, eOp );
+ case svMatrix :
+ IF_NOT_OPCODE_ERROR( ocPush, ScMatrixToken);
+ return new ScMatrixToken( pMat );
+ case svIndex :
+ if (eOp == ocTableRef)
+ return new ScTableRefToken( table.nIndex, table.eItem);
+ else
+ return new FormulaIndexToken( eOp, name.nIndex, name.nSheet);
+ case svExternalSingleRef:
+ {
+ svl::SharedString aTabName(maExternalName); // string not interned
+ return new ScExternalSingleRefToken(extref.nFileId, aTabName, extref.aRef.Ref1);
+ }
+ case svExternalDoubleRef:
+ {
+ svl::SharedString aTabName(maExternalName); // string not interned
+ return new ScExternalDoubleRefToken(extref.nFileId, aTabName, extref.aRef);
+ }
+ case svExternalName:
+ {
+ svl::SharedString aName(maExternalName); // string not interned
+ return new ScExternalNameToken( extname.nFileId, aName );
+ }
+ case svJump :
+ return new FormulaJumpToken( eOp, nJump );
+ case svExternal :
+ return new FormulaExternalToken( eOp, sbyte.cByte, maExternalName );
+ case svFAP :
+ return new FormulaFAPToken( eOp, sbyte.cByte, nullptr );
+ case svMissing :
+ IF_NOT_OPCODE_ERROR( ocMissing, FormulaMissingToken);
+ return new FormulaMissingToken;
+ case svSep :
+ return new FormulaToken( svSep,eOp );
+ case svError :
+ return new FormulaErrorToken( nError );
+ case svUnknown :
+ return new FormulaUnknownToken( eOp );
+ default:
+ {
+ SAL_WARN("sc.core", "unknown ScRawToken::CreateToken() type " << int(GetType()));
+ return new FormulaUnknownToken( ocBad );
+ }
+ }
+#undef IF_NOT_OPCODE_ERROR
+}
+
+namespace {
+
+// TextEqual: if same formula entered (for optimization in sort)
+bool checkTextEqual( const ScSheetLimits& rLimits, const FormulaToken& _rToken1, const FormulaToken& _rToken2 )
+{
+ assert(
+ (_rToken1.GetType() == svSingleRef || _rToken1.GetType() == svDoubleRef)
+ && _rToken1.FormulaToken::operator ==(_rToken2));
+
+ // in relative Refs only compare relative parts
+
+ ScComplexRefData aTemp1;
+ if ( _rToken1.GetType() == svSingleRef )
+ {
+ aTemp1.Ref1 = *_rToken1.GetSingleRef();
+ aTemp1.Ref2 = aTemp1.Ref1;
+ }
+ else
+ aTemp1 = *_rToken1.GetDoubleRef();
+
+ ScComplexRefData aTemp2;
+ if ( _rToken2.GetType() == svSingleRef )
+ {
+ aTemp2.Ref1 = *_rToken2.GetSingleRef();
+ aTemp2.Ref2 = aTemp2.Ref1;
+ }
+ else
+ aTemp2 = *_rToken2.GetDoubleRef();
+
+ ScAddress aPos;
+ ScRange aRange1 = aTemp1.toAbs(rLimits, aPos), aRange2 = aTemp2.toAbs(rLimits, aPos);
+
+ // memcmp doesn't work because of the alignment byte after bFlags.
+ // After SmartRelAbs only absolute parts have to be compared.
+ return aRange1 == aRange2 && aTemp1.Ref1.FlagValue() == aTemp2.Ref1.FlagValue() && aTemp1.Ref2.FlagValue() == aTemp2.Ref2.FlagValue();
+}
+
+}
+
+#if DEBUG_FORMULA_COMPILER
+void DumpToken(formula::FormulaToken const & rToken)
+{
+ switch (rToken.GetType()) {
+ case svSingleRef:
+ cout << "-- ScSingleRefToken" << endl;
+ rToken.GetSingleRef()->Dump(1);
+ break;
+ case svDoubleRef:
+ cout << "-- ScDoubleRefToken" << endl;
+ rToken.GetDoubleRef()->Dump(1);
+ break;
+ default:
+ cout << "-- FormulaToken" << endl;
+ cout << " opcode: " << int(rToken.GetOpCode()) << " " <<
+ formula::FormulaCompiler::GetNativeSymbol( rToken.GetOpCode()).toUtf8().getStr() << endl;
+ cout << " type: " << static_cast<int>(rToken.GetType()) << endl;
+ switch (rToken.GetType())
+ {
+ case svDouble:
+ cout << " value: " << rToken.GetDouble() << endl;
+ break;
+ case svString:
+ cout << " string: "
+ << OUStringToOString(rToken.GetString().getString(), RTL_TEXTENCODING_UTF8).getStr()
+ << endl;
+ break;
+ default:
+ ;
+ }
+ break;
+ }
+}
+#endif
+
+FormulaTokenRef extendRangeReference( ScSheetLimits& rLimits, FormulaToken & rTok1, FormulaToken & rTok2,
+ const ScAddress & rPos, bool bReuseDoubleRef )
+{
+
+ StackVar sv1 = rTok1.GetType();
+ // Doing a RangeOp with RefList is probably utter nonsense, but Xcl
+ // supports it, so do we.
+ if (sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList
+ && sv1 != svExternalSingleRef && sv1 != svExternalDoubleRef)
+ return nullptr;
+ StackVar sv2 = rTok2.GetType();
+ if (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList)
+ return nullptr;
+
+ ScTokenRef xRes;
+ bool bExternal = (sv1 == svExternalSingleRef);
+ if ((sv1 == svSingleRef || bExternal) && sv2 == svSingleRef)
+ {
+ // Range references like Sheet1.A1:A2 are generalized and built by
+ // first creating a DoubleRef from the first SingleRef, effectively
+ // generating Sheet1.A1:A1, and then extending that with A2 as if
+ // Sheet1.A1:A1:A2 was encountered, so the mechanisms to adjust the
+ // references apply as well.
+
+ /* Given the current structure of external references an external
+ * reference can only be extended if the second reference does not
+ * point to a different sheet. 'file'#Sheet1.A1:A2 is ok,
+ * 'file'#Sheet1.A1:Sheet2.A2 is not. Since we can't determine from a
+ * svSingleRef whether the sheet would be different from the one given
+ * in the external reference, we have to bail out if there is any sheet
+ * specified. NOTE: Xcl does handle external 3D references as in
+ * '[file]Sheet1:Sheet2'!A1:A2
+ *
+ * FIXME: For OOo syntax be smart and remember an external singleref
+ * encountered and if followed by ocRange and singleref, create an
+ * external singleref for the second singleref. Both could then be
+ * merged here. For Xcl syntax already parse an external range
+ * reference entirely, cumbersome. */
+
+ const ScSingleRefData& rRef2 = *rTok2.GetSingleRef();
+ if (bExternal && rRef2.IsFlag3D())
+ return nullptr;
+
+ ScComplexRefData aRef;
+ aRef.Ref1 = aRef.Ref2 = *rTok1.GetSingleRef();
+ aRef.Ref2.SetFlag3D( false);
+ aRef.Extend(rLimits, rRef2, rPos);
+ if (bExternal)
+ xRes = new ScExternalDoubleRefToken( rTok1.GetIndex(), rTok1.GetString(), aRef);
+ else
+ xRes = new ScDoubleRefToken(rLimits, aRef);
+ }
+ else
+ {
+ bExternal |= (sv1 == svExternalDoubleRef);
+ const ScRefList* pRefList = nullptr;
+ if (sv1 == svDoubleRef)
+ {
+ xRes = (bReuseDoubleRef && rTok1.GetRef() == 1 ? &rTok1 : rTok1.Clone());
+ sv1 = svUnknown; // mark as handled
+ }
+ else if (sv2 == svDoubleRef)
+ {
+ xRes = (bReuseDoubleRef && rTok2.GetRef() == 1 ? &rTok2 : rTok2.Clone());
+ sv2 = svUnknown; // mark as handled
+ }
+ else if (sv1 == svRefList)
+ pRefList = rTok1.GetRefList();
+ else if (sv2 == svRefList)
+ pRefList = rTok2.GetRefList();
+ if (pRefList)
+ {
+ if (pRefList->empty())
+ return nullptr;
+ if (bExternal)
+ return nullptr; // external reference list not possible
+ xRes = new ScDoubleRefToken(rLimits, (*pRefList)[0] );
+ }
+ if (!xRes)
+ return nullptr; // shouldn't happen...
+ StackVar sv[2] = { sv1, sv2 };
+ formula::FormulaToken* pt[2] = { &rTok1, &rTok2 };
+ ScComplexRefData& rRef = *xRes->GetDoubleRef();
+ for (size_t i=0; i<2; ++i)
+ {
+ switch (sv[i])
+ {
+ case svSingleRef:
+ rRef.Extend(rLimits, *pt[i]->GetSingleRef(), rPos);
+ break;
+ case svDoubleRef:
+ rRef.Extend(rLimits, *pt[i]->GetDoubleRef(), rPos);
+ break;
+ case svRefList:
+ {
+ const ScRefList* p = pt[i]->GetRefList();
+ if (p->empty())
+ return nullptr;
+ for (const auto& rRefData : *p)
+ {
+ rRef.Extend(rLimits, rRefData, rPos);
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D())
+ return nullptr; // no other sheets with external refs
+ else
+ rRef.Extend(rLimits, *pt[i]->GetSingleRef(), rPos);
+ break;
+ case svExternalDoubleRef:
+ if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D())
+ return nullptr; // no other sheets with external refs
+ else
+ rRef.Extend(rLimits, *pt[i]->GetDoubleRef(), rPos);
+ break;
+ default:
+ ; // nothing, prevent compiler warning
+ }
+ }
+ }
+ return FormulaTokenRef(xRes.get());
+}
+
+// real implementations of virtual functions
+
+const ScSingleRefData* ScSingleRefToken::GetSingleRef() const { return &aSingleRef; }
+ScSingleRefData* ScSingleRefToken::GetSingleRef() { return &aSingleRef; }
+bool ScSingleRefToken::TextEqual( const FormulaToken& _rToken ) const
+{
+ return FormulaToken::operator ==(_rToken) && checkTextEqual(mrSheetLimits, *this, _rToken);
+}
+bool ScSingleRefToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && aSingleRef == *r.GetSingleRef();
+}
+
+const ScSingleRefData* ScDoubleRefToken::GetSingleRef() const { return &aDoubleRef.Ref1; }
+ScSingleRefData* ScDoubleRefToken::GetSingleRef() { return &aDoubleRef.Ref1; }
+const ScComplexRefData* ScDoubleRefToken::GetDoubleRef() const { return &aDoubleRef; }
+ScComplexRefData* ScDoubleRefToken::GetDoubleRef() { return &aDoubleRef; }
+const ScSingleRefData* ScDoubleRefToken::GetSingleRef2() const { return &aDoubleRef.Ref2; }
+ScSingleRefData* ScDoubleRefToken::GetSingleRef2() { return &aDoubleRef.Ref2; }
+bool ScDoubleRefToken::TextEqual( const FormulaToken& _rToken ) const
+{
+ return FormulaToken::operator ==(_rToken) && checkTextEqual(mrSheetLimits, *this, _rToken);
+}
+bool ScDoubleRefToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && aDoubleRef == *r.GetDoubleRef();
+}
+
+const ScRefList* ScRefListToken::GetRefList() const { return &aRefList; }
+ ScRefList* ScRefListToken::GetRefList() { return &aRefList; }
+ bool ScRefListToken::IsArrayResult() const { return mbArrayResult; }
+bool ScRefListToken::operator==( const FormulaToken& r ) const
+{
+ if (!FormulaToken::operator==( r ) || &aRefList != r.GetRefList())
+ return false;
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(&r);
+ return p && mbArrayResult == p->IsArrayResult();
+}
+
+ScMatrixToken::ScMatrixToken( const ScMatrixRef& p ) :
+ FormulaToken(formula::svMatrix), pMatrix(p) {}
+
+ScMatrixToken::ScMatrixToken( const ScMatrixToken& ) = default;
+
+const ScMatrix* ScMatrixToken::GetMatrix() const { return pMatrix.get(); }
+ScMatrix* ScMatrixToken::GetMatrix() { return pMatrix.get(); }
+bool ScMatrixToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && pMatrix == r.GetMatrix();
+}
+
+ScMatrixRangeToken::ScMatrixRangeToken( const sc::RangeMatrix& rMat ) :
+ FormulaToken(formula::svMatrix), mpMatrix(rMat.mpMat)
+{
+ maRef.InitRange(rMat.mnCol1, rMat.mnRow1, rMat.mnTab1, rMat.mnCol2, rMat.mnRow2, rMat.mnTab2);
+}
+
+ScMatrixRangeToken::ScMatrixRangeToken( const ScMatrixRangeToken& ) = default;
+
+sal_uInt8 ScMatrixRangeToken::GetByte() const
+{
+ return MATRIX_TOKEN_HAS_RANGE;
+}
+
+const ScMatrix* ScMatrixRangeToken::GetMatrix() const
+{
+ return mpMatrix.get();
+}
+
+ScMatrix* ScMatrixRangeToken::GetMatrix()
+{
+ return mpMatrix.get();
+}
+
+const ScComplexRefData* ScMatrixRangeToken::GetDoubleRef() const
+{
+ return &maRef;
+}
+
+ScComplexRefData* ScMatrixRangeToken::GetDoubleRef()
+{
+ return &maRef;
+}
+
+bool ScMatrixRangeToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==(r) && mpMatrix == r.GetMatrix();
+}
+
+FormulaToken* ScMatrixRangeToken::Clone() const
+{
+ return new ScMatrixRangeToken(*this);
+}
+
+ScExternalSingleRefToken::ScExternalSingleRefToken( sal_uInt16 nFileId, const svl::SharedString& rTabName, const ScSingleRefData& r ) :
+ FormulaToken( svExternalSingleRef, ocPush),
+ mnFileId(nFileId),
+ maTabName(rTabName),
+ maSingleRef(r)
+{
+}
+
+ScExternalSingleRefToken::~ScExternalSingleRefToken()
+{
+}
+
+sal_uInt16 ScExternalSingleRefToken::GetIndex() const
+{
+ return mnFileId;
+}
+
+const svl::SharedString & ScExternalSingleRefToken::GetString() const
+{
+ return maTabName;
+}
+
+const ScSingleRefData* ScExternalSingleRefToken::GetSingleRef() const
+{
+ return &maSingleRef;
+}
+
+ScSingleRefData* ScExternalSingleRefToken::GetSingleRef()
+{
+ return &maSingleRef;
+}
+
+bool ScExternalSingleRefToken::operator ==( const FormulaToken& r ) const
+{
+ if (!FormulaToken::operator==(r))
+ return false;
+
+ if (mnFileId != r.GetIndex())
+ return false;
+
+ if (maTabName != r.GetString())
+ return false;
+
+ return maSingleRef == *r.GetSingleRef();
+}
+
+ScExternalDoubleRefToken::ScExternalDoubleRefToken( sal_uInt16 nFileId, const svl::SharedString& rTabName, const ScComplexRefData& r ) :
+ FormulaToken( svExternalDoubleRef, ocPush),
+ mnFileId(nFileId),
+ maTabName(rTabName),
+ maDoubleRef(r)
+{
+}
+
+ScExternalDoubleRefToken::~ScExternalDoubleRefToken()
+{
+}
+
+sal_uInt16 ScExternalDoubleRefToken::GetIndex() const
+{
+ return mnFileId;
+}
+
+const svl::SharedString & ScExternalDoubleRefToken::GetString() const
+{
+ return maTabName;
+}
+
+const ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef() const
+{
+ return &maDoubleRef.Ref1;
+}
+
+ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef()
+{
+ return &maDoubleRef.Ref1;
+}
+
+const ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef2() const
+{
+ return &maDoubleRef.Ref2;
+}
+
+ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef2()
+{
+ return &maDoubleRef.Ref2;
+}
+
+const ScComplexRefData* ScExternalDoubleRefToken::GetDoubleRef() const
+{
+ return &maDoubleRef;
+}
+
+ScComplexRefData* ScExternalDoubleRefToken::GetDoubleRef()
+{
+ return &maDoubleRef;
+}
+
+bool ScExternalDoubleRefToken::operator ==( const FormulaToken& r ) const
+{
+ if (!FormulaToken::operator==(r))
+ return false;
+
+ if (mnFileId != r.GetIndex())
+ return false;
+
+ if (maTabName != r.GetString())
+ return false;
+
+ return maDoubleRef == *r.GetDoubleRef();
+}
+
+ScExternalNameToken::ScExternalNameToken( sal_uInt16 nFileId, const svl::SharedString& rName ) :
+ FormulaToken( svExternalName, ocPush),
+ mnFileId(nFileId),
+ maName(rName)
+{
+}
+
+ScExternalNameToken::~ScExternalNameToken() {}
+
+sal_uInt16 ScExternalNameToken::GetIndex() const
+{
+ return mnFileId;
+}
+
+const svl::SharedString & ScExternalNameToken::GetString() const
+{
+ return maName;
+}
+
+bool ScExternalNameToken::operator==( const FormulaToken& r ) const
+{
+ if ( !FormulaToken::operator==(r) )
+ return false;
+
+ if (mnFileId != r.GetIndex())
+ return false;
+
+ return maName.getData() == r.GetString().getData();
+}
+
+ScTableRefToken::ScTableRefToken( sal_uInt16 nIndex, ScTableRefToken::Item eItem ) :
+ FormulaToken( svIndex, ocTableRef),
+ mnIndex(nIndex),
+ meItem(eItem)
+{
+}
+
+ScTableRefToken::ScTableRefToken( const ScTableRefToken& r ) :
+ FormulaToken(r),
+ mxAreaRefRPN( r.mxAreaRefRPN ? r.mxAreaRefRPN->Clone() : nullptr),
+ mnIndex(r.mnIndex),
+ meItem(r.meItem)
+{
+}
+
+ScTableRefToken::~ScTableRefToken() {}
+
+sal_uInt16 ScTableRefToken::GetIndex() const
+{
+ return mnIndex;
+}
+
+void ScTableRefToken::SetIndex( sal_uInt16 n )
+{
+ mnIndex = n;
+}
+
+sal_Int16 ScTableRefToken::GetSheet() const
+{
+ // Code asking for this may have to be adapted as it might assume an
+ // svIndex token would always be ocName or ocDBArea.
+ SAL_WARN("sc.core","ScTableRefToken::GetSheet - maybe adapt caller to know about TableRef?");
+ // Database range is always global.
+ return -1;
+}
+
+ScTableRefToken::Item ScTableRefToken::GetItem() const
+{
+ return meItem;
+}
+
+void ScTableRefToken::AddItem( ScTableRefToken::Item eItem )
+{
+ meItem = static_cast<ScTableRefToken::Item>(meItem | eItem);
+}
+
+void ScTableRefToken::SetAreaRefRPN( formula::FormulaToken* pToken )
+{
+ mxAreaRefRPN = pToken;
+}
+
+formula::FormulaToken* ScTableRefToken::GetAreaRefRPN() const
+{
+ return mxAreaRefRPN.get();
+}
+
+bool ScTableRefToken::operator==( const FormulaToken& r ) const
+{
+ if ( !FormulaToken::operator==(r) )
+ return false;
+
+ if (mnIndex != r.GetIndex())
+ return false;
+
+ const ScTableRefToken* p = dynamic_cast<const ScTableRefToken*>(&r);
+ if (!p)
+ return false;
+
+ if (meItem != p->GetItem())
+ return false;
+
+ if (!mxAreaRefRPN && !p->mxAreaRefRPN)
+ ; // nothing
+ else if (!mxAreaRefRPN || !p->mxAreaRefRPN)
+ return false;
+ else if (!(*mxAreaRefRPN == *(p->mxAreaRefRPN)))
+ return false;
+
+ return true;
+}
+
+ScJumpMatrixToken::ScJumpMatrixToken(std::shared_ptr<ScJumpMatrix> p)
+ : FormulaToken(formula::svJumpMatrix)
+ , mpJumpMatrix(std::move(p))
+{}
+
+ScJumpMatrixToken::ScJumpMatrixToken( const ScJumpMatrixToken & ) = default;
+
+ScJumpMatrix* ScJumpMatrixToken::GetJumpMatrix() const
+{
+ return mpJumpMatrix.get();
+}
+
+bool ScJumpMatrixToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && mpJumpMatrix.get() == r.GetJumpMatrix();
+}
+
+ScJumpMatrixToken::~ScJumpMatrixToken()
+{
+}
+
+double ScEmptyCellToken::GetDouble() const { return 0.0; }
+
+const svl::SharedString & ScEmptyCellToken::GetString() const
+{
+ return svl::SharedString::getEmptyString();
+}
+
+bool ScEmptyCellToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) &&
+ bInherited == static_cast< const ScEmptyCellToken & >(r).IsInherited() &&
+ bDisplayedAsString == static_cast< const ScEmptyCellToken & >(r).IsDisplayedAsString();
+}
+
+ScMatrixCellResultToken::ScMatrixCellResultToken( const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL ) :
+ FormulaToken(formula::svMatrixCell), xMatrix(pMat), xUpperLeft(pUL) {}
+
+ScMatrixCellResultToken::ScMatrixCellResultToken( const ScMatrixCellResultToken& ) = default;
+
+double ScMatrixCellResultToken::GetDouble() const { return xUpperLeft->GetDouble(); }
+
+ScMatrixCellResultToken::~ScMatrixCellResultToken() {}
+
+const svl::SharedString & ScMatrixCellResultToken::GetString() const
+{
+ return xUpperLeft->GetString();
+}
+
+const ScMatrix* ScMatrixCellResultToken::GetMatrix() const { return xMatrix.get(); }
+// Non-const GetMatrix() is private and unused but must be implemented to
+// satisfy vtable linkage.
+ScMatrix* ScMatrixCellResultToken::GetMatrix()
+{
+ return const_cast<ScMatrix*>(xMatrix.get());
+}
+
+bool ScMatrixCellResultToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) &&
+ xUpperLeft == static_cast<const ScMatrixCellResultToken &>(r).xUpperLeft &&
+ xMatrix == static_cast<const ScMatrixCellResultToken &>(r).xMatrix;
+}
+
+FormulaToken* ScMatrixCellResultToken::Clone() const
+{
+ return new ScMatrixCellResultToken(*this);
+}
+
+void ScMatrixCellResultToken::Assign( const ScMatrixCellResultToken & r )
+{
+ xMatrix = r.xMatrix;
+ xUpperLeft = r.xUpperLeft;
+}
+
+ScMatrixFormulaCellToken::ScMatrixFormulaCellToken(
+ SCCOL nC, SCROW nR, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL ) :
+ ScMatrixCellResultToken(pMat, pUL), nRows(nR), nCols(nC)
+{
+ CloneUpperLeftIfNecessary();
+}
+
+ScMatrixFormulaCellToken::ScMatrixFormulaCellToken( SCCOL nC, SCROW nR ) :
+ ScMatrixCellResultToken(nullptr, nullptr), nRows(nR), nCols(nC) {}
+
+ScMatrixFormulaCellToken::ScMatrixFormulaCellToken( const ScMatrixFormulaCellToken& r ) :
+ ScMatrixCellResultToken(r), nRows(r.nRows), nCols(r.nCols)
+{
+ CloneUpperLeftIfNecessary();
+}
+
+ScMatrixFormulaCellToken::~ScMatrixFormulaCellToken() {}
+
+bool ScMatrixFormulaCellToken::operator==( const FormulaToken& r ) const
+{
+ const ScMatrixFormulaCellToken* p = dynamic_cast<const ScMatrixFormulaCellToken*>(&r);
+ return p && ScMatrixCellResultToken::operator==( r ) &&
+ nCols == p->nCols && nRows == p->nRows;
+}
+
+void ScMatrixFormulaCellToken::CloneUpperLeftIfNecessary()
+{
+ if (xUpperLeft && xUpperLeft->GetType() == svDouble)
+ xUpperLeft = xUpperLeft->Clone();
+}
+
+void ScMatrixFormulaCellToken::Assign( const ScMatrixCellResultToken & r )
+{
+ ScMatrixCellResultToken::Assign( r);
+
+ CloneUpperLeftIfNecessary();
+}
+
+void ScMatrixFormulaCellToken::Assign( const formula::FormulaToken& r )
+{
+ if (this == &r)
+ return;
+ const ScMatrixCellResultToken* p = dynamic_cast<const ScMatrixCellResultToken*>(&r);
+ if (p)
+ ScMatrixCellResultToken::Assign( *p);
+ else
+ {
+ OSL_ENSURE( r.GetType() != svMatrix, "ScMatrixFormulaCellToken::operator=: assigning ScMatrixToken to ScMatrixFormulaCellToken is not proper, use ScMatrixCellResultToken instead");
+ if (r.GetType() == svMatrix)
+ {
+ xUpperLeft = nullptr;
+ xMatrix = r.GetMatrix();
+ }
+ else
+ {
+ xUpperLeft = &r;
+ xMatrix = nullptr;
+ CloneUpperLeftIfNecessary();
+ }
+ }
+}
+
+void ScMatrixFormulaCellToken::SetUpperLeftDouble( double f )
+{
+ switch (GetUpperLeftType())
+ {
+ case svDouble:
+ const_cast<FormulaToken*>(xUpperLeft.get())->GetDoubleAsReference() = f;
+ break;
+ case svString:
+ xUpperLeft = new FormulaDoubleToken( f);
+ break;
+ case svUnknown:
+ if (!xUpperLeft)
+ {
+ xUpperLeft = new FormulaDoubleToken( f);
+ break;
+ }
+ [[fallthrough]];
+ default:
+ {
+ OSL_FAIL("ScMatrixFormulaCellToken::SetUpperLeftDouble: not modifying unhandled token type");
+ }
+ }
+}
+
+void ScMatrixFormulaCellToken::ResetResult()
+{
+ xMatrix = nullptr;
+ xUpperLeft = nullptr;
+}
+
+ScHybridCellToken::ScHybridCellToken(
+ double f, const svl::SharedString & rStr, const OUString & rFormula, bool bEmptyDisplayedAsString ) :
+ FormulaToken( formula::svHybridCell ),
+ mfDouble( f ), maString( rStr ),
+ maFormula( rFormula ),
+ mbEmptyDisplayedAsString( bEmptyDisplayedAsString)
+{
+ // caller, make up your mind...
+ assert( !bEmptyDisplayedAsString || (f == 0.0 && rStr.getString().isEmpty()));
+}
+
+double ScHybridCellToken::GetDouble() const { return mfDouble; }
+
+const svl::SharedString & ScHybridCellToken::GetString() const
+{
+ return maString;
+}
+
+bool ScHybridCellToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) &&
+ mfDouble == r.GetDouble() && maString == r.GetString() &&
+ maFormula == static_cast<const ScHybridCellToken &>(r).GetFormula();
+}
+
+bool ScTokenArray::AddFormulaToken(
+ const css::sheet::FormulaToken& rToken, svl::SharedStringPool& rSPool, formula::ExternalReferenceHelper* pExtRef)
+{
+ bool bError = FormulaTokenArray::AddFormulaToken(rToken, rSPool, pExtRef);
+ if ( bError )
+ {
+ bError = false;
+ const OpCode eOpCode = static_cast<OpCode>(rToken.OpCode); // assuming equal values for the moment
+
+ const uno::TypeClass eClass = rToken.Data.getValueTypeClass();
+ switch ( eClass )
+ {
+ case uno::TypeClass_STRUCT:
+ {
+ uno::Type aType = rToken.Data.getValueType();
+ if ( aType.equals( cppu::UnoType<sheet::SingleReference>::get() ) )
+ {
+ ScSingleRefData aSingleRef;
+ sheet::SingleReference aApiRef;
+ rToken.Data >>= aApiRef;
+ lcl_SingleRefToCalc( aSingleRef, aApiRef );
+ if ( eOpCode == ocPush )
+ AddSingleReference( aSingleRef );
+ else if ( eOpCode == ocColRowName )
+ AddColRowName( aSingleRef );
+ else
+ bError = true;
+ }
+ else if ( aType.equals( cppu::UnoType<sheet::ComplexReference>::get() ) )
+ {
+ ScComplexRefData aComplRef;
+ sheet::ComplexReference aApiRef;
+ rToken.Data >>= aApiRef;
+ lcl_SingleRefToCalc( aComplRef.Ref1, aApiRef.Reference1 );
+ lcl_SingleRefToCalc( aComplRef.Ref2, aApiRef.Reference2 );
+
+ if ( eOpCode == ocPush )
+ AddDoubleReference( aComplRef );
+ else
+ bError = true;
+ }
+ else if ( aType.equals( cppu::UnoType<sheet::NameToken>::get() ) )
+ {
+ sheet::NameToken aTokenData;
+ rToken.Data >>= aTokenData;
+ if ( eOpCode == ocName )
+ {
+ SAL_WARN_IF( aTokenData.Sheet < -1 || std::numeric_limits<sal_Int16>::max() < aTokenData.Sheet,
+ "sc.core",
+ "ScTokenArray::AddFormulaToken - NameToken.Sheet out of limits: " << aTokenData.Sheet);
+ sal_Int16 nSheet = static_cast<sal_Int16>(aTokenData.Sheet);
+ AddRangeName(aTokenData.Index, nSheet);
+ }
+ else if (eOpCode == ocDBArea)
+ AddDBRange(aTokenData.Index);
+ else if (eOpCode == ocTableRef)
+ bError = true; /* TODO: implementation */
+ else
+ bError = true;
+ }
+ else if ( aType.equals( cppu::UnoType<sheet::ExternalReference>::get() ) )
+ {
+ sheet::ExternalReference aApiExtRef;
+ if( (eOpCode == ocPush) && (rToken.Data >>= aApiExtRef) && (0 <= aApiExtRef.Index) && (aApiExtRef.Index <= SAL_MAX_UINT16) )
+ {
+ sal_uInt16 nFileId = static_cast< sal_uInt16 >( aApiExtRef.Index );
+ sheet::SingleReference aApiSRef;
+ sheet::ComplexReference aApiCRef;
+ OUString aName;
+ if( aApiExtRef.Reference >>= aApiSRef )
+ {
+ // try to resolve cache index to sheet name
+ size_t nCacheId = static_cast< size_t >( aApiSRef.Sheet );
+ OUString aTabName = pExtRef->getCacheTableName( nFileId, nCacheId );
+ if( !aTabName.isEmpty() )
+ {
+ ScSingleRefData aSingleRef;
+ // convert column/row settings, set sheet index to absolute
+ lcl_ExternalRefToCalc( aSingleRef, aApiSRef );
+ AddExternalSingleReference( nFileId, rSPool.intern( aTabName), aSingleRef );
+ }
+ else
+ bError = true;
+ }
+ else if( aApiExtRef.Reference >>= aApiCRef )
+ {
+ // try to resolve cache index to sheet name.
+ size_t nCacheId = static_cast< size_t >( aApiCRef.Reference1.Sheet );
+ OUString aTabName = pExtRef->getCacheTableName( nFileId, nCacheId );
+ if( !aTabName.isEmpty() )
+ {
+ ScComplexRefData aComplRef;
+ // convert column/row settings, set sheet index to absolute
+ lcl_ExternalRefToCalc( aComplRef.Ref1, aApiCRef.Reference1 );
+ lcl_ExternalRefToCalc( aComplRef.Ref2, aApiCRef.Reference2 );
+ // NOTE: This assumes that cached sheets are in consecutive order!
+ aComplRef.Ref2.SetAbsTab(
+ aComplRef.Ref1.Tab() + static_cast<SCTAB>(aApiCRef.Reference2.Sheet - aApiCRef.Reference1.Sheet));
+ AddExternalDoubleReference( nFileId, rSPool.intern( aTabName), aComplRef );
+ }
+ else
+ bError = true;
+ }
+ else if( aApiExtRef.Reference >>= aName )
+ {
+ if( !aName.isEmpty() )
+ AddExternalName( nFileId, rSPool.intern( aName) );
+ else
+ bError = true;
+ }
+ else
+ bError = true;
+ }
+ else
+ bError = true;
+ }
+ else
+ bError = true; // unknown struct
+ }
+ break;
+ case uno::TypeClass_SEQUENCE:
+ {
+ if ( eOpCode != ocPush )
+ bError = true; // not an inline array
+ else if (!rToken.Data.getValueType().equals( cppu::UnoType<
+ uno::Sequence< uno::Sequence< uno::Any >>>::get()))
+ bError = true; // unexpected sequence type
+ else
+ {
+ ScMatrixRef xMat = ScSequenceToMatrix::CreateMixedMatrix( rToken.Data);
+ if (xMat)
+ AddMatrix( xMat);
+ else
+ bError = true;
+ }
+ }
+ break;
+ default:
+ bError = true;
+ }
+ }
+ return bError;
+}
+
+void ScTokenArray::CheckForThreading( const FormulaToken& r )
+{
+#if HAVE_CPP_CONSTINIT_SORTED_VECTOR
+ constinit
+#endif
+ static const o3tl::sorted_vector<OpCode> aThreadedCalcDenyList({
+ ocIndirect,
+ ocMacro,
+ ocOffset,
+ ocTableOp,
+ ocCell,
+ ocMatch,
+ ocInfo,
+ ocStyle,
+ ocDBAverage,
+ ocDBCount,
+ ocDBCount2,
+ ocDBGet,
+ ocDBMax,
+ ocDBMin,
+ ocDBProduct,
+ ocDBStdDev,
+ ocDBStdDevP,
+ ocDBSum,
+ ocDBVar,
+ ocDBVarP,
+ ocText,
+ ocSheet,
+ ocExternal,
+ ocDde,
+ ocWebservice,
+ ocGetPivotData
+ });
+
+ // Don't enable threading once we decided to disable it.
+ if (!mbThreadingEnabled)
+ return;
+
+ static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
+
+ if (bThreadingProhibited)
+ {
+ mbThreadingEnabled = false;
+ return;
+ }
+
+ OpCode eOp = r.GetOpCode();
+
+ if (aThreadedCalcDenyList.find(eOp) != aThreadedCalcDenyList.end())
+ {
+ SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables threaded calculation of formula group");
+ mbThreadingEnabled = false;
+ return;
+ }
+
+ if (eOp != ocPush)
+ return;
+
+ switch (r.GetType())
+ {
+ case svExternalDoubleRef:
+ case svExternalSingleRef:
+ case svExternalName:
+ case svMatrix:
+ SAL_INFO("sc.core.formulagroup", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType())
+ << " disables threaded calculation of formula group");
+ mbThreadingEnabled = false;
+ return;
+ default:
+ break;
+ }
+}
+
+void ScTokenArray::CheckToken( const FormulaToken& r )
+{
+ if (mbThreadingEnabled)
+ CheckForThreading(r);
+
+ if (IsFormulaVectorDisabled())
+ return; // It's already disabled. No more checking needed.
+
+ OpCode eOp = r.GetOpCode();
+
+ if (SC_OPCODE_START_FUNCTION <= eOp && eOp < SC_OPCODE_STOP_FUNCTION)
+ {
+ if (ScInterpreter::GetGlobalConfig().mbOpenCLSubsetOnly &&
+ ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->find(eOp) == ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->end())
+ {
+ SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledNotInSubSet;
+ mbOpenCLEnabled = false;
+ return;
+ }
+
+ // We support vectorization for the following opcodes.
+ switch (eOp)
+ {
+ case ocAverage:
+ case ocMin:
+ case ocMinA:
+ case ocMax:
+ case ocMaxA:
+ case ocSum:
+ case ocSumIfs:
+ case ocSumProduct:
+ case ocCount:
+ case ocCount2:
+ case ocVLookup:
+ case ocSLN:
+ case ocIRR:
+ case ocMIRR:
+ case ocPMT:
+ case ocRate:
+ case ocRRI:
+ case ocPpmt:
+ case ocFisher:
+ case ocFisherInv:
+ case ocGamma:
+ case ocGammaLn:
+ case ocNotAvail:
+ case ocGauss:
+ case ocGeoMean:
+ case ocHarMean:
+ case ocSYD:
+ case ocCorrel:
+ case ocNegBinomVert:
+ case ocPearson:
+ case ocRSQ:
+ case ocCos:
+ case ocCosecant:
+ case ocCosecantHyp:
+ case ocISPMT:
+ case ocPDuration:
+ case ocSinHyp:
+ case ocAbs:
+ case ocPV:
+ case ocSin:
+ case ocTan:
+ case ocTanHyp:
+ case ocStandard:
+ case ocWeibull:
+ case ocMedian:
+ case ocDDB:
+ case ocFV:
+ case ocVBD:
+ case ocKurt:
+ case ocNper:
+ case ocNormDist:
+ case ocArcCos:
+ case ocSqrt:
+ case ocArcCosHyp:
+ case ocNPV:
+ case ocStdNormDist:
+ case ocNormInv:
+ case ocSNormInv:
+ case ocPermut:
+ case ocPermutationA:
+ case ocPhi:
+ case ocIpmt:
+ case ocConfidence:
+ case ocIntercept:
+ case ocDB:
+ case ocLogInv:
+ case ocArcCot:
+ case ocCosHyp:
+ case ocCritBinom:
+ case ocArcCotHyp:
+ case ocArcSin:
+ case ocArcSinHyp:
+ case ocArcTan:
+ case ocArcTanHyp:
+ case ocBitAnd:
+ case ocForecast:
+ case ocLogNormDist:
+ case ocGammaDist:
+ case ocLn:
+ case ocRound:
+ case ocCot:
+ case ocCotHyp:
+ case ocFDist:
+ case ocVar:
+ case ocChiDist:
+ case ocPower:
+ case ocOdd:
+ case ocChiSqDist:
+ case ocChiSqInv:
+ case ocGammaInv:
+ case ocFloor:
+ case ocFInv:
+ case ocFTest:
+ case ocB:
+ case ocBetaDist:
+ case ocExp:
+ case ocLog10:
+ case ocExpDist:
+ case ocAverageIfs:
+ case ocCountIfs:
+ case ocCombinA:
+ case ocEven:
+ case ocLog:
+ case ocMod:
+ case ocTrunc:
+ case ocSkew:
+ case ocArcTan2:
+ case ocBitOr:
+ case ocBitLshift:
+ case ocBitRshift:
+ case ocBitXor:
+ case ocChiInv:
+ case ocPoissonDist:
+ case ocSumSQ:
+ case ocSkewp:
+ case ocBinomDist:
+ case ocVarP:
+ case ocCeil:
+ case ocCombin:
+ case ocDevSq:
+ case ocStDev:
+ case ocSlope:
+ case ocSTEYX:
+ case ocZTest:
+ case ocPi:
+ case ocRandom:
+ case ocProduct:
+ case ocHypGeomDist:
+ case ocSumX2MY2:
+ case ocSumX2DY2:
+ case ocBetaInv:
+ case ocTTest:
+ case ocTDist:
+ case ocTInv:
+ case ocSumXMY2:
+ case ocStDevP:
+ case ocCovar:
+ case ocAnd:
+ case ocOr:
+ case ocNot:
+ case ocXor:
+ case ocDBMax:
+ case ocDBMin:
+ case ocDBProduct:
+ case ocDBAverage:
+ case ocDBStdDev:
+ case ocDBStdDevP:
+ case ocDBSum:
+ case ocDBVar:
+ case ocDBVarP:
+ case ocAverageIf:
+ case ocDBCount:
+ case ocDBCount2:
+ case ocDeg:
+ case ocRoundUp:
+ case ocRoundDown:
+ case ocInt:
+ case ocRad:
+ case ocCountIf:
+ case ocIsEven:
+ case ocIsOdd:
+ case ocFact:
+ case ocAverageA:
+ case ocVarA:
+ case ocVarPA:
+ case ocStDevA:
+ case ocStDevPA:
+ case ocSecant:
+ case ocSecantHyp:
+ case ocSumIf:
+ case ocNegSub:
+ case ocAveDev:
+ // Don't change the state.
+ break;
+ default:
+ SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledByOpCode;
+ mbOpenCLEnabled = false;
+ return;
+ }
+ }
+ else if (eOp == ocPush)
+ {
+ // This is a stack variable. See if this is a reference.
+
+ switch (r.GetType())
+ {
+ case svByte:
+ case svDouble:
+ case svString:
+ // Don't change the state.
+ break;
+ case svSingleRef:
+ case svDoubleRef:
+ // Depends on the reference state.
+ meVectorState = FormulaVectorCheckReference;
+ break;
+ case svError:
+ case svEmptyCell:
+ case svExternal:
+ case svExternalDoubleRef:
+ case svExternalName:
+ case svExternalSingleRef:
+ case svFAP:
+ case svHybridCell:
+ case svIndex:
+ case svJump:
+ case svJumpMatrix:
+ case svMatrix:
+ case svMatrixCell:
+ case svMissing:
+ case svRefList:
+ case svSep:
+ case svUnknown:
+ // We don't support vectorization on these.
+ SAL_INFO("sc.opencl", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType()) << " disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledByStackVariable;
+ mbOpenCLEnabled = false;
+ return;
+ default:
+ ;
+ }
+ }
+ else if (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
+ {
+ if (ScInterpreter::GetGlobalConfig().mbOpenCLSubsetOnly &&
+ ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->find(eOp) == ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->end())
+ {
+ SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledNotInSubSet;
+ mbOpenCLEnabled = false;
+ return;
+ }
+ }
+ else
+ {
+ // All the rest, special commands, separators, error codes, ...
+ switch (eOp)
+ {
+ default:
+ // Default is off, no vectorization.
+ // Mentioning some specific values below to indicate why.
+
+ case ocName:
+ // Named expression would need "recursive" handling of its
+ // token array for vector state in
+ // ScFormulaCell::InterpretFormulaGroup() and below.
+
+ case ocDBArea:
+ // Certainly not a vectorization of the entire area...
+
+ case ocTableRef:
+ // May result in a single cell or range reference, depending on
+ // context.
+
+ case ocColRowName:
+ // The associated reference is the name cell with which to
+ // create the implicit intersection.
+
+ case ocColRowNameAuto:
+ // Auto column/row names lead to references computed in
+ // interpreter.
+
+ SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledByOpCode;
+ mbOpenCLEnabled = false;
+ return;
+
+ // Known good, don't change state.
+ case ocStop:
+ case ocExternal:
+ case ocOpen:
+ case ocClose:
+ case ocSep:
+ case ocArrayOpen:
+ case ocArrayRowSep:
+ case ocArrayColSep:
+ case ocArrayClose:
+ case ocMissing:
+ case ocBad:
+ case ocSpaces:
+ case ocWhitespace:
+ case ocSkip:
+ case ocPercentSign:
+ case ocErrNull:
+ case ocErrDivZero:
+ case ocErrValue:
+ case ocErrRef:
+ case ocErrName:
+ case ocErrNum:
+ case ocErrNA:
+ break;
+ case ocIf:
+ case ocIfError:
+ case ocIfNA:
+ case ocChoose:
+ // Jump commands are now supported.
+ break;
+ }
+ }
+}
+
+bool ScTokenArray::ImplGetReference( ScRange& rRange, const ScAddress& rPos, bool bValidOnly ) const
+{
+ bool bIs = false;
+ if ( pCode && nLen == 1 )
+ {
+ const FormulaToken* pToken = pCode[0];
+ if ( pToken )
+ {
+ if ( pToken->GetType() == svSingleRef )
+ {
+ const ScSingleRefData& rRef = *static_cast<const ScSingleRefToken*>(pToken)->GetSingleRef();
+ rRange.aStart = rRange.aEnd = rRef.toAbs(*mxSheetLimits, rPos);
+ bIs = !bValidOnly || mxSheetLimits->ValidAddress(rRange.aStart);
+ }
+ else if ( pToken->GetType() == svDoubleRef )
+ {
+ const ScComplexRefData& rCompl = *static_cast<const ScDoubleRefToken*>(pToken)->GetDoubleRef();
+ const ScSingleRefData& rRef1 = rCompl.Ref1;
+ const ScSingleRefData& rRef2 = rCompl.Ref2;
+ rRange.aStart = rRef1.toAbs(*mxSheetLimits, rPos);
+ rRange.aEnd = rRef2.toAbs(*mxSheetLimits, rPos);
+ bIs = !bValidOnly || mxSheetLimits->ValidRange(rRange);
+ }
+ }
+ }
+ return bIs;
+}
+
+namespace {
+
+// we want to compare for similar not identical formulae
+// so we can't use actual row & column indices.
+size_t HashSingleRef( const ScSingleRefData& rRef )
+{
+ size_t nVal = 0;
+
+ nVal += size_t(rRef.IsColRel());
+ nVal += (size_t(rRef.IsRowRel()) << 1);
+ nVal += (size_t(rRef.IsTabRel()) << 2);
+
+ return nVal;
+}
+
+}
+
+void ScTokenArray::GenHash()
+{
+ static const OUStringHash aHasher;
+
+ size_t nHash = 1;
+ OpCode eOp;
+ StackVar eType;
+ const formula::FormulaToken* p;
+ sal_uInt16 n = std::min<sal_uInt16>(nLen, 20);
+ for (sal_uInt16 i = 0; i < n; ++i)
+ {
+ p = pCode[i];
+ eOp = p->GetOpCode();
+ if (eOp == ocPush)
+ {
+ // This is stack variable. Do additional differentiation.
+ eType = p->GetType();
+ switch (eType)
+ {
+ case svByte:
+ {
+ // Constant value.
+ sal_uInt8 nVal = p->GetByte();
+ nHash += static_cast<size_t>(nVal);
+ }
+ break;
+ case svDouble:
+ {
+ // Constant value.
+ double fVal = p->GetDouble();
+ nHash += std::hash<double>()(fVal);
+ }
+ break;
+ case svString:
+ {
+ // Constant string.
+ OUString aStr = p->GetString().getString();
+ nHash += aHasher(aStr);
+ }
+ break;
+ case svSingleRef:
+ {
+ size_t nVal = HashSingleRef(*p->GetSingleRef());
+ nHash += nVal;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ size_t nVal1 = HashSingleRef(rRef.Ref1);
+ size_t nVal2 = HashSingleRef(rRef.Ref2);
+ nHash += nVal1;
+ nHash += nVal2;
+ }
+ break;
+ default:
+ // Use the opcode value in all the other cases.
+ nHash += static_cast<size_t>(eOp);
+ }
+ }
+ else
+ // Use the opcode value in all the other cases.
+ nHash += static_cast<size_t>(eOp);
+
+ nHash = (nHash << 4) - nHash;
+ }
+
+ mnHashValue = nHash;
+}
+
+void ScTokenArray::ResetVectorState()
+{
+ mbOpenCLEnabled = ScCalcConfig::isOpenCLEnabled();
+ meVectorState = mbOpenCLEnabled ? FormulaVectorEnabled : FormulaVectorDisabled;
+ mbThreadingEnabled = ScCalcConfig::isThreadingEnabled();
+}
+
+bool ScTokenArray::IsFormulaVectorDisabled() const
+{
+ switch (meVectorState)
+ {
+ case FormulaVectorDisabled:
+ case FormulaVectorDisabledByOpCode:
+ case FormulaVectorDisabledByStackVariable:
+ case FormulaVectorDisabledNotInSubSet:
+ return true;
+ default:
+ ;
+ }
+
+ return false;
+}
+
+bool ScTokenArray::IsInvariant() const
+{
+ FormulaToken** p = pCode.get();
+ FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+ for (; p != pEnd; ++p)
+ {
+ switch ((*p)->GetType())
+ {
+ case svSingleRef:
+ case svExternalSingleRef:
+ {
+ const ScSingleRefData& rRef = *(*p)->GetSingleRef();
+ if (rRef.IsRowRel())
+ return false;
+ }
+ break;
+ case svDoubleRef:
+ case svExternalDoubleRef:
+ {
+ const ScComplexRefData& rRef = *(*p)->GetDoubleRef();
+ if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel())
+ return false;
+ }
+ break;
+ case svIndex:
+ return false;
+ default:
+ ;
+ }
+ }
+
+ return true;
+}
+
+bool ScTokenArray::IsReference( ScRange& rRange, const ScAddress& rPos ) const
+{
+ return ImplGetReference(rRange, rPos, false);
+}
+
+bool ScTokenArray::IsValidReference( ScRange& rRange, const ScAddress& rPos ) const
+{
+ return ImplGetReference(rRange, rPos, true);
+}
+
+ScTokenArray::ScTokenArray(const ScDocument& rDoc) :
+ mxSheetLimits(&rDoc.GetSheetLimits()),
+ mnHashValue(0)
+{
+ ResetVectorState();
+}
+
+ScTokenArray::ScTokenArray(ScSheetLimits& rLimits) :
+ mxSheetLimits(&rLimits),
+ mnHashValue(0)
+{
+ ResetVectorState();
+}
+
+ScTokenArray::~ScTokenArray()
+{
+}
+
+ScTokenArray& ScTokenArray::operator=( const ScTokenArray& rArr )
+{
+ Clear();
+ Assign( rArr );
+ mnHashValue = rArr.mnHashValue;
+ meVectorState = rArr.meVectorState;
+ mbOpenCLEnabled = rArr.mbOpenCLEnabled;
+ mbThreadingEnabled = rArr.mbThreadingEnabled;
+ return *this;
+}
+
+ScTokenArray& ScTokenArray::operator=( ScTokenArray&& rArr )
+{
+ mxSheetLimits = std::move(rArr.mxSheetLimits);
+ mnHashValue = rArr.mnHashValue;
+ meVectorState = rArr.meVectorState;
+ mbOpenCLEnabled = rArr.mbOpenCLEnabled;
+ mbThreadingEnabled = rArr.mbThreadingEnabled;
+ Move(std::move(rArr));
+ return *this;
+}
+
+bool ScTokenArray::EqualTokens( const ScTokenArray* pArr2) const
+{
+ // We only compare the non-RPN array
+ if ( pArr2->nLen != nLen )
+ return false;
+
+ FormulaToken** ppToken1 = GetArray();
+ FormulaToken** ppToken2 = pArr2->GetArray();
+ for (sal_uInt16 i=0; i<nLen; i++)
+ {
+ if ( ppToken1[i] != ppToken2[i] &&
+ !(*ppToken1[i] == *ppToken2[i]) )
+ return false; // Difference
+ }
+ return true; // All entries are the same
+}
+
+void ScTokenArray::Clear()
+{
+ mnHashValue = 0;
+ ResetVectorState();
+ FormulaTokenArray::Clear();
+}
+
+std::unique_ptr<ScTokenArray> ScTokenArray::Clone() const
+{
+ std::unique_ptr<ScTokenArray> p(new ScTokenArray(*mxSheetLimits));
+ p->nLen = nLen;
+ p->nRPN = nRPN;
+ p->nMode = nMode;
+ p->nError = nError;
+ p->bHyperLink = bHyperLink;
+ p->mnHashValue = mnHashValue;
+ p->meVectorState = meVectorState;
+ p->mbOpenCLEnabled = mbOpenCLEnabled;
+ p->mbThreadingEnabled = mbThreadingEnabled;
+ p->mbFromRangeName = mbFromRangeName;
+ p->mbShareable = mbShareable;
+
+ FormulaToken** pp;
+ if( nLen )
+ {
+ p->pCode.reset(new FormulaToken*[ nLen ]);
+ pp = p->pCode.get();
+ memcpy( pp, pCode.get(), nLen * sizeof( formula::FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nLen; i++, pp++ )
+ {
+ *pp = (*pp)->Clone();
+ (*pp)->IncRef();
+ }
+ }
+ if( nRPN )
+ {
+ pp = p->pRPN = new FormulaToken*[ nRPN ];
+ memcpy( pp, pRPN, nRPN * sizeof( formula::FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nRPN; i++, pp++ )
+ {
+ FormulaToken* t = *pp;
+ if( t->GetRef() > 1 )
+ {
+ FormulaToken** p2 = pCode.get();
+ sal_uInt16 nIdx = 0xFFFF;
+ for( sal_uInt16 j = 0; j < nLen; j++, p2++ )
+ {
+ if( *p2 == t )
+ {
+ nIdx = j; break;
+ }
+ }
+ if( nIdx == 0xFFFF )
+ *pp = t->Clone();
+ else
+ *pp = p->pCode[ nIdx ];
+ }
+ else
+ *pp = t->Clone();
+ (*pp)->IncRef();
+ }
+ }
+ return p;
+}
+
+ScTokenArray ScTokenArray::CloneValue() const
+{
+ ScTokenArray aNew(*mxSheetLimits);
+ aNew.nLen = nLen;
+ aNew.nRPN = nRPN;
+ aNew.nMode = nMode;
+ aNew.nError = nError;
+ aNew.bHyperLink = bHyperLink;
+ aNew.mnHashValue = mnHashValue;
+ aNew.meVectorState = meVectorState;
+ aNew.mbOpenCLEnabled = mbOpenCLEnabled;
+ aNew.mbThreadingEnabled = mbThreadingEnabled;
+ aNew.mbFromRangeName = mbFromRangeName;
+ aNew.mbShareable = mbShareable;
+
+ FormulaToken** pp;
+ if( nLen )
+ {
+ aNew.pCode.reset(new FormulaToken*[ nLen ]);
+ pp = aNew.pCode.get();
+ memcpy( pp, pCode.get(), nLen * sizeof( formula::FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nLen; i++, pp++ )
+ {
+ *pp = (*pp)->Clone();
+ (*pp)->IncRef();
+ }
+ }
+ if( nRPN )
+ {
+ pp = aNew.pRPN = new FormulaToken*[ nRPN ];
+ memcpy( pp, pRPN, nRPN * sizeof( formula::FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nRPN; i++, pp++ )
+ {
+ FormulaToken* t = *pp;
+ if( t->GetRef() > 1 )
+ {
+ FormulaToken** p2 = pCode.get();
+ sal_uInt16 nIdx = 0xFFFF;
+ for( sal_uInt16 j = 0; j < nLen; j++, p2++ )
+ {
+ if( *p2 == t )
+ {
+ nIdx = j; break;
+ }
+ }
+ if( nIdx == 0xFFFF )
+ *pp = t->Clone();
+ else
+ *pp = aNew.pCode[ nIdx ];
+ }
+ else
+ *pp = t->Clone();
+ (*pp)->IncRef();
+ }
+ }
+ return aNew;
+}
+
+FormulaToken* ScTokenArray::AddRawToken( const ScRawToken& r )
+{
+ return Add( r.CreateToken(*mxSheetLimits) );
+}
+
+// Utility function to ensure that there is strict alternation of values and
+// separators.
+static bool
+checkArraySep( bool & bPrevWasSep, bool bNewVal )
+{
+ bool bResult = (bPrevWasSep == bNewVal);
+ bPrevWasSep = bNewVal;
+ return bResult;
+}
+
+FormulaToken* ScTokenArray::MergeArray( )
+{
+ int nCol = -1, nRow = 0;
+ int i, nPrevRowSep = -1, nStart = 0;
+ bool bPrevWasSep = false; // top of stack is ocArrayClose
+ FormulaToken* t;
+ bool bNumeric = false; // numeric value encountered in current element
+
+ // (1) Iterate from the end to the start to find matrix dims
+ // and do basic validation.
+ for ( i = nLen ; i-- > nStart ; )
+ {
+ t = pCode[i];
+ switch ( t->GetOpCode() )
+ {
+ case ocPush :
+ if( checkArraySep( bPrevWasSep, false ) )
+ {
+ return nullptr;
+ }
+
+ // no references or nested arrays
+ if ( t->GetType() != svDouble && t->GetType() != svString )
+ {
+ return nullptr;
+ }
+ bNumeric = (t->GetType() == svDouble);
+ break;
+
+ case ocMissing :
+ case ocTrue :
+ case ocFalse :
+ if( checkArraySep( bPrevWasSep, false ) )
+ {
+ return nullptr;
+ }
+ bNumeric = false;
+ break;
+
+ case ocArrayColSep :
+ case ocSep :
+ if( checkArraySep( bPrevWasSep, true ) )
+ {
+ return nullptr;
+ }
+ bNumeric = false;
+ break;
+
+ case ocArrayClose :
+ // not possible with the , but check just in case
+ // something changes in the future
+ if( i != (nLen-1))
+ {
+ return nullptr;
+ }
+
+ if( checkArraySep( bPrevWasSep, true ) )
+ {
+ return nullptr;
+ }
+
+ nPrevRowSep = i;
+ bNumeric = false;
+ break;
+
+ case ocArrayOpen :
+ nStart = i; // stop iteration
+ [[fallthrough]]; // to ArrayRowSep
+
+ case ocArrayRowSep :
+ if( checkArraySep( bPrevWasSep, true ) )
+ {
+ return nullptr;
+ }
+
+ if( nPrevRowSep < 0 || // missing ocArrayClose
+ ((nPrevRowSep - i) % 2) == 1) // no complex elements
+ {
+ return nullptr;
+ }
+
+ if( nCol < 0 )
+ {
+ nCol = (nPrevRowSep - i) / 2;
+ }
+ else if( (nPrevRowSep - i)/2 != nCol) // irregular array
+ {
+ return nullptr;
+ }
+
+ nPrevRowSep = i;
+ nRow++;
+ bNumeric = false;
+ break;
+
+ case ocNegSub :
+ case ocAdd :
+ // negation or unary plus must precede numeric value
+ if( !bNumeric )
+ {
+ return nullptr;
+ }
+ --nPrevRowSep; // shorten this row by 1
+ bNumeric = false; // one level only, no --42
+ break;
+
+ case ocSpaces :
+ case ocWhitespace :
+ // ignore spaces
+ --nPrevRowSep; // shorten this row by 1
+ break;
+
+ default :
+ // no functions or operators
+ return nullptr;
+ }
+ }
+ if( nCol <= 0 || nRow <= 0 )
+ return nullptr;
+
+ int nSign = 1;
+ ScMatrix* pArray = new ScMatrix(nCol, nRow, 0.0);
+ for ( i = nStart, nCol = 0, nRow = 0 ; i < nLen ; i++ )
+ {
+ t = pCode[i];
+
+ switch ( t->GetOpCode() )
+ {
+ case ocPush :
+ if ( t->GetType() == svDouble )
+ {
+ pArray->PutDouble( t->GetDouble() * nSign, nCol, nRow );
+ nSign = 1;
+ }
+ else if ( t->GetType() == svString )
+ {
+ pArray->PutString(t->GetString(), nCol, nRow);
+ }
+ break;
+
+ case ocMissing :
+ pArray->PutEmpty( nCol, nRow );
+ break;
+
+ case ocTrue :
+ pArray->PutBoolean( true, nCol, nRow );
+ break;
+
+ case ocFalse :
+ pArray->PutBoolean( false, nCol, nRow );
+ break;
+
+ case ocArrayColSep :
+ case ocSep :
+ nCol++;
+ break;
+
+ case ocArrayRowSep :
+ nRow++; nCol = 0;
+ break;
+
+ case ocNegSub :
+ nSign = -nSign;
+ break;
+
+ default :
+ break;
+ }
+ pCode[i] = nullptr;
+ t->DecRef();
+ }
+ nLen = sal_uInt16( nStart );
+ return AddMatrix( pArray );
+}
+
+void ScTokenArray::MergeRangeReference( const ScAddress & rPos )
+{
+ if (!pCode || !nLen)
+ return;
+ sal_uInt16 nIdx = nLen;
+
+ // The actual types are checked in extendRangeReference().
+ FormulaToken *p3 = PeekPrev(nIdx); // ref
+ if (!p3)
+ return;
+ FormulaToken *p2 = PeekPrev(nIdx); // ocRange
+ if (!p2 || p2->GetOpCode() != ocRange)
+ return;
+ FormulaToken *p1 = PeekPrev(nIdx); // ref
+ if (!p1)
+ return;
+ FormulaTokenRef p = extendRangeReference( *mxSheetLimits, *p1, *p3, rPos, true);
+ if (p)
+ {
+ p->IncRef();
+ p1->DecRef();
+ p2->DecRef();
+ p3->DecRef();
+ nLen -= 2;
+ pCode[ nLen-1 ] = p.get();
+ }
+}
+
+FormulaToken* ScTokenArray::AddOpCode( OpCode e )
+{
+ ScRawToken t;
+ t.SetOpCode( e );
+ return AddRawToken( t );
+}
+
+FormulaToken* ScTokenArray::AddSingleReference( const ScSingleRefData& rRef )
+{
+ return Add( new ScSingleRefToken( *mxSheetLimits, rRef ) );
+}
+
+FormulaToken* ScTokenArray::AddMatrixSingleReference( const ScSingleRefData& rRef )
+{
+ return Add( new ScSingleRefToken(*mxSheetLimits, rRef, ocMatRef ) );
+}
+
+FormulaToken* ScTokenArray::AddDoubleReference( const ScComplexRefData& rRef )
+{
+ return Add( new ScDoubleRefToken(*mxSheetLimits, rRef ) );
+}
+
+FormulaToken* ScTokenArray::AddMatrix( const ScMatrixRef& p )
+{
+ return Add( new ScMatrixToken( p ) );
+}
+
+void ScTokenArray::AddRangeName( sal_uInt16 n, sal_Int16 nSheet )
+{
+ Add( new FormulaIndexToken( ocName, n, nSheet));
+}
+
+FormulaToken* ScTokenArray::AddDBRange( sal_uInt16 n )
+{
+ return Add( new FormulaIndexToken( ocDBArea, n));
+}
+
+FormulaToken* ScTokenArray::AddExternalName( sal_uInt16 nFileId, const svl::SharedString& rName )
+{
+ return Add( new ScExternalNameToken(nFileId, rName) );
+}
+
+void ScTokenArray::AddExternalSingleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName,
+ const ScSingleRefData& rRef )
+{
+ Add( new ScExternalSingleRefToken(nFileId, rTabName, rRef) );
+}
+
+FormulaToken* ScTokenArray::AddExternalDoubleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName,
+ const ScComplexRefData& rRef )
+{
+ return Add( new ScExternalDoubleRefToken(nFileId, rTabName, rRef) );
+}
+
+FormulaToken* ScTokenArray::AddColRowName( const ScSingleRefData& rRef )
+{
+ return Add( new ScSingleRefToken(*mxSheetLimits, rRef, ocColRowName ) );
+}
+
+void ScTokenArray::AssignXMLString( const OUString &rText, const OUString &rFormulaNmsp )
+{
+ sal_uInt16 nTokens = 1;
+ FormulaToken *aTokens[2];
+
+ aTokens[0] = new FormulaStringOpToken( ocStringXML, svl::SharedString( rText) ); // string not interned
+ if( !rFormulaNmsp.isEmpty() )
+ aTokens[ nTokens++ ] = new FormulaStringOpToken( ocStringXML,
+ svl::SharedString( rFormulaNmsp) ); // string not interned
+
+ Assign( nTokens, aTokens );
+}
+
+bool ScTokenArray::GetAdjacentExtendOfOuterFuncRefs( SCCOLROW& nExtend,
+ const ScAddress& rPos, ScDirection eDir )
+{
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+ switch ( eDir )
+ {
+ case DIR_BOTTOM :
+ if ( rPos.Row() >= mxSheetLimits->mnMaxRow )
+ return false;
+ nExtend = rPos.Row();
+ nRow = nExtend + 1;
+ break;
+ case DIR_RIGHT :
+ if ( rPos.Col() >= mxSheetLimits->mnMaxCol )
+ return false;
+ nExtend = rPos.Col();
+ nCol = static_cast<SCCOL>(nExtend) + 1;
+ break;
+ case DIR_TOP :
+ if ( rPos.Row() <= 0 )
+ return false;
+ nExtend = rPos.Row();
+ nRow = nExtend - 1;
+ break;
+ case DIR_LEFT :
+ if ( rPos.Col() <= 0 )
+ return false;
+ nExtend = rPos.Col();
+ nCol = static_cast<SCCOL>(nExtend) - 1;
+ break;
+ default:
+ OSL_FAIL( "unknown Direction" );
+ return false;
+ }
+ if ( pRPN && nRPN )
+ {
+ FormulaToken* t = pRPN[nRPN-1];
+ if ( t->GetType() == svByte )
+ {
+ sal_uInt8 nParamCount = t->GetByte();
+ if ( nParamCount && nRPN > nParamCount )
+ {
+ bool bRet = false;
+ sal_uInt16 nParam = nRPN - nParamCount - 1;
+ for ( ; nParam < nRPN-1; nParam++ )
+ {
+ FormulaToken* p = pRPN[nParam];
+ switch ( p->GetType() )
+ {
+ case svSingleRef :
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+ switch ( eDir )
+ {
+ case DIR_BOTTOM :
+ if (aAbs.Row() == nRow && aAbs.Row() > nExtend)
+ {
+ nExtend = aAbs.Row();
+ bRet = true;
+ }
+ break;
+ case DIR_RIGHT :
+ if (aAbs.Col() == nCol && static_cast<SCCOLROW>(aAbs.Col()) > nExtend)
+ {
+ nExtend = aAbs.Col();
+ bRet = true;
+ }
+ break;
+ case DIR_TOP :
+ if (aAbs.Row() == nRow && aAbs.Row() < nExtend)
+ {
+ nExtend = aAbs.Row();
+ bRet = true;
+ }
+ break;
+ case DIR_LEFT :
+ if (aAbs.Col() == nCol && static_cast<SCCOLROW>(aAbs.Col()) < nExtend)
+ {
+ nExtend = aAbs.Col();
+ bRet = true;
+ }
+ break;
+ }
+ }
+ break;
+ case svDoubleRef :
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+ switch ( eDir )
+ {
+ case DIR_BOTTOM :
+ if (aAbs.aStart.Row() == nRow && aAbs.aEnd.Row() > nExtend)
+ {
+ nExtend = aAbs.aEnd.Row();
+ bRet = true;
+ }
+ break;
+ case DIR_RIGHT :
+ if (aAbs.aStart.Col() == nCol && static_cast<SCCOLROW>(aAbs.aEnd.Col()) > nExtend)
+ {
+ nExtend = aAbs.aEnd.Col();
+ bRet = true;
+ }
+ break;
+ case DIR_TOP :
+ if (aAbs.aEnd.Row() == nRow && aAbs.aStart.Row() < nExtend)
+ {
+ nExtend = aAbs.aStart.Row();
+ bRet = true;
+ }
+ break;
+ case DIR_LEFT :
+ if (aAbs.aEnd.Col() == nCol && static_cast<SCCOLROW>(aAbs.aStart.Col()) < nExtend)
+ {
+ nExtend = aAbs.aStart.Col();
+ bRet = true;
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ } // switch
+ } // for
+ return bRet;
+ }
+ }
+ }
+ return false;
+}
+
+namespace {
+
+void GetExternalTableData(const ScDocument* pOldDoc, const ScDocument* pNewDoc, const SCTAB nTab, OUString& rTabName, sal_uInt16& rFileId)
+{
+ const OUString& aFileName = pOldDoc->GetFileURL();
+ rFileId = pNewDoc->GetExternalRefManager()->getExternalFileId(aFileName);
+ rTabName = pOldDoc->GetCopyTabName(nTab);
+ if (rTabName.isEmpty())
+ pOldDoc->GetName(nTab, rTabName);
+}
+
+bool IsInCopyRange( const ScRange& rRange, const ScDocument* pClipDoc )
+{
+ ScClipParam& rClipParam = const_cast<ScDocument*>(pClipDoc)->GetClipParam();
+ return rClipParam.maRanges.Contains(rRange);
+}
+
+bool SkipReference(formula::FormulaToken* pToken, const ScAddress& rPos, const ScDocument& rOldDoc, bool bRangeName, bool bCheckCopyArea)
+{
+ ScRange aRange;
+
+ if (!ScRefTokenHelper::getRangeFromToken(&rOldDoc, aRange, pToken, rPos))
+ return true;
+
+ if (bRangeName && aRange.aStart.Tab() == rPos.Tab())
+ {
+ switch (pToken->GetType())
+ {
+ case svDoubleRef:
+ {
+ ScSingleRefData& rRef = *pToken->GetSingleRef2();
+ if (rRef.IsColRel() || rRef.IsRowRel())
+ return true;
+ }
+ [[fallthrough]];
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *pToken->GetSingleRef();
+ if (rRef.IsColRel() || rRef.IsRowRel())
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (bCheckCopyArea && IsInCopyRange(aRange, &rOldDoc))
+ return true;
+
+ return false;
+}
+
+void AdjustSingleRefData( ScSingleRefData& rRef, const ScAddress& rOldPos, const ScAddress& rNewPos)
+{
+ SCCOL nCols = rNewPos.Col() - rOldPos.Col();
+ SCROW nRows = rNewPos.Row() - rOldPos.Row();
+ SCTAB nTabs = rNewPos.Tab() - rOldPos.Tab();
+
+ if (!rRef.IsColRel())
+ rRef.IncCol(nCols);
+
+ if (!rRef.IsRowRel())
+ rRef.IncRow(nRows);
+
+ if (!rRef.IsTabRel())
+ rRef.IncTab(nTabs);
+}
+
+}
+
+void ScTokenArray::ReadjustAbsolute3DReferences( const ScDocument& rOldDoc, ScDocument& rNewDoc, const ScAddress& rPos, bool bRangeName )
+{
+ for ( sal_uInt16 j=0; j<nLen; ++j )
+ {
+ switch ( pCode[j]->GetType() )
+ {
+ case svDoubleRef :
+ {
+ if (SkipReference(pCode[j], rPos, rOldDoc, bRangeName, true))
+ continue;
+
+ ScComplexRefData& rRef = *pCode[j]->GetDoubleRef();
+ ScSingleRefData& rRef2 = rRef.Ref2;
+ ScSingleRefData& rRef1 = rRef.Ref1;
+
+ if ( (rRef2.IsFlag3D() && !rRef2.IsTabRel()) || (rRef1.IsFlag3D() && !rRef1.IsTabRel()) )
+ {
+ OUString aTabName;
+ sal_uInt16 nFileId;
+ GetExternalTableData(&rOldDoc, &rNewDoc, rRef1.Tab(), aTabName, nFileId);
+ ReplaceToken( j, new ScExternalDoubleRefToken( nFileId,
+ rNewDoc.GetSharedStringPool().intern( aTabName), rRef), CODE_AND_RPN);
+ // ATTENTION: rRef can't be used after this point
+ }
+ }
+ break;
+ case svSingleRef :
+ {
+ if (SkipReference(pCode[j], rPos, rOldDoc, bRangeName, true))
+ continue;
+
+ ScSingleRefData& rRef = *pCode[j]->GetSingleRef();
+
+ if ( rRef.IsFlag3D() && !rRef.IsTabRel() )
+ {
+ OUString aTabName;
+ sal_uInt16 nFileId;
+ GetExternalTableData(&rOldDoc, &rNewDoc, rRef.Tab(), aTabName, nFileId);
+ ReplaceToken( j, new ScExternalSingleRefToken( nFileId,
+ rNewDoc.GetSharedStringPool().intern( aTabName), rRef), CODE_AND_RPN);
+ // ATTENTION: rRef can't be used after this point
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+}
+
+void ScTokenArray::AdjustAbsoluteRefs( const ScDocument& rOldDoc, const ScAddress& rOldPos, const ScAddress& rNewPos,
+ bool bCheckCopyRange)
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, true);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch ( p->GetType() )
+ {
+ case svDoubleRef :
+ {
+ if (!SkipReference(p, rOldPos, rOldDoc, false, bCheckCopyRange))
+ continue;
+
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScSingleRefData& rRef2 = rRef.Ref2;
+ ScSingleRefData& rRef1 = rRef.Ref1;
+
+ AdjustSingleRefData( rRef1, rOldPos, rNewPos );
+ AdjustSingleRefData( rRef2, rOldPos, rNewPos );
+ }
+ break;
+ case svSingleRef :
+ {
+ if (!SkipReference(p, rOldPos, rOldDoc, false, bCheckCopyRange))
+ continue;
+
+ ScSingleRefData& rRef = *p->GetSingleRef();
+
+ AdjustSingleRefData( rRef, rOldPos, rNewPos );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+}
+
+void ScTokenArray::AdjustSheetLocalNameReferences( SCTAB nOldTab, SCTAB nNewTab )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch ( p->GetType() )
+ {
+ case svDoubleRef :
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScSingleRefData& rRef2 = rRef.Ref2;
+ ScSingleRefData& rRef1 = rRef.Ref1;
+
+ if (!rRef1.IsTabRel() && rRef1.Tab() == nOldTab)
+ rRef1.SetAbsTab( nNewTab);
+ if (!rRef2.IsTabRel() && rRef2.Tab() == nOldTab)
+ rRef2.SetAbsTab( nNewTab);
+ if (!rRef1.IsTabRel() && !rRef2.IsTabRel() && rRef1.Tab() > rRef2.Tab())
+ {
+ SCTAB nTab = rRef1.Tab();
+ rRef1.SetAbsTab( rRef2.Tab());
+ rRef2.SetAbsTab( nTab);
+ }
+ }
+ break;
+ case svSingleRef :
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+
+ if (!rRef.IsTabRel() && rRef.Tab() == nOldTab)
+ rRef.SetAbsTab( nNewTab);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+bool ScTokenArray::ReferencesSheet( SCTAB nTab, SCTAB nPosTab ) const
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken* const * pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken* const * const pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ const FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch ( p->GetType() )
+ {
+ case svDoubleRef :
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ const ScSingleRefData& rRef2 = rRef.Ref2;
+ const ScSingleRefData& rRef1 = rRef.Ref1;
+
+ SCTAB nTab1 = (rRef1.IsTabRel() ? rRef1.Tab() + nPosTab : rRef1.Tab());
+ SCTAB nTab2 = (rRef2.IsTabRel() ? rRef2.Tab() + nPosTab : rRef2.Tab());
+ if (nTab1 <= nTab && nTab <= nTab2)
+ return true;
+ }
+ break;
+ case svSingleRef :
+ {
+ const ScSingleRefData& rRef = *p->GetSingleRef();
+ if (rRef.IsTabRel())
+ {
+ if (rRef.Tab() + nPosTab == nTab)
+ return true;
+ }
+ else
+ {
+ if (rRef.Tab() == nTab)
+ return true;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+ return false;
+}
+
+namespace {
+
+ScRange getSelectedRange( const sc::RefUpdateContext& rCxt )
+{
+ ScRange aSelectedRange(ScAddress::INITIALIZE_INVALID);
+ if (rCxt.mnColDelta < 0)
+ {
+ // Delete and shift to left.
+ aSelectedRange.aStart = ScAddress(rCxt.maRange.aStart.Col()+rCxt.mnColDelta, rCxt.maRange.aStart.Row(), rCxt.maRange.aStart.Tab());
+ aSelectedRange.aEnd = ScAddress(rCxt.maRange.aStart.Col()-1, rCxt.maRange.aEnd.Row(), rCxt.maRange.aEnd.Tab());
+ }
+ else if (rCxt.mnRowDelta < 0)
+ {
+ // Delete and shift up.
+ aSelectedRange.aStart = ScAddress(rCxt.maRange.aStart.Col(), rCxt.maRange.aStart.Row()+rCxt.mnRowDelta, rCxt.maRange.aStart.Tab());
+ aSelectedRange.aEnd = ScAddress(rCxt.maRange.aEnd.Col(), rCxt.maRange.aStart.Row()-1, rCxt.maRange.aEnd.Tab());
+ }
+ else if (rCxt.mnTabDelta < 0)
+ {
+ // Deleting sheets.
+ // TODO : Figure out what to do here.
+ }
+ else if (rCxt.mnColDelta > 0)
+ {
+ // Insert and shift to the right.
+ aSelectedRange.aStart = rCxt.maRange.aStart;
+ aSelectedRange.aEnd = ScAddress(rCxt.maRange.aStart.Col()+rCxt.mnColDelta-1, rCxt.maRange.aEnd.Row(), rCxt.maRange.aEnd.Tab());
+ }
+ else if (rCxt.mnRowDelta > 0)
+ {
+ // Insert and shift down.
+ aSelectedRange.aStart = rCxt.maRange.aStart;
+ aSelectedRange.aEnd = ScAddress(rCxt.maRange.aEnd.Col(), rCxt.maRange.aStart.Row()+rCxt.mnRowDelta-1, rCxt.maRange.aEnd.Tab());
+ }
+ else if (rCxt.mnTabDelta > 0)
+ {
+ // Inserting sheets.
+ // TODO : Figure out what to do here.
+ }
+
+ return aSelectedRange;
+}
+
+void setRefDeleted( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
+{
+ if (rCxt.mnColDelta < 0)
+ rRef.SetColDeleted(true);
+ else if (rCxt.mnRowDelta < 0)
+ rRef.SetRowDeleted(true);
+ else if (rCxt.mnTabDelta < 0)
+ rRef.SetTabDeleted(true);
+}
+
+void restoreDeletedRef( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
+{
+ if (rCxt.mnColDelta)
+ {
+ if (rRef.IsColDeleted())
+ rRef.SetColDeleted(false);
+ }
+ else if (rCxt.mnRowDelta)
+ {
+ if (rRef.IsRowDeleted())
+ rRef.SetRowDeleted(false);
+ }
+ else if (rCxt.mnTabDelta)
+ {
+ if (rRef.IsTabDeleted())
+ rRef.SetTabDeleted(false);
+ }
+}
+
+void setRefDeleted( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt )
+{
+ if (rCxt.mnColDelta < 0)
+ {
+ rRef.Ref1.SetColDeleted(true);
+ rRef.Ref2.SetColDeleted(true);
+ }
+ else if (rCxt.mnRowDelta < 0)
+ {
+ rRef.Ref1.SetRowDeleted(true);
+ rRef.Ref2.SetRowDeleted(true);
+ }
+ else if (rCxt.mnTabDelta < 0)
+ {
+ rRef.Ref1.SetTabDeleted(true);
+ rRef.Ref2.SetTabDeleted(true);
+ }
+}
+
+void restoreDeletedRef( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt )
+{
+ restoreDeletedRef(rRef.Ref1, rCxt);
+ restoreDeletedRef(rRef.Ref2, rCxt);
+}
+
+enum ShrinkResult
+{
+ UNMODIFIED,
+ SHRUNK,
+ STICKY
+};
+
+ShrinkResult shrinkRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rDeletedRange,
+ const ScComplexRefData& rRef )
+{
+ if (!rDeletedRange.Intersects(rRefRange))
+ return UNMODIFIED;
+
+ if (rCxt.mnColDelta < 0)
+ {
+ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ // Entire rows are not affected, columns are anchored.
+ return STICKY;
+
+ // Shifting left.
+ if (rRefRange.aStart.Row() < rDeletedRange.aStart.Row() || rDeletedRange.aEnd.Row() < rRefRange.aEnd.Row())
+ // Deleted range is only partially overlapping in vertical direction. Bail out.
+ return UNMODIFIED;
+
+ if (rDeletedRange.aStart.Col() <= rRefRange.aStart.Col())
+ {
+ if (rRefRange.aEnd.Col() <= rDeletedRange.aEnd.Col())
+ {
+ // Reference is entirely deleted.
+ rRefRange.SetInvalid();
+ }
+ else
+ {
+ // The reference range is truncated on the left.
+ SCCOL nOffset = rDeletedRange.aStart.Col() - rRefRange.aStart.Col();
+ SCCOL nDelta = rRefRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta+nOffset);
+ rRefRange.aStart.IncCol(nOffset);
+ }
+ }
+ else if (rDeletedRange.aEnd.Col() < rRefRange.aEnd.Col())
+ {
+ if (rRefRange.IsEndColSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return STICKY;
+
+ // Reference is deleted in the middle. Move the last column
+ // position to the left.
+ SCCOL nDelta = rDeletedRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
+ }
+ else
+ {
+ if (rRefRange.IsEndColSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return STICKY;
+
+ // The reference range is truncated on the right.
+ SCCOL nDelta = rDeletedRange.aStart.Col() - rRefRange.aEnd.Col() - 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
+ }
+ return SHRUNK;
+ }
+ else if (rCxt.mnRowDelta < 0)
+ {
+ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ // Entire columns are not affected, rows are anchored.
+ return STICKY;
+
+ // Shifting up.
+
+ if (rRefRange.aStart.Col() < rDeletedRange.aStart.Col() || rDeletedRange.aEnd.Col() < rRefRange.aEnd.Col())
+ // Deleted range is only partially overlapping in horizontal direction. Bail out.
+ return UNMODIFIED;
+
+ if (rDeletedRange.aStart.Row() <= rRefRange.aStart.Row())
+ {
+ if (rRefRange.aEnd.Row() <= rDeletedRange.aEnd.Row())
+ {
+ // Reference is entirely deleted.
+ rRefRange.SetInvalid();
+ }
+ else
+ {
+ // The reference range is truncated on the top.
+ SCROW nOffset = rDeletedRange.aStart.Row() - rRefRange.aStart.Row();
+ SCROW nDelta = rRefRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta+nOffset);
+ rRefRange.aStart.IncRow(nOffset);
+ }
+ }
+ else if (rDeletedRange.aEnd.Row() < rRefRange.aEnd.Row())
+ {
+ if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return STICKY;
+
+ // Reference is deleted in the middle. Move the last row
+ // position upward.
+ SCROW nDelta = rDeletedRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
+ }
+ else
+ {
+ if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return STICKY;
+
+ // The reference range is truncated on the bottom.
+ SCROW nDelta = rDeletedRange.aStart.Row() - rRefRange.aEnd.Row() - 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
+ }
+ return SHRUNK;
+ }
+
+ return UNMODIFIED;
+}
+
+bool expandRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rSelectedRange,
+ const ScComplexRefData& rRef )
+{
+ if (!rSelectedRange.Intersects(rRefRange))
+ return false;
+
+ if (rCxt.mnColDelta > 0)
+ {
+ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ // Entire rows are not affected, columns are anchored.
+ return false;
+
+ // Insert and shifting right.
+ if (rRefRange.aStart.Row() < rSelectedRange.aStart.Row() || rSelectedRange.aEnd.Row() < rRefRange.aEnd.Row())
+ // Selected range is only partially overlapping in vertical direction. Bail out.
+ return false;
+
+ if (rCxt.mrDoc.IsExpandRefs())
+ {
+ if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1)
+ // Reference must be at least two columns wide.
+ return false;
+ }
+ else
+ {
+ if (rSelectedRange.aStart.Col() <= rRefRange.aStart.Col())
+ // Selected range is at the left end and the edge expansion is turned off. No expansion.
+ return false;
+ }
+
+ if (rRefRange.IsEndColSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return false;
+
+ // Move the last column position to the right.
+ SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
+ return true;
+ }
+ else if (rCxt.mnRowDelta > 0)
+ {
+ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ // Entire columns are not affected, rows are anchored.
+ return false;
+
+ // Insert and shifting down.
+ if (rRefRange.aStart.Col() < rSelectedRange.aStart.Col() || rSelectedRange.aEnd.Col() < rRefRange.aEnd.Col())
+ // Selected range is only partially overlapping in horizontal direction. Bail out.
+ return false;
+
+ if (rCxt.mrDoc.IsExpandRefs())
+ {
+ if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1)
+ // Reference must be at least two rows tall.
+ return false;
+ }
+ else
+ {
+ if (rSelectedRange.aStart.Row() <= rRefRange.aStart.Row())
+ // Selected range is at the top end and the edge expansion is turned off. No expansion.
+ return false;
+ }
+
+ if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return false;
+
+ // Move the last row position down.
+ SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Check if the referenced range is expandable when the selected range is
+ * not overlapping the referenced range.
+ */
+bool expandRangeByEdge( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rSelectedRange,
+ const ScComplexRefData& rRef )
+{
+ if (!rCxt.mrDoc.IsExpandRefs())
+ // Edge-expansion is turned off.
+ return false;
+
+ if (rSelectedRange.aStart.Tab() > rRefRange.aStart.Tab() || rRefRange.aEnd.Tab() > rSelectedRange.aEnd.Tab())
+ // Sheet references not within selected range.
+ return false;
+
+ if (rCxt.mnColDelta > 0)
+ {
+ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ // Entire rows are not affected, columns are anchored.
+ return false;
+
+ // Insert and shift right.
+
+ if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1)
+ // Reference must be at least two columns wide.
+ return false;
+
+ if (rRefRange.aStart.Row() < rSelectedRange.aStart.Row() || rSelectedRange.aEnd.Row() < rRefRange.aEnd.Row())
+ // Selected range is only partially overlapping in vertical direction. Bail out.
+ return false;
+
+ if (rSelectedRange.aStart.Col() - rRefRange.aEnd.Col() != 1)
+ // Selected range is not immediately adjacent. Bail out.
+ return false;
+
+ if (rRefRange.IsEndColSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return false;
+
+ // Move the last column position to the right.
+ SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
+ return true;
+ }
+ else if (rCxt.mnRowDelta > 0)
+ {
+ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ // Entire columns are not affected, rows are anchored.
+ return false;
+
+ if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1)
+ // Reference must be at least two rows tall.
+ return false;
+
+ if (rRefRange.aStart.Col() < rSelectedRange.aStart.Col() || rSelectedRange.aEnd.Col() < rRefRange.aEnd.Col())
+ // Selected range is only partially overlapping in horizontal direction. Bail out.
+ return false;
+
+ if (rSelectedRange.aStart.Row() - rRefRange.aEnd.Row() != 1)
+ // Selected range is not immediately adjacent. Bail out.
+ return false;
+
+ if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return false;
+
+ // Move the last row position down.
+ SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
+ return true;
+ }
+
+ return false;
+}
+
+bool isNameModified( const sc::UpdatedRangeNames& rUpdatedNames, SCTAB nOldTab, const formula::FormulaToken& rToken )
+{
+ SCTAB nTab = -1;
+ if (rToken.GetSheet() >= 0)
+ nTab = nOldTab;
+
+ // Check if this named expression has been modified.
+ return rUpdatedNames.isNameUpdated(nTab, rToken.GetIndex());
+}
+
+bool isDBDataModified( const ScDocument& rDoc, const formula::FormulaToken& rToken )
+{
+ // Check if this DBData has been modified.
+ const ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex( rToken.GetIndex());
+ if (!pDBData)
+ return true;
+
+ return pDBData->IsModified();
+}
+
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnShift( const sc::RefUpdateContext& rCxt, const ScAddress& rOldPos )
+{
+ ScRange aSelectedRange = getSelectedRange(rCxt);
+
+ sc::RefUpdateResult aRes;
+ ScAddress aNewPos = rOldPos;
+ bool bCellShifted = rCxt.maRange.Contains(rOldPos);
+ if (bCellShifted)
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aNewPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
+ {
+ assert(!"can't move");
+ }
+ }
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ if (rCxt.isDeleted() && aSelectedRange.Contains(aAbs))
+ {
+ // This reference is in the deleted region.
+ setRefDeleted(rRef, rCxt);
+ aRes.mbValueChanged = true;
+ break;
+ }
+
+ if (!rCxt.isDeleted() && rRef.IsDeleted())
+ {
+ // Check if the token has reference to previously deleted region.
+ ScAddress aCheckPos = rRef.toAbs(*mxSheetLimits, aNewPos);
+ if (rCxt.maRange.Contains(aCheckPos))
+ {
+ restoreDeletedRef(rRef, rCxt);
+ aRes.mbValueChanged = true;
+ break;
+ }
+ }
+
+ if (rCxt.maRange.Contains(aAbs))
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
+ aAbs = aErrorPos;
+ aRes.mbReferenceModified = true;
+ }
+
+ rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ if (rCxt.isDeleted())
+ {
+ if (aSelectedRange.Contains(aAbs))
+ {
+ // This reference is in the deleted region.
+ setRefDeleted(rRef, rCxt);
+ aRes.mbValueChanged = true;
+ break;
+ }
+ else if (aSelectedRange.Intersects(aAbs))
+ {
+ const ShrinkResult eSR = shrinkRange(rCxt, aAbs, aSelectedRange, rRef);
+ if (eSR == SHRUNK)
+ {
+ // The reference range has been shrunk.
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ aRes.mbValueChanged = true;
+ aRes.mbReferenceModified = true;
+ break;
+ }
+ else if (eSR == STICKY)
+ {
+ // The reference range stays the same but a
+ // new (empty) cell range is shifted in and
+ // may change the calculation result.
+ aRes.mbValueChanged = true;
+ // Sticky when intersecting the selected
+ // range means also that the other
+ // conditions below are not met,
+ // specifically not the
+ // if (rCxt.maRange.Contains(aAbs))
+ // that is able to update the reference,
+ // but aSelectedRange does not intersect
+ // with rCxt.maRange so that can't happen
+ // and we can bail out early without
+ // updating the reference.
+ break;
+ }
+ }
+ }
+
+ if (!rCxt.isDeleted() && rRef.IsDeleted())
+ {
+ // Check if the token has reference to previously deleted region.
+ ScRange aCheckRange = rRef.toAbs(*mxSheetLimits, aNewPos);
+ if (aSelectedRange.Contains(aCheckRange))
+ {
+ // This reference was previously in the deleted region. Restore it.
+ restoreDeletedRef(rRef, rCxt);
+ aRes.mbValueChanged = true;
+ break;
+ }
+ }
+
+ if (rCxt.isInserted())
+ {
+ if (expandRange(rCxt, aAbs, aSelectedRange, rRef))
+ {
+ // The reference range has been expanded.
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ aRes.mbValueChanged = true;
+ aRes.mbReferenceModified = true;
+ break;
+ }
+
+ if (expandRangeByEdge(rCxt, aAbs, aSelectedRange, rRef))
+ {
+ // The reference range has been expanded on the edge.
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ aRes.mbValueChanged = true;
+ aRes.mbReferenceModified = true;
+ break;
+ }
+ }
+
+ if (rCxt.maRange.Contains(aAbs))
+ {
+ // We shift either by column or by row, not both,
+ // so moving the reference has only to be done in
+ // the non-sticky case.
+ if ((rCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ || (rCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())))
+ {
+ // In entire col/row, values are shifted within
+ // the reference, which affects all positional
+ // results like in MATCH or matrix positions.
+ aRes.mbValueChanged = true;
+ }
+ else
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aAbs.MoveSticky(rCxt.mrDoc, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange))
+ aAbs = aErrorRange;
+ aRes.mbReferenceModified = true;
+ }
+ }
+ else if (rCxt.maRange.Intersects(aAbs))
+ {
+ // Part of the referenced range is being shifted. This
+ // will change the values of the range.
+ aRes.mbValueChanged = true;
+ }
+
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ // For external reference, just reset the reference with
+ // respect to the new cell position.
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ // Same as above.
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ if (rCxt.mnTabDelta &&
+ rCxt.maRange.aStart.Tab() <= nOldTab && nOldTab <= rCxt.maRange.aEnd.Tab())
+ {
+ aRes.mbNameModified = true;
+ (*pp)->SetSheet( nOldTab + rCxt.mnTabDelta);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+
+ return aRes;
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMove(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ sc::RefUpdateResult aRes;
+
+ if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
+ // The cell hasn't moved at all.
+ return aRes;
+
+ // When moving, the range in the context is the destination range. We need
+ // to use the old range prior to the move for hit analysis.
+ ScRange aOldRange = rCxt.maRange;
+ ScRange aErrorMoveRange( ScAddress::UNINITIALIZED );
+ if (!aOldRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorMoveRange, rCxt.mrDoc))
+ {
+ assert(!"can't move");
+ }
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ // Additionally, do not update the references from cells within the moved
+ // range as they lead to #REF! errors here. These #REF! cannot by fixed
+ // later in UpdateTranspose().
+ if (rCxt.mbTransposed && (aOldRange.Contains(rOldPos) || aOldRange.Contains(aAbs)))
+ break;
+
+ if (aOldRange.Contains(aAbs))
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
+ aAbs = aErrorPos;
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.maRange.Contains(aAbs))
+ {
+ // Referenced cell has been overwritten.
+ aRes.mbValueChanged = true;
+ }
+
+ rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
+ rRef.SetFlag3D(aAbs.Tab() != rNewPos.Tab() || !rRef.IsTabRel());
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ // Additionally, do not update the references from cells within the moved
+ // range as they lead to #REF! errors here. These #REF! cannot by fixed
+ // later in UpdateTranspose().
+ if (rCxt.mbTransposed && (aOldRange.Contains(rOldPos) || aOldRange.Contains(aAbs)))
+ break;
+
+ if (aOldRange.Contains(aAbs))
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
+ aAbs = aErrorRange;
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.maRange.Contains(aAbs))
+ {
+ // Referenced range has been entirely overwritten.
+ aRes.mbValueChanged = true;
+ }
+
+ rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
+ // Absolute sheet reference => set 3D flag.
+ // More than one sheet referenced => has to have both 3D flags.
+ // If end part has 3D flag => start part must have it too.
+ rRef.Ref2.SetFlag3D(aAbs.aStart.Tab() != aAbs.aEnd.Tab() || !rRef.Ref2.IsTabRel());
+ rRef.Ref1.SetFlag3D(aAbs.aStart.Tab() != rNewPos.Tab() || !rRef.Ref1.IsTabRel() ||
+ rRef.Ref2.IsFlag3D());
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+
+ return aRes;
+}
+
+void ScTokenArray::MoveReferenceColReorder(
+ const ScAddress& rPos, SCTAB nTab, SCROW nRow1, SCROW nRow2, const sc::ColRowReorderMapType& rColMap )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+
+ if (aAbs.Tab() == nTab && nRow1 <= aAbs.Row() && aAbs.Row() <= nRow2)
+ {
+ // Inside reordered row range.
+ sc::ColRowReorderMapType::const_iterator it = rColMap.find(aAbs.Col());
+ if (it != rColMap.end())
+ {
+ // This column is reordered.
+ SCCOL nNewCol = it->second;
+ aAbs.SetCol(nNewCol);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
+ }
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+
+ if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
+ // Must be a single-sheet reference.
+ break;
+
+ if (aAbs.aStart.Col() != aAbs.aEnd.Col())
+ // Whole range must fit in a single column.
+ break;
+
+ if (aAbs.aStart.Tab() == nTab && nRow1 <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= nRow2)
+ {
+ // Inside reordered row range.
+ sc::ColRowReorderMapType::const_iterator it = rColMap.find(aAbs.aStart.Col());
+ if (it != rColMap.end())
+ {
+ // This column is reordered.
+ SCCOL nNewCol = it->second;
+ aAbs.aStart.SetCol(nNewCol);
+ aAbs.aEnd.SetCol(nNewCol);
+ rRef.SetRange(*mxSheetLimits, aAbs, rPos);
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::MoveReferenceRowReorder( const ScAddress& rPos, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, const sc::ColRowReorderMapType& rRowMap )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+
+ if (aAbs.Tab() == nTab && nCol1 <= aAbs.Col() && aAbs.Col() <= nCol2)
+ {
+ // Inside reordered column range.
+ sc::ColRowReorderMapType::const_iterator it = rRowMap.find(aAbs.Row());
+ if (it != rRowMap.end())
+ {
+ // This column is reordered.
+ SCROW nNewRow = it->second;
+ aAbs.SetRow(nNewRow);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
+ }
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+
+ if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
+ // Must be a single-sheet reference.
+ break;
+
+ if (aAbs.aStart.Row() != aAbs.aEnd.Row())
+ // Whole range must fit in a single row.
+ break;
+
+ if (aAbs.aStart.Tab() == nTab && nCol1 <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= nCol2)
+ {
+ // Inside reordered column range.
+ sc::ColRowReorderMapType::const_iterator it = rRowMap.find(aAbs.aStart.Row());
+ if (it != rRowMap.end())
+ {
+ // This row is reordered.
+ SCROW nNewRow = it->second;
+ aAbs.aStart.SetRow(nNewRow);
+ aAbs.aEnd.SetRow(nNewRow);
+ rRef.SetRange(*mxSheetLimits, aAbs, rPos);
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+namespace {
+
+bool adjustSingleRefInName(
+ ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos,
+ ScComplexRefData* pEndOfComplex )
+{
+ ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ if (aAbs.Tab() < rCxt.maRange.aStart.Tab() || rCxt.maRange.aEnd.Tab() < aAbs.Tab())
+ {
+ // This references a sheet that has not shifted. Don't change it.
+ return false;
+ }
+
+ if (!rCxt.maRange.Contains(rRef.toAbs(rCxt.mrDoc, rPos)))
+ return false;
+
+ bool bChanged = false;
+
+ if (rCxt.mnColDelta && !rRef.IsColRel())
+ {
+ // Adjust absolute column reference.
+ if (rCxt.maRange.aStart.Col() <= rRef.Col() && rRef.Col() <= rCxt.maRange.aEnd.Col())
+ {
+ if (pEndOfComplex)
+ {
+ if (pEndOfComplex->IncEndColSticky(rCxt.mrDoc, rCxt.mnColDelta, rPos))
+ bChanged = true;
+ }
+ else
+ {
+ rRef.IncCol(rCxt.mnColDelta);
+ bChanged = true;
+ }
+ }
+ }
+
+ if (rCxt.mnRowDelta && !rRef.IsRowRel())
+ {
+ // Adjust absolute row reference.
+ if (rCxt.maRange.aStart.Row() <= rRef.Row() && rRef.Row() <= rCxt.maRange.aEnd.Row())
+ {
+ if (pEndOfComplex)
+ {
+ if (pEndOfComplex->IncEndRowSticky(rCxt.mrDoc, rCxt.mnRowDelta, rPos))
+ bChanged = true;
+ }
+ else
+ {
+ rRef.IncRow(rCxt.mnRowDelta);
+ bChanged = true;
+ }
+ }
+ }
+
+ if (!rRef.IsTabRel() && rCxt.mnTabDelta)
+ {
+ // Sheet range has already been checked above.
+ rRef.IncTab(rCxt.mnTabDelta);
+ bChanged = true;
+ }
+
+ return bChanged;
+}
+
+bool adjustDoubleRefInName(
+ ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
+{
+ bool bRefChanged = false;
+ if (rCxt.mrDoc.IsExpandRefs())
+ {
+ if (rCxt.mnRowDelta > 0 && !rRef.Ref1.IsRowRel() && !rRef.Ref2.IsRowRel())
+ {
+ ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+ // Expand only if at least two rows tall.
+ if (aAbs.aStart.Row() < aAbs.aEnd.Row())
+ {
+ // Check and see if we should expand the range at the top.
+ ScRange aSelectedRange = getSelectedRange(rCxt);
+ if (aSelectedRange.Intersects(aAbs))
+ {
+ // Selection intersects the referenced range. Only expand the
+ // bottom position.
+ rRef.IncEndRowSticky(rCxt.mrDoc, rCxt.mnRowDelta, rPos);
+ return true;
+ }
+ }
+ }
+ if (rCxt.mnColDelta > 0 && !rRef.Ref1.IsColRel() && !rRef.Ref2.IsColRel())
+ {
+ ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+ // Expand only if at least two columns wide.
+ if (aAbs.aStart.Col() < aAbs.aEnd.Col())
+ {
+ // Check and see if we should expand the range at the left.
+ ScRange aSelectedRange = getSelectedRange(rCxt);
+ if (aSelectedRange.Intersects(aAbs))
+ {
+ // Selection intersects the referenced range. Only expand the
+ // right position.
+ rRef.IncEndColSticky(rCxt.mrDoc, rCxt.mnColDelta, rPos);
+ return true;
+ }
+ }
+ }
+ }
+
+ if ((rCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ || (rCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())))
+ {
+ sc::RefUpdateContext aCxt( rCxt.mrDoc);
+ // We only need a few parameters of RefUpdateContext.
+ aCxt.maRange = rCxt.maRange;
+ aCxt.mnColDelta = rCxt.mnColDelta;
+ aCxt.mnRowDelta = rCxt.mnRowDelta;
+ aCxt.mnTabDelta = rCxt.mnTabDelta;
+
+ // References to entire col/row are not to be adjusted in the other axis.
+ if (aCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ aCxt.mnRowDelta = 0;
+ if (aCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ aCxt.mnColDelta = 0;
+ if (!aCxt.mnColDelta && !aCxt.mnRowDelta && !aCxt.mnTabDelta)
+ // early bailout
+ return bRefChanged;
+
+ // Ref2 before Ref1 for sticky ends.
+ if (adjustSingleRefInName(rRef.Ref2, aCxt, rPos, &rRef))
+ bRefChanged = true;
+
+ if (adjustSingleRefInName(rRef.Ref1, aCxt, rPos, nullptr))
+ bRefChanged = true;
+ }
+ else
+ {
+ // Ref2 before Ref1 for sticky ends.
+ if (adjustSingleRefInName(rRef.Ref2, rCxt, rPos, &rRef))
+ bRefChanged = true;
+
+ if (adjustSingleRefInName(rRef.Ref1, rCxt, rPos, nullptr))
+ bRefChanged = true;
+ }
+
+ return bRefChanged;
+}
+
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceInName(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
+{
+ if (rCxt.meMode == URM_MOVE)
+ return AdjustReferenceInMovedName(rCxt, rPos);
+
+ sc::RefUpdateResult aRes;
+
+ if (rCxt.meMode == URM_COPY)
+ // Copying cells does not modify named expressions.
+ return aRes;
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ if (rCxt.mnRowDelta < 0)
+ {
+ // row(s) deleted.
+
+ if (rRef.IsRowRel())
+ // Don't modify relative references in names.
+ break;
+
+ ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ if (aAbs.Col() < rCxt.maRange.aStart.Col() || rCxt.maRange.aEnd.Col() < aAbs.Col())
+ // column of the reference is not in the deleted column range.
+ break;
+
+ if (aAbs.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.Tab() < rCxt.maRange.aStart.Tab())
+ // wrong tables
+ break;
+
+ const SCROW nDelStartRow = rCxt.maRange.aStart.Row() + rCxt.mnRowDelta;
+ const SCROW nDelEndRow = nDelStartRow - rCxt.mnRowDelta - 1;
+
+ if (nDelStartRow <= aAbs.Row() && aAbs.Row() <= nDelEndRow)
+ {
+ // This reference is deleted.
+ rRef.SetRowDeleted(true);
+ aRes.mbReferenceModified = true;
+ break;
+ }
+ }
+ else if (rCxt.mnColDelta < 0)
+ {
+ // column(s) deleted.
+
+ if (rRef.IsColRel())
+ // Don't modify relative references in names.
+ break;
+
+ ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ if (aAbs.Row() < rCxt.maRange.aStart.Row() || rCxt.maRange.aEnd.Row() < aAbs.Row())
+ // row of the reference is not in the deleted row range.
+ break;
+
+ if (aAbs.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.Tab() < rCxt.maRange.aStart.Tab())
+ // wrong tables
+ break;
+
+ const SCCOL nDelStartCol = rCxt.maRange.aStart.Col() + rCxt.mnColDelta;
+ const SCCOL nDelEndCol = nDelStartCol - rCxt.mnColDelta - 1;
+
+ if (nDelStartCol <= aAbs.Col() && aAbs.Col() <= nDelEndCol)
+ {
+ // This reference is deleted.
+ rRef.SetColDeleted(true);
+ aRes.mbReferenceModified = true;
+ break;
+ }
+ }
+
+ if (adjustSingleRefInName(rRef, rCxt, rPos, nullptr))
+ aRes.mbReferenceModified = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ if (aAbs.aStart.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.aEnd.Tab() < rCxt.maRange.aStart.Tab())
+ // Sheet references not affected.
+ break;
+
+ if (rCxt.maRange.Contains(aAbs))
+ {
+ // This range is entirely within the shifted region.
+ if (adjustDoubleRefInName(rRef, rCxt, rPos))
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.mnRowDelta < 0)
+ {
+ // row(s) deleted.
+
+ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ // Rows of entire columns are not affected.
+ break;
+
+ if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel())
+ // Don't modify relative references in names.
+ break;
+
+ if (aAbs.aStart.Col() < rCxt.maRange.aStart.Col() || rCxt.maRange.aEnd.Col() < aAbs.aEnd.Col())
+ // column range of the reference is not entirely in the deleted column range.
+ break;
+
+ ScRange aDeleted = rCxt.maRange;
+ aDeleted.aStart.IncRow(rCxt.mnRowDelta);
+ aDeleted.aEnd.SetRow(aDeleted.aStart.Row()-rCxt.mnRowDelta-1);
+
+ if (aAbs.aEnd.Row() < aDeleted.aStart.Row() || aDeleted.aEnd.Row() < aAbs.aStart.Row())
+ // reference range doesn't intersect with the deleted range.
+ break;
+
+ if (aDeleted.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= aDeleted.aEnd.Row())
+ {
+ // This reference is entirely deleted.
+ rRef.Ref1.SetRowDeleted(true);
+ rRef.Ref2.SetRowDeleted(true);
+ aRes.mbReferenceModified = true;
+ break;
+ }
+
+ if (aAbs.aStart.Row() < aDeleted.aStart.Row())
+ {
+ if (!aAbs.IsEndRowSticky(rCxt.mrDoc))
+ {
+ if (aDeleted.aEnd.Row() < aAbs.aEnd.Row())
+ // Deleted in the middle. Make the reference shorter.
+ rRef.Ref2.IncRow(rCxt.mnRowDelta);
+ else
+ // Deleted at tail end. Cut off the lower part.
+ rRef.Ref2.SetAbsRow(aDeleted.aStart.Row()-1);
+ }
+ }
+ else
+ {
+ // Deleted at the top. Cut the top off and shift up.
+ rRef.Ref1.SetAbsRow(aDeleted.aEnd.Row()+1);
+ rRef.Ref1.IncRow(rCxt.mnRowDelta);
+ if (!aAbs.IsEndRowSticky(rCxt.mrDoc))
+ rRef.Ref2.IncRow(rCxt.mnRowDelta);
+ }
+
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.mnColDelta < 0)
+ {
+ // column(s) deleted.
+
+ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ // Rows of entire rows are not affected.
+ break;
+
+ if (rRef.Ref1.IsColRel() || rRef.Ref2.IsColRel())
+ // Don't modify relative references in names.
+ break;
+
+ if (aAbs.aStart.Row() < rCxt.maRange.aStart.Row() || rCxt.maRange.aEnd.Row() < aAbs.aEnd.Row())
+ // row range of the reference is not entirely in the deleted row range.
+ break;
+
+ ScRange aDeleted = rCxt.maRange;
+ aDeleted.aStart.IncCol(rCxt.mnColDelta);
+ aDeleted.aEnd.SetCol(aDeleted.aStart.Col()-rCxt.mnColDelta-1);
+
+ if (aAbs.aEnd.Col() < aDeleted.aStart.Col() || aDeleted.aEnd.Col() < aAbs.aStart.Col())
+ // reference range doesn't intersect with the deleted range.
+ break;
+
+ if (aDeleted.aStart.Col() <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= aDeleted.aEnd.Col())
+ {
+ // This reference is entirely deleted.
+ rRef.Ref1.SetColDeleted(true);
+ rRef.Ref2.SetColDeleted(true);
+ aRes.mbReferenceModified = true;
+ break;
+ }
+
+ if (aAbs.aStart.Col() < aDeleted.aStart.Col())
+ {
+ if (!aAbs.IsEndColSticky(rCxt.mrDoc))
+ {
+ if (aDeleted.aEnd.Col() < aAbs.aEnd.Col())
+ // Deleted in the middle. Make the reference shorter.
+ rRef.Ref2.IncCol(rCxt.mnColDelta);
+ else
+ // Deleted at tail end. Cut off the right part.
+ rRef.Ref2.SetAbsCol(aDeleted.aStart.Col()-1);
+ }
+ }
+ else
+ {
+ // Deleted at the left. Cut the left off and shift left.
+ rRef.Ref1.SetAbsCol(aDeleted.aEnd.Col()+1);
+ rRef.Ref1.IncCol(rCxt.mnColDelta);
+ if (!aAbs.IsEndColSticky(rCxt.mrDoc))
+ rRef.Ref2.IncCol(rCxt.mnColDelta);
+ }
+
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.maRange.Intersects(aAbs))
+ {
+ if (rCxt.mnColDelta && rCxt.maRange.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= rCxt.maRange.aEnd.Row())
+ {
+ if (adjustDoubleRefInName(rRef, rCxt, rPos))
+ aRes.mbReferenceModified = true;
+ }
+ if (rCxt.mnRowDelta && rCxt.maRange.aStart.Col() <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= rCxt.maRange.aEnd.Col())
+ {
+ if (adjustDoubleRefInName(rRef, rCxt, rPos))
+ aRes.mbReferenceModified = true;
+ }
+ }
+ else if (rCxt.mnRowDelta > 0 && rCxt.mrDoc.IsExpandRefs())
+ {
+ // Check if we could expand range reference by the bottom
+ // edge. For named expressions, we only expand absolute
+ // references. Reference must be at least two rows
+ // tall.
+ if (!rRef.Ref1.IsRowRel() && !rRef.Ref2.IsRowRel() &&
+ aAbs.aStart.Row() < aAbs.aEnd.Row() &&
+ aAbs.aEnd.Row()+1 == rCxt.maRange.aStart.Row())
+ {
+ // Expand by the bottom edge.
+ rRef.Ref2.IncRow(rCxt.mnRowDelta);
+ aRes.mbReferenceModified = true;
+ }
+ }
+ else if (rCxt.mnColDelta > 0 && rCxt.mrDoc.IsExpandRefs())
+ {
+ // Check if we could expand range reference by the right
+ // edge. For named expressions, we only expand absolute
+ // references. Reference must be at least two
+ // columns wide.
+ if (!rRef.Ref1.IsColRel() && !rRef.Ref2.IsColRel() &&
+ aAbs.aStart.Col() < aAbs.aEnd.Col() &&
+ aAbs.aEnd.Col()+1 == rCxt.maRange.aStart.Col())
+ {
+ // Expand by the right edge.
+ rRef.Ref2.IncCol(rCxt.mnColDelta);
+ aRes.mbReferenceModified = true;
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ return aRes;
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceInMovedName( const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
+{
+ // When moving, the range is the destination range.
+ ScRange aOldRange = rCxt.maRange;
+ ScRange aErrorMoveRange( ScAddress::UNINITIALIZED );
+ if (!aOldRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorMoveRange, rCxt.mrDoc))
+ {
+ assert(!"can't move");
+ }
+
+ // In a named expression, we'll move the reference only when the reference
+ // is entirely absolute.
+
+ sc::RefUpdateResult aRes;
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ if (rRef.IsColRel() || rRef.IsRowRel() || rRef.IsTabRel())
+ continue;
+
+ ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ if (rCxt.mbTransposed && aOldRange.Contains(aAbs))
+ break;
+
+ if (aOldRange.Contains(aAbs))
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
+ aAbs = aErrorPos;
+ aRes.mbReferenceModified = true;
+ }
+
+ rRef.SetAddress(rCxt.mrDoc.GetSheetLimits(), aAbs, rPos);
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ if (rRef.Ref1.IsColRel() || rRef.Ref1.IsRowRel() || rRef.Ref1.IsTabRel() ||
+ rRef.Ref2.IsColRel() || rRef.Ref2.IsRowRel() || rRef.Ref2.IsTabRel())
+ continue;
+
+ ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ if (rCxt.mbTransposed && aOldRange.Contains(aAbs))
+ break;
+
+ if (aOldRange.Contains(aAbs))
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
+ aAbs = aErrorRange;
+ aRes.mbReferenceModified = true;
+ }
+
+ rRef.SetRange(rCxt.mrDoc.GetSheetLimits(), aAbs, rPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ return aRes;
+}
+
+namespace {
+
+bool adjustSingleRefOnDeletedTab( const ScSheetLimits& rLimits, ScSingleRefData& rRef, SCTAB nDelPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ ScAddress aAbs = rRef.toAbs(rLimits, rOldPos);
+ if (nDelPos <= aAbs.Tab() && aAbs.Tab() < nDelPos + nSheets)
+ {
+ rRef.SetTabDeleted(true);
+ return true;
+ }
+
+ if (nDelPos < aAbs.Tab())
+ {
+ // Reference sheet needs to be adjusted.
+ aAbs.IncTab(-1*nSheets);
+ rRef.SetAddress(rLimits, aAbs, rNewPos);
+ return true;
+ }
+ else if (rOldPos.Tab() != rNewPos.Tab())
+ {
+ // Cell itself has moved.
+ rRef.SetAddress(rLimits, aAbs, rNewPos);
+ return true;
+ }
+
+ return false;
+}
+
+bool adjustSingleRefOnInsertedTab( const ScSheetLimits& rLimits, ScSingleRefData& rRef, SCTAB nInsPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ ScAddress aAbs = rRef.toAbs(rLimits, rOldPos);
+ if (nInsPos <= aAbs.Tab())
+ {
+ // Reference sheet needs to be adjusted.
+ aAbs.IncTab(nSheets);
+ rRef.SetAddress(rLimits, aAbs, rNewPos);
+ return true;
+ }
+ else if (rOldPos.Tab() != rNewPos.Tab())
+ {
+ // Cell itself has moved.
+ rRef.SetAddress(rLimits, aAbs, rNewPos);
+ return true;
+ }
+
+ return false;
+}
+
+bool adjustDoubleRefOnDeleteTab(const ScSheetLimits& rLimits, ScComplexRefData& rRef, SCTAB nDelPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos)
+{
+ ScSingleRefData& rRef1 = rRef.Ref1;
+ ScSingleRefData& rRef2 = rRef.Ref2;
+ ScAddress aStartPos = rRef1.toAbs(rLimits, rOldPos);
+ ScAddress aEndPos = rRef2.toAbs(rLimits, rOldPos);
+ bool bMoreThanOneTab = aStartPos.Tab() != aEndPos.Tab();
+ bool bModified = false;
+ if (bMoreThanOneTab && aStartPos.Tab() == nDelPos && nDelPos + nSheets <= aEndPos.Tab())
+ {
+ if (rRef1.IsTabRel() && aStartPos.Tab() < rOldPos.Tab())
+ {
+ rRef1.IncTab(nSheets);
+ bModified = true;
+ }
+ }
+ else
+ {
+ bModified = adjustSingleRefOnDeletedTab(rLimits, rRef1, nDelPos, nSheets, rOldPos, rNewPos);
+ }
+
+ if (bMoreThanOneTab && aEndPos.Tab() == nDelPos && aStartPos.Tab() <= nDelPos - nSheets)
+ {
+ if (!rRef2.IsTabRel() || rOldPos.Tab() < aEndPos.Tab())
+ {
+ rRef2.IncTab(-nSheets);
+ bModified = true;
+ }
+ }
+ else
+ {
+ bModified |= adjustSingleRefOnDeletedTab(rLimits, rRef2, nDelPos, nSheets, rOldPos, rNewPos);
+ }
+ return bModified;
+}
+
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnDeletedTab( const sc::RefUpdateDeleteTabContext& rCxt, const ScAddress& rOldPos )
+{
+ sc::RefUpdateResult aRes;
+ ScAddress aNewPos = rOldPos;
+ ScRangeUpdater::UpdateDeleteTab( aNewPos, rCxt);
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ if (adjustSingleRefOnDeletedTab(*mxSheetLimits, rRef, rCxt.mnDeletePos, rCxt.mnSheets, rOldPos, aNewPos))
+ aRes.mbReferenceModified = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ aRes.mbReferenceModified |= adjustDoubleRefOnDeleteTab(*mxSheetLimits, rRef, rCxt.mnDeletePos, rCxt.mnSheets, rOldPos, aNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ if (rCxt.mnDeletePos <= nOldTab)
+ {
+ aRes.mbNameModified = true;
+ if (rCxt.mnDeletePos + rCxt.mnSheets <= nOldTab)
+ (*pp)->SetSheet( nOldTab - rCxt.mnSheets);
+ else
+ // Would point to a deleted sheet. Invalidate.
+ (*pp)->SetSheet( SCTAB_MAX);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+ return aRes;
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnInsertedTab( const sc::RefUpdateInsertTabContext& rCxt, const ScAddress& rOldPos )
+{
+ sc::RefUpdateResult aRes;
+ ScAddress aNewPos = rOldPos;
+ if (rCxt.mnInsertPos <= rOldPos.Tab())
+ aNewPos.IncTab(rCxt.mnSheets);
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
+ aRes.mbReferenceModified = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef.Ref1, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
+ aRes.mbReferenceModified = true;
+ if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef.Ref2, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
+ aRes.mbReferenceModified = true;
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ if (rCxt.mnInsertPos <= nOldTab)
+ {
+ aRes.mbNameModified = true;
+ (*pp)->SetSheet( nOldTab + rCxt.mnSheets);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+ return aRes;
+}
+
+namespace {
+
+bool adjustTabOnMove( ScAddress& rPos, const sc::RefUpdateMoveTabContext& rCxt )
+{
+ SCTAB nNewTab = rCxt.getNewTab(rPos.Tab());
+ if (nNewTab == rPos.Tab())
+ return false;
+
+ rPos.SetTab(nNewTab);
+ return true;
+}
+
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMovedTab( const sc::RefUpdateMoveTabContext& rCxt, const ScAddress& rOldPos )
+{
+ sc::RefUpdateResult aRes;
+ if (rCxt.mnOldPos == rCxt.mnNewPos)
+ return aRes;
+
+ ScAddress aNewPos = rOldPos;
+ if (adjustTabOnMove(aNewPos, rCxt))
+ aRes.mbReferenceModified = true;
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ if (adjustTabOnMove(aAbs, rCxt))
+ aRes.mbReferenceModified = true;
+ rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ if (adjustTabOnMove(aAbs.aStart, rCxt))
+ aRes.mbReferenceModified = true;
+ if (adjustTabOnMove(aAbs.aEnd, rCxt))
+ aRes.mbReferenceModified = true;
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ SCTAB nNewTab = rCxt.getNewTab( nOldTab);
+ if (nNewTab != nOldTab)
+ {
+ aRes.mbNameModified = true;
+ (*pp)->SetSheet( nNewTab);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+
+ return aRes;
+}
+
+void ScTokenArray::AdjustReferenceOnMovedOrigin( const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ case svExternalSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ case svDoubleRef:
+ case svExternalDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::AdjustReferenceOnMovedOriginIfOtherSheet( const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ bool bAdjust = false;
+ switch (p->GetType())
+ {
+ case svExternalSingleRef:
+ bAdjust = true; // always
+ [[fallthrough]];
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ if (!bAdjust)
+ bAdjust = (aAbs.Tab() != rOldPos.Tab());
+ if (bAdjust)
+ rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ case svExternalDoubleRef:
+ bAdjust = true; // always
+ [[fallthrough]];
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ if (!bAdjust)
+ bAdjust = (rOldPos.Tab() < aAbs.aStart.Tab() || aAbs.aEnd.Tab() < rOldPos.Tab());
+ if (bAdjust)
+ rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::AdjustReferenceOnCopy( const ScAddress& rNewPos )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ rRef.PutInOrder( rNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+namespace {
+
+void clearTabDeletedFlag( const ScSheetLimits& rLimits, ScSingleRefData& rRef, const ScAddress& rPos, SCTAB nStartTab, SCTAB nEndTab )
+{
+ if (!rRef.IsTabDeleted())
+ return;
+
+ ScAddress aAbs = rRef.toAbs(rLimits, rPos);
+ if (nStartTab <= aAbs.Tab() && aAbs.Tab() <= nEndTab)
+ rRef.SetTabDeleted(false);
+}
+
+}
+
+void ScTokenArray::ClearTabDeleted( const ScAddress& rPos, SCTAB nStartTab, SCTAB nEndTab )
+{
+ if (nEndTab < nStartTab)
+ return;
+
+ FormulaToken** p = pCode.get();
+ FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+ for (; p != pEnd; ++p)
+ {
+ switch ((*p)->GetType())
+ {
+ case svSingleRef:
+ {
+ formula::FormulaToken* pToken = *p;
+ ScSingleRefData& rRef = *pToken->GetSingleRef();
+ clearTabDeletedFlag(*mxSheetLimits, rRef, rPos, nStartTab, nEndTab);
+ }
+ break;
+ case svDoubleRef:
+ {
+ formula::FormulaToken* pToken = *p;
+ ScComplexRefData& rRef = *pToken->GetDoubleRef();
+ clearTabDeletedFlag(*mxSheetLimits, rRef.Ref1, rPos, nStartTab, nEndTab);
+ clearTabDeletedFlag(*mxSheetLimits, rRef.Ref2, rPos, nStartTab, nEndTab);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+namespace {
+
+void checkBounds(
+ const ScSheetLimits& rLimits,
+ const ScAddress& rPos, SCROW nGroupLen, const ScRange& rCheckRange,
+ const ScSingleRefData& rRef, std::vector<SCROW>& rBounds, const ScRange* pDeletedRange )
+{
+ if (!rRef.IsRowRel())
+ return;
+
+ ScRange aAbs(rRef.toAbs(rLimits, rPos));
+ aAbs.aEnd.IncRow(nGroupLen-1);
+ if (!rCheckRange.Intersects(aAbs) && (!pDeletedRange || !pDeletedRange->Intersects(aAbs)))
+ return;
+
+ // Get the boundary row positions.
+ if (aAbs.aEnd.Row() < rCheckRange.aStart.Row() && (!pDeletedRange || aAbs.aEnd.Row() < pDeletedRange->aStart.Row()))
+ // No intersections.
+ return;
+
+ // rCheckRange may be a virtual non-existent row being shifted in.
+ if (aAbs.aStart.Row() <= rCheckRange.aStart.Row() && rCheckRange.aStart.Row() < rLimits.GetMaxRowCount())
+ {
+ // +-+ <---- top
+ // | |
+ // +--+-+--+ <---- boundary row position
+ // | | | |
+ // | |
+ // +-------+
+
+ // Add offset from the reference top to the cell position.
+ SCROW nOffset = rCheckRange.aStart.Row() - aAbs.aStart.Row();
+ rBounds.push_back(rPos.Row()+nOffset);
+ }
+ // Same for deleted range.
+ if (pDeletedRange && aAbs.aStart.Row() <= pDeletedRange->aStart.Row())
+ {
+ SCROW nOffset = pDeletedRange->aStart.Row() - aAbs.aStart.Row();
+ SCROW nRow = rPos.Row() + nOffset;
+ // Unlike for rCheckRange, for pDeletedRange nRow can be anywhere>=0.
+ if (rLimits.ValidRow(nRow))
+ rBounds.push_back(nRow);
+ }
+
+ if (aAbs.aEnd.Row() >= rCheckRange.aEnd.Row())
+ {
+ // only check for end range
+
+ // +-------+
+ // | |
+ // | | | |
+ // +--+-+--+ <---- boundary row position
+ // | |
+ // +-+
+
+ // Ditto.
+ SCROW nOffset = rCheckRange.aEnd.Row() + 1 - aAbs.aStart.Row();
+ rBounds.push_back(rPos.Row()+nOffset);
+ }
+ // Same for deleted range.
+ if (pDeletedRange && aAbs.aEnd.Row() >= pDeletedRange->aEnd.Row())
+ {
+ SCROW nOffset = pDeletedRange->aEnd.Row() + 1 - aAbs.aStart.Row();
+ SCROW nRow = rPos.Row() + nOffset;
+ // Unlike for rCheckRange, for pDeletedRange nRow can be ~anywhere.
+ if (rLimits.ValidRow(nRow))
+ rBounds.push_back(nRow);
+ }
+}
+
+void checkBounds(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen,
+ const ScSingleRefData& rRef, std::vector<SCROW>& rBounds)
+{
+ if (!rRef.IsRowRel())
+ return;
+
+ ScRange aDeletedRange( ScAddress::UNINITIALIZED );
+ const ScRange* pDeletedRange = nullptr;
+
+ ScRange aCheckRange = rCxt.maRange;
+ if (rCxt.meMode == URM_MOVE)
+ {
+ // Check bounds against the old range prior to the move.
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aCheckRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
+ {
+ assert(!"can't move");
+ }
+
+ // Check bounds also against the range moved into.
+ pDeletedRange = &rCxt.maRange;
+ }
+ else if (rCxt.meMode == URM_INSDEL &&
+ ((rCxt.mnColDelta < 0 && rCxt.maRange.aStart.Col() > 0) ||
+ (rCxt.mnRowDelta < 0 && rCxt.maRange.aStart.Row() > 0)))
+ {
+ // Check bounds also against deleted range where cells are shifted
+ // into and references need to be invalidated.
+ aDeletedRange = getSelectedRange( rCxt);
+ pDeletedRange = &aDeletedRange;
+ }
+
+ checkBounds(rCxt.mrDoc.GetSheetLimits(), rPos, nGroupLen, aCheckRange, rRef, rBounds, pDeletedRange);
+}
+
+}
+
+void ScTokenArray::CheckRelativeReferenceBounds(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen, std::vector<SCROW>& rBounds ) const
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ checkBounds(rCxt, rPos, nGroupLen, *p->GetSingleRef(), rBounds);
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ checkBounds(rCxt, rPos, nGroupLen, rRef.Ref1, rBounds);
+ checkBounds(rCxt, rPos, nGroupLen, rRef.Ref2, rBounds);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::CheckRelativeReferenceBounds(
+ const ScAddress& rPos, SCROW nGroupLen, const ScRange& rRange, std::vector<SCROW>& rBounds ) const
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rRef = *p->GetSingleRef();
+ checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef, rBounds, nullptr);
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef.Ref1, rBounds, nullptr);
+ checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef.Ref2, rBounds, nullptr);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::CheckExpandReferenceBounds(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen, std::vector<SCROW>& rBounds ) const
+{
+ const SCROW nInsRow = rCxt.maRange.aStart.Row();
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken* const * pp = aPtrs.maPointerRange[j].mpStart;
+ const FormulaToken* const * pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ const FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ bool bStartRowRelative = rRef.Ref1.IsRowRel();
+ bool bEndRowRelative = rRef.Ref2.IsRowRel();
+
+ // For absolute references nothing needs to be done, they stay
+ // the same for all and if to be expanded the group will be
+ // adjusted later.
+ if (!bStartRowRelative && !bEndRowRelative)
+ break; // switch
+
+ ScRange aAbsStart(rRef.toAbs(*mxSheetLimits, rPos));
+ ScAddress aPos(rPos);
+ aPos.IncRow(nGroupLen);
+ ScRange aAbsEnd(rRef.toAbs(*mxSheetLimits, aPos));
+ // References must be at least two rows to be expandable.
+ if ((aAbsStart.aEnd.Row() - aAbsStart.aStart.Row() < 1) &&
+ (aAbsEnd.aEnd.Row() - aAbsEnd.aStart.Row() < 1))
+ break; // switch
+
+ // Only need to process if an edge may be touching the
+ // insertion row anywhere within the run of the group.
+ if (!((aAbsStart.aStart.Row() <= nInsRow && nInsRow <= aAbsEnd.aStart.Row()) ||
+ (aAbsStart.aEnd.Row() <= nInsRow && nInsRow <= aAbsEnd.aEnd.Row())))
+ break; // switch
+
+ SCROW nStartRow = aAbsStart.aStart.Row();
+ SCROW nEndRow = aAbsStart.aEnd.Row();
+ // Position on first relevant range.
+ SCROW nOffset = 0;
+ if (nEndRow + 1 < nInsRow)
+ {
+ if (bEndRowRelative)
+ {
+ nOffset = nInsRow - nEndRow - 1;
+ nEndRow += nOffset;
+ if (bStartRowRelative)
+ nStartRow += nOffset;
+ }
+ else // bStartRowRelative==true
+ {
+ nOffset = nInsRow - nStartRow;
+ nStartRow += nOffset;
+ // Start is overtaking End, swap.
+ bStartRowRelative = false;
+ bEndRowRelative = true;
+ }
+ }
+ for (SCROW i = nOffset; i < nGroupLen; ++i)
+ {
+ bool bSplit = (nStartRow == nInsRow || nEndRow + 1 == nInsRow);
+ if (bSplit)
+ rBounds.push_back( rPos.Row() + i);
+
+ if (bEndRowRelative)
+ ++nEndRow;
+ if (bStartRowRelative)
+ {
+ ++nStartRow;
+ if (!bEndRowRelative && nStartRow == nEndRow)
+ {
+ // Start is overtaking End, swap.
+ bStartRowRelative = false;
+ bEndRowRelative = true;
+ }
+ }
+ if (nInsRow < nStartRow || (!bStartRowRelative && nInsRow <= nEndRow))
+ {
+ if (bSplit && (++i < nGroupLen))
+ rBounds.push_back( rPos.Row() + i);
+ break; // for, out of range now
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+namespace {
+
+void appendDouble( const sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, double fVal )
+{
+ if (rCxt.mxOpCodeMap->isEnglish())
+ {
+ rtl::math::doubleToUStringBuffer(
+ rBuf, fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
+ }
+ else
+ {
+ SvtSysLocale aSysLocale;
+ rtl::math::doubleToUStringBuffer(
+ rBuf, fVal,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ aSysLocale.GetLocaleData().getNumDecimalSep()[0], true);
+ }
+}
+
+void appendString( OUStringBuffer& rBuf, const OUString& rStr )
+{
+ rBuf.append('"');
+ rBuf.append(rStr.replaceAll("\"", "\"\""));
+ rBuf.append('"');
+}
+
+void appendTokenByType( ScSheetLimits& rLimits, sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, const FormulaToken& rToken,
+ const ScAddress& rPos, bool bFromRangeName )
+{
+ if (rToken.IsExternalRef())
+ {
+ size_t nFileId = rToken.GetIndex();
+ OUString aTabName = rToken.GetString().getString();
+ if (nFileId >= rCxt.maExternalFileNames.size())
+ // out of bound
+ return;
+
+ OUString aFileName = rCxt.maExternalFileNames[nFileId];
+
+ switch (rToken.GetType())
+ {
+ case svExternalName:
+ rBuf.append(rCxt.mpRefConv->makeExternalNameStr(nFileId, aFileName, aTabName));
+ break;
+ case svExternalSingleRef:
+ rCxt.mpRefConv->makeExternalRefStr(
+ rLimits, rBuf, rPos, nFileId, aFileName, aTabName, *rToken.GetSingleRef());
+ break;
+ case svExternalDoubleRef:
+ {
+ sc::TokenStringContext::IndexNamesMapType::const_iterator it =
+ rCxt.maExternalCachedTabNames.find(nFileId);
+
+ if (it == rCxt.maExternalCachedTabNames.end())
+ return;
+
+ rCxt.mpRefConv->makeExternalRefStr(
+ rLimits, rBuf, rPos, nFileId, aFileName, it->second, aTabName,
+ *rToken.GetDoubleRef());
+ }
+ break;
+ default:
+ // warning, not error, otherwise we may end up with a never
+ // ending message box loop if this was the cursor cell to be redrawn.
+ OSL_FAIL("appendTokenByType: unknown type of ocExternalRef");
+ }
+ return;
+ }
+
+ OpCode eOp = rToken.GetOpCode();
+ switch (rToken.GetType())
+ {
+ case svDouble:
+ appendDouble(rCxt, rBuf, rToken.GetDouble());
+ break;
+ case svString:
+ {
+ OUString aStr = rToken.GetString().getString();
+ if (eOp == ocBad || eOp == ocStringXML)
+ {
+ rBuf.append(aStr);
+ return;
+ }
+
+ appendString(rBuf, aStr);
+ }
+ break;
+ case svSingleRef:
+ {
+ if (rCxt.mpRefConv)
+ {
+ const ScSingleRefData& rRef = *rToken.GetSingleRef();
+ ScComplexRefData aRef;
+ aRef.Ref1 = rRef;
+ aRef.Ref2 = rRef;
+ rCxt.mpRefConv->makeRefStr(rLimits, rBuf, rCxt.meGram, rPos, rCxt.maErrRef, rCxt.maTabNames, aRef, true,
+ bFromRangeName);
+ }
+ else
+ rBuf.append(rCxt.maErrRef);
+ }
+ break;
+ case svDoubleRef:
+ {
+ if (rCxt.mpRefConv)
+ {
+ const ScComplexRefData& rRef = *rToken.GetDoubleRef();
+ rCxt.mpRefConv->makeRefStr(rLimits, rBuf, rCxt.meGram, rPos, rCxt.maErrRef, rCxt.maTabNames, rRef, false,
+ bFromRangeName);
+ }
+ else
+ rBuf.append(rCxt.maErrRef);
+ }
+ break;
+ case svMatrix:
+ {
+ const ScMatrix* pMat = rToken.GetMatrix();
+ if (!pMat)
+ return;
+
+ size_t nC, nMaxC, nR, nMaxR;
+ pMat->GetDimensions(nMaxC, nMaxR);
+
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayOpen));
+ for (nR = 0 ; nR < nMaxR ; ++nR)
+ {
+ if (nR > 0)
+ {
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayRowSep));
+ }
+
+ for (nC = 0 ; nC < nMaxC ; ++nC)
+ {
+ if (nC > 0)
+ {
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayColSep));
+ }
+
+ if (pMat->IsValue(nC, nR))
+ {
+ if (pMat->IsBoolean(nC, nR))
+ {
+ bool bVal = pMat->GetDouble(nC, nR) != 0.0;
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(bVal ? ocTrue : ocFalse));
+ }
+ else
+ {
+ FormulaError nErr = pMat->GetError(nC, nR);
+ if (nErr != FormulaError::NONE)
+ rBuf.append(ScGlobal::GetErrorString(nErr));
+ else
+ appendDouble(rCxt, rBuf, pMat->GetDouble(nC, nR));
+ }
+ }
+ else if (pMat->IsEmpty(nC, nR))
+ {
+ // Skip it.
+ }
+ else if (pMat->IsStringOrEmpty(nC, nR))
+ appendString(rBuf, pMat->GetString(nC, nR).getString());
+ }
+ }
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayClose));
+ }
+ break;
+ case svIndex:
+ {
+ typedef sc::TokenStringContext::IndexNameMapType NameType;
+
+ sal_uInt16 nIndex = rToken.GetIndex();
+ switch (eOp)
+ {
+ case ocName:
+ {
+ SCTAB nTab = rToken.GetSheet();
+ if (nTab < 0)
+ {
+ // global named range
+ NameType::const_iterator it = rCxt.maGlobalRangeNames.find(nIndex);
+ if (it == rCxt.maGlobalRangeNames.end())
+ {
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ break;
+ }
+
+ rBuf.append(it->second);
+ }
+ else
+ {
+ // sheet-local named range
+ if (nTab != rPos.Tab())
+ {
+ // On other sheet.
+ OUString aName;
+ if (o3tl::make_unsigned(nTab) < rCxt.maTabNames.size())
+ aName = rCxt.maTabNames[nTab];
+ if (!aName.isEmpty())
+ {
+ ScCompiler::CheckTabQuotes( aName, rCxt.mpRefConv->meConv);
+ rBuf.append( aName);
+ }
+ else
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ rBuf.append( rCxt.mpRefConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR));
+ }
+
+ sc::TokenStringContext::TabIndexMapType::const_iterator itTab = rCxt.maSheetRangeNames.find(nTab);
+ if (itTab == rCxt.maSheetRangeNames.end())
+ {
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ break;
+ }
+
+ const NameType& rNames = itTab->second;
+ NameType::const_iterator it = rNames.find(nIndex);
+ if (it == rNames.end())
+ {
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ break;
+ }
+
+ rBuf.append(it->second);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ {
+ NameType::const_iterator it = rCxt.maNamedDBs.find(nIndex);
+ if (it != rCxt.maNamedDBs.end())
+ rBuf.append(it->second);
+ }
+ break;
+ default:
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ }
+ }
+ break;
+ case svExternal:
+ {
+ // mapped or translated name of AddIns
+ OUString aAddIn = rToken.GetExternal();
+ bool bMapped = rCxt.mxOpCodeMap->isPODF(); // ODF 1.1 directly uses programmatical name
+ if (!bMapped && rCxt.mxOpCodeMap->hasExternals())
+ {
+ const ExternalHashMap& rExtMap = rCxt.mxOpCodeMap->getReverseExternalHashMap();
+ ExternalHashMap::const_iterator it = rExtMap.find(aAddIn);
+ if (it != rExtMap.end())
+ {
+ aAddIn = it->second;
+ bMapped = true;
+ }
+ }
+
+ if (!bMapped && !rCxt.mxOpCodeMap->isEnglish())
+ ScGlobal::GetAddInCollection()->LocalizeString(aAddIn);
+
+ rBuf.append(aAddIn);
+ }
+ break;
+ case svError:
+ {
+ FormulaError nErr = rToken.GetError();
+ OpCode eOpErr;
+ switch (nErr)
+ {
+ break;
+ case FormulaError::DivisionByZero:
+ eOpErr = ocErrDivZero;
+ break;
+ case FormulaError::NoValue:
+ eOpErr = ocErrValue;
+ break;
+ case FormulaError::NoRef:
+ eOpErr = ocErrRef;
+ break;
+ case FormulaError::NoName:
+ eOpErr = ocErrName;
+ break;
+ case FormulaError::IllegalFPOperation:
+ eOpErr = ocErrNum;
+ break;
+ case FormulaError::NotAvailable:
+ eOpErr = ocErrNA;
+ break;
+ case FormulaError::NoCode:
+ default:
+ eOpErr = ocErrNull;
+ }
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(eOpErr));
+ }
+ break;
+ case svByte:
+ case svJump:
+ case svFAP:
+ case svMissing:
+ case svSep:
+ default:
+ ;
+ }
+}
+
+}
+
+OUString ScTokenArray::CreateString( sc::TokenStringContext& rCxt, const ScAddress& rPos ) const
+{
+ if (!nLen)
+ return OUString();
+
+ OUStringBuffer aBuf;
+
+ FormulaToken** p = pCode.get();
+ FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+ for (; p != pEnd; ++p)
+ {
+ const FormulaToken* pToken = *p;
+ OpCode eOp = pToken->GetOpCode();
+ /* FIXME: why does this ignore the count of spaces? */
+ if (eOp == ocSpaces)
+ {
+ // TODO : Handle intersection operator '!!'.
+ aBuf.append(' ');
+ continue;
+ }
+ else if (eOp == ocWhitespace)
+ {
+ aBuf.append( pToken->GetChar());
+ continue;
+ }
+
+ if (eOp < rCxt.mxOpCodeMap->getSymbolCount())
+ aBuf.append(rCxt.mxOpCodeMap->getSymbol(eOp));
+
+ appendTokenByType(*mxSheetLimits, rCxt, aBuf, *pToken, rPos, IsFromRangeName());
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+namespace {
+
+void wrapAddress( ScAddress& rPos, SCCOL nMaxCol, SCROW nMaxRow )
+{
+ if (rPos.Col() > nMaxCol)
+ rPos.SetCol(rPos.Col() % (nMaxCol+1));
+ if (rPos.Row() > nMaxRow)
+ rPos.SetRow(rPos.Row() % (nMaxRow+1));
+}
+
+template<typename T> void wrapRange( T& n1, T& n2, T nMax )
+{
+ if (n2 > nMax)
+ {
+ if (n1 == 0)
+ n2 = nMax; // Truncate to full range instead of wrapping to a weird range.
+ else
+ n2 = n2 % (nMax+1);
+ }
+ if (n1 > nMax)
+ n1 = n1 % (nMax+1);
+}
+
+void wrapColRange( ScRange& rRange, SCCOL nMaxCol )
+{
+ SCCOL nCol1 = rRange.aStart.Col();
+ SCCOL nCol2 = rRange.aEnd.Col();
+ wrapRange( nCol1, nCol2, nMaxCol);
+ rRange.aStart.SetCol( nCol1);
+ rRange.aEnd.SetCol( nCol2);
+}
+
+void wrapRowRange( ScRange& rRange, SCROW nMaxRow )
+{
+ SCROW nRow1 = rRange.aStart.Row();
+ SCROW nRow2 = rRange.aEnd.Row();
+ wrapRange( nRow1, nRow2, nMaxRow);
+ rRange.aStart.SetRow( nRow1);
+ rRange.aEnd.SetRow( nRow2);
+}
+
+}
+
+void ScTokenArray::WrapReference( const ScAddress& rPos, SCCOL nMaxCol, SCROW nMaxRow )
+{
+ FormulaToken** p = pCode.get();
+ FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+ for (; p != pEnd; ++p)
+ {
+ switch ((*p)->GetType())
+ {
+ case svSingleRef:
+ {
+ formula::FormulaToken* pToken = *p;
+ ScSingleRefData& rRef = *pToken->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+ wrapAddress(aAbs, nMaxCol, nMaxRow);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
+ }
+ break;
+ case svDoubleRef:
+ {
+ formula::FormulaToken* pToken = *p;
+ ScComplexRefData& rRef = *pToken->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+ // Entire columns/rows are sticky.
+ if (!rRef.IsEntireCol(*mxSheetLimits) && !rRef.IsEntireRow(*mxSheetLimits))
+ {
+ wrapColRange( aAbs, nMaxCol);
+ wrapRowRange( aAbs, nMaxRow);
+ }
+ else if (rRef.IsEntireCol(*mxSheetLimits) && !rRef.IsEntireRow(*mxSheetLimits))
+ wrapColRange( aAbs, nMaxCol);
+ else if (!rRef.IsEntireCol(*mxSheetLimits) && rRef.IsEntireRow(*mxSheetLimits))
+ wrapRowRange( aAbs, nMaxRow);
+ // else nothing if both, column and row, are entire.
+ aAbs.PutInOrder();
+ rRef.SetRange(*mxSheetLimits, aAbs, rPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+sal_Int32 ScTokenArray::GetWeight() const
+{
+ sal_Int32 nResult = 0;
+ for (auto i = 0; i < nRPN; ++i)
+ {
+ switch ((*pRPN[i]).GetType())
+ {
+ case svDoubleRef:
+ {
+ const auto pComplexRef = (*pRPN[i]).GetDoubleRef();
+
+ // Number of cells referenced divided by 10.
+ const double nNumCellsTerm =
+ (1 + (pComplexRef->Ref2.Row() - pComplexRef->Ref1.Row())) *
+ (1 + (pComplexRef->Ref2.Col() - pComplexRef->Ref1.Col())) / 10.;
+
+ if (nNumCellsTerm + nResult < SAL_MAX_INT32)
+ nResult += nNumCellsTerm;
+ else
+ nResult = SAL_MAX_INT32;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ if (nResult == 0)
+ nResult = 1;
+
+ return nResult;
+}
+
+#if DEBUG_FORMULA_COMPILER
+
+void ScTokenArray::Dump() const
+{
+ cout << "+++ Normal Tokens +++" << endl;
+ for (sal_uInt16 i = 0; i < nLen; ++i)
+ {
+ DumpToken(*pCode[i]);
+ }
+
+ cout << "+++ RPN Tokens +++" << endl;
+ for (sal_uInt16 i = 0; i < nRPN; ++i)
+ {
+ DumpToken(*pRPN[i]);
+ }
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */