xref: /core/sc/source/core/tool/address.cxx (revision fe687d1b)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 
24 #include <address.hxx>
25 #include <global.hxx>
26 #include <compiler.hxx>
27 #include <document.hxx>
28 #include <externalrefmgr.hxx>
29 
30 #include <osl/diagnose.h>
31 #include <o3tl/underlyingenumvalue.hxx>
32 #include <com/sun/star/frame/XModel.hpp>
33 #include <com/sun/star/sheet/ExternalLinkInfo.hpp>
34 #include <com/sun/star/sheet/ExternalLinkType.hpp>
35 #include <sfx2/objsh.hxx>
36 #include <tools/urlobj.hxx>
37 #include <sal/log.hxx>
38 #include <rtl/character.hxx>
39 #include <unotools/charclass.hxx>
40 
41 using namespace css;
42 
43 const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 );
44 
45 ScAddress::Details::Details ( const ScDocument& rDoc,
46                               const ScAddress& rAddr ) :
47     eConv( rDoc.GetAddressConvention() ),
48     nRow( rAddr.Row() ),
49     nCol( rAddr.Col() )
50 {}
51 
52 namespace {
53 
54 const sal_Unicode* parseQuotedNameWithBuffer( const sal_Unicode* pStart, const sal_Unicode* p, OUString& rName )
55 {
56     // The current character must be on the 2nd quote.
57 
58     // Push all the characters up to the current, but skip the very first
59     // character which is the opening quote.
60     OUStringBuffer aBuf(std::u16string_view(pStart+1, p-pStart-1));
61 
62     ++p; // Skip the 2nd quote.
63     sal_Unicode cPrev = 0;
64     for (; *p; ++p)
65     {
66         if (*p == '\'')
67         {
68             if (cPrev == '\'')
69             {
70                 // double single-quote equals one single quote.
71                 aBuf.append(*p);
72                 cPrev = 0;
73                 continue;
74             }
75         }
76         else if (cPrev == '\'')
77         {
78             // We are past the closing quote.  We're done!
79             rName = aBuf.makeStringAndClear();
80             return p;
81         }
82         else
83             aBuf.append(*p);
84         cPrev = *p;
85     }
86 
87     return pStart;
88 }
89 
90 /**
91  * Parse from the opening single quote to the closing single quote.  Inside
92  * the quotes, a single quote character is encoded by double single-quote
93  * characters.
94  *
95  * @param p pointer to the first character to begin parsing.
96  * @param rName (reference) parsed name within the quotes.  If the name is
97  *              empty, either the parsing failed or it's an empty quote.
98  *
99  * @return pointer to the character immediately after the closing single
100  *         quote.
101  */
102 const sal_Unicode* parseQuotedName( const sal_Unicode* p, OUString& rName )
103 {
104     if (*p != '\'')
105         return p;
106 
107     const sal_Unicode* pStart = p;
108     sal_Unicode cPrev = 0;
109     for (++p; *p; ++p)
110     {
111         if (*p == '\'')
112         {
113             if (cPrev == '\'')
114             {
115                 // double single-quote equals one single quote.
116                 return parseQuotedNameWithBuffer(pStart, p, rName);
117             }
118         }
119         else if (cPrev == '\'')
120         {
121             // We are past the closing quote.  We're done!  Skip the opening
122             // and closing quotes.
123             rName = OUString(pStart+1, p - pStart-2);
124             return p;
125         }
126 
127         cPrev = *p;
128     }
129 
130     rName.clear();
131     return pStart;
132 }
133 
134 }
135 
136 static sal_Int64 sal_Unicode_strtol ( const sal_Unicode*  p, const sal_Unicode** pEnd )
137 {
138     sal_Int64 accum = 0, prev = 0;
139     bool is_neg = false;
140 
141     if( *p == '-' )
142     {
143         is_neg = true;
144         p++;
145     }
146     else if( *p == '+' )
147         p++;
148 
149     while (rtl::isAsciiDigit( *p ))
150     {
151         accum = accum * 10 + *p - '0';
152         if( accum < prev )
153         {
154             *pEnd = nullptr;
155             return 0;
156         }
157         prev = accum;
158         p++;
159     }
160 
161     *pEnd = p;
162     return is_neg ? -accum : accum;
163 }
164 
165 static const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p )
166 {
167     if ( p )
168     {
169         while(  *p == ' ' )
170             ++p;
171     }
172     return p;
173 }
174 
175 // Compare ignore case ASCII.
176 static bool lcl_isString( const sal_Unicode* p1, const OUString& rStr )
177 {
178     const size_t n = rStr.getLength();
179     if (!n)
180         return false;
181     const sal_Unicode* p2 = rStr.getStr();
182     for (size_t i=0; i<n; ++i)
183     {
184         if (!p1[i])
185             return false;
186         if (p1[i] != p2[i])
187         {
188             sal_Unicode c1 = p1[i];
189             if ('A' <= c1 && c1 <= 'Z')
190                 c1 += 0x20;
191             if (c1 < 'a' || 'z' < c1)
192                 return false;   // not a letter
193 
194             sal_Unicode c2 = p2[i];
195             if ('A' <= c2 && c2 <= 'Z')
196                 c2 += 0x20;
197             if (c2 < 'a' || 'z' < c2)
198                 return false;   // not a letter to match
199 
200             if (c1 != c2)
201                 return false;   // lower case doesn't match either
202         }
203     }
204     return true;
205 }
206 
207 /** Determines the number of sheets an external reference spans and sets
208     rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
209     bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
210     cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
211     is set to rEndTabName.
212     @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
213              result in the identical file ID. Else <TRUE/>.
214  */
215 static bool lcl_ScRange_External_TabSpan(
216                             ScRange & rRange,
217                             ScRefFlags & rFlags,
218                             ScAddress::ExternalInfo* pExtInfo,
219                             const OUString & rExternDocName,
220                             const OUString & rStartTabName,
221                             const OUString & rEndTabName,
222                             const ScDocument& rDoc )
223 {
224     if (rExternDocName.isEmpty())
225         return !pExtInfo || !pExtInfo->mbExternal;
226 
227     ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
228     if (pRefMgr->isOwnDocument( rExternDocName))
229     {
230         // This is an internal document.  Get the sheet positions from the
231         // ScDocument instance.
232         if (!rStartTabName.isEmpty())
233         {
234             SCTAB nTab;
235             if (rDoc.GetTable(rStartTabName, nTab))
236                 rRange.aStart.SetTab(nTab);
237         }
238 
239         if (!rEndTabName.isEmpty())
240         {
241             SCTAB nTab;
242             if (rDoc.GetTable(rEndTabName, nTab))
243                 rRange.aEnd.SetTab(nTab);
244         }
245         return !pExtInfo || !pExtInfo->mbExternal;
246     }
247 
248     sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName);
249 
250     if (pExtInfo)
251     {
252         if (pExtInfo->mbExternal)
253         {
254             if (pExtInfo->mnFileId != nFileId)
255                 return false;
256         }
257         else
258         {
259             pExtInfo->mbExternal = true;
260             pExtInfo->maTabName = rStartTabName;
261             pExtInfo->mnFileId = nFileId;
262         }
263     }
264 
265     if (rEndTabName.isEmpty() || rStartTabName == rEndTabName)
266     {
267         rRange.aEnd.SetTab( rRange.aStart.Tab());
268         return true;
269     }
270 
271     SCTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName);
272     if (nSpan == -1)
273         rFlags &= ~ScRefFlags(ScRefFlags::TAB_VALID | ScRefFlags::TAB2_VALID);
274     else if (nSpan == 0)
275         rFlags &= ~ScRefFlags::TAB2_VALID;
276     else if (nSpan >= 1)
277         rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1);
278     else // (nSpan < -1)
279     {
280         rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1);
281         if (pExtInfo)
282             pExtInfo->maTabName = rEndTabName;
283     }
284     return true;
285 }
286 
287 /** Returns NULL if the string should be a sheet name, but is invalid.
288     Returns a pointer to the first character after the sheet name, if there was
289     any, else pointer to start.
290     @param pMsoxlQuoteStop
291         Starting _within_ a quoted name, but still may be 3D; quoted name stops
292         at pMsoxlQuoteStop
293  */
294 static const sal_Unicode * lcl_XL_ParseSheetRef( const sal_Unicode* start,
295                                                  OUString& rExternTabName,
296                                                  bool bAllow3D,
297                                                  const sal_Unicode* pMsoxlQuoteStop,
298                                                  const OUString* pErrRef )
299 {
300     OUString aTabName;
301     const sal_Unicode *p = start;
302 
303     // XL only seems to use single quotes for sheet names.
304     if (pMsoxlQuoteStop)
305     {
306         const sal_Unicode* pCurrentStart = p;
307         while (p < pMsoxlQuoteStop)
308         {
309             if (*p == '\'')
310             {
311                 // We pre-analyzed the quoting, no checks needed here.
312                 if (*++p == '\'')
313                 {
314                     aTabName += std::u16string_view( pCurrentStart,
315                             sal::static_int_cast<sal_Int32>( p - pCurrentStart));
316                     pCurrentStart = ++p;
317                 }
318             }
319             else if (*p == ':')
320             {
321                 break;  // while
322             }
323             else
324                 ++p;
325         }
326         if (pCurrentStart < p)
327             aTabName += std::u16string_view( pCurrentStart, sal::static_int_cast<sal_Int32>( p - pCurrentStart));
328         if (aTabName.isEmpty())
329             return nullptr;
330         if (p == pMsoxlQuoteStop)
331             ++p;    // position on ! of ...'!...
332         if( *p != '!' && ( !bAllow3D || *p != ':' ) )
333             return (!bAllow3D && *p == ':') ? p : start;
334     }
335     else if( *p == '\'')
336     {
337         p = parseQuotedName(p, aTabName);
338         if (aTabName.isEmpty())
339             return nullptr;
340     }
341     else if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '!')
342     {
343         p += pErrRef->getLength();  // position after "#REF!" on '!'
344         // XXX NOTE: caller has to check the name and that it wasn't quoted.
345         aTabName = *pErrRef;
346     }
347     else
348     {
349         bool only_digits = true;
350 
351         /*
352          * Valid: Normal!a1
353          * Valid: x.y!a1
354          * Invalid: .y!a1
355          *
356          * Some names starting with digits are actually valid, but
357          * unparse quoted. Things are quite tricky: most sheet names
358          * starting with a digit are ok, but not those starting with
359          * "[0-9]*\." or "[0-9]+[eE]".
360          *
361          * Valid: 42!a1
362          * Valid: 4x!a1
363          * Invalid: 1.!a1
364          * Invalid: 1e!a1
365          */
366         while( true )
367         {
368             const sal_Unicode uc = *p;
369             if( rtl::isAsciiAlpha( uc ) || uc == '_' )
370             {
371                 if( only_digits && p != start &&
372                    (uc == 'e' || uc == 'E' ) )
373                 {
374                     p = start;
375                     break;
376                 }
377                 only_digits = false;
378                 p++;
379             }
380             else if( rtl::isAsciiDigit( uc ))
381             {
382                 p++;
383             }
384             else if( uc == '.' )
385             {
386                 if( only_digits ) // Valid, except after only digits.
387                 {
388                     p = start;
389                     break;
390                 }
391                 p++;
392             }
393             else if (uc > 127)
394             {
395                 // non ASCII character is allowed.
396                 ++p;
397             }
398             else
399                 break;
400         }
401 
402         if( *p != '!' && ( !bAllow3D || *p != ':' ) )
403             return (!bAllow3D && *p == ':') ? p : start;
404 
405         aTabName += std::u16string_view( start, sal::static_int_cast<sal_Int32>( p - start ) );
406     }
407 
408     rExternTabName = aTabName;
409     return p;
410 }
411 
412 /** Tries to obtain the external document index and replace by actual document
413     name.
414 
415     @param ppErrRet
416            Contains the default pointer the caller would return if this method
417            returns FALSE, may be replaced by NULL for type or data errors.
418 
419     @returns FALSE only if the input name is numeric and not within the index
420     sequence, or the link type cannot be determined or data mismatch. Returns
421     TRUE in all other cases, also when there is no index sequence or the input
422     name is not numeric.
423  */
424 static bool lcl_XL_getExternalDoc( const sal_Unicode** ppErrRet, OUString& rExternDocName,
425                                    const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
426 {
427     // 1-based, sequence starts with an empty element.
428     if (pExternalLinks && pExternalLinks->hasElements())
429     {
430         // A numeric "document name" is an index into the sequence.
431         if (CharClass::isAsciiNumeric( rExternDocName))
432         {
433             sal_Int32 i = rExternDocName.toInt32();
434             if (i < 0 || i >= pExternalLinks->getLength())
435                 return false;   // with default *ppErrRet
436             const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i];
437             switch (rInfo.Type)
438             {
439                 case sheet::ExternalLinkType::DOCUMENT :
440                     {
441                         OUString aStr;
442                         if (!(rInfo.Data >>= aStr))
443                         {
444                             SAL_INFO(
445                                 "sc.core",
446                                 "Data type mismatch for ExternalLinkInfo "
447                                     << i);
448                             *ppErrRet = nullptr;
449                             return false;
450                         }
451                         rExternDocName = aStr;
452                     }
453                     break;
454                     case sheet::ExternalLinkType::SELF :
455                         return false;   // ???
456                     case sheet::ExternalLinkType::SPECIAL :
457                         // silently return nothing (do not assert), caller has to handle this
458                         *ppErrRet = nullptr;
459                         return false;
460                 default:
461                     SAL_INFO(
462                         "sc.core",
463                         "unhandled ExternalLinkType " << rInfo.Type
464                             << " for index " << i);
465                     *ppErrRet = nullptr;
466                     return false;
467             }
468         }
469     }
470     return true;
471 }
472 
473 const sal_Unicode* ScRange::Parse_XL_Header(
474                                 const sal_Unicode* p,
475                                 const ScDocument& rDoc,
476                                 OUString& rExternDocName,
477                                 OUString& rStartTabName,
478                                 OUString& rEndTabName,
479                                 ScRefFlags& nFlags,
480                                 bool bOnlyAcceptSingle,
481                                 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
482                                 const OUString* pErrRef )
483 {
484     const sal_Unicode* startTabs, *start = p;
485     ScRefFlags nSaveFlags = nFlags;
486 
487     // Is this an external reference ?
488     rStartTabName.clear();
489     rEndTabName.clear();
490     rExternDocName.clear();
491     const sal_Unicode* pMsoxlQuoteStop = nullptr;
492     if (*p == '[')
493     {
494         ++p;
495         // Only single quotes are correct, and a double single quote escapes a
496         // single quote text inside the quoted text.
497         if (*p == '\'')
498         {
499             p = parseQuotedName(p, rExternDocName);
500             if (*p != ']' || rExternDocName.isEmpty())
501             {
502                 rExternDocName.clear();
503                 return start;
504             }
505         }
506         else
507         {
508             // non-quoted file name.
509             p = ScGlobal::UnicodeStrChr( start+1, ']' );
510             if( p == nullptr )
511                 return start;
512             rExternDocName += std::u16string_view( start+1, sal::static_int_cast<sal_Int32>( p-(start+1) ) );
513         }
514         ++p;
515 
516         const sal_Unicode* pErrRet = start;
517         if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
518             return pErrRet;
519 
520         rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, rDoc.GetDocumentShell());
521     }
522     else if (*p == '\'')
523     {
524         // Sickness in Excel's ODF msoxl namespace:
525         // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7  or
526         // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
527         // But, 'Sheet1'!B3 would also be a valid!
528         // Excel does not allow [ and ] characters in sheet names though.
529         // But, more sickness comes with MOOXML as there may be
530         // '[1]Sheet 4'!$A$1  where [1] is the external doc's index.
531         p = parseQuotedName(p, rExternDocName);
532         if (*p != '!')
533         {
534             rExternDocName.clear();
535             return start;
536         }
537         if (!rExternDocName.isEmpty())
538         {
539             sal_Int32 nOpen = rExternDocName.indexOf( '[');
540             if (nOpen == -1)
541                 rExternDocName.clear();
542             else
543             {
544                 sal_Int32 nClose = rExternDocName.indexOf( ']', nOpen+1);
545                 if (nClose == -1)
546                     rExternDocName.clear();
547                 else
548                 {
549                     rExternDocName = rExternDocName.copy(0, nClose);
550                     rExternDocName = rExternDocName.replaceAt( nOpen, 1, u"");
551                     pMsoxlQuoteStop = p - 1;    // the ' quote char
552                     // There may be embedded escaped quotes, just matching the
553                     // doc name's length may not work.
554                     for (p = start; *p != '['; ++p)
555                         ;
556                     for ( ; *p != ']'; ++p)
557                         ;
558                     ++p;
559 
560                     // Handle '[1]Sheet 4'!$A$1
561                     if (nOpen == 0)
562                     {
563                         const sal_Unicode* pErrRet = start;
564                         if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
565                             return pErrRet;
566                     }
567                 }
568             }
569         }
570         if (rExternDocName.isEmpty())
571             p = start;
572     }
573 
574     startTabs = p;
575     p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop, pErrRef);
576     if( nullptr == p )
577         return start;       // invalid tab
578     if (bOnlyAcceptSingle && *p == ':')
579         return nullptr;        // 3D
580     const sal_Unicode* startEndTabs = nullptr;
581     if( p != startTabs )
582     {
583         nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D | ScRefFlags::TAB_ABS;
584         if( *p == ':' ) // 3d ref
585         {
586             startEndTabs = p + 1;
587             p = lcl_XL_ParseSheetRef( startEndTabs, rEndTabName, false, pMsoxlQuoteStop, pErrRef);
588             if( p == nullptr )
589             {
590                 nFlags = nSaveFlags;
591                 return start; // invalid tab
592             }
593             nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS;
594         }
595         else
596         {
597             // If only one sheet is given, the full reference is still valid,
598             // only the second 3D flag is not set.
599             nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_ABS;
600             aEnd.SetTab( aStart.Tab() );
601         }
602 
603         if( *p++ != '!' )
604         {
605             nFlags = nSaveFlags;
606             return start;   // syntax error
607         }
608         else
609             p = lcl_eatWhiteSpace( p );
610     }
611     else
612     {
613         nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB2_VALID;
614         // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
615     }
616 
617     if (!rExternDocName.isEmpty())
618     {
619         ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
620         pRefMgr->convertToAbsName(rExternDocName);
621     }
622     else
623     {
624         // Internal reference.
625         if (rStartTabName.isEmpty())
626         {
627             nFlags = nSaveFlags;
628             return start;
629         }
630 
631         SCTAB nTab;
632         if ((pErrRef && *startTabs != '\'' && rStartTabName == *pErrRef) || !rDoc.GetTable(rStartTabName, nTab))
633         {
634             // invalid table name.
635             nFlags &= ~ScRefFlags::TAB_VALID;
636             nTab = -1;
637         }
638 
639         aStart.SetTab(nTab);
640         aEnd.SetTab(nTab);
641 
642         if (!rEndTabName.isEmpty())
643         {
644             if ((pErrRef && startEndTabs && *startEndTabs != '\'' && rEndTabName == *pErrRef) ||
645                     !rDoc.GetTable(rEndTabName, nTab))
646             {
647                 // invalid table name.
648                 nFlags &= ~ScRefFlags::TAB2_VALID;
649                 nTab = -1;
650             }
651 
652             aEnd.SetTab(nTab);
653         }
654     }
655     return p;
656 }
657 
658 static const sal_Unicode* lcl_r1c1_get_col( const ScSheetLimits& rSheetLimits,
659                                             const sal_Unicode* p,
660                                             const ScAddress::Details& rDetails,
661                                             ScAddress* pAddr, ScRefFlags* nFlags )
662 {
663     const sal_Unicode *pEnd;
664     sal_Int64 n;
665     bool isRelative;
666 
667     if( p[0] == '\0' )
668         return nullptr;
669 
670     p++;
671     isRelative = *p == '[';
672     if( isRelative )
673         p++;
674     n = sal_Unicode_strtol( p, &pEnd );
675     if( nullptr == pEnd )
676         return nullptr;
677 
678     if( p == pEnd ) // C is a relative ref with offset 0
679     {
680         if( isRelative )
681             return nullptr;
682         n = rDetails.nCol;
683     }
684     else if( isRelative )
685     {
686         if( *pEnd != ']' )
687             return nullptr;
688         n += rDetails.nCol;
689         pEnd++;
690     }
691     else
692     {
693         *nFlags |= ScRefFlags::COL_ABS;
694         n--;
695     }
696 
697     if( n < 0 || n >= rSheetLimits.GetMaxColCount())
698         return nullptr;
699     pAddr->SetCol( static_cast<SCCOL>( n ) );
700     *nFlags |= ScRefFlags::COL_VALID;
701 
702     return pEnd;
703 }
704 
705 static const sal_Unicode* lcl_r1c1_get_row(
706                                     const ScSheetLimits& rSheetLimits,
707                                     const sal_Unicode* p,
708                                     const ScAddress::Details& rDetails,
709                                     ScAddress* pAddr, ScRefFlags* nFlags )
710 {
711     const sal_Unicode *pEnd;
712     bool isRelative;
713 
714     if( p[0] == '\0' )
715         return nullptr;
716 
717     p++;
718     isRelative = *p == '[';
719     if( isRelative )
720         p++;
721     sal_Int64 n = sal_Unicode_strtol( p, &pEnd );
722     if( nullptr == pEnd )
723         return nullptr;
724 
725     if( p == pEnd ) // R is a relative ref with offset 0
726     {
727         if( isRelative )
728             return nullptr;
729         n = rDetails.nRow;
730     }
731     else if( isRelative )
732     {
733         if( *pEnd != ']' )
734             return nullptr;
735         n += rDetails.nRow;
736         pEnd++;
737     }
738     else
739     {
740         *nFlags |= ScRefFlags::ROW_ABS;
741         n--;
742     }
743 
744     if( n < 0 || n >= rSheetLimits.GetMaxRowCount() )
745         return nullptr;
746     pAddr->SetRow( static_cast<SCROW>( n ) );
747     *nFlags |= ScRefFlags::ROW_VALID;
748 
749     return pEnd;
750 }
751 
752 static ScRefFlags lcl_ScRange_Parse_XL_R1C1( ScRange& r,
753                                              const sal_Unicode* p,
754                                              const ScDocument& rDoc,
755                                              const ScAddress::Details& rDetails,
756                                              bool bOnlyAcceptSingle,
757                                              ScAddress::ExternalInfo* pExtInfo,
758                                              sal_Int32* pSheetEndPos )
759 {
760     const sal_Unicode* const pStart = p;
761     if (pSheetEndPos)
762         *pSheetEndPos = 0;
763     const sal_Unicode* pTmp = nullptr;
764     OUString aExternDocName, aStartTabName, aEndTabName;
765     ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID;
766     // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
767     ScRefFlags nFlags2 = ScRefFlags::TAB_VALID;
768 
769     p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName,
770             aEndTabName, nFlags, bOnlyAcceptSingle );
771 
772     ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
773     if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
774     {
775         *pSheetEndPos = p - pStart;
776         nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
777     }
778 
779     if (!aExternDocName.isEmpty())
780         lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
781                 aStartTabName, aEndTabName, rDoc);
782 
783     if( nullptr == p )
784         return ScRefFlags::ZERO;
785 
786     if( *p == 'R' || *p == 'r' )
787     {
788         if( nullptr == (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )) )
789             return nBailOutFlags;
790 
791         if( *p != 'C' && *p != 'c' )    // full row R#
792         {
793             if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) ||
794                 nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )))
795             {
796                 // Only the initial row number is given, or the second row
797                 // number is invalid. Fallback to just the initial R
798                 applyStartToEndFlags(nFlags);
799                 r.aEnd.SetRow( r.aStart.Row() );
800             }
801             else // pTmp != nullptr
802             {
803                 // Full row range successfully parsed.
804                 applyStartToEndFlags(nFlags, nFlags2);
805                 p = pTmp;
806             }
807 
808             if (p[0] != 0)
809             {
810                 // any trailing invalid character must invalidate the whole address.
811                 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
812                             ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
813                 return nFlags;
814             }
815 
816             nFlags |=
817                 ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID |
818                 ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
819             r.aStart.SetCol( 0 );
820             r.aEnd.SetCol( rDoc.MaxCol() );
821 
822             return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
823         }
824         else if( nullptr == (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )))
825         {
826             return ScRefFlags::ZERO;
827         }
828 
829         if( p[0] != ':' ||
830             (p[1] != 'R' && p[1] != 'r') ||
831             nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )) ||
832             (*pTmp != 'C' && *pTmp != 'c') ||
833             nullptr == (pTmp = lcl_r1c1_get_col( rDoc.GetSheetLimits(), pTmp, rDetails, &r.aEnd, &nFlags2 )))
834         {
835             // single cell reference
836 
837             if (p[0] != 0)
838             {
839                 // any trailing invalid character must invalidate the whole address.
840                 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID);
841                 return nFlags;
842             }
843 
844             return bOnlyAcceptSingle ? nFlags : ScRefFlags::ZERO;
845         }
846         assert(pTmp);
847         p = pTmp;
848 
849         // double reference
850 
851         if (p[0] != 0)
852         {
853             // any trailing invalid character must invalidate the whole range.
854             nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
855                         ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
856             return nFlags;
857         }
858 
859         applyStartToEndFlags(nFlags, nFlags2);
860         return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
861     }
862     else if( *p == 'C' || *p == 'c' )   // full col C#
863     {
864         if( nullptr == (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )))
865             return nBailOutFlags;
866 
867         if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') ||
868             nullptr == (pTmp = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )))
869         {    // Fallback to just the initial C
870             applyStartToEndFlags(nFlags);
871             r.aEnd.SetCol( r.aStart.Col() );
872         }
873         else // pTmp != nullptr
874         {
875             applyStartToEndFlags(nFlags, nFlags2);
876             p = pTmp;
877         }
878 
879         if (p[0] != 0)
880         {
881             // any trailing invalid character must invalidate the whole address.
882             nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
883                         ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
884             return nFlags;
885         }
886 
887         nFlags |=
888             ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID |
889             ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
890         r.aStart.SetRow( 0 );
891         r.aEnd.SetRow( rDoc.MaxRow() );
892 
893         return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
894     }
895 
896     return nBailOutFlags;
897 }
898 
899 static const sal_Unicode* lcl_a1_get_col( const ScDocument& rDoc,
900                                                 const sal_Unicode* p,
901                                                  ScAddress* pAddr,
902                                                  ScRefFlags* nFlags,
903                                                  const OUString* pErrRef )
904 {
905     if( *p == '$' )
906     {
907         *nFlags |= ScRefFlags::COL_ABS;
908         p++;
909     }
910 
911     if (pErrRef && lcl_isString( p, *pErrRef))
912     {
913         p += pErrRef->getLength();
914         *nFlags &= ~ScRefFlags::COL_VALID;
915         pAddr->SetCol(-1);
916         return p;
917     }
918 
919     if( !rtl::isAsciiAlpha( *p ) )
920         return nullptr;
921 
922     sal_Int64 nCol = rtl::toAsciiUpperCase( *p++ ) - 'A';
923     const SCCOL nMaxCol = rDoc.MaxCol();
924     while (nCol <= nMaxCol && rtl::isAsciiAlpha(*p))
925         nCol = ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A';
926     if( nCol > nMaxCol || nCol < 0 || rtl::isAsciiAlpha( *p ) )
927         return nullptr;
928 
929     *nFlags |= ScRefFlags::COL_VALID;
930     pAddr->SetCol( sal::static_int_cast<SCCOL>( nCol ));
931 
932     return p;
933 }
934 
935 static const sal_Unicode* lcl_a1_get_row( const ScDocument& rDoc,
936                                                  const sal_Unicode* p,
937                                                  ScAddress* pAddr,
938                                                  ScRefFlags* nFlags,
939                                                  const OUString* pErrRef )
940 {
941     const sal_Unicode *pEnd;
942 
943     if( *p == '$' )
944     {
945         *nFlags |= ScRefFlags::ROW_ABS;
946         p++;
947     }
948 
949     if (pErrRef && lcl_isString( p, *pErrRef))
950     {
951         p += pErrRef->getLength();
952         *nFlags &= ~ScRefFlags::ROW_VALID;
953         pAddr->SetRow(-1);
954         return p;
955     }
956 
957     sal_Int64 n = sal_Unicode_strtol( p, &pEnd ) - 1;
958     if( nullptr == pEnd || p == pEnd || n < 0 || n > rDoc.MaxRow() )
959         return nullptr;
960 
961     *nFlags |= ScRefFlags::ROW_VALID;
962     pAddr->SetRow( sal::static_int_cast<SCROW>(n) );
963 
964     return pEnd;
965 }
966 
967 /// B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ...
968 static bool isValidSingleton( ScRefFlags nFlags, ScRefFlags nFlags2 )
969 {
970     bool bCols = (nFlags & ScRefFlags::COL_VALID) && ((nFlags & ScRefFlags::COL2_VALID) || (nFlags2 & ScRefFlags::COL_VALID));
971     bool bRows = (nFlags & ScRefFlags::ROW_VALID) && ((nFlags & ScRefFlags::ROW2_VALID) || (nFlags2 & ScRefFlags::ROW_VALID));
972     return bCols != bRows;
973 }
974 
975 static ScRefFlags lcl_ScRange_Parse_XL_A1( ScRange& r,
976                                            const sal_Unicode* p,
977                                            const ScDocument& rDoc,
978                                            bool bOnlyAcceptSingle,
979                                            ScAddress::ExternalInfo* pExtInfo,
980                                            const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
981                                            sal_Int32* pSheetEndPos,
982                                            const OUString* pErrRef )
983 {
984     const sal_Unicode* const pStart = p;
985     if (pSheetEndPos)
986         *pSheetEndPos = 0;
987     const sal_Unicode* tmp1, *tmp2;
988     OUString aExternDocName, aStartTabName, aEndTabName; // for external link table
989     ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID, nFlags2 = ScRefFlags::TAB_VALID;
990 
991     p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName,
992             aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks, pErrRef );
993 
994     ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
995     if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
996     {
997         *pSheetEndPos = p - pStart;
998         nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
999     }
1000 
1001     if (!aExternDocName.isEmpty())
1002         lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
1003                 aStartTabName, aEndTabName, rDoc);
1004 
1005     if( nullptr == p )
1006         return nBailOutFlags;
1007 
1008     tmp1 = lcl_a1_get_col( rDoc, p, &r.aStart, &nFlags, pErrRef);
1009     if( tmp1 == nullptr )          // Is it a row only reference 3:5
1010     {
1011         if( bOnlyAcceptSingle ) // by definition full row refs are ranges
1012             return nBailOutFlags;
1013 
1014         tmp1 = lcl_a1_get_row( rDoc, p, &r.aStart, &nFlags, pErrRef);
1015 
1016         tmp1 = lcl_eatWhiteSpace( tmp1 );
1017         if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
1018             return nBailOutFlags;
1019 
1020         tmp1 = lcl_eatWhiteSpace( tmp1 );
1021         tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
1022         if( !tmp2 || *tmp2 != 0 )   // Must have fully parsed a singleton.
1023             return nBailOutFlags;
1024 
1025         r.aStart.SetCol( 0 ); r.aEnd.SetCol( rDoc.MaxCol() );
1026         nFlags |=
1027             ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID |
1028             ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
1029         applyStartToEndFlags(nFlags, nFlags2);
1030         return nFlags;
1031     }
1032 
1033     tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aStart, &nFlags, pErrRef);
1034     if( tmp2 == nullptr )          // check for col only reference F:H
1035     {
1036         if( bOnlyAcceptSingle ) // by definition full col refs are ranges
1037             return nBailOutFlags;
1038 
1039         tmp1 = lcl_eatWhiteSpace( tmp1 );
1040         if( *tmp1++ != ':' )    // Even a singleton requires ':' (eg F:F)
1041             return nBailOutFlags;
1042 
1043         tmp1 = lcl_eatWhiteSpace( tmp1 );
1044         tmp2 = lcl_a1_get_col( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
1045         if( !tmp2 || *tmp2 != 0 )   // Must have fully parsed a singleton.
1046             return nBailOutFlags;
1047 
1048         r.aStart.SetRow( 0 ); r.aEnd.SetRow( rDoc.MaxRow() );
1049         nFlags |=
1050             ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID |
1051             ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
1052         applyStartToEndFlags(nFlags, nFlags2);
1053         return nFlags;
1054     }
1055 
1056     // prepare as if it's a singleton, in case we want to fall back */
1057     r.aEnd.SetCol( r.aStart.Col() );
1058     r.aEnd.SetRow( r.aStart.Row() );    // don't overwrite sheet number as parsed in Parse_XL_Header()
1059 
1060     if ( bOnlyAcceptSingle )
1061     {
1062         if ( *tmp2 == 0 )
1063             return nFlags;
1064         else
1065         {
1066             // any trailing invalid character must invalidate the address.
1067             nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID);
1068             return nFlags;
1069         }
1070     }
1071 
1072     tmp2 = lcl_eatWhiteSpace( tmp2 );
1073     if( *tmp2 != ':' )
1074     {
1075         // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
1076         // not. Any trailing invalid character invalidates the range.
1077         if (*tmp2 == 0 && (nFlags & ScRefFlags::TAB2_3D))
1078         {
1079             if (nFlags & ScRefFlags::COL_ABS)
1080                 nFlags |= ScRefFlags::COL2_ABS;
1081             if (nFlags & ScRefFlags::ROW_ABS)
1082                 nFlags |= ScRefFlags::ROW2_ABS;
1083         }
1084         else
1085             nFlags &= ~ScRefFlags(ScRefFlags::VALID |
1086                     ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
1087                     ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
1088         return nFlags;
1089     }
1090 
1091     p = lcl_eatWhiteSpace( tmp2+1 );   // after ':'
1092     tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef);
1093     if( !tmp1 && aEndTabName.isEmpty() )     // Probably the aEndTabName was specified after the first range
1094     {
1095         p = lcl_XL_ParseSheetRef( p, aEndTabName, false, nullptr, pErrRef);
1096         if( p )
1097         {
1098             SCTAB nTab = 0;
1099             if( !aEndTabName.isEmpty() && rDoc.GetTable( aEndTabName, nTab ) )
1100             {
1101                 r.aEnd.SetTab( nTab );
1102                 nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS;
1103             }
1104             if (*p == '!' || *p == ':')
1105                 p = lcl_eatWhiteSpace( p+1 );
1106             tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef);
1107         }
1108     }
1109     if( !tmp1 ) // strange, but maybe valid singleton
1110         return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID);
1111 
1112     tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
1113     if( !tmp2 ) // strange, but maybe valid singleton
1114         return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID);
1115 
1116     if ( *tmp2 != 0 )
1117     {
1118         // any trailing invalid character must invalidate the range.
1119         nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
1120                     ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
1121         return nFlags;
1122     }
1123 
1124     applyStartToEndFlags(nFlags, nFlags2);
1125     return nFlags;
1126 }
1127 
1128 /**
1129     @param p        pointer to null-terminated sal_Unicode string
1130     @param rRawRes  returns ScRefFlags::... flags without the final check for full
1131                     validity that is applied to the return value, with which
1132                     two addresses that form a column or row singleton range,
1133                     e.g. A:A or 1:1, can be detected. Used in
1134                     lcl_ScRange_Parse_OOo().
1135     @param pRange   pointer to range where rAddr effectively is *pRange->aEnd,
1136                     used in conjunction with pExtInfo to determine the tab span
1137                     of a 3D reference.
1138  */
1139 static ScRefFlags lcl_ScAddress_Parse_OOo( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
1140                                            ScRefFlags& rRawRes,
1141                                            ScAddress::ExternalInfo* pExtInfo,
1142                                            ScRange* pRange,
1143                                            sal_Int32* pSheetEndPos,
1144                                            const OUString* pErrRef )
1145 {
1146     const sal_Unicode* const pStart = p;
1147     if (pSheetEndPos)
1148         *pSheetEndPos = 0;
1149     ScRefFlags  nRes = ScRefFlags::ZERO;
1150     rRawRes = ScRefFlags::ZERO;
1151     OUString aDocName;       // the pure Document Name
1152     OUString aTab;
1153     bool    bExtDoc = false;
1154     bool    bExtDocInherited = false;
1155 
1156     // Lets see if this is a reference to something in an external file.  A
1157     // document name is always quoted and has a trailing #.
1158     if (*p == '\'')
1159     {
1160         OUString aTmp;
1161         p = parseQuotedName(p, aTmp);
1162         aDocName = aTmp;
1163         if (*p++ == SC_COMPILER_FILE_TAB_SEP)
1164             bExtDoc = true;
1165         else
1166             // This is not a document name.  Perhaps a quoted relative table
1167             // name.
1168             p = pStart;
1169     }
1170     else if (pExtInfo && pExtInfo->mbExternal)
1171     {
1172         // This is an external reference.
1173         bExtDoc = bExtDocInherited = true;
1174     }
1175 
1176     SCCOL   nCol = 0;
1177     SCROW   nRow = 0;
1178     SCTAB   nTab = 0;
1179     ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
1180     ScRefFlags nBits = ScRefFlags::TAB_VALID;
1181     const sal_Unicode* q;
1182     if ( ScGlobal::FindUnquoted( p, '.') )
1183     {
1184         nRes |= ScRefFlags::TAB_3D;
1185         if ( bExtDoc )
1186             nRes |= ScRefFlags::TAB_ABS;
1187         if (*p == '$')
1188         {
1189             nRes |= ScRefFlags::TAB_ABS;
1190             p++;
1191         }
1192 
1193         if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '.')
1194         {
1195             // #REF! particle of an invalidated reference plus sheet separator.
1196             p += pErrRef->getLength() + 1;
1197             nRes &= ~ScRefFlags::TAB_VALID;
1198             nTab = -1;
1199         }
1200         else
1201         {
1202             if (*p == '\'')
1203             {
1204                 // Tokens that start at ' can have anything in them until a final
1205                 // ' but '' marks an escaped '.  We've earlier guaranteed that a
1206                 // string containing '' will be surrounded by '.
1207                 p = parseQuotedName(p, aTab);
1208             }
1209             else
1210             {
1211                 OUStringBuffer aTabAcc;
1212                 while (*p)
1213                 {
1214                     if( *p == '.')
1215                         break;
1216 
1217                     if( *p == '\'' )
1218                     {
1219                         p++; break;
1220                     }
1221                     aTabAcc.append(*p);
1222                     p++;
1223                 }
1224                 aTab = aTabAcc.makeStringAndClear();
1225             }
1226             if (*p != '.')
1227                 nBits = ScRefFlags::ZERO;
1228             else
1229             {
1230                 ++p;
1231                 if (!bExtDoc && !rDoc.GetTable( aTab, nTab ))
1232                     nBits = ScRefFlags::ZERO;
1233             }
1234         }
1235 
1236         if (pSheetEndPos && (nBits & ScRefFlags::TAB_VALID))
1237         {
1238             *pSheetEndPos = p - pStart;
1239             nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
1240         }
1241     }
1242     else
1243     {
1244         if (bExtDoc && !bExtDocInherited)
1245             return nRes;        // After a document a sheet must follow.
1246         nTab = rAddr.Tab();
1247     }
1248     nRes |= nBits;
1249 
1250     q = p;
1251     if (*p)
1252     {
1253         nBits = ScRefFlags::COL_VALID;
1254         if (*p == '$')
1255         {
1256             nBits |= ScRefFlags::COL_ABS;
1257             p++;
1258         }
1259 
1260         if (pErrRef && lcl_isString( p, *pErrRef))
1261         {
1262             // #REF! particle of an invalidated reference.
1263             p += pErrRef->getLength();
1264             nBits &= ~ScRefFlags::COL_VALID;
1265             nCol = -1;
1266         }
1267         else
1268         {
1269             if (rtl::isAsciiAlpha( *p ))
1270             {
1271                 const SCCOL nMaxCol = rDoc.MaxCol();
1272                 sal_Int64 n = rtl::toAsciiUpperCase( *p++ ) - 'A';
1273                 while (n < nMaxCol && rtl::isAsciiAlpha(*p))
1274                     n = ((n + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A';
1275                 if (n > nMaxCol || n < 0 || (*p && *p != '$' && !rtl::isAsciiDigit( *p ) &&
1276                         (!pErrRef || !lcl_isString( p, *pErrRef))))
1277                     nBits = ScRefFlags::ZERO;
1278                 else
1279                     nCol = sal::static_int_cast<SCCOL>( n );
1280             }
1281             else
1282                 nBits = ScRefFlags::ZERO;
1283 
1284             if( nBits == ScRefFlags::ZERO )
1285                 p = q;
1286         }
1287         nRes |= nBits;
1288     }
1289 
1290     q = p;
1291     if (*p)
1292     {
1293         nBits = ScRefFlags::ROW_VALID;
1294         if (*p == '$')
1295         {
1296             nBits |= ScRefFlags::ROW_ABS;
1297             p++;
1298         }
1299 
1300         if (pErrRef && lcl_isString( p, *pErrRef))
1301         {
1302             // #REF! particle of an invalidated reference.
1303             p += pErrRef->getLength();
1304             // Clearing the ROW_VALID bit here is not possible because of the
1305             // check at the end whether only a valid column was detected in
1306             // which case all bits are cleared because it could be any other
1307             // name. Instead, set to an absolute invalid row value. This will
1308             // display a $#REF! instead of #REF! if the error value was
1309             // relative, but live with it.
1310             nBits |= ScRefFlags::ROW_ABS;
1311             nRow = -1;
1312         }
1313         else
1314         {
1315             if( !rtl::isAsciiDigit( *p ) )
1316             {
1317                 nBits = ScRefFlags::ZERO;
1318                 nRow = -1;
1319             }
1320             else
1321             {
1322                 sal_Int64 n = rtl_ustr_toInt32( p, 10 ) - 1;
1323                 while (rtl::isAsciiDigit( *p ))
1324                     p++;
1325                 const SCROW nMaxRow = rDoc.MaxRow();
1326                 if( n < 0 || n > nMaxRow )
1327                     nBits = ScRefFlags::ZERO;
1328                 nRow = sal::static_int_cast<SCROW>(n);
1329             }
1330             if( nBits == ScRefFlags::ZERO )
1331                 p = q;
1332         }
1333         nRes |= nBits;
1334     }
1335 
1336     rAddr.Set( nCol, nRow, nTab );
1337 
1338     if (!*p && bExtDoc)
1339     {
1340         ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
1341 
1342         // Need document name if inherited.
1343         if (bExtDocInherited)
1344         {
1345             // The FileId was created using the original file name, so
1346             // obtain that. Otherwise lcl_ScRange_External_TabSpan() would
1347             // retrieve a FileId for the real name and bail out if that
1348             // differed from pExtInfo->mnFileId, as is the case when
1349             // loading documents that refer external files relative to the
1350             // current own document but were saved from a different path
1351             // than loaded.
1352             const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId, true);
1353             if (pFileName)
1354                 aDocName = *pFileName;
1355             else
1356                 nRes = ScRefFlags::ZERO;
1357         }
1358         pRefMgr->convertToAbsName(aDocName);
1359 
1360         if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
1361         {
1362             if (!rDoc.GetTable( aTab, nTab ))
1363                 nRes = ScRefFlags::ZERO;
1364             else
1365             {
1366                 rAddr.SetTab( nTab);
1367                 nRes |= ScRefFlags::TAB_VALID;
1368             }
1369         }
1370         else
1371         {
1372             if (!pExtInfo)
1373                 nRes = ScRefFlags::ZERO;
1374             else
1375             {
1376                 if (!pExtInfo->mbExternal)
1377                 {
1378                     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
1379 
1380                     pExtInfo->mbExternal = true;
1381                     pExtInfo->maTabName = aTab;
1382                     pExtInfo->mnFileId = nFileId;
1383 
1384                     if (pRefMgr->getSingleRefToken(nFileId, aTab,
1385                                 ScAddress(nCol, nRow, 0), nullptr,
1386                                 &nTab))
1387                     {
1388                         rAddr.SetTab( nTab);
1389                         nRes |= ScRefFlags::TAB_VALID;
1390                     }
1391                     else
1392                         nRes = ScRefFlags::ZERO;
1393                 }
1394                 else
1395                 {
1396                     // This is a call for the second part of the reference,
1397                     // we must have the range to adapt tab span.
1398                     if (!pRange)
1399                         nRes = ScRefFlags::ZERO;
1400                     else
1401                     {
1402                         ScRefFlags nFlags = nRes | ScRefFlags::TAB2_VALID;
1403                         if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
1404                                     pExtInfo, aDocName,
1405                                     pExtInfo->maTabName, aTab, rDoc))
1406                             nRes &= ~ScRefFlags::TAB_VALID;
1407                         else
1408                         {
1409                             if (nFlags & ScRefFlags::TAB2_VALID)
1410                             {
1411                                 rAddr.SetTab( pRange->aEnd.Tab());
1412                                 nRes |= ScRefFlags::TAB_VALID;
1413                             }
1414                             else
1415                                 nRes &= ~ScRefFlags::TAB_VALID;
1416                         }
1417                     }
1418                 }
1419             }
1420         }
1421     }
1422     else if (bExtDoc && pExtInfo && !bExtDocInherited && !pExtInfo->mbExternal && pSheetEndPos)
1423     {
1424         // Pass partial info up to caller, there may be an external name
1425         // following, and if after *pSheetEndPos it's external sheet-local.
1426         // For global names aTab is empty and *pSheetEndPos==0.
1427         pExtInfo->mbExternal = true;
1428         pExtInfo->maTabName = aTab;
1429         pExtInfo->mnFileId = rDoc.GetExternalRefManager()->getExternalFileId(aDocName);
1430     }
1431 
1432     rRawRes |= nRes;
1433 
1434     if ( !(nRes & ScRefFlags::ROW_VALID) && (nRes & ScRefFlags::COL_VALID)
1435             && !( (nRes & ScRefFlags::TAB_3D) && (nRes & ScRefFlags::TAB_VALID)) )
1436     {   // no Row, no Tab, but Col => DM (...), B (...) et al
1437         nRes = ScRefFlags::ZERO;
1438     }
1439     if( !*p )
1440     {
1441         ScRefFlags nMask = nRes & ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID );
1442         if( nMask == ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ) )
1443             nRes |= ScRefFlags::VALID;
1444     }
1445     else
1446         nRes = rRawRes = nBailOutFlags;
1447     return nRes;
1448 }
1449 
1450 static ScRefFlags lcl_ScAddress_Parse ( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
1451                                         const ScAddress::Details& rDetails,
1452                                         ScAddress::ExternalInfo* pExtInfo,
1453                                         const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1454                                         sal_Int32* pSheetEndPos,
1455                                         const OUString* pErrRef )
1456 {
1457     if( !*p )
1458         return ScRefFlags::ZERO;
1459 
1460     switch (rDetails.eConv)
1461     {
1462         case formula::FormulaGrammar::CONV_XL_A1:
1463         case formula::FormulaGrammar::CONV_XL_OOX:
1464         {
1465             ScRange rRange = rAddr;
1466             ScRefFlags nFlags = lcl_ScRange_Parse_XL_A1(
1467                     rRange, p, rDoc, true, pExtInfo,
1468                     (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
1469                     pSheetEndPos, pErrRef);
1470             rAddr = rRange.aStart;
1471             return nFlags;
1472         }
1473         case formula::FormulaGrammar::CONV_XL_R1C1:
1474         {
1475             ScRange rRange = rAddr;
1476             ScRefFlags nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, rDoc, rDetails, true, pExtInfo, pSheetEndPos);
1477             rAddr = rRange.aStart;
1478             return nFlags;
1479         }
1480         default :
1481         case formula::FormulaGrammar::CONV_OOO:
1482         {
1483             ScRefFlags nRawRes = ScRefFlags::ZERO;
1484             return lcl_ScAddress_Parse_OOo( p, rDoc, rAddr, nRawRes, pExtInfo, nullptr, pSheetEndPos, pErrRef);
1485         }
1486     }
1487 }
1488 
1489 bool ConvertSingleRef( const ScDocument& rDoc, const OUString& rRefString,
1490                        SCTAB nDefTab, ScRefAddress& rRefAddress,
1491                        const ScAddress::Details& rDetails,
1492                        ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1493 {
1494     bool bRet = false;
1495     if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1496     {
1497         ScAddress aAddr( 0, 0, nDefTab );
1498         ScRefFlags nRes = aAddr.Parse( rRefString, rDoc, rDetails, pExtInfo);
1499         if ( nRes & ScRefFlags::VALID )
1500         {
1501             rRefAddress.Set( aAddr,
1502                     ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
1503                     ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
1504                     ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
1505             bRet = true;
1506         }
1507     }
1508     return bRet;
1509 }
1510 
1511 bool ConvertDoubleRef( const ScDocument& rDoc, const OUString& rRefString, SCTAB nDefTab,
1512                        ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1513                        const ScAddress::Details& rDetails,
1514                        ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1515 {
1516     bool bRet = false;
1517     if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1518     {
1519         ScRange aRange( ScAddress( 0, 0, nDefTab));
1520         ScRefFlags nRes = aRange.Parse( rRefString, rDoc, rDetails, pExtInfo);
1521         if ( nRes & ScRefFlags::VALID )
1522         {
1523             rStartRefAddress.Set( aRange.aStart,
1524                     ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
1525                     ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
1526                     ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
1527             rEndRefAddress.Set( aRange.aEnd,
1528                     ((nRes & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO),
1529                     ((nRes & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO),
1530                     ((nRes & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO));
1531             bRet = true;
1532         }
1533     }
1534     return bRet;
1535 }
1536 
1537 ScRefFlags ScAddress::Parse( const OUString& r, const ScDocument& rDoc,
1538                              const Details& rDetails,
1539                              ExternalInfo* pExtInfo,
1540                              const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1541                              sal_Int32* pSheetEndPos,
1542                              const OUString* pErrRef )
1543 {
1544     return lcl_ScAddress_Parse( r.getStr(), rDoc, *this, rDetails, pExtInfo, pExternalLinks, pSheetEndPos, pErrRef);
1545 }
1546 
1547 ScRange ScRange::Intersection( const ScRange& rOther ) const
1548 {
1549     SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col());
1550     SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col());
1551     SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row());
1552     SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row());
1553     SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab());
1554     SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab());
1555 
1556     if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2)
1557         return ScRange(ScAddress::INITIALIZE_INVALID);
1558 
1559     return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1560 }
1561 
1562 void ScRange::ExtendTo( const ScRange& rRange )
1563 {
1564     OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1565     if( IsValid() )
1566     {
1567         aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) );
1568         aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) );
1569         aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1570         aEnd.SetCol(   std::max( aEnd.Col(),   rRange.aEnd.Col() ) );
1571         aEnd.SetRow(   std::max( aEnd.Row(),   rRange.aEnd.Row() ) );
1572         aEnd.SetTab(   std::max( aEnd.Tab(),   rRange.aEnd.Tab() ) );
1573     }
1574     else
1575         *this = rRange;
1576 }
1577 
1578 static ScRefFlags lcl_ScRange_Parse_OOo( ScRange& rRange,
1579                                          const OUString& r,
1580                                          const ScDocument& rDoc,
1581                                          ScAddress::ExternalInfo* pExtInfo,
1582                                          const OUString* pErrRef )
1583 {
1584     ScRefFlags nRes1 = ScRefFlags::ZERO, nRes2 = ScRefFlags::ZERO;
1585     sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':');
1586     if (nPos != -1)
1587     {
1588         OUStringBuffer aTmp(r);
1589         aTmp[nPos] = 0;
1590         const sal_Unicode* p = aTmp.getStr();
1591         ScRefFlags nRawRes1 = ScRefFlags::ZERO;
1592         nRes1 = lcl_ScAddress_Parse_OOo( p, rDoc, rRange.aStart, nRawRes1, pExtInfo, nullptr, nullptr, pErrRef);
1593         if ((nRes1 != ScRefFlags::ZERO) ||
1594                 ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1595                  (nRawRes1 & ScRefFlags::TAB_VALID)))
1596         {
1597             rRange.aEnd = rRange.aStart;  // sheet must be initialized identical to first sheet
1598             ScRefFlags nRawRes2 = ScRefFlags::ZERO;
1599             nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, rDoc, rRange.aEnd, nRawRes2,
1600                     pExtInfo, &rRange, nullptr, pErrRef);
1601             if (!((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) &&
1602                     // If not fully valid addresses, check if both have a valid
1603                     // column or row, and both have valid (or omitted) sheet references.
1604                     (nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1605                     (nRawRes1 & ScRefFlags::TAB_VALID) &&
1606                     (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1607                     (nRawRes2 & ScRefFlags::TAB_VALID) &&
1608                     // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A
1609                     ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) ==
1610                      (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID))))
1611             {
1612                 nRes1 = nRawRes1 | ScRefFlags::VALID;
1613                 nRes2 = nRawRes2 | ScRefFlags::VALID;
1614                 if (nRawRes1 & ScRefFlags::COL_VALID)
1615                 {
1616                     rRange.aStart.SetRow(0);
1617                     rRange.aEnd.SetRow(rDoc.MaxRow());
1618                     nRes1 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS;
1619                     nRes2 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS;
1620                 }
1621                 else
1622                 {
1623                     rRange.aStart.SetCol(0);
1624                     rRange.aEnd.SetCol( rDoc.MaxCol() );
1625                     nRes1 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
1626                     nRes2 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
1627                 }
1628             }
1629             else if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
1630             {
1631                 // Flag entire column/row references so they can be displayed
1632                 // as such. If the sticky reference parts are not both
1633                 // absolute or relative, assume that the user thought about
1634                 // something we should not touch.
1635                 if (rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() &&
1636                         ((nRes1 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO) &&
1637                         ((nRes2 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO))
1638                 {
1639                     nRes1 |= ScRefFlags::ROW_ABS;
1640                     nRes2 |= ScRefFlags::ROW_ABS;
1641                 }
1642                 else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
1643                         ((nRes1 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO) && ((nRes2 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO))
1644                 {
1645                     nRes1 |= ScRefFlags::COL_ABS;
1646                     nRes2 |= ScRefFlags::COL_ABS;
1647                 }
1648             }
1649             if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
1650             {
1651                 // PutInOrder / Justify
1652                 ScRefFlags nMask, nBits1, nBits2;
1653                 SCCOL nTempCol;
1654                 if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) )
1655                 {
1656                     rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol);
1657                     nMask = (ScRefFlags::COL_VALID | ScRefFlags::COL_ABS);
1658                     nBits1 = nRes1 & nMask;
1659                     nBits2 = nRes2 & nMask;
1660                     nRes1 = (nRes1 & ~nMask) | nBits2;
1661                     nRes2 = (nRes2 & ~nMask) | nBits1;
1662                 }
1663                 SCROW nTempRow;
1664                 if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) )
1665                 {
1666                     rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow);
1667                     nMask = (ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS);
1668                     nBits1 = nRes1 & nMask;
1669                     nBits2 = nRes2 & nMask;
1670                     nRes1 = (nRes1 & ~nMask) | nBits2;
1671                     nRes2 = (nRes2 & ~nMask) | nBits1;
1672                 }
1673                 SCTAB nTempTab;
1674                 if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) )
1675                 {
1676                     rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab);
1677                     nMask = (ScRefFlags::TAB_VALID | ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D);
1678                     nBits1 = nRes1 & nMask;
1679                     nBits2 = nRes2 & nMask;
1680                     nRes1 = (nRes1 & ~nMask) | nBits2;
1681                     nRes2 = (nRes2 & ~nMask) | nBits1;
1682                 }
1683                 if ( ((nRes1 & ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
1684                             == ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
1685                   && !(nRes2 & ScRefFlags::TAB_3D) )
1686                     nRes2 |= ScRefFlags::TAB_ABS;
1687             }
1688             else
1689             {
1690                 // Don't leave around valid half references.
1691                 nRes1 = nRes2 = ScRefFlags::ZERO;
1692             }
1693         }
1694     }
1695     applyStartToEndFlags(nRes1, nRes2 & ScRefFlags::BITS);
1696     nRes1 |= nRes2 & ScRefFlags::VALID;
1697     return nRes1;
1698 }
1699 
1700 ScRefFlags ScRange::Parse( const OUString& rString, const ScDocument& rDoc,
1701                            const ScAddress::Details& rDetails,
1702                            ScAddress::ExternalInfo* pExtInfo,
1703                            const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1704                            const OUString* pErrRef )
1705 {
1706     if (rString.isEmpty())
1707         return ScRefFlags::ZERO;
1708 
1709     switch (rDetails.eConv)
1710     {
1711         case formula::FormulaGrammar::CONV_XL_A1:
1712         case formula::FormulaGrammar::CONV_XL_OOX:
1713         {
1714             return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), rDoc, false, pExtInfo,
1715                     (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
1716                     nullptr, pErrRef );
1717         }
1718 
1719         case formula::FormulaGrammar::CONV_XL_R1C1:
1720         {
1721             return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr );
1722         }
1723 
1724         default:
1725         case formula::FormulaGrammar::CONV_OOO:
1726         {
1727             return lcl_ScRange_Parse_OOo( *this, rString, rDoc, pExtInfo, pErrRef );
1728         }
1729     }
1730 }
1731 
1732 // Accept a full range, or an address
1733 ScRefFlags ScRange::ParseAny( const OUString& rString, const ScDocument& rDoc,
1734                               const ScAddress::Details& rDetails )
1735 {
1736     ScRefFlags nRet = Parse( rString, rDoc, rDetails );
1737     const ScRefFlags nValid = ScRefFlags::VALID | ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID;
1738 
1739     if ( (nRet & nValid) != nValid )
1740     {
1741         ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number
1742         nRet = aAdr.Parse( rString, rDoc, rDetails );
1743         if ( nRet & ScRefFlags::VALID )
1744             aStart = aEnd = aAdr;
1745     }
1746     return nRet;
1747 }
1748 
1749 // Parse only full row references
1750 ScRefFlags ScRange::ParseCols( const ScDocument& rDoc,
1751                                const OUString& rStr,
1752                                const ScAddress::Details& rDetails )
1753 {
1754     if (rStr.isEmpty())
1755         return ScRefFlags::ZERO;
1756 
1757     const sal_Unicode* p = rStr.getStr();
1758     ScRefFlags nRes = ScRefFlags::ZERO;
1759     ScRefFlags ignored = ScRefFlags::ZERO;
1760 
1761     switch (rDetails.eConv)
1762     {
1763     default :
1764     case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1765     case formula::FormulaGrammar::CONV_XL_A1:
1766     case formula::FormulaGrammar::CONV_XL_OOX:
1767         if (nullptr != (p = lcl_a1_get_col( rDoc, p, &aStart, &ignored, nullptr) ) )
1768         {
1769             if( p[0] == ':')
1770             {
1771                 if( nullptr != (p = lcl_a1_get_col( rDoc, p+1, &aEnd, &ignored, nullptr)))
1772                 {
1773                     nRes = ScRefFlags::COL_VALID;
1774                 }
1775             }
1776             else
1777             {
1778                 aEnd = aStart;
1779                 nRes = ScRefFlags::COL_VALID;
1780             }
1781         }
1782         break;
1783 
1784     case formula::FormulaGrammar::CONV_XL_R1C1:
1785         if ((p[0] == 'C' || p[0] == 'c') &&
1786             nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored )))
1787         {
1788             if( p[0] == ':')
1789             {
1790                 if( (p[1] == 'C' || p[1] == 'c') &&
1791                     nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored )))
1792                 {
1793                     nRes = ScRefFlags::COL_VALID;
1794                 }
1795             }
1796             else
1797             {
1798                 aEnd = aStart;
1799                 nRes = ScRefFlags::COL_VALID;
1800             }
1801         }
1802         break;
1803     }
1804 
1805     return (p != nullptr && *p == '\0') ? nRes : ScRefFlags::ZERO;
1806 }
1807 
1808 // Parse only full row references
1809 void ScRange::ParseRows( const ScDocument& rDoc,
1810                                const OUString& rStr,
1811                                const ScAddress::Details& rDetails )
1812 {
1813     if (rStr.isEmpty())
1814         return;
1815 
1816     const sal_Unicode* p = rStr.getStr();
1817     ScRefFlags ignored = ScRefFlags::ZERO;
1818 
1819     switch (rDetails.eConv)
1820     {
1821     default :
1822     case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1823     case formula::FormulaGrammar::CONV_XL_A1:
1824     case formula::FormulaGrammar::CONV_XL_OOX:
1825         if (nullptr != (p = lcl_a1_get_row( rDoc, p, &aStart, &ignored, nullptr) ) )
1826         {
1827             if( p[0] == ':')
1828             {
1829                 lcl_a1_get_row( rDoc, p+1, &aEnd, &ignored, nullptr);
1830             }
1831             else
1832             {
1833                 aEnd = aStart;
1834             }
1835         }
1836         break;
1837 
1838     case formula::FormulaGrammar::CONV_XL_R1C1:
1839         if ((p[0] == 'R' || p[0] == 'r') &&
1840             nullptr != (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored )))
1841         {
1842             if( p[0] == ':')
1843             {
1844                 if( p[1] == 'R' || p[1] == 'r' )
1845                 {
1846                     lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored );
1847                 }
1848             }
1849             else
1850             {
1851                 aEnd = aStart;
1852             }
1853         }
1854         break;
1855     }
1856 }
1857 
1858 template<typename T > static void lcl_ScColToAlpha( T& rBuf, SCCOL nCol )
1859 {
1860     if (nCol < 26*26)
1861     {
1862         if (nCol < 26)
1863             rBuf.append( static_cast<char>( 'A' + nCol ));
1864         else
1865         {
1866             rBuf.append( static_cast<char>( 'A' + nCol / 26 - 1 ));
1867             rBuf.append( static_cast<char>( 'A' + nCol % 26 ));
1868         }
1869     }
1870     else
1871     {
1872         sal_Int32 nInsert = rBuf.getLength();
1873         while (nCol >= 26)
1874         {
1875             SCCOL nC = nCol % 26;
1876             rBuf.insert(nInsert, static_cast<char> ( 'A' + nC ));
1877             nCol = sal::static_int_cast<SCCOL>( nCol - nC );
1878             nCol = nCol / 26 - 1;
1879         }
1880         rBuf.insert(nInsert, static_cast<char> ( 'A' + nCol ));
1881     }
1882 }
1883 
1884 void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol)
1885 {
1886     lcl_ScColToAlpha(rBuf, nCol);
1887 }
1888 
1889 template <typename T > static void lcl_a1_append_c ( T &rString, int nCol, bool bIsAbs )
1890 {
1891     if( bIsAbs )
1892         rString.append("$");
1893     lcl_ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) );
1894 }
1895 
1896 template <typename T > static void lcl_a1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs )
1897 {
1898     if ( bIsAbs )
1899         rString.append("$");
1900     rString.append( nRow + 1 );
1901 }
1902 
1903 template <typename T > static void lcl_r1c1_append_c ( T &rString, sal_Int32 nCol, bool bIsAbs,
1904                                        const ScAddress::Details& rDetails )
1905 {
1906     rString.append("C");
1907     if (bIsAbs)
1908     {
1909         rString.append( nCol + 1 );
1910     }
1911     else
1912     {
1913         nCol -= rDetails.nCol;
1914         if (nCol != 0) {
1915             rString.append("[").append(nCol).append("]");
1916         }
1917     }
1918 }
1919 
1920 template <typename T > static void lcl_r1c1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs,
1921                                        const ScAddress::Details& rDetails )
1922 {
1923     rString.append("R");
1924     if (bIsAbs)
1925     {
1926         rString.append( nRow + 1 );
1927     }
1928     else
1929     {
1930         nRow -= rDetails.nRow;
1931         if (nRow != 0) {
1932             rString.append("[").append(nRow).append("]");
1933         }
1934     }
1935 }
1936 
1937 static OUString getFileNameFromDoc( const ScDocument* pDoc )
1938 {
1939     // TODO : er points at ScGlobal::GetAbsDocName()
1940     // as a better template.  Look into it
1941     OUString sFileName;
1942     SfxObjectShell* pShell;
1943 
1944     if( nullptr != pDoc &&
1945         nullptr != (pShell = pDoc->GetDocumentShell() ) )
1946     {
1947         uno::Reference< frame::XModel > xModel = pShell->GetModel();
1948         if( xModel.is() )
1949         {
1950             if( !xModel->getURL().isEmpty() )
1951             {
1952                 INetURLObject aURL( xModel->getURL() );
1953                 sFileName = aURL.GetLastName();
1954             }
1955             else
1956                 sFileName = pShell->GetTitle();
1957         }
1958     }
1959     return sFileName;
1960 }
1961 
1962 
1963 static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString)
1964 {
1965     rString.append(sString);
1966 }
1967 
1968 static void lcl_string_append(OStringBuffer &rString, std::u16string_view sString)
1969 {
1970     rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8  ));
1971 }
1972 
1973 template<typename T > static void lcl_Format( T& r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags,
1974                                   const ScDocument* pDoc,
1975                                   const ScAddress::Details& rDetails)
1976 {
1977     if( nFlags & ScRefFlags::VALID )
1978         nFlags |= ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID;
1979     if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) )
1980     {
1981         if ( nTab < 0 || nTab >= pDoc->GetTableCount() )
1982         {
1983             lcl_string_append(r, ScCompiler::GetNativeSymbol(ocErrRef));
1984             return;
1985         }
1986         if( nFlags & ScRefFlags::TAB_3D )
1987         {
1988             OUString aTabName, aDocName;
1989             pDoc->GetName(nTab, aTabName);
1990             assert( !aTabName.isEmpty() && "empty sheet name");
1991             // External Reference, same as in ScCompiler::MakeTabStr()
1992             if( aTabName[0] == '\'' )
1993             {   // "'Doc'#Tab"
1994                 sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName);
1995                 if (nPos != -1)
1996                 {
1997                     aDocName = aTabName.copy( 0, nPos + 1 );
1998                     aTabName = aTabName.copy( nPos + 1 );
1999                 }
2000             }
2001             else if( nFlags & ScRefFlags::FORCE_DOC )
2002             {
2003                 // VBA has an 'external' flag that forces the addition of the
2004                 // tab name _and_ the doc name.  The VBA code would be
2005                 // needlessly complicated if it constructed an actual external
2006                 // reference so we add this somewhat cheesy kludge to force the
2007                 // addition of the document name even for non-external references
2008                 aDocName = getFileNameFromDoc( pDoc );
2009             }
2010             ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
2011 
2012             switch( rDetails.eConv )
2013             {
2014             default :
2015             case formula::FormulaGrammar::CONV_OOO:
2016                 lcl_string_append(r, aDocName);
2017                 if( nFlags & ScRefFlags::TAB_ABS )
2018                     r.append("$");
2019                 lcl_string_append(r, aTabName);
2020                 r.append(".");
2021                 break;
2022 
2023             case formula::FormulaGrammar::CONV_XL_OOX:
2024                 if (!aTabName.isEmpty() && aTabName[0] == '\'')
2025                 {
2026                     if (!aDocName.isEmpty())
2027                     {
2028                         lcl_string_append(r.append("'["), aDocName);
2029                         r.append("]");
2030                         lcl_string_append(r, aTabName.subView(1));
2031                     }
2032                     else
2033                     {
2034                         lcl_string_append(r, aTabName);
2035                     }
2036                     r.append("!");
2037                     break;
2038                 }
2039                 [[fallthrough]];
2040             case formula::FormulaGrammar::CONV_XL_A1:
2041             case formula::FormulaGrammar::CONV_XL_R1C1:
2042                 if (!aDocName.isEmpty())
2043                 {
2044                     lcl_string_append(r.append("["), aDocName);
2045                     r.append("]");
2046                 }
2047                 lcl_string_append(r, aTabName);
2048                 r.append("!");
2049                 break;
2050             }
2051         }
2052     }
2053     switch( rDetails.eConv )
2054     {
2055     default :
2056     case formula::FormulaGrammar::CONV_OOO:
2057     case formula::FormulaGrammar::CONV_XL_A1:
2058     case formula::FormulaGrammar::CONV_XL_OOX:
2059         if( nFlags & ScRefFlags::COL_VALID )
2060             lcl_a1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2061         if( nFlags & ScRefFlags::ROW_VALID )
2062             lcl_a1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2063         break;
2064 
2065     case formula::FormulaGrammar::CONV_XL_R1C1:
2066         if( nFlags & ScRefFlags::ROW_VALID )
2067             lcl_r1c1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2068         if( nFlags & ScRefFlags::COL_VALID )
2069             lcl_r1c1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2070         break;
2071     }
2072 }
2073 
2074 void ScAddress::Format( OStringBuffer& r, ScRefFlags nFlags,
2075                                   const ScDocument* pDoc,
2076                                   const Details& rDetails) const
2077 {
2078     lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
2079 }
2080 
2081 OUString ScAddress::Format(ScRefFlags nFlags, const ScDocument* pDoc,
2082                            const Details& rDetails) const
2083 {
2084     OUStringBuffer r;
2085     lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
2086     return r.makeStringAndClear();
2087 }
2088 
2089 static void lcl_Split_DocTab( const ScDocument& rDoc,  SCTAB nTab,
2090                               const ScAddress::Details& rDetails,
2091                               ScRefFlags nFlags,
2092                               OUString& rTabName, OUString& rDocName )
2093 {
2094     rDoc.GetName(nTab, rTabName);
2095     rDocName.clear();
2096     // External reference, same as in ScCompiler::MakeTabStr()
2097     if ( rTabName[0] == '\'' )
2098     {   // "'Doc'#Tab"
2099         sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName);
2100         if (nPos != -1)
2101         {
2102             rDocName = rTabName.copy( 0, nPos + 1 );
2103             rTabName = rTabName.copy( nPos + 1 );
2104         }
2105     }
2106     else if( nFlags & ScRefFlags::FORCE_DOC )
2107     {
2108         // VBA has an 'external' flag that forces the addition of the
2109         // tab name _and_ the doc name.  The VBA code would be
2110         // needlessly complicated if it constructed an actual external
2111         // reference so we add this somewhat cheesy kludge to force the
2112         // addition of the document name even for non-external references
2113         rDocName = getFileNameFromDoc(&rDoc);
2114     }
2115     ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
2116 }
2117 
2118 static void lcl_ScRange_Format_XL_Header( OUStringBuffer& rString, const ScRange& rRange,
2119                                           ScRefFlags nFlags, const ScDocument& rDoc,
2120                                           const ScAddress::Details& rDetails )
2121 {
2122     if( !(nFlags & ScRefFlags::TAB_3D) )
2123         return;
2124 
2125     OUString aTabName, aDocName;
2126     lcl_Split_DocTab( rDoc, rRange.aStart.Tab(), rDetails, nFlags, aTabName, aDocName );
2127     switch (rDetails.eConv)
2128     {
2129         case formula::FormulaGrammar::CONV_XL_OOX:
2130             if (!aTabName.isEmpty() && aTabName[0] == '\'')
2131             {
2132                 if (!aDocName.isEmpty())
2133                 {
2134                     rString.append("'[" + aDocName + "]" + aTabName.subView(1));
2135                 }
2136                 else
2137                 {
2138                     rString.append(aTabName);
2139                 }
2140                 break;
2141             }
2142             [[fallthrough]];
2143         default:
2144             if (!aDocName.isEmpty())
2145             {
2146                 rString.append("[" + aDocName + "]");
2147             }
2148             rString.append(aTabName);
2149         break;
2150     }
2151     if( nFlags & ScRefFlags::TAB2_3D )
2152     {
2153         lcl_Split_DocTab( rDoc, rRange.aEnd.Tab(), rDetails, nFlags, aTabName, aDocName );
2154         rString.append(":");
2155         rString.append(aTabName);
2156     }
2157     rString.append("!");
2158 }
2159 
2160 // helpers used in ScRange::Format
2161 static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags)
2162 {
2163     return static_cast<bool>(nFlags & ScRefFlags::COL_ABS) != static_cast<bool>(nFlags & ScRefFlags::COL2_ABS);
2164 }
2165 static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags)
2166 {
2167     return static_cast<bool>(nFlags & ScRefFlags::ROW_ABS) != static_cast<bool>(nFlags & ScRefFlags::ROW2_ABS);
2168 }
2169 
2170 OUString ScRange::Format( const ScDocument& rDoc, ScRefFlags nFlags,
2171                           const ScAddress::Details& rDetails, bool bFullAddressNotation ) const
2172 {
2173     if( !( nFlags & ScRefFlags::VALID ) )
2174     {
2175         return ScCompiler::GetNativeSymbol(ocErrRef);
2176     }
2177 
2178     OUStringBuffer r;
2179     switch( rDetails.eConv ) {
2180     default :
2181     case formula::FormulaGrammar::CONV_OOO: {
2182         bool bOneTab = (aStart.Tab() == aEnd.Tab());
2183         if ( !bOneTab )
2184             nFlags |= ScRefFlags::TAB_3D;
2185         r = aStart.Format(nFlags, &rDoc, rDetails);
2186         if( aStart != aEnd ||
2187             lcl_ColAbsFlagDiffer( nFlags ) ||
2188             lcl_RowAbsFlagDiffer( nFlags ))
2189         {
2190             const ScDocument* pDoc = &rDoc;
2191             // move flags of end reference to start reference, mask with BITS to exclude FORCE_DOC flag
2192             nFlags = ScRefFlags::VALID | (ScRefFlags(o3tl::to_underlying(nFlags) >> 4) & ScRefFlags::BITS);
2193             if ( bOneTab )
2194                 pDoc = nullptr;
2195             else
2196                 nFlags |= ScRefFlags::TAB_3D;
2197             OUString aName(aEnd.Format(nFlags, pDoc, rDetails));
2198             r.append(":");
2199             r.append(aName);
2200         }
2201         break;
2202     }
2203 
2204     case formula::FormulaGrammar::CONV_XL_A1:
2205     case formula::FormulaGrammar::CONV_XL_OOX: {
2206         SCCOL nMaxCol = rDoc.MaxCol();
2207         SCROW nMaxRow = rDoc.MaxRow();
2208 
2209         lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
2210         if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
2211         {
2212             // Full col refs always require 2 rows (2:2)
2213             lcl_a1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2214             r.append(":");
2215             lcl_a1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
2216         }
2217         else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
2218         {
2219             // Full row refs always require 2 cols (A:A)
2220             lcl_a1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2221             r.append(":");
2222             lcl_a1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
2223         }
2224         else
2225         {
2226             lcl_a1_append_c ( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2227             lcl_a1_append_r ( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2228             if( aStart.Col() != aEnd.Col() ||
2229                 lcl_ColAbsFlagDiffer( nFlags ) ||
2230                 aStart.Row() != aEnd.Row() ||
2231                 lcl_RowAbsFlagDiffer( nFlags ) ) {
2232                 r.append(":");
2233                 lcl_a1_append_c ( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
2234                 lcl_a1_append_r ( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
2235             }
2236         }
2237         break;
2238     }
2239 
2240     case formula::FormulaGrammar::CONV_XL_R1C1: {
2241         SCCOL nMaxCol = rDoc.MaxCol();
2242         SCROW nMaxRow = rDoc.MaxRow();
2243 
2244         lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
2245         if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
2246         {
2247             lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2248             if( aStart.Row() != aEnd.Row() ||
2249                 lcl_RowAbsFlagDiffer( nFlags ) ) {
2250                 r.append(":");
2251                 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
2252             }
2253         }
2254         else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
2255         {
2256             lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2257             if( aStart.Col() != aEnd.Col() ||
2258                 lcl_ColAbsFlagDiffer( nFlags )) {
2259                 r.append(":");
2260                 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
2261             }
2262         }
2263         else
2264         {
2265             lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2266             lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2267             if( aStart.Col() != aEnd.Col() ||
2268                 lcl_ColAbsFlagDiffer( nFlags ) ||
2269                 aStart.Row() != aEnd.Row() ||
2270                 lcl_RowAbsFlagDiffer( nFlags ) ) {
2271                 r.append(":");
2272                 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
2273                 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
2274             }
2275         }
2276         break;
2277     }
2278     }
2279     return r.makeStringAndClear();
2280 }
2281 
2282 bool ScAddress::Move( SCCOL dx, SCROW dy, SCTAB dz, ScAddress& rErrorPos, const ScDocument& rDoc )
2283 {
2284     SCTAB nMaxTab = rDoc.GetTableCount();
2285     SCCOL nMaxCol = rDoc.MaxCol();
2286     SCROW nMaxRow = rDoc.MaxRow();
2287     dx = Col() + dx;
2288     dy = Row() + dy;
2289     dz = Tab() + dz;
2290     bool bValid = true;
2291     rErrorPos.SetCol(dx);
2292     if( dx < 0 )
2293     {
2294         dx = 0;
2295         bValid = false;
2296     }
2297     else if( dx > nMaxCol )
2298     {
2299         dx = nMaxCol;
2300         bValid =false;
2301     }
2302     rErrorPos.SetRow(dy);
2303     if( dy < 0 )
2304     {
2305         dy = 0;
2306         bValid = false;
2307     }
2308     else if( dy > nMaxRow )
2309     {
2310         dy = nMaxRow;
2311         bValid =false;
2312     }
2313     rErrorPos.SetTab(dz);
2314     if( dz < 0 )
2315     {
2316         dz = 0;
2317         bValid = false;
2318     }
2319     else if( dz > nMaxTab )
2320     {
2321         // Always set MAXTAB+1 so further checks without ScDocument detect invalid.
2322         rErrorPos.SetTab(MAXTAB+1);
2323         dz = nMaxTab;
2324         bValid =false;
2325     }
2326     Set( dx, dy, dz );
2327     return bValid;
2328 }
2329 
2330 bool ScRange::Move( SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange, const ScDocument& rDoc )
2331 {
2332     SCCOL nMaxCol = rDoc.MaxCol();
2333     SCROW nMaxRow = rDoc.MaxRow();
2334     if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
2335         dy = 0;     // Entire column not to be moved.
2336     if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
2337         dx = 0;     // Entire row not to be moved.
2338     bool b = aStart.Move( dx, dy, dz, rErrorRange.aStart, rDoc );
2339     b &= aEnd.Move( dx, dy, dz, rErrorRange.aEnd, rDoc );
2340     return b;
2341 }
2342 
2343 bool ScRange::MoveSticky( const ScDocument& rDoc, SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange )
2344 {
2345     const SCCOL nMaxCol = rDoc.MaxCol();
2346     const SCROW nMaxRow = rDoc.MaxRow();
2347     bool bColRange = (aStart.Col() < aEnd.Col());
2348     bool bRowRange = (aStart.Row() < aEnd.Row());
2349     if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
2350         dy = 0;     // Entire column not to be moved.
2351     if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
2352         dx = 0;     // Entire row not to be moved.
2353     bool b1 = aStart.Move( dx, dy, dz, rErrorRange.aStart, rDoc );
2354     if (dx && bColRange && aEnd.Col() == nMaxCol)
2355         dx = 0;     // End column sticky.
2356     if (dy && bRowRange && aEnd.Row() == nMaxRow)
2357         dy = 0;     // End row sticky.
2358     SCTAB nOldTab = aEnd.Tab();
2359     bool b2 = aEnd.Move( dx, dy, dz, rErrorRange.aEnd, rDoc );
2360     if (!b2)
2361     {
2362         // End column or row of a range may have become sticky.
2363         bColRange = (!dx || (bColRange && aEnd.Col() == nMaxCol));
2364         if (dx && bColRange)
2365             rErrorRange.aEnd.SetCol(nMaxCol);
2366         bRowRange = (!dy || (bRowRange && aEnd.Row() == nMaxRow));
2367         if (dy && bRowRange)
2368             rErrorRange.aEnd.SetRow(nMaxRow);
2369         b2 = bColRange && bRowRange && (aEnd.Tab() - nOldTab == dz);
2370     }
2371     return b1 && b2;
2372 }
2373 
2374 void ScRange::IncColIfNotLessThan(const ScDocument& rDoc, SCCOL nStartCol, SCCOL nOffset)
2375 {
2376     if (aStart.Col() >= nStartCol)
2377     {
2378         aStart.IncCol(nOffset);
2379         if (aStart.Col() < 0)
2380             aStart.SetCol(0);
2381         else if(aStart.Col() > rDoc.MaxCol())
2382             aStart.SetCol(rDoc.MaxCol());
2383     }
2384     if (aEnd.Col() >= nStartCol)
2385     {
2386         aEnd.IncCol(nOffset);
2387         if (aEnd.Col() < 0)
2388             aEnd.SetCol(0);
2389         else if(aEnd.Col() > rDoc.MaxCol())
2390             aEnd.SetCol(rDoc.MaxCol());
2391     }
2392 }
2393 
2394 void ScRange::IncRowIfNotLessThan(const ScDocument& rDoc, SCROW nStartRow, SCROW nOffset)
2395 {
2396     if (aStart.Row() >= nStartRow)
2397     {
2398         aStart.IncRow(nOffset);
2399         if (aStart.Row() < 0)
2400             aStart.SetRow(0);
2401         else if(aStart.Row() > rDoc.MaxRow())
2402             aStart.SetRow(rDoc.MaxRow());
2403     }
2404     if (aEnd.Row() >= nStartRow)
2405     {
2406         aEnd.IncRow(nOffset);
2407         if (aEnd.Row() < 0)
2408             aEnd.SetRow(0);
2409         else if(aEnd.Row() > rDoc.MaxRow())
2410             aEnd.SetRow(rDoc.MaxRow());
2411     }
2412 }
2413 
2414 bool ScRange::IsEndColSticky( const ScDocument& rDoc ) const
2415 {
2416     // Only in an actual column range, i.e. not if both columns are MAXCOL.
2417     return aEnd.Col() == rDoc.MaxCol() && aStart.Col() < aEnd.Col();
2418 }
2419 
2420 bool ScRange::IsEndRowSticky( const ScDocument& rDoc ) const
2421 {
2422     // Only in an actual row range, i.e. not if both rows are MAXROW.
2423     return aEnd.Row() == rDoc.MaxRow() && aStart.Row() < aEnd.Row();
2424 }
2425 
2426 void ScRange::IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta )
2427 {
2428     SCCOL nCol = aEnd.Col();
2429     if (aStart.Col() >= nCol)
2430     {
2431         // Less than two columns => not sticky.
2432         aEnd.IncCol( nDelta);
2433         return;
2434     }
2435 
2436     const SCCOL nMaxCol = rDoc.MaxCol();
2437     if (nCol == nMaxCol)
2438         // already sticky
2439         return;
2440 
2441     if (nCol < nMaxCol)
2442         aEnd.SetCol( ::std::min( static_cast<SCCOL>(nCol + nDelta), nMaxCol));
2443     else
2444         aEnd.IncCol( nDelta);   // was greater than nMaxCol, caller should know...
2445 }
2446 
2447 void ScRange::IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta )
2448 {
2449     SCROW nRow = aEnd.Row();
2450     if (aStart.Row() >= nRow)
2451     {
2452         // Less than two rows => not sticky.
2453         aEnd.IncRow( nDelta);
2454         return;
2455     }
2456 
2457     if (nRow == rDoc.MaxRow())
2458         // already sticky
2459         return;
2460 
2461     if (nRow < rDoc.MaxRow())
2462         aEnd.SetRow( ::std::min( static_cast<SCROW>(nRow + nDelta), rDoc.MaxRow()));
2463     else
2464         aEnd.IncRow( nDelta);   // was greater than rDoc.MaxRow(), caller should know...
2465 }
2466 
2467 OUString ScAddress::GetColRowString() const
2468 {
2469     OUStringBuffer aString;
2470 
2471     switch( detailsOOOa1.eConv )
2472     {
2473     default :
2474     case formula::FormulaGrammar::CONV_OOO:
2475     case formula::FormulaGrammar::CONV_XL_A1:
2476     case formula::FormulaGrammar::CONV_XL_OOX:
2477         lcl_ScColToAlpha( aString, nCol);
2478         aString.append(nRow+1);
2479         break;
2480 
2481     case formula::FormulaGrammar::CONV_XL_R1C1:
2482         lcl_r1c1_append_r ( aString, nRow, false/*bAbsolute*/, detailsOOOa1 );
2483         lcl_r1c1_append_c ( aString, nCol, false/*bAbsolute*/, detailsOOOa1 );
2484         break;
2485     }
2486 
2487     return aString.makeStringAndClear();
2488 }
2489 
2490 OUString ScRefAddress::GetRefString( const ScDocument& rDoc, SCTAB nActTab,
2491                                      const ScAddress::Details& rDetails ) const
2492 {
2493     if ( Tab()+1 > rDoc.GetTableCount() )
2494         return ScCompiler::GetNativeSymbol(ocErrRef);
2495 
2496     ScRefFlags nFlags = ScRefFlags::VALID;
2497     if ( nActTab != Tab() )
2498     {
2499         nFlags |= ScRefFlags::TAB_3D;
2500         if ( !bRelTab )
2501             nFlags |= ScRefFlags::TAB_ABS;
2502     }
2503     if ( !bRelCol )
2504         nFlags |= ScRefFlags::COL_ABS;
2505     if ( !bRelRow )
2506         nFlags |= ScRefFlags::ROW_ABS;
2507 
2508     return aAdr.Format(nFlags, &rDoc, rDetails);
2509 }
2510 
2511 bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, const OUString& rStr)
2512 {
2513     SCCOL nResult = 0;
2514     sal_Int32 nStop = rStr.getLength();
2515     sal_Int32 nPos = 0;
2516     sal_Unicode c;
2517     const SCCOL nMaxCol = rDoc.MaxCol();
2518     while (nResult <= nMaxCol && nPos < nStop && (c = rStr[nPos]) != 0 &&
2519             rtl::isAsciiAlpha(c))
2520     {
2521         if (nPos > 0)
2522             nResult = (nResult + 1) * 26;
2523         nResult += ScGlobal::ToUpperAlpha(c) - 'A';
2524         ++nPos;
2525     }
2526     bool bOk = (rDoc.ValidCol(nResult) && nPos > 0);
2527     if (bOk)
2528         rCol = nResult;
2529     return bOk;
2530 }
2531 
2532 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2533