summaryrefslogtreecommitdiffstats
path: root/sc/source/core/tool/address.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/tool/address.cxx')
-rw-r--r--sc/source/core/tool/address.cxx2532
1 files changed, 2532 insertions, 0 deletions
diff --git a/sc/source/core/tool/address.cxx b/sc/source/core/tool/address.cxx
new file mode 100644
index 000000000..a1e93472f
--- /dev/null
+++ b/sc/source/core/tool/address.cxx
@@ -0,0 +1,2532 @@
+/* -*- 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 <sal/config.h>
+
+#include <string_view>
+
+#include <address.hxx>
+#include <global.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+#include <externalrefmgr.hxx>
+
+#include <osl/diagnose.h>
+#include <o3tl/underlyingenumvalue.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/ExternalLinkInfo.hpp>
+#include <com/sun/star/sheet/ExternalLinkType.hpp>
+#include <sfx2/objsh.hxx>
+#include <tools/urlobj.hxx>
+#include <sal/log.hxx>
+#include <rtl/character.hxx>
+#include <unotools/charclass.hxx>
+
+using namespace css;
+
+const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 );
+
+ScAddress::Details::Details ( const ScDocument& rDoc,
+ const ScAddress& rAddr ) :
+ eConv( rDoc.GetAddressConvention() ),
+ nRow( rAddr.Row() ),
+ nCol( rAddr.Col() )
+{}
+
+namespace {
+
+const sal_Unicode* parseQuotedNameWithBuffer( const sal_Unicode* pStart, const sal_Unicode* p, OUString& rName )
+{
+ // The current character must be on the 2nd quote.
+
+ // Push all the characters up to the current, but skip the very first
+ // character which is the opening quote.
+ OUStringBuffer aBuf(std::u16string_view(pStart+1, p-pStart-1));
+
+ ++p; // Skip the 2nd quote.
+ sal_Unicode cPrev = 0;
+ for (; *p; ++p)
+ {
+ if (*p == '\'')
+ {
+ if (cPrev == '\'')
+ {
+ // double single-quote equals one single quote.
+ aBuf.append(*p);
+ cPrev = 0;
+ continue;
+ }
+ }
+ else if (cPrev == '\'')
+ {
+ // We are past the closing quote. We're done!
+ rName = aBuf.makeStringAndClear();
+ return p;
+ }
+ else
+ aBuf.append(*p);
+ cPrev = *p;
+ }
+
+ return pStart;
+}
+
+/**
+ * Parse from the opening single quote to the closing single quote. Inside
+ * the quotes, a single quote character is encoded by double single-quote
+ * characters.
+ *
+ * @param p pointer to the first character to begin parsing.
+ * @param rName (reference) parsed name within the quotes. If the name is
+ * empty, either the parsing failed or it's an empty quote.
+ *
+ * @return pointer to the character immediately after the closing single
+ * quote.
+ */
+const sal_Unicode* parseQuotedName( const sal_Unicode* p, OUString& rName )
+{
+ if (*p != '\'')
+ return p;
+
+ const sal_Unicode* pStart = p;
+ sal_Unicode cPrev = 0;
+ for (++p; *p; ++p)
+ {
+ if (*p == '\'')
+ {
+ if (cPrev == '\'')
+ {
+ // double single-quote equals one single quote.
+ return parseQuotedNameWithBuffer(pStart, p, rName);
+ }
+ }
+ else if (cPrev == '\'')
+ {
+ // We are past the closing quote. We're done! Skip the opening
+ // and closing quotes.
+ rName = OUString(pStart+1, p - pStart-2);
+ return p;
+ }
+
+ cPrev = *p;
+ }
+
+ rName.clear();
+ return pStart;
+}
+
+}
+
+static sal_Int64 sal_Unicode_strtol ( const sal_Unicode* p, const sal_Unicode** pEnd )
+{
+ sal_Int64 accum = 0, prev = 0;
+ bool is_neg = false;
+
+ if( *p == '-' )
+ {
+ is_neg = true;
+ p++;
+ }
+ else if( *p == '+' )
+ p++;
+
+ while (rtl::isAsciiDigit( *p ))
+ {
+ accum = accum * 10 + *p - '0';
+ if( accum < prev )
+ {
+ *pEnd = nullptr;
+ return 0;
+ }
+ prev = accum;
+ p++;
+ }
+
+ *pEnd = p;
+ return is_neg ? -accum : accum;
+}
+
+static const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p )
+{
+ if ( p )
+ {
+ while( *p == ' ' )
+ ++p;
+ }
+ return p;
+}
+
+// Compare ignore case ASCII.
+static bool lcl_isString( const sal_Unicode* p1, const OUString& rStr )
+{
+ const size_t n = rStr.getLength();
+ if (!n)
+ return false;
+ const sal_Unicode* p2 = rStr.getStr();
+ for (size_t i=0; i<n; ++i)
+ {
+ if (!p1[i])
+ return false;
+ if (p1[i] != p2[i])
+ {
+ sal_Unicode c1 = p1[i];
+ if ('A' <= c1 && c1 <= 'Z')
+ c1 += 0x20;
+ if (c1 < 'a' || 'z' < c1)
+ return false; // not a letter
+
+ sal_Unicode c2 = p2[i];
+ if ('A' <= c2 && c2 <= 'Z')
+ c2 += 0x20;
+ if (c2 < 'a' || 'z' < c2)
+ return false; // not a letter to match
+
+ if (c1 != c2)
+ return false; // lower case doesn't match either
+ }
+ }
+ return true;
+}
+
+/** Determines the number of sheets an external reference spans and sets
+ rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
+ bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
+ cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
+ is set to rEndTabName.
+ @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
+ result in the identical file ID. Else <TRUE/>.
+ */
+static bool lcl_ScRange_External_TabSpan(
+ ScRange & rRange,
+ ScRefFlags & rFlags,
+ ScAddress::ExternalInfo* pExtInfo,
+ const OUString & rExternDocName,
+ const OUString & rStartTabName,
+ const OUString & rEndTabName,
+ const ScDocument& rDoc )
+{
+ if (rExternDocName.isEmpty())
+ return !pExtInfo || !pExtInfo->mbExternal;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ if (pRefMgr->isOwnDocument( rExternDocName))
+ {
+ // This is an internal document. Get the sheet positions from the
+ // ScDocument instance.
+ if (!rStartTabName.isEmpty())
+ {
+ SCTAB nTab;
+ if (rDoc.GetTable(rStartTabName, nTab))
+ rRange.aStart.SetTab(nTab);
+ }
+
+ if (!rEndTabName.isEmpty())
+ {
+ SCTAB nTab;
+ if (rDoc.GetTable(rEndTabName, nTab))
+ rRange.aEnd.SetTab(nTab);
+ }
+ return !pExtInfo || !pExtInfo->mbExternal;
+ }
+
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName);
+
+ if (pExtInfo)
+ {
+ if (pExtInfo->mbExternal)
+ {
+ if (pExtInfo->mnFileId != nFileId)
+ return false;
+ }
+ else
+ {
+ pExtInfo->mbExternal = true;
+ pExtInfo->maTabName = rStartTabName;
+ pExtInfo->mnFileId = nFileId;
+ }
+ }
+
+ if (rEndTabName.isEmpty() || rStartTabName == rEndTabName)
+ {
+ rRange.aEnd.SetTab( rRange.aStart.Tab());
+ return true;
+ }
+
+ SCTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName);
+ if (nSpan == -1)
+ rFlags &= ~ScRefFlags(ScRefFlags::TAB_VALID | ScRefFlags::TAB2_VALID);
+ else if (nSpan == 0)
+ rFlags &= ~ScRefFlags::TAB2_VALID;
+ else if (nSpan >= 1)
+ rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1);
+ else // (nSpan < -1)
+ {
+ rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1);
+ if (pExtInfo)
+ pExtInfo->maTabName = rEndTabName;
+ }
+ return true;
+}
+
+/** Returns NULL if the string should be a sheet name, but is invalid.
+ Returns a pointer to the first character after the sheet name, if there was
+ any, else pointer to start.
+ @param pMsoxlQuoteStop
+ Starting _within_ a quoted name, but still may be 3D; quoted name stops
+ at pMsoxlQuoteStop
+ */
+static const sal_Unicode * lcl_XL_ParseSheetRef( const sal_Unicode* start,
+ OUString& rExternTabName,
+ bool bAllow3D,
+ const sal_Unicode* pMsoxlQuoteStop,
+ const OUString* pErrRef )
+{
+ OUString aTabName;
+ const sal_Unicode *p = start;
+
+ // XL only seems to use single quotes for sheet names.
+ if (pMsoxlQuoteStop)
+ {
+ const sal_Unicode* pCurrentStart = p;
+ while (p < pMsoxlQuoteStop)
+ {
+ if (*p == '\'')
+ {
+ // We pre-analyzed the quoting, no checks needed here.
+ if (*++p == '\'')
+ {
+ aTabName += std::u16string_view( pCurrentStart,
+ sal::static_int_cast<sal_Int32>( p - pCurrentStart));
+ pCurrentStart = ++p;
+ }
+ }
+ else if (*p == ':')
+ {
+ break; // while
+ }
+ else
+ ++p;
+ }
+ if (pCurrentStart < p)
+ aTabName += std::u16string_view( pCurrentStart, sal::static_int_cast<sal_Int32>( p - pCurrentStart));
+ if (aTabName.isEmpty())
+ return nullptr;
+ if (p == pMsoxlQuoteStop)
+ ++p; // position on ! of ...'!...
+ if( *p != '!' && ( !bAllow3D || *p != ':' ) )
+ return (!bAllow3D && *p == ':') ? p : start;
+ }
+ else if( *p == '\'')
+ {
+ p = parseQuotedName(p, aTabName);
+ if (aTabName.isEmpty())
+ return nullptr;
+ }
+ else if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '!')
+ {
+ p += pErrRef->getLength(); // position after "#REF!" on '!'
+ // XXX NOTE: caller has to check the name and that it wasn't quoted.
+ aTabName = *pErrRef;
+ }
+ else
+ {
+ bool only_digits = true;
+
+ /*
+ * Valid: Normal!a1
+ * Valid: x.y!a1
+ * Invalid: .y!a1
+ *
+ * Some names starting with digits are actually valid, but
+ * unparse quoted. Things are quite tricky: most sheet names
+ * starting with a digit are ok, but not those starting with
+ * "[0-9]*\." or "[0-9]+[eE]".
+ *
+ * Valid: 42!a1
+ * Valid: 4x!a1
+ * Invalid: 1.!a1
+ * Invalid: 1e!a1
+ */
+ while( true )
+ {
+ const sal_Unicode uc = *p;
+ if( rtl::isAsciiAlpha( uc ) || uc == '_' )
+ {
+ if( only_digits && p != start &&
+ (uc == 'e' || uc == 'E' ) )
+ {
+ p = start;
+ break;
+ }
+ only_digits = false;
+ p++;
+ }
+ else if( rtl::isAsciiDigit( uc ))
+ {
+ p++;
+ }
+ else if( uc == '.' )
+ {
+ if( only_digits ) // Valid, except after only digits.
+ {
+ p = start;
+ break;
+ }
+ p++;
+ }
+ else if (uc > 127)
+ {
+ // non ASCII character is allowed.
+ ++p;
+ }
+ else
+ break;
+ }
+
+ if( *p != '!' && ( !bAllow3D || *p != ':' ) )
+ return (!bAllow3D && *p == ':') ? p : start;
+
+ aTabName += std::u16string_view( start, sal::static_int_cast<sal_Int32>( p - start ) );
+ }
+
+ rExternTabName = aTabName;
+ return p;
+}
+
+/** Tries to obtain the external document index and replace by actual document
+ name.
+
+ @param ppErrRet
+ Contains the default pointer the caller would return if this method
+ returns FALSE, may be replaced by NULL for type or data errors.
+
+ @returns FALSE only if the input name is numeric and not within the index
+ sequence, or the link type cannot be determined or data mismatch. Returns
+ TRUE in all other cases, also when there is no index sequence or the input
+ name is not numeric.
+ */
+static bool lcl_XL_getExternalDoc( const sal_Unicode** ppErrRet, OUString& rExternDocName,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
+{
+ // 1-based, sequence starts with an empty element.
+ if (pExternalLinks && pExternalLinks->hasElements())
+ {
+ // A numeric "document name" is an index into the sequence.
+ if (CharClass::isAsciiNumeric( rExternDocName))
+ {
+ sal_Int32 i = rExternDocName.toInt32();
+ if (i < 0 || i >= pExternalLinks->getLength())
+ return false; // with default *ppErrRet
+ const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i];
+ switch (rInfo.Type)
+ {
+ case sheet::ExternalLinkType::DOCUMENT :
+ {
+ OUString aStr;
+ if (!(rInfo.Data >>= aStr))
+ {
+ SAL_INFO(
+ "sc.core",
+ "Data type mismatch for ExternalLinkInfo "
+ << i);
+ *ppErrRet = nullptr;
+ return false;
+ }
+ rExternDocName = aStr;
+ }
+ break;
+ case sheet::ExternalLinkType::SELF :
+ return false; // ???
+ case sheet::ExternalLinkType::SPECIAL :
+ // silently return nothing (do not assert), caller has to handle this
+ *ppErrRet = nullptr;
+ return false;
+ default:
+ SAL_INFO(
+ "sc.core",
+ "unhandled ExternalLinkType " << rInfo.Type
+ << " for index " << i);
+ *ppErrRet = nullptr;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+const sal_Unicode* ScRange::Parse_XL_Header(
+ const sal_Unicode* p,
+ const ScDocument& rDoc,
+ OUString& rExternDocName,
+ OUString& rStartTabName,
+ OUString& rEndTabName,
+ ScRefFlags& nFlags,
+ bool bOnlyAcceptSingle,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ const OUString* pErrRef )
+{
+ const sal_Unicode* startTabs, *start = p;
+ ScRefFlags nSaveFlags = nFlags;
+
+ // Is this an external reference ?
+ rStartTabName.clear();
+ rEndTabName.clear();
+ rExternDocName.clear();
+ const sal_Unicode* pMsoxlQuoteStop = nullptr;
+ if (*p == '[')
+ {
+ ++p;
+ // Only single quotes are correct, and a double single quote escapes a
+ // single quote text inside the quoted text.
+ if (*p == '\'')
+ {
+ p = parseQuotedName(p, rExternDocName);
+ if (*p != ']' || rExternDocName.isEmpty())
+ {
+ rExternDocName.clear();
+ return start;
+ }
+ }
+ else
+ {
+ // non-quoted file name.
+ p = ScGlobal::UnicodeStrChr( start+1, ']' );
+ if( p == nullptr )
+ return start;
+ rExternDocName += std::u16string_view( start+1, sal::static_int_cast<sal_Int32>( p-(start+1) ) );
+ }
+ ++p;
+
+ const sal_Unicode* pErrRet = start;
+ if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
+ return pErrRet;
+
+ rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, rDoc.GetDocumentShell());
+ }
+ else if (*p == '\'')
+ {
+ // Sickness in Excel's ODF msoxl namespace:
+ // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or
+ // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
+ // But, 'Sheet1'!B3 would also be a valid!
+ // Excel does not allow [ and ] characters in sheet names though.
+ // But, more sickness comes with MOOXML as there may be
+ // '[1]Sheet 4'!$A$1 where [1] is the external doc's index.
+ p = parseQuotedName(p, rExternDocName);
+ if (*p != '!')
+ {
+ rExternDocName.clear();
+ return start;
+ }
+ if (!rExternDocName.isEmpty())
+ {
+ sal_Int32 nOpen = rExternDocName.indexOf( '[');
+ if (nOpen == -1)
+ rExternDocName.clear();
+ else
+ {
+ sal_Int32 nClose = rExternDocName.indexOf( ']', nOpen+1);
+ if (nClose == -1)
+ rExternDocName.clear();
+ else
+ {
+ rExternDocName = rExternDocName.copy(0, nClose);
+ rExternDocName = rExternDocName.replaceAt( nOpen, 1, u"");
+ pMsoxlQuoteStop = p - 1; // the ' quote char
+ // There may be embedded escaped quotes, just matching the
+ // doc name's length may not work.
+ for (p = start; *p != '['; ++p)
+ ;
+ for ( ; *p != ']'; ++p)
+ ;
+ ++p;
+
+ // Handle '[1]Sheet 4'!$A$1
+ if (nOpen == 0)
+ {
+ const sal_Unicode* pErrRet = start;
+ if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
+ return pErrRet;
+ }
+ }
+ }
+ }
+ if (rExternDocName.isEmpty())
+ p = start;
+ }
+
+ startTabs = p;
+ p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop, pErrRef);
+ if( nullptr == p )
+ return start; // invalid tab
+ if (bOnlyAcceptSingle && *p == ':')
+ return nullptr; // 3D
+ const sal_Unicode* startEndTabs = nullptr;
+ if( p != startTabs )
+ {
+ nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D | ScRefFlags::TAB_ABS;
+ if( *p == ':' ) // 3d ref
+ {
+ startEndTabs = p + 1;
+ p = lcl_XL_ParseSheetRef( startEndTabs, rEndTabName, false, pMsoxlQuoteStop, pErrRef);
+ if( p == nullptr )
+ {
+ nFlags = nSaveFlags;
+ return start; // invalid tab
+ }
+ nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS;
+ }
+ else
+ {
+ // If only one sheet is given, the full reference is still valid,
+ // only the second 3D flag is not set.
+ nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_ABS;
+ aEnd.SetTab( aStart.Tab() );
+ }
+
+ if( *p++ != '!' )
+ {
+ nFlags = nSaveFlags;
+ return start; // syntax error
+ }
+ else
+ p = lcl_eatWhiteSpace( p );
+ }
+ else
+ {
+ nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB2_VALID;
+ // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
+ }
+
+ if (!rExternDocName.isEmpty())
+ {
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ pRefMgr->convertToAbsName(rExternDocName);
+ }
+ else
+ {
+ // Internal reference.
+ if (rStartTabName.isEmpty())
+ {
+ nFlags = nSaveFlags;
+ return start;
+ }
+
+ SCTAB nTab;
+ if ((pErrRef && *startTabs != '\'' && rStartTabName == *pErrRef) || !rDoc.GetTable(rStartTabName, nTab))
+ {
+ // invalid table name.
+ nFlags &= ~ScRefFlags::TAB_VALID;
+ nTab = -1;
+ }
+
+ aStart.SetTab(nTab);
+ aEnd.SetTab(nTab);
+
+ if (!rEndTabName.isEmpty())
+ {
+ if ((pErrRef && startEndTabs && *startEndTabs != '\'' && rEndTabName == *pErrRef) ||
+ !rDoc.GetTable(rEndTabName, nTab))
+ {
+ // invalid table name.
+ nFlags &= ~ScRefFlags::TAB2_VALID;
+ nTab = -1;
+ }
+
+ aEnd.SetTab(nTab);
+ }
+ }
+ return p;
+}
+
+static const sal_Unicode* lcl_r1c1_get_col( const ScSheetLimits& rSheetLimits,
+ const sal_Unicode* p,
+ const ScAddress::Details& rDetails,
+ ScAddress* pAddr, ScRefFlags* nFlags )
+{
+ const sal_Unicode *pEnd;
+ sal_Int64 n;
+ bool isRelative;
+
+ if( p[0] == '\0' )
+ return nullptr;
+
+ p++;
+ isRelative = *p == '[';
+ if( isRelative )
+ p++;
+ n = sal_Unicode_strtol( p, &pEnd );
+ if( nullptr == pEnd )
+ return nullptr;
+
+ if( p == pEnd ) // C is a relative ref with offset 0
+ {
+ if( isRelative )
+ return nullptr;
+ n = rDetails.nCol;
+ }
+ else if( isRelative )
+ {
+ if( *pEnd != ']' )
+ return nullptr;
+ n += rDetails.nCol;
+ pEnd++;
+ }
+ else
+ {
+ *nFlags |= ScRefFlags::COL_ABS;
+ n--;
+ }
+
+ if( n < 0 || n >= rSheetLimits.GetMaxColCount())
+ return nullptr;
+ pAddr->SetCol( static_cast<SCCOL>( n ) );
+ *nFlags |= ScRefFlags::COL_VALID;
+
+ return pEnd;
+}
+
+static const sal_Unicode* lcl_r1c1_get_row(
+ const ScSheetLimits& rSheetLimits,
+ const sal_Unicode* p,
+ const ScAddress::Details& rDetails,
+ ScAddress* pAddr, ScRefFlags* nFlags )
+{
+ const sal_Unicode *pEnd;
+ bool isRelative;
+
+ if( p[0] == '\0' )
+ return nullptr;
+
+ p++;
+ isRelative = *p == '[';
+ if( isRelative )
+ p++;
+ sal_Int64 n = sal_Unicode_strtol( p, &pEnd );
+ if( nullptr == pEnd )
+ return nullptr;
+
+ if( p == pEnd ) // R is a relative ref with offset 0
+ {
+ if( isRelative )
+ return nullptr;
+ n = rDetails.nRow;
+ }
+ else if( isRelative )
+ {
+ if( *pEnd != ']' )
+ return nullptr;
+ n += rDetails.nRow;
+ pEnd++;
+ }
+ else
+ {
+ *nFlags |= ScRefFlags::ROW_ABS;
+ n--;
+ }
+
+ if( n < 0 || n >= rSheetLimits.GetMaxRowCount() )
+ return nullptr;
+ pAddr->SetRow( static_cast<SCROW>( n ) );
+ *nFlags |= ScRefFlags::ROW_VALID;
+
+ return pEnd;
+}
+
+static ScRefFlags lcl_ScRange_Parse_XL_R1C1( ScRange& r,
+ const sal_Unicode* p,
+ const ScDocument& rDoc,
+ const ScAddress::Details& rDetails,
+ bool bOnlyAcceptSingle,
+ ScAddress::ExternalInfo* pExtInfo,
+ sal_Int32* pSheetEndPos )
+{
+ const sal_Unicode* const pStart = p;
+ if (pSheetEndPos)
+ *pSheetEndPos = 0;
+ const sal_Unicode* pTmp = nullptr;
+ OUString aExternDocName, aStartTabName, aEndTabName;
+ ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID;
+ // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
+ ScRefFlags nFlags2 = ScRefFlags::TAB_VALID;
+
+ p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName,
+ aEndTabName, nFlags, bOnlyAcceptSingle );
+
+ ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
+ if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
+ {
+ *pSheetEndPos = p - pStart;
+ nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
+ }
+
+ if (!aExternDocName.isEmpty())
+ lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
+ aStartTabName, aEndTabName, rDoc);
+
+ if( nullptr == p )
+ return ScRefFlags::ZERO;
+
+ if( *p == 'R' || *p == 'r' )
+ {
+ if( nullptr == (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )) )
+ return nBailOutFlags;
+
+ if( *p != 'C' && *p != 'c' ) // full row R#
+ {
+ if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) ||
+ nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )))
+ {
+ // Only the initial row number is given, or the second row
+ // number is invalid. Fallback to just the initial R
+ applyStartToEndFlags(nFlags);
+ r.aEnd.SetRow( r.aStart.Row() );
+ }
+ else // pTmp != nullptr
+ {
+ // Full row range successfully parsed.
+ applyStartToEndFlags(nFlags, nFlags2);
+ p = pTmp;
+ }
+
+ if (p[0] != 0)
+ {
+ // any trailing invalid character must invalidate the whole address.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ nFlags |=
+ ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID |
+ ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
+ r.aStart.SetCol( 0 );
+ r.aEnd.SetCol( rDoc.MaxCol() );
+
+ return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
+ }
+ else if( nullptr == (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )))
+ {
+ return ScRefFlags::ZERO;
+ }
+
+ if( p[0] != ':' ||
+ (p[1] != 'R' && p[1] != 'r') ||
+ nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )) ||
+ (*pTmp != 'C' && *pTmp != 'c') ||
+ nullptr == (pTmp = lcl_r1c1_get_col( rDoc.GetSheetLimits(), pTmp, rDetails, &r.aEnd, &nFlags2 )))
+ {
+ // single cell reference
+
+ if (p[0] != 0)
+ {
+ // any trailing invalid character must invalidate the whole address.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID);
+ return nFlags;
+ }
+
+ return bOnlyAcceptSingle ? nFlags : ScRefFlags::ZERO;
+ }
+ assert(pTmp);
+ p = pTmp;
+
+ // double reference
+
+ if (p[0] != 0)
+ {
+ // any trailing invalid character must invalidate the whole range.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ applyStartToEndFlags(nFlags, nFlags2);
+ return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
+ }
+ else if( *p == 'C' || *p == 'c' ) // full col C#
+ {
+ if( nullptr == (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )))
+ return nBailOutFlags;
+
+ if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') ||
+ nullptr == (pTmp = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )))
+ { // Fallback to just the initial C
+ applyStartToEndFlags(nFlags);
+ r.aEnd.SetCol( r.aStart.Col() );
+ }
+ else // pTmp != nullptr
+ {
+ applyStartToEndFlags(nFlags, nFlags2);
+ p = pTmp;
+ }
+
+ if (p[0] != 0)
+ {
+ // any trailing invalid character must invalidate the whole address.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ nFlags |=
+ ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID |
+ ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
+ r.aStart.SetRow( 0 );
+ r.aEnd.SetRow( rDoc.MaxRow() );
+
+ return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
+ }
+
+ return nBailOutFlags;
+}
+
+static const sal_Unicode* lcl_a1_get_col( const ScDocument& rDoc,
+ const sal_Unicode* p,
+ ScAddress* pAddr,
+ ScRefFlags* nFlags,
+ const OUString* pErrRef )
+{
+ if( *p == '$' )
+ {
+ *nFlags |= ScRefFlags::COL_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef))
+ {
+ p += pErrRef->getLength();
+ *nFlags &= ~ScRefFlags::COL_VALID;
+ pAddr->SetCol(-1);
+ return p;
+ }
+
+ if( !rtl::isAsciiAlpha( *p ) )
+ return nullptr;
+
+ sal_Int64 nCol = rtl::toAsciiUpperCase( *p++ ) - 'A';
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ while (nCol <= nMaxCol && rtl::isAsciiAlpha(*p))
+ nCol = ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A';
+ if( nCol > nMaxCol || nCol < 0 || rtl::isAsciiAlpha( *p ) )
+ return nullptr;
+
+ *nFlags |= ScRefFlags::COL_VALID;
+ pAddr->SetCol( sal::static_int_cast<SCCOL>( nCol ));
+
+ return p;
+}
+
+static const sal_Unicode* lcl_a1_get_row( const ScDocument& rDoc,
+ const sal_Unicode* p,
+ ScAddress* pAddr,
+ ScRefFlags* nFlags,
+ const OUString* pErrRef )
+{
+ const sal_Unicode *pEnd;
+
+ if( *p == '$' )
+ {
+ *nFlags |= ScRefFlags::ROW_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef))
+ {
+ p += pErrRef->getLength();
+ *nFlags &= ~ScRefFlags::ROW_VALID;
+ pAddr->SetRow(-1);
+ return p;
+ }
+
+ sal_Int64 n = sal_Unicode_strtol( p, &pEnd ) - 1;
+ if( nullptr == pEnd || p == pEnd || n < 0 || n > rDoc.MaxRow() )
+ return nullptr;
+
+ *nFlags |= ScRefFlags::ROW_VALID;
+ pAddr->SetRow( sal::static_int_cast<SCROW>(n) );
+
+ return pEnd;
+}
+
+/// B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ...
+static bool isValidSingleton( ScRefFlags nFlags, ScRefFlags nFlags2 )
+{
+ bool bCols = (nFlags & ScRefFlags::COL_VALID) && ((nFlags & ScRefFlags::COL2_VALID) || (nFlags2 & ScRefFlags::COL_VALID));
+ bool bRows = (nFlags & ScRefFlags::ROW_VALID) && ((nFlags & ScRefFlags::ROW2_VALID) || (nFlags2 & ScRefFlags::ROW_VALID));
+ return bCols != bRows;
+}
+
+static ScRefFlags lcl_ScRange_Parse_XL_A1( ScRange& r,
+ const sal_Unicode* p,
+ const ScDocument& rDoc,
+ bool bOnlyAcceptSingle,
+ ScAddress::ExternalInfo* pExtInfo,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ sal_Int32* pSheetEndPos,
+ const OUString* pErrRef )
+{
+ const sal_Unicode* const pStart = p;
+ if (pSheetEndPos)
+ *pSheetEndPos = 0;
+ const sal_Unicode* tmp1, *tmp2;
+ OUString aExternDocName, aStartTabName, aEndTabName; // for external link table
+ ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID, nFlags2 = ScRefFlags::TAB_VALID;
+
+ p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName,
+ aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks, pErrRef );
+
+ ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
+ if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
+ {
+ *pSheetEndPos = p - pStart;
+ nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
+ }
+
+ if (!aExternDocName.isEmpty())
+ lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
+ aStartTabName, aEndTabName, rDoc);
+
+ if( nullptr == p )
+ return nBailOutFlags;
+
+ tmp1 = lcl_a1_get_col( rDoc, p, &r.aStart, &nFlags, pErrRef);
+ if( tmp1 == nullptr ) // Is it a row only reference 3:5
+ {
+ if( bOnlyAcceptSingle ) // by definition full row refs are ranges
+ return nBailOutFlags;
+
+ tmp1 = lcl_a1_get_row( rDoc, p, &r.aStart, &nFlags, pErrRef);
+
+ tmp1 = lcl_eatWhiteSpace( tmp1 );
+ if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
+ return nBailOutFlags;
+
+ tmp1 = lcl_eatWhiteSpace( tmp1 );
+ tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
+ if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton.
+ return nBailOutFlags;
+
+ r.aStart.SetCol( 0 ); r.aEnd.SetCol( rDoc.MaxCol() );
+ nFlags |=
+ ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID |
+ ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
+ applyStartToEndFlags(nFlags, nFlags2);
+ return nFlags;
+ }
+
+ tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aStart, &nFlags, pErrRef);
+ if( tmp2 == nullptr ) // check for col only reference F:H
+ {
+ if( bOnlyAcceptSingle ) // by definition full col refs are ranges
+ return nBailOutFlags;
+
+ tmp1 = lcl_eatWhiteSpace( tmp1 );
+ if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F)
+ return nBailOutFlags;
+
+ tmp1 = lcl_eatWhiteSpace( tmp1 );
+ tmp2 = lcl_a1_get_col( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
+ if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton.
+ return nBailOutFlags;
+
+ r.aStart.SetRow( 0 ); r.aEnd.SetRow( rDoc.MaxRow() );
+ nFlags |=
+ ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID |
+ ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
+ applyStartToEndFlags(nFlags, nFlags2);
+ return nFlags;
+ }
+
+ // prepare as if it's a singleton, in case we want to fall back */
+ r.aEnd.SetCol( r.aStart.Col() );
+ r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header()
+
+ if ( bOnlyAcceptSingle )
+ {
+ if ( *tmp2 == 0 )
+ return nFlags;
+ else
+ {
+ // any trailing invalid character must invalidate the address.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID);
+ return nFlags;
+ }
+ }
+
+ tmp2 = lcl_eatWhiteSpace( tmp2 );
+ if( *tmp2 != ':' )
+ {
+ // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
+ // not. Any trailing invalid character invalidates the range.
+ if (*tmp2 == 0 && (nFlags & ScRefFlags::TAB2_3D))
+ {
+ if (nFlags & ScRefFlags::COL_ABS)
+ nFlags |= ScRefFlags::COL2_ABS;
+ if (nFlags & ScRefFlags::ROW_ABS)
+ nFlags |= ScRefFlags::ROW2_ABS;
+ }
+ else
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID |
+ ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ p = lcl_eatWhiteSpace( tmp2+1 ); // after ':'
+ tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef);
+ if( !tmp1 && aEndTabName.isEmpty() ) // Probably the aEndTabName was specified after the first range
+ {
+ p = lcl_XL_ParseSheetRef( p, aEndTabName, false, nullptr, pErrRef);
+ if( p )
+ {
+ SCTAB nTab = 0;
+ if( !aEndTabName.isEmpty() && rDoc.GetTable( aEndTabName, nTab ) )
+ {
+ r.aEnd.SetTab( nTab );
+ nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS;
+ }
+ if (*p == '!' || *p == ':')
+ p = lcl_eatWhiteSpace( p+1 );
+ tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef);
+ }
+ }
+ if( !tmp1 ) // strange, but maybe valid singleton
+ return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID);
+
+ tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
+ if( !tmp2 ) // strange, but maybe valid singleton
+ return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID);
+
+ if ( *tmp2 != 0 )
+ {
+ // any trailing invalid character must invalidate the range.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ applyStartToEndFlags(nFlags, nFlags2);
+ return nFlags;
+}
+
+/**
+ @param p pointer to null-terminated sal_Unicode string
+ @param rRawRes returns ScRefFlags::... flags without the final check for full
+ validity that is applied to the return value, with which
+ two addresses that form a column or row singleton range,
+ e.g. A:A or 1:1, can be detected. Used in
+ lcl_ScRange_Parse_OOo().
+ @param pRange pointer to range where rAddr effectively is *pRange->aEnd,
+ used in conjunction with pExtInfo to determine the tab span
+ of a 3D reference.
+ */
+static ScRefFlags lcl_ScAddress_Parse_OOo( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
+ ScRefFlags& rRawRes,
+ ScAddress::ExternalInfo* pExtInfo,
+ ScRange* pRange,
+ sal_Int32* pSheetEndPos,
+ const OUString* pErrRef )
+{
+ const sal_Unicode* const pStart = p;
+ if (pSheetEndPos)
+ *pSheetEndPos = 0;
+ ScRefFlags nRes = ScRefFlags::ZERO;
+ rRawRes = ScRefFlags::ZERO;
+ OUString aDocName; // the pure Document Name
+ OUString aTab;
+ bool bExtDoc = false;
+ bool bExtDocInherited = false;
+
+ // Lets see if this is a reference to something in an external file. A
+ // document name is always quoted and has a trailing #.
+ if (*p == '\'')
+ {
+ OUString aTmp;
+ p = parseQuotedName(p, aTmp);
+ aDocName = aTmp;
+ if (*p++ == SC_COMPILER_FILE_TAB_SEP)
+ bExtDoc = true;
+ else
+ // This is not a document name. Perhaps a quoted relative table
+ // name.
+ p = pStart;
+ }
+ else if (pExtInfo && pExtInfo->mbExternal)
+ {
+ // This is an external reference.
+ bExtDoc = bExtDocInherited = true;
+ }
+
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+ SCTAB nTab = 0;
+ ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
+ ScRefFlags nBits = ScRefFlags::TAB_VALID;
+ const sal_Unicode* q;
+ if ( ScGlobal::FindUnquoted( p, '.') )
+ {
+ nRes |= ScRefFlags::TAB_3D;
+ if ( bExtDoc )
+ nRes |= ScRefFlags::TAB_ABS;
+ if (*p == '$')
+ {
+ nRes |= ScRefFlags::TAB_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '.')
+ {
+ // #REF! particle of an invalidated reference plus sheet separator.
+ p += pErrRef->getLength() + 1;
+ nRes &= ~ScRefFlags::TAB_VALID;
+ nTab = -1;
+ }
+ else
+ {
+ if (*p == '\'')
+ {
+ // Tokens that start at ' can have anything in them until a final
+ // ' but '' marks an escaped '. We've earlier guaranteed that a
+ // string containing '' will be surrounded by '.
+ p = parseQuotedName(p, aTab);
+ }
+ else
+ {
+ OUStringBuffer aTabAcc;
+ while (*p)
+ {
+ if( *p == '.')
+ break;
+
+ if( *p == '\'' )
+ {
+ p++; break;
+ }
+ aTabAcc.append(*p);
+ p++;
+ }
+ aTab = aTabAcc.makeStringAndClear();
+ }
+ if (*p != '.')
+ nBits = ScRefFlags::ZERO;
+ else
+ {
+ ++p;
+ if (!bExtDoc && !rDoc.GetTable( aTab, nTab ))
+ nBits = ScRefFlags::ZERO;
+ }
+ }
+
+ if (pSheetEndPos && (nBits & ScRefFlags::TAB_VALID))
+ {
+ *pSheetEndPos = p - pStart;
+ nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
+ }
+ }
+ else
+ {
+ if (bExtDoc && !bExtDocInherited)
+ return nRes; // After a document a sheet must follow.
+ nTab = rAddr.Tab();
+ }
+ nRes |= nBits;
+
+ q = p;
+ if (*p)
+ {
+ nBits = ScRefFlags::COL_VALID;
+ if (*p == '$')
+ {
+ nBits |= ScRefFlags::COL_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef))
+ {
+ // #REF! particle of an invalidated reference.
+ p += pErrRef->getLength();
+ nBits &= ~ScRefFlags::COL_VALID;
+ nCol = -1;
+ }
+ else
+ {
+ if (rtl::isAsciiAlpha( *p ))
+ {
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ sal_Int64 n = rtl::toAsciiUpperCase( *p++ ) - 'A';
+ while (n < nMaxCol && rtl::isAsciiAlpha(*p))
+ n = ((n + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A';
+ if (n > nMaxCol || n < 0 || (*p && *p != '$' && !rtl::isAsciiDigit( *p ) &&
+ (!pErrRef || !lcl_isString( p, *pErrRef))))
+ nBits = ScRefFlags::ZERO;
+ else
+ nCol = sal::static_int_cast<SCCOL>( n );
+ }
+ else
+ nBits = ScRefFlags::ZERO;
+
+ if( nBits == ScRefFlags::ZERO )
+ p = q;
+ }
+ nRes |= nBits;
+ }
+
+ q = p;
+ if (*p)
+ {
+ nBits = ScRefFlags::ROW_VALID;
+ if (*p == '$')
+ {
+ nBits |= ScRefFlags::ROW_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef))
+ {
+ // #REF! particle of an invalidated reference.
+ p += pErrRef->getLength();
+ // Clearing the ROW_VALID bit here is not possible because of the
+ // check at the end whether only a valid column was detected in
+ // which case all bits are cleared because it could be any other
+ // name. Instead, set to an absolute invalid row value. This will
+ // display a $#REF! instead of #REF! if the error value was
+ // relative, but live with it.
+ nBits |= ScRefFlags::ROW_ABS;
+ nRow = -1;
+ }
+ else
+ {
+ if( !rtl::isAsciiDigit( *p ) )
+ {
+ nBits = ScRefFlags::ZERO;
+ nRow = -1;
+ }
+ else
+ {
+ sal_Int64 n = rtl_ustr_toInt32( p, 10 ) - 1;
+ while (rtl::isAsciiDigit( *p ))
+ p++;
+ const SCROW nMaxRow = rDoc.MaxRow();
+ if( n < 0 || n > nMaxRow )
+ nBits = ScRefFlags::ZERO;
+ nRow = sal::static_int_cast<SCROW>(n);
+ }
+ if( nBits == ScRefFlags::ZERO )
+ p = q;
+ }
+ nRes |= nBits;
+ }
+
+ rAddr.Set( nCol, nRow, nTab );
+
+ if (!*p && bExtDoc)
+ {
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+
+ // Need document name if inherited.
+ if (bExtDocInherited)
+ {
+ // The FileId was created using the original file name, so
+ // obtain that. Otherwise lcl_ScRange_External_TabSpan() would
+ // retrieve a FileId for the real name and bail out if that
+ // differed from pExtInfo->mnFileId, as is the case when
+ // loading documents that refer external files relative to the
+ // current own document but were saved from a different path
+ // than loaded.
+ const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId, true);
+ if (pFileName)
+ aDocName = *pFileName;
+ else
+ nRes = ScRefFlags::ZERO;
+ }
+ pRefMgr->convertToAbsName(aDocName);
+
+ if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
+ {
+ if (!rDoc.GetTable( aTab, nTab ))
+ nRes = ScRefFlags::ZERO;
+ else
+ {
+ rAddr.SetTab( nTab);
+ nRes |= ScRefFlags::TAB_VALID;
+ }
+ }
+ else
+ {
+ if (!pExtInfo)
+ nRes = ScRefFlags::ZERO;
+ else
+ {
+ if (!pExtInfo->mbExternal)
+ {
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
+
+ pExtInfo->mbExternal = true;
+ pExtInfo->maTabName = aTab;
+ pExtInfo->mnFileId = nFileId;
+
+ if (pRefMgr->getSingleRefToken(nFileId, aTab,
+ ScAddress(nCol, nRow, 0), nullptr,
+ &nTab))
+ {
+ rAddr.SetTab( nTab);
+ nRes |= ScRefFlags::TAB_VALID;
+ }
+ else
+ nRes = ScRefFlags::ZERO;
+ }
+ else
+ {
+ // This is a call for the second part of the reference,
+ // we must have the range to adapt tab span.
+ if (!pRange)
+ nRes = ScRefFlags::ZERO;
+ else
+ {
+ ScRefFlags nFlags = nRes | ScRefFlags::TAB2_VALID;
+ if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
+ pExtInfo, aDocName,
+ pExtInfo->maTabName, aTab, rDoc))
+ nRes &= ~ScRefFlags::TAB_VALID;
+ else
+ {
+ if (nFlags & ScRefFlags::TAB2_VALID)
+ {
+ rAddr.SetTab( pRange->aEnd.Tab());
+ nRes |= ScRefFlags::TAB_VALID;
+ }
+ else
+ nRes &= ~ScRefFlags::TAB_VALID;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (bExtDoc && pExtInfo && !bExtDocInherited && !pExtInfo->mbExternal && pSheetEndPos)
+ {
+ // Pass partial info up to caller, there may be an external name
+ // following, and if after *pSheetEndPos it's external sheet-local.
+ // For global names aTab is empty and *pSheetEndPos==0.
+ pExtInfo->mbExternal = true;
+ pExtInfo->maTabName = aTab;
+ pExtInfo->mnFileId = rDoc.GetExternalRefManager()->getExternalFileId(aDocName);
+ }
+
+ rRawRes |= nRes;
+
+ if ( !(nRes & ScRefFlags::ROW_VALID) && (nRes & ScRefFlags::COL_VALID)
+ && !( (nRes & ScRefFlags::TAB_3D) && (nRes & ScRefFlags::TAB_VALID)) )
+ { // no Row, no Tab, but Col => DM (...), B (...) et al
+ nRes = ScRefFlags::ZERO;
+ }
+ if( !*p )
+ {
+ ScRefFlags nMask = nRes & ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID );
+ if( nMask == ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ) )
+ nRes |= ScRefFlags::VALID;
+ }
+ else
+ nRes = rRawRes = nBailOutFlags;
+ return nRes;
+}
+
+static ScRefFlags lcl_ScAddress_Parse ( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
+ const ScAddress::Details& rDetails,
+ ScAddress::ExternalInfo* pExtInfo,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ sal_Int32* pSheetEndPos,
+ const OUString* pErrRef )
+{
+ if( !*p )
+ return ScRefFlags::ZERO;
+
+ switch (rDetails.eConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ {
+ ScRange rRange = rAddr;
+ ScRefFlags nFlags = lcl_ScRange_Parse_XL_A1(
+ rRange, p, rDoc, true, pExtInfo,
+ (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
+ pSheetEndPos, pErrRef);
+ rAddr = rRange.aStart;
+ return nFlags;
+ }
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ {
+ ScRange rRange = rAddr;
+ ScRefFlags nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, rDoc, rDetails, true, pExtInfo, pSheetEndPos);
+ rAddr = rRange.aStart;
+ return nFlags;
+ }
+ default :
+ case formula::FormulaGrammar::CONV_OOO:
+ {
+ ScRefFlags nRawRes = ScRefFlags::ZERO;
+ return lcl_ScAddress_Parse_OOo( p, rDoc, rAddr, nRawRes, pExtInfo, nullptr, pSheetEndPos, pErrRef);
+ }
+ }
+}
+
+bool ConvertSingleRef( const ScDocument& rDoc, const OUString& rRefString,
+ SCTAB nDefTab, ScRefAddress& rRefAddress,
+ const ScAddress::Details& rDetails,
+ ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
+{
+ bool bRet = false;
+ if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
+ {
+ ScAddress aAddr( 0, 0, nDefTab );
+ ScRefFlags nRes = aAddr.Parse( rRefString, rDoc, rDetails, pExtInfo);
+ if ( nRes & ScRefFlags::VALID )
+ {
+ rRefAddress.Set( aAddr,
+ ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool ConvertDoubleRef( const ScDocument& rDoc, const OUString& rRefString, SCTAB nDefTab,
+ ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
+ const ScAddress::Details& rDetails,
+ ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
+{
+ bool bRet = false;
+ if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
+ {
+ ScRange aRange( ScAddress( 0, 0, nDefTab));
+ ScRefFlags nRes = aRange.Parse( rRefString, rDoc, rDetails, pExtInfo);
+ if ( nRes & ScRefFlags::VALID )
+ {
+ rStartRefAddress.Set( aRange.aStart,
+ ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
+ rEndRefAddress.Set( aRange.aEnd,
+ ((nRes & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO));
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+ScRefFlags ScAddress::Parse( const OUString& r, const ScDocument& rDoc,
+ const Details& rDetails,
+ ExternalInfo* pExtInfo,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ sal_Int32* pSheetEndPos,
+ const OUString* pErrRef )
+{
+ return lcl_ScAddress_Parse( r.getStr(), rDoc, *this, rDetails, pExtInfo, pExternalLinks, pSheetEndPos, pErrRef);
+}
+
+ScRange ScRange::Intersection( const ScRange& rOther ) const
+{
+ SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col());
+ SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col());
+ SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row());
+ SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row());
+ SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab());
+ SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab());
+
+ if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2)
+ return ScRange(ScAddress::INITIALIZE_INVALID);
+
+ return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+}
+
+void ScRange::ExtendTo( const ScRange& rRange )
+{
+ OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
+ if( IsValid() )
+ {
+ aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) );
+ aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) );
+ aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) );
+ aEnd.SetCol( std::max( aEnd.Col(), rRange.aEnd.Col() ) );
+ aEnd.SetRow( std::max( aEnd.Row(), rRange.aEnd.Row() ) );
+ aEnd.SetTab( std::max( aEnd.Tab(), rRange.aEnd.Tab() ) );
+ }
+ else
+ *this = rRange;
+}
+
+static ScRefFlags lcl_ScRange_Parse_OOo( ScRange& rRange,
+ const OUString& r,
+ const ScDocument& rDoc,
+ ScAddress::ExternalInfo* pExtInfo,
+ const OUString* pErrRef )
+{
+ ScRefFlags nRes1 = ScRefFlags::ZERO, nRes2 = ScRefFlags::ZERO;
+ sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':');
+ if (nPos != -1)
+ {
+ OUStringBuffer aTmp(r);
+ aTmp[nPos] = 0;
+ const sal_Unicode* p = aTmp.getStr();
+ ScRefFlags nRawRes1 = ScRefFlags::ZERO;
+ nRes1 = lcl_ScAddress_Parse_OOo( p, rDoc, rRange.aStart, nRawRes1, pExtInfo, nullptr, nullptr, pErrRef);
+ if ((nRes1 != ScRefFlags::ZERO) ||
+ ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
+ (nRawRes1 & ScRefFlags::TAB_VALID)))
+ {
+ rRange.aEnd = rRange.aStart; // sheet must be initialized identical to first sheet
+ ScRefFlags nRawRes2 = ScRefFlags::ZERO;
+ nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, rDoc, rRange.aEnd, nRawRes2,
+ pExtInfo, &rRange, nullptr, pErrRef);
+ if (!((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) &&
+ // If not fully valid addresses, check if both have a valid
+ // column or row, and both have valid (or omitted) sheet references.
+ (nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
+ (nRawRes1 & ScRefFlags::TAB_VALID) &&
+ (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
+ (nRawRes2 & ScRefFlags::TAB_VALID) &&
+ // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A
+ ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) ==
+ (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID))))
+ {
+ nRes1 = nRawRes1 | ScRefFlags::VALID;
+ nRes2 = nRawRes2 | ScRefFlags::VALID;
+ if (nRawRes1 & ScRefFlags::COL_VALID)
+ {
+ rRange.aStart.SetRow(0);
+ rRange.aEnd.SetRow(rDoc.MaxRow());
+ nRes1 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS;
+ nRes2 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS;
+ }
+ else
+ {
+ rRange.aStart.SetCol(0);
+ rRange.aEnd.SetCol( rDoc.MaxCol() );
+ nRes1 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
+ nRes2 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
+ }
+ }
+ else if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
+ {
+ // Flag entire column/row references so they can be displayed
+ // as such. If the sticky reference parts are not both
+ // absolute or relative, assume that the user thought about
+ // something we should not touch.
+ if (rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() &&
+ ((nRes1 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO) &&
+ ((nRes2 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO))
+ {
+ nRes1 |= ScRefFlags::ROW_ABS;
+ nRes2 |= ScRefFlags::ROW_ABS;
+ }
+ else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
+ ((nRes1 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO) && ((nRes2 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO))
+ {
+ nRes1 |= ScRefFlags::COL_ABS;
+ nRes2 |= ScRefFlags::COL_ABS;
+ }
+ }
+ if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
+ {
+ // PutInOrder / Justify
+ ScRefFlags nMask, nBits1, nBits2;
+ SCCOL nTempCol;
+ if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) )
+ {
+ rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol);
+ nMask = (ScRefFlags::COL_VALID | ScRefFlags::COL_ABS);
+ nBits1 = nRes1 & nMask;
+ nBits2 = nRes2 & nMask;
+ nRes1 = (nRes1 & ~nMask) | nBits2;
+ nRes2 = (nRes2 & ~nMask) | nBits1;
+ }
+ SCROW nTempRow;
+ if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) )
+ {
+ rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow);
+ nMask = (ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS);
+ nBits1 = nRes1 & nMask;
+ nBits2 = nRes2 & nMask;
+ nRes1 = (nRes1 & ~nMask) | nBits2;
+ nRes2 = (nRes2 & ~nMask) | nBits1;
+ }
+ SCTAB nTempTab;
+ if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) )
+ {
+ rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab);
+ nMask = (ScRefFlags::TAB_VALID | ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D);
+ nBits1 = nRes1 & nMask;
+ nBits2 = nRes2 & nMask;
+ nRes1 = (nRes1 & ~nMask) | nBits2;
+ nRes2 = (nRes2 & ~nMask) | nBits1;
+ }
+ if ( ((nRes1 & ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
+ == ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
+ && !(nRes2 & ScRefFlags::TAB_3D) )
+ nRes2 |= ScRefFlags::TAB_ABS;
+ }
+ else
+ {
+ // Don't leave around valid half references.
+ nRes1 = nRes2 = ScRefFlags::ZERO;
+ }
+ }
+ }
+ applyStartToEndFlags(nRes1, nRes2 & ScRefFlags::BITS);
+ nRes1 |= nRes2 & ScRefFlags::VALID;
+ return nRes1;
+}
+
+ScRefFlags ScRange::Parse( const OUString& rString, const ScDocument& rDoc,
+ const ScAddress::Details& rDetails,
+ ScAddress::ExternalInfo* pExtInfo,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ const OUString* pErrRef )
+{
+ if (rString.isEmpty())
+ return ScRefFlags::ZERO;
+
+ switch (rDetails.eConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ {
+ return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), rDoc, false, pExtInfo,
+ (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
+ nullptr, pErrRef );
+ }
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ {
+ return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr );
+ }
+
+ default:
+ case formula::FormulaGrammar::CONV_OOO:
+ {
+ return lcl_ScRange_Parse_OOo( *this, rString, rDoc, pExtInfo, pErrRef );
+ }
+ }
+}
+
+// Accept a full range, or an address
+ScRefFlags ScRange::ParseAny( const OUString& rString, const ScDocument& rDoc,
+ const ScAddress::Details& rDetails )
+{
+ ScRefFlags nRet = Parse( rString, rDoc, rDetails );
+ const ScRefFlags nValid = ScRefFlags::VALID | ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID;
+
+ if ( (nRet & nValid) != nValid )
+ {
+ ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number
+ nRet = aAdr.Parse( rString, rDoc, rDetails );
+ if ( nRet & ScRefFlags::VALID )
+ aStart = aEnd = aAdr;
+ }
+ return nRet;
+}
+
+// Parse only full row references
+ScRefFlags ScRange::ParseCols( const ScDocument& rDoc,
+ const OUString& rStr,
+ const ScAddress::Details& rDetails )
+{
+ if (rStr.isEmpty())
+ return ScRefFlags::ZERO;
+
+ const sal_Unicode* p = rStr.getStr();
+ ScRefFlags nRes = ScRefFlags::ZERO;
+ ScRefFlags ignored = ScRefFlags::ZERO;
+
+ switch (rDetails.eConv)
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if (nullptr != (p = lcl_a1_get_col( rDoc, p, &aStart, &ignored, nullptr) ) )
+ {
+ if( p[0] == ':')
+ {
+ if( nullptr != (p = lcl_a1_get_col( rDoc, p+1, &aEnd, &ignored, nullptr)))
+ {
+ nRes = ScRefFlags::COL_VALID;
+ }
+ }
+ else
+ {
+ aEnd = aStart;
+ nRes = ScRefFlags::COL_VALID;
+ }
+ }
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ if ((p[0] == 'C' || p[0] == 'c') &&
+ nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored )))
+ {
+ if( p[0] == ':')
+ {
+ if( (p[1] == 'C' || p[1] == 'c') &&
+ nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored )))
+ {
+ nRes = ScRefFlags::COL_VALID;
+ }
+ }
+ else
+ {
+ aEnd = aStart;
+ nRes = ScRefFlags::COL_VALID;
+ }
+ }
+ break;
+ }
+
+ return (p != nullptr && *p == '\0') ? nRes : ScRefFlags::ZERO;
+}
+
+// Parse only full row references
+void ScRange::ParseRows( const ScDocument& rDoc,
+ const OUString& rStr,
+ const ScAddress::Details& rDetails )
+{
+ if (rStr.isEmpty())
+ return;
+
+ const sal_Unicode* p = rStr.getStr();
+ ScRefFlags ignored = ScRefFlags::ZERO;
+
+ switch (rDetails.eConv)
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if (nullptr != (p = lcl_a1_get_row( rDoc, p, &aStart, &ignored, nullptr) ) )
+ {
+ if( p[0] == ':')
+ {
+ lcl_a1_get_row( rDoc, p+1, &aEnd, &ignored, nullptr);
+ }
+ else
+ {
+ aEnd = aStart;
+ }
+ }
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ if ((p[0] == 'R' || p[0] == 'r') &&
+ nullptr != (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored )))
+ {
+ if( p[0] == ':')
+ {
+ if( p[1] == 'R' || p[1] == 'r' )
+ {
+ lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored );
+ }
+ }
+ else
+ {
+ aEnd = aStart;
+ }
+ }
+ break;
+ }
+}
+
+template<typename T > static void lcl_ScColToAlpha( T& rBuf, SCCOL nCol )
+{
+ if (nCol < 26*26)
+ {
+ if (nCol < 26)
+ rBuf.append( static_cast<char>( 'A' + nCol ));
+ else
+ {
+ rBuf.append( static_cast<char>( 'A' + nCol / 26 - 1 ));
+ rBuf.append( static_cast<char>( 'A' + nCol % 26 ));
+ }
+ }
+ else
+ {
+ sal_Int32 nInsert = rBuf.getLength();
+ while (nCol >= 26)
+ {
+ SCCOL nC = nCol % 26;
+ rBuf.insert(nInsert, static_cast<char> ( 'A' + nC ));
+ nCol = sal::static_int_cast<SCCOL>( nCol - nC );
+ nCol = nCol / 26 - 1;
+ }
+ rBuf.insert(nInsert, static_cast<char> ( 'A' + nCol ));
+ }
+}
+
+void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol)
+{
+ lcl_ScColToAlpha(rBuf, nCol);
+}
+
+template <typename T > static void lcl_a1_append_c ( T &rString, int nCol, bool bIsAbs )
+{
+ if( bIsAbs )
+ rString.append("$");
+ lcl_ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) );
+}
+
+template <typename T > static void lcl_a1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs )
+{
+ if ( bIsAbs )
+ rString.append("$");
+ rString.append( nRow + 1 );
+}
+
+template <typename T > static void lcl_r1c1_append_c ( T &rString, sal_Int32 nCol, bool bIsAbs,
+ const ScAddress::Details& rDetails )
+{
+ rString.append("C");
+ if (bIsAbs)
+ {
+ rString.append( nCol + 1 );
+ }
+ else
+ {
+ nCol -= rDetails.nCol;
+ if (nCol != 0) {
+ rString.append("[").append(nCol).append("]");
+ }
+ }
+}
+
+template <typename T > static void lcl_r1c1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs,
+ const ScAddress::Details& rDetails )
+{
+ rString.append("R");
+ if (bIsAbs)
+ {
+ rString.append( nRow + 1 );
+ }
+ else
+ {
+ nRow -= rDetails.nRow;
+ if (nRow != 0) {
+ rString.append("[").append(nRow).append("]");
+ }
+ }
+}
+
+static OUString getFileNameFromDoc( const ScDocument* pDoc )
+{
+ // TODO : er points at ScGlobal::GetAbsDocName()
+ // as a better template. Look into it
+ OUString sFileName;
+ SfxObjectShell* pShell;
+
+ if( nullptr != pDoc &&
+ nullptr != (pShell = pDoc->GetDocumentShell() ) )
+ {
+ uno::Reference< frame::XModel > xModel = pShell->GetModel();
+ if( xModel.is() )
+ {
+ if( !xModel->getURL().isEmpty() )
+ {
+ INetURLObject aURL( xModel->getURL() );
+ sFileName = aURL.GetLastName();
+ }
+ else
+ sFileName = pShell->GetTitle();
+ }
+ }
+ return sFileName;
+}
+
+
+static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString)
+{
+ rString.append(sString);
+}
+
+static void lcl_string_append(OStringBuffer &rString, std::u16string_view sString)
+{
+ rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8 ));
+}
+
+template<typename T > static void lcl_Format( T& r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags,
+ const ScDocument* pDoc,
+ const ScAddress::Details& rDetails)
+{
+ if( nFlags & ScRefFlags::VALID )
+ nFlags |= ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID;
+ if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) )
+ {
+ if ( nTab < 0 || nTab >= pDoc->GetTableCount() )
+ {
+ lcl_string_append(r, ScCompiler::GetNativeSymbol(ocErrRef));
+ return;
+ }
+ if( nFlags & ScRefFlags::TAB_3D )
+ {
+ OUString aTabName, aDocName;
+ pDoc->GetName(nTab, aTabName);
+ assert( !aTabName.isEmpty() && "empty sheet name");
+ // External Reference, same as in ScCompiler::MakeTabStr()
+ if( aTabName[0] == '\'' )
+ { // "'Doc'#Tab"
+ sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName);
+ if (nPos != -1)
+ {
+ aDocName = aTabName.copy( 0, nPos + 1 );
+ aTabName = aTabName.copy( nPos + 1 );
+ }
+ }
+ else if( nFlags & ScRefFlags::FORCE_DOC )
+ {
+ // VBA has an 'external' flag that forces the addition of the
+ // tab name _and_ the doc name. The VBA code would be
+ // needlessly complicated if it constructed an actual external
+ // reference so we add this somewhat cheesy kludge to force the
+ // addition of the document name even for non-external references
+ aDocName = getFileNameFromDoc( pDoc );
+ }
+ ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
+
+ switch( rDetails.eConv )
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO:
+ lcl_string_append(r, aDocName);
+ if( nFlags & ScRefFlags::TAB_ABS )
+ r.append("$");
+ lcl_string_append(r, aTabName);
+ r.append(".");
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if (!aTabName.isEmpty() && aTabName[0] == '\'')
+ {
+ if (!aDocName.isEmpty())
+ {
+ lcl_string_append(r.append("'["), aDocName);
+ r.append("]");
+ lcl_string_append(r, aTabName.subView(1));
+ }
+ else
+ {
+ lcl_string_append(r, aTabName);
+ }
+ r.append("!");
+ break;
+ }
+ [[fallthrough]];
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ if (!aDocName.isEmpty())
+ {
+ lcl_string_append(r.append("["), aDocName);
+ r.append("]");
+ }
+ lcl_string_append(r, aTabName);
+ r.append("!");
+ break;
+ }
+ }
+ }
+ switch( rDetails.eConv )
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO:
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if( nFlags & ScRefFlags::COL_VALID )
+ lcl_a1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
+ if( nFlags & ScRefFlags::ROW_VALID )
+ lcl_a1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ if( nFlags & ScRefFlags::ROW_VALID )
+ lcl_r1c1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
+ if( nFlags & ScRefFlags::COL_VALID )
+ lcl_r1c1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
+ break;
+ }
+}
+
+void ScAddress::Format( OStringBuffer& r, ScRefFlags nFlags,
+ const ScDocument* pDoc,
+ const Details& rDetails) const
+{
+ lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
+}
+
+OUString ScAddress::Format(ScRefFlags nFlags, const ScDocument* pDoc,
+ const Details& rDetails) const
+{
+ OUStringBuffer r;
+ lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
+ return r.makeStringAndClear();
+}
+
+static void lcl_Split_DocTab( const ScDocument& rDoc, SCTAB nTab,
+ const ScAddress::Details& rDetails,
+ ScRefFlags nFlags,
+ OUString& rTabName, OUString& rDocName )
+{
+ rDoc.GetName(nTab, rTabName);
+ rDocName.clear();
+ // External reference, same as in ScCompiler::MakeTabStr()
+ if (!rTabName.isEmpty() && rTabName[0] == '\'')
+ { // "'Doc'#Tab"
+ sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName);
+ if (nPos != -1)
+ {
+ rDocName = rTabName.copy( 0, nPos + 1 );
+ rTabName = rTabName.copy( nPos + 1 );
+ }
+ }
+ else if( nFlags & ScRefFlags::FORCE_DOC )
+ {
+ // VBA has an 'external' flag that forces the addition of the
+ // tab name _and_ the doc name. The VBA code would be
+ // needlessly complicated if it constructed an actual external
+ // reference so we add this somewhat cheesy kludge to force the
+ // addition of the document name even for non-external references
+ rDocName = getFileNameFromDoc(&rDoc);
+ }
+ ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
+}
+
+static void lcl_ScRange_Format_XL_Header( OUStringBuffer& rString, const ScRange& rRange,
+ ScRefFlags nFlags, const ScDocument& rDoc,
+ const ScAddress::Details& rDetails )
+{
+ if( !(nFlags & ScRefFlags::TAB_3D) )
+ return;
+
+ OUString aTabName, aDocName;
+ lcl_Split_DocTab( rDoc, rRange.aStart.Tab(), rDetails, nFlags, aTabName, aDocName );
+ switch (rDetails.eConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if (!aTabName.isEmpty() && aTabName[0] == '\'')
+ {
+ if (!aDocName.isEmpty())
+ {
+ rString.append("'[" + aDocName + "]" + aTabName.subView(1));
+ }
+ else
+ {
+ rString.append(aTabName);
+ }
+ break;
+ }
+ [[fallthrough]];
+ default:
+ if (!aDocName.isEmpty())
+ {
+ rString.append("[" + aDocName + "]");
+ }
+ rString.append(aTabName);
+ break;
+ }
+ if( nFlags & ScRefFlags::TAB2_3D )
+ {
+ lcl_Split_DocTab( rDoc, rRange.aEnd.Tab(), rDetails, nFlags, aTabName, aDocName );
+ rString.append(":");
+ rString.append(aTabName);
+ }
+ rString.append("!");
+}
+
+// helpers used in ScRange::Format
+static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags)
+{
+ return static_cast<bool>(nFlags & ScRefFlags::COL_ABS) != static_cast<bool>(nFlags & ScRefFlags::COL2_ABS);
+}
+static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags)
+{
+ return static_cast<bool>(nFlags & ScRefFlags::ROW_ABS) != static_cast<bool>(nFlags & ScRefFlags::ROW2_ABS);
+}
+
+OUString ScRange::Format( const ScDocument& rDoc, ScRefFlags nFlags,
+ const ScAddress::Details& rDetails, bool bFullAddressNotation ) const
+{
+ if( !( nFlags & ScRefFlags::VALID ) )
+ {
+ return ScCompiler::GetNativeSymbol(ocErrRef);
+ }
+
+ OUStringBuffer r;
+ switch( rDetails.eConv ) {
+ default :
+ case formula::FormulaGrammar::CONV_OOO: {
+ bool bOneTab = (aStart.Tab() == aEnd.Tab());
+ if ( !bOneTab )
+ nFlags |= ScRefFlags::TAB_3D;
+ r = aStart.Format(nFlags, &rDoc, rDetails);
+ if( aStart != aEnd ||
+ lcl_ColAbsFlagDiffer( nFlags ) ||
+ lcl_RowAbsFlagDiffer( nFlags ))
+ {
+ const ScDocument* pDoc = &rDoc;
+ // move flags of end reference to start reference, mask with BITS to exclude FORCE_DOC flag
+ nFlags = ScRefFlags::VALID | (ScRefFlags(o3tl::to_underlying(nFlags) >> 4) & ScRefFlags::BITS);
+ if ( bOneTab )
+ pDoc = nullptr;
+ else
+ nFlags |= ScRefFlags::TAB_3D;
+ OUString aName(aEnd.Format(nFlags, pDoc, rDetails));
+ r.append(":");
+ r.append(aName);
+ }
+ break;
+ }
+
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX: {
+ SCCOL nMaxCol = rDoc.MaxCol();
+ SCROW nMaxRow = rDoc.MaxRow();
+
+ lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
+ if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
+ {
+ // Full col refs always require 2 rows (2:2)
+ lcl_a1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
+ r.append(":");
+ lcl_a1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
+ }
+ else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
+ {
+ // Full row refs always require 2 cols (A:A)
+ lcl_a1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
+ r.append(":");
+ lcl_a1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
+ }
+ else
+ {
+ lcl_a1_append_c ( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
+ lcl_a1_append_r ( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
+ if( aStart.Col() != aEnd.Col() ||
+ lcl_ColAbsFlagDiffer( nFlags ) ||
+ aStart.Row() != aEnd.Row() ||
+ lcl_RowAbsFlagDiffer( nFlags ) ) {
+ r.append(":");
+ lcl_a1_append_c ( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
+ lcl_a1_append_r ( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
+ }
+ }
+ break;
+ }
+
+ case formula::FormulaGrammar::CONV_XL_R1C1: {
+ SCCOL nMaxCol = rDoc.MaxCol();
+ SCROW nMaxRow = rDoc.MaxRow();
+
+ lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
+ if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
+ {
+ lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
+ if( aStart.Row() != aEnd.Row() ||
+ lcl_RowAbsFlagDiffer( nFlags ) ) {
+ r.append(":");
+ lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
+ }
+ }
+ else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
+ {
+ lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
+ if( aStart.Col() != aEnd.Col() ||
+ lcl_ColAbsFlagDiffer( nFlags )) {
+ r.append(":");
+ lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
+ }
+ }
+ else
+ {
+ lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
+ lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
+ if( aStart.Col() != aEnd.Col() ||
+ lcl_ColAbsFlagDiffer( nFlags ) ||
+ aStart.Row() != aEnd.Row() ||
+ lcl_RowAbsFlagDiffer( nFlags ) ) {
+ r.append(":");
+ lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
+ lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
+ }
+ }
+ break;
+ }
+ }
+ return r.makeStringAndClear();
+}
+
+bool ScAddress::Move( SCCOL dx, SCROW dy, SCTAB dz, ScAddress& rErrorPos, const ScDocument& rDoc )
+{
+ SCTAB nMaxTab = rDoc.GetTableCount();
+ SCCOL nMaxCol = rDoc.MaxCol();
+ SCROW nMaxRow = rDoc.MaxRow();
+ dx = Col() + dx;
+ dy = Row() + dy;
+ dz = Tab() + dz;
+ bool bValid = true;
+ rErrorPos.SetCol(dx);
+ if( dx < 0 )
+ {
+ dx = 0;
+ bValid = false;
+ }
+ else if( dx > nMaxCol )
+ {
+ dx = nMaxCol;
+ bValid =false;
+ }
+ rErrorPos.SetRow(dy);
+ if( dy < 0 )
+ {
+ dy = 0;
+ bValid = false;
+ }
+ else if( dy > nMaxRow )
+ {
+ dy = nMaxRow;
+ bValid =false;
+ }
+ rErrorPos.SetTab(dz);
+ if( dz < 0 )
+ {
+ dz = 0;
+ bValid = false;
+ }
+ else if( dz > nMaxTab )
+ {
+ // Always set MAXTAB+1 so further checks without ScDocument detect invalid.
+ rErrorPos.SetTab(MAXTAB+1);
+ dz = nMaxTab;
+ bValid =false;
+ }
+ Set( dx, dy, dz );
+ return bValid;
+}
+
+bool ScRange::Move( SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange, const ScDocument& rDoc )
+{
+ SCCOL nMaxCol = rDoc.MaxCol();
+ SCROW nMaxRow = rDoc.MaxRow();
+ if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
+ dy = 0; // Entire column not to be moved.
+ if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
+ dx = 0; // Entire row not to be moved.
+ bool b = aStart.Move( dx, dy, dz, rErrorRange.aStart, rDoc );
+ b &= aEnd.Move( dx, dy, dz, rErrorRange.aEnd, rDoc );
+ return b;
+}
+
+bool ScRange::MoveSticky( const ScDocument& rDoc, SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange )
+{
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ const SCROW nMaxRow = rDoc.MaxRow();
+ bool bColRange = (aStart.Col() < aEnd.Col());
+ bool bRowRange = (aStart.Row() < aEnd.Row());
+ if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
+ dy = 0; // Entire column not to be moved.
+ if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
+ dx = 0; // Entire row not to be moved.
+ bool b1 = aStart.Move( dx, dy, dz, rErrorRange.aStart, rDoc );
+ if (dx && bColRange && aEnd.Col() == nMaxCol)
+ dx = 0; // End column sticky.
+ if (dy && bRowRange && aEnd.Row() == nMaxRow)
+ dy = 0; // End row sticky.
+ SCTAB nOldTab = aEnd.Tab();
+ bool b2 = aEnd.Move( dx, dy, dz, rErrorRange.aEnd, rDoc );
+ if (!b2)
+ {
+ // End column or row of a range may have become sticky.
+ bColRange = (!dx || (bColRange && aEnd.Col() == nMaxCol));
+ if (dx && bColRange)
+ rErrorRange.aEnd.SetCol(nMaxCol);
+ bRowRange = (!dy || (bRowRange && aEnd.Row() == nMaxRow));
+ if (dy && bRowRange)
+ rErrorRange.aEnd.SetRow(nMaxRow);
+ b2 = bColRange && bRowRange && (aEnd.Tab() - nOldTab == dz);
+ }
+ return b1 && b2;
+}
+
+void ScRange::IncColIfNotLessThan(const ScDocument& rDoc, SCCOL nStartCol, SCCOL nOffset)
+{
+ if (aStart.Col() >= nStartCol)
+ {
+ aStart.IncCol(nOffset);
+ if (aStart.Col() < 0)
+ aStart.SetCol(0);
+ else if(aStart.Col() > rDoc.MaxCol())
+ aStart.SetCol(rDoc.MaxCol());
+ }
+ if (aEnd.Col() >= nStartCol)
+ {
+ aEnd.IncCol(nOffset);
+ if (aEnd.Col() < 0)
+ aEnd.SetCol(0);
+ else if(aEnd.Col() > rDoc.MaxCol())
+ aEnd.SetCol(rDoc.MaxCol());
+ }
+}
+
+void ScRange::IncRowIfNotLessThan(const ScDocument& rDoc, SCROW nStartRow, SCROW nOffset)
+{
+ if (aStart.Row() >= nStartRow)
+ {
+ aStart.IncRow(nOffset);
+ if (aStart.Row() < 0)
+ aStart.SetRow(0);
+ else if(aStart.Row() > rDoc.MaxRow())
+ aStart.SetRow(rDoc.MaxRow());
+ }
+ if (aEnd.Row() >= nStartRow)
+ {
+ aEnd.IncRow(nOffset);
+ if (aEnd.Row() < 0)
+ aEnd.SetRow(0);
+ else if(aEnd.Row() > rDoc.MaxRow())
+ aEnd.SetRow(rDoc.MaxRow());
+ }
+}
+
+bool ScRange::IsEndColSticky( const ScDocument& rDoc ) const
+{
+ // Only in an actual column range, i.e. not if both columns are MAXCOL.
+ return aEnd.Col() == rDoc.MaxCol() && aStart.Col() < aEnd.Col();
+}
+
+bool ScRange::IsEndRowSticky( const ScDocument& rDoc ) const
+{
+ // Only in an actual row range, i.e. not if both rows are MAXROW.
+ return aEnd.Row() == rDoc.MaxRow() && aStart.Row() < aEnd.Row();
+}
+
+void ScRange::IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta )
+{
+ SCCOL nCol = aEnd.Col();
+ if (aStart.Col() >= nCol)
+ {
+ // Less than two columns => not sticky.
+ aEnd.IncCol( nDelta);
+ return;
+ }
+
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ if (nCol == nMaxCol)
+ // already sticky
+ return;
+
+ if (nCol < nMaxCol)
+ aEnd.SetCol( ::std::min( static_cast<SCCOL>(nCol + nDelta), nMaxCol));
+ else
+ aEnd.IncCol( nDelta); // was greater than nMaxCol, caller should know...
+}
+
+void ScRange::IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta )
+{
+ SCROW nRow = aEnd.Row();
+ if (aStart.Row() >= nRow)
+ {
+ // Less than two rows => not sticky.
+ aEnd.IncRow( nDelta);
+ return;
+ }
+
+ if (nRow == rDoc.MaxRow())
+ // already sticky
+ return;
+
+ if (nRow < rDoc.MaxRow())
+ aEnd.SetRow( ::std::min( static_cast<SCROW>(nRow + nDelta), rDoc.MaxRow()));
+ else
+ aEnd.IncRow( nDelta); // was greater than rDoc.MaxRow(), caller should know...
+}
+
+OUString ScAddress::GetColRowString() const
+{
+ OUStringBuffer aString;
+
+ switch( detailsOOOa1.eConv )
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO:
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ lcl_ScColToAlpha( aString, nCol);
+ aString.append(nRow+1);
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ lcl_r1c1_append_r ( aString, nRow, false/*bAbsolute*/, detailsOOOa1 );
+ lcl_r1c1_append_c ( aString, nCol, false/*bAbsolute*/, detailsOOOa1 );
+ break;
+ }
+
+ return aString.makeStringAndClear();
+}
+
+OUString ScRefAddress::GetRefString( const ScDocument& rDoc, SCTAB nActTab,
+ const ScAddress::Details& rDetails ) const
+{
+ if ( Tab()+1 > rDoc.GetTableCount() )
+ return ScCompiler::GetNativeSymbol(ocErrRef);
+
+ ScRefFlags nFlags = ScRefFlags::VALID;
+ if ( nActTab != Tab() )
+ {
+ nFlags |= ScRefFlags::TAB_3D;
+ if ( !bRelTab )
+ nFlags |= ScRefFlags::TAB_ABS;
+ }
+ if ( !bRelCol )
+ nFlags |= ScRefFlags::COL_ABS;
+ if ( !bRelRow )
+ nFlags |= ScRefFlags::ROW_ABS;
+
+ return aAdr.Format(nFlags, &rDoc, rDetails);
+}
+
+bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, const OUString& rStr)
+{
+ SCCOL nResult = 0;
+ sal_Int32 nStop = rStr.getLength();
+ sal_Int32 nPos = 0;
+ sal_Unicode c;
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ while (nResult <= nMaxCol && nPos < nStop && (c = rStr[nPos]) != 0 &&
+ rtl::isAsciiAlpha(c))
+ {
+ if (nPos > 0)
+ nResult = (nResult + 1) * 26;
+ nResult += ScGlobal::ToUpperAlpha(c) - 'A';
+ ++nPos;
+ }
+ bool bOk = (rDoc.ValidCol(nResult) && nPos > 0);
+ if (bOk)
+ rCol = nResult;
+ return bOk;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */