xref: /core/sc/source/core/tool/address.cxx (revision 1250aecd)
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 tools::Long sal_Unicode_strtol ( const sal_Unicode*  p, const sal_Unicode** pEnd )
137 {
138     tools::Long 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 += OUString( 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 += OUString( 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 += OUString( 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 += OUString( 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, "");
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 sal_Unicode* p,
659                                             const ScAddress::Details& rDetails,
660                                             ScAddress* pAddr, ScRefFlags* nFlags )
661 {
662     const sal_Unicode *pEnd;
663     tools::Long n;
664     bool isRelative;
665 
666     if( p[0] == '\0' )
667         return nullptr;
668 
669     p++;
670     isRelative = *p == '[';
671     if( isRelative )
672         p++;
673     n = sal_Unicode_strtol( p, &pEnd );
674     if( nullptr == pEnd )
675         return nullptr;
676 
677     if( p == pEnd ) // C is a relative ref with offset 0
678     {
679         if( isRelative )
680             return nullptr;
681         n = rDetails.nCol;
682     }
683     else if( isRelative )
684     {
685         if( *pEnd != ']' )
686             return nullptr;
687         n += rDetails.nCol;
688         pEnd++;
689     }
690     else
691     {
692         *nFlags |= ScRefFlags::COL_ABS;
693         n--;
694     }
695 
696     if( n < 0 || n >= MAXCOLCOUNT )
697         return nullptr;
698     pAddr->SetCol( static_cast<SCCOL>( n ) );
699     *nFlags |= ScRefFlags::COL_VALID;
700 
701     return pEnd;
702 }
703 
704 static const sal_Unicode* lcl_r1c1_get_row(
705                                     const ScSheetLimits& rSheetLimits,
706                                     const sal_Unicode* p,
707                                     const ScAddress::Details& rDetails,
708                                     ScAddress* pAddr, ScRefFlags* nFlags )
709 {
710     const sal_Unicode *pEnd;
711     tools::Long n;
712     bool isRelative;
713 
714     if( p[0] == '\0' )
715         return nullptr;
716 
717     p++;
718     isRelative = *p == '[';
719     if( isRelative )
720         p++;
721     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( 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( 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( 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( 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     SCCOL nCol;
906 
907     if( *p == '$' )
908     {
909         *nFlags |= ScRefFlags::COL_ABS;
910         p++;
911     }
912 
913     if (pErrRef && lcl_isString( p, *pErrRef))
914     {
915         p += pErrRef->getLength();
916         *nFlags &= ~ScRefFlags::COL_VALID;
917         pAddr->SetCol(-1);
918         return p;
919     }
920 
921     if( !rtl::isAsciiAlpha( *p ) )
922         return nullptr;
923 
924     nCol = sal::static_int_cast<SCCOL>( rtl::toAsciiUpperCase( *p++ ) - 'A' );
925     const SCCOL nMaxCol = rDoc.MaxCol();
926     while (nCol <= nMaxCol && rtl::isAsciiAlpha(*p))
927         nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A' );
928     if( nCol > nMaxCol || rtl::isAsciiAlpha( *p ) )
929         return nullptr;
930 
931     *nFlags |= ScRefFlags::COL_VALID;
932     pAddr->SetCol( nCol );
933 
934     return p;
935 }
936 
937 static const sal_Unicode* lcl_a1_get_row( const ScDocument& rDoc,
938                                                  const sal_Unicode* p,
939                                                  ScAddress* pAddr,
940                                                  ScRefFlags* nFlags,
941                                                  const OUString* pErrRef )
942 {
943     const sal_Unicode *pEnd;
944     tools::Long n;
945 
946     if( *p == '$' )
947     {
948         *nFlags |= ScRefFlags::ROW_ABS;
949         p++;
950     }
951 
952     if (pErrRef && lcl_isString( p, *pErrRef))
953     {
954         p += pErrRef->getLength();
955         *nFlags &= ~ScRefFlags::ROW_VALID;
956         pAddr->SetRow(-1);
957         return p;
958     }
959 
960     n = sal_Unicode_strtol( p, &pEnd ) - 1;
961     if( nullptr == pEnd || p == pEnd || n < 0 || n > rDoc.MaxRow() )
962         return nullptr;
963 
964     *nFlags |= ScRefFlags::ROW_VALID;
965     pAddr->SetRow( static_cast<SCROW>(n) );
966 
967     return pEnd;
968 }
969 
970 /// B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ...
971 static bool isValidSingleton( ScRefFlags nFlags, ScRefFlags nFlags2 )
972 {
973     bool bCols = (nFlags & ScRefFlags::COL_VALID) && ((nFlags & ScRefFlags::COL2_VALID) || (nFlags2 & ScRefFlags::COL_VALID));
974     bool bRows = (nFlags & ScRefFlags::ROW_VALID) && ((nFlags & ScRefFlags::ROW2_VALID) || (nFlags2 & ScRefFlags::ROW_VALID));
975     return bCols != bRows;
976 }
977 
978 static ScRefFlags lcl_ScRange_Parse_XL_A1( ScRange& r,
979                                            const sal_Unicode* p,
980                                            const ScDocument& rDoc,
981                                            bool bOnlyAcceptSingle,
982                                            ScAddress::ExternalInfo* pExtInfo,
983                                            const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
984                                            sal_Int32* pSheetEndPos,
985                                            const OUString* pErrRef )
986 {
987     const sal_Unicode* const pStart = p;
988     if (pSheetEndPos)
989         *pSheetEndPos = 0;
990     const sal_Unicode* tmp1, *tmp2;
991     OUString aExternDocName, aStartTabName, aEndTabName; // for external link table
992     ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID, nFlags2 = ScRefFlags::TAB_VALID;
993 
994     p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName,
995             aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks, pErrRef );
996 
997     ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
998     if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
999     {
1000         *pSheetEndPos = p - pStart;
1001         nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
1002     }
1003 
1004     if (!aExternDocName.isEmpty())
1005         lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
1006                 aStartTabName, aEndTabName, rDoc);
1007 
1008     if( nullptr == p )
1009         return nBailOutFlags;
1010 
1011     tmp1 = lcl_a1_get_col( rDoc, p, &r.aStart, &nFlags, pErrRef);
1012     if( tmp1 == nullptr )          // Is it a row only reference 3:5
1013     {
1014         if( bOnlyAcceptSingle ) // by definition full row refs are ranges
1015             return nBailOutFlags;
1016 
1017         tmp1 = lcl_a1_get_row( rDoc, p, &r.aStart, &nFlags, pErrRef);
1018 
1019         tmp1 = lcl_eatWhiteSpace( tmp1 );
1020         if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
1021             return nBailOutFlags;
1022 
1023         tmp1 = lcl_eatWhiteSpace( tmp1 );
1024         tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
1025         if( !tmp2 || *tmp2 != 0 )   // Must have fully parsed a singleton.
1026             return nBailOutFlags;
1027 
1028         r.aStart.SetCol( 0 ); r.aEnd.SetCol( rDoc.MaxCol() );
1029         nFlags |=
1030             ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID |
1031             ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
1032         applyStartToEndFlags(nFlags, nFlags2);
1033         return nFlags;
1034     }
1035 
1036     tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aStart, &nFlags, pErrRef);
1037     if( tmp2 == nullptr )          // check for col only reference F:H
1038     {
1039         if( bOnlyAcceptSingle ) // by definition full col refs are ranges
1040             return nBailOutFlags;
1041 
1042         tmp1 = lcl_eatWhiteSpace( tmp1 );
1043         if( *tmp1++ != ':' )    // Even a singleton requires ':' (eg F:F)
1044             return nBailOutFlags;
1045 
1046         tmp1 = lcl_eatWhiteSpace( tmp1 );
1047         tmp2 = lcl_a1_get_col( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
1048         if( !tmp2 || *tmp2 != 0 )   // Must have fully parsed a singleton.
1049             return nBailOutFlags;
1050 
1051         r.aStart.SetRow( 0 ); r.aEnd.SetRow( rDoc.MaxRow() );
1052         nFlags |=
1053             ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID |
1054             ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
1055         applyStartToEndFlags(nFlags, nFlags2);
1056         return nFlags;
1057     }
1058 
1059     // prepare as if it's a singleton, in case we want to fall back */
1060     r.aEnd.SetCol( r.aStart.Col() );
1061     r.aEnd.SetRow( r.aStart.Row() );    // don't overwrite sheet number as parsed in Parse_XL_Header()
1062 
1063     if ( bOnlyAcceptSingle )
1064     {
1065         if ( *tmp2 == 0 )
1066             return nFlags;
1067         else
1068         {
1069             // any trailing invalid character must invalidate the address.
1070             nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID);
1071             return nFlags;
1072         }
1073     }
1074 
1075     tmp2 = lcl_eatWhiteSpace( tmp2 );
1076     if( *tmp2 != ':' )
1077     {
1078         // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
1079         // not. Any trailing invalid character invalidates the range.
1080         if (*tmp2 == 0 && (nFlags & ScRefFlags::TAB2_3D))
1081         {
1082             if (nFlags & ScRefFlags::COL_ABS)
1083                 nFlags |= ScRefFlags::COL2_ABS;
1084             if (nFlags & ScRefFlags::ROW_ABS)
1085                 nFlags |= ScRefFlags::ROW2_ABS;
1086         }
1087         else
1088             nFlags &= ~ScRefFlags(ScRefFlags::VALID |
1089                     ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
1090                     ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
1091         return nFlags;
1092     }
1093 
1094     p = lcl_eatWhiteSpace( tmp2+1 );   // after ':'
1095     tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef);
1096     if( !tmp1 && aEndTabName.isEmpty() )     // Probably the aEndTabName was specified after the first range
1097     {
1098         p = lcl_XL_ParseSheetRef( p, aEndTabName, false, nullptr, pErrRef);
1099         if( p )
1100         {
1101             SCTAB nTab = 0;
1102             if( !aEndTabName.isEmpty() && rDoc.GetTable( aEndTabName, nTab ) )
1103             {
1104                 r.aEnd.SetTab( nTab );
1105                 nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS;
1106             }
1107             if (*p == '!' || *p == ':')
1108                 p = lcl_eatWhiteSpace( p+1 );
1109             tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef);
1110         }
1111     }
1112     if( !tmp1 ) // strange, but maybe valid singleton
1113         return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID);
1114 
1115     tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
1116     if( !tmp2 ) // strange, but maybe valid singleton
1117         return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID);
1118 
1119     if ( *tmp2 != 0 )
1120     {
1121         // any trailing invalid character must invalidate the range.
1122         nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
1123                     ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
1124         return nFlags;
1125     }
1126 
1127     applyStartToEndFlags(nFlags, nFlags2);
1128     return nFlags;
1129 }
1130 
1131 /**
1132     @param p        pointer to null-terminated sal_Unicode string
1133     @param rRawRes  returns ScRefFlags::... flags without the final check for full
1134                     validity that is applied to the return value, with which
1135                     two addresses that form a column or row singleton range,
1136                     e.g. A:A or 1:1, can be detected. Used in
1137                     lcl_ScRange_Parse_OOo().
1138     @param pRange   pointer to range where rAddr effectively is *pRange->aEnd,
1139                     used in conjunction with pExtInfo to determine the tab span
1140                     of a 3D reference.
1141  */
1142 static ScRefFlags lcl_ScAddress_Parse_OOo( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
1143                                            ScRefFlags& rRawRes,
1144                                            ScAddress::ExternalInfo* pExtInfo,
1145                                            ScRange* pRange,
1146                                            sal_Int32* pSheetEndPos,
1147                                            const OUString* pErrRef )
1148 {
1149     const sal_Unicode* const pStart = p;
1150     if (pSheetEndPos)
1151         *pSheetEndPos = 0;
1152     ScRefFlags  nRes = ScRefFlags::ZERO;
1153     rRawRes = ScRefFlags::ZERO;
1154     OUString aDocName;       // the pure Document Name
1155     OUString aTab;
1156     bool    bExtDoc = false;
1157     bool    bExtDocInherited = false;
1158 
1159     // Lets see if this is a reference to something in an external file.  A
1160     // document name is always quoted and has a trailing #.
1161     if (*p == '\'')
1162     {
1163         OUString aTmp;
1164         p = parseQuotedName(p, aTmp);
1165         aDocName = aTmp;
1166         if (*p++ == SC_COMPILER_FILE_TAB_SEP)
1167             bExtDoc = true;
1168         else
1169             // This is not a document name.  Perhaps a quoted relative table
1170             // name.
1171             p = pStart;
1172     }
1173     else if (pExtInfo && pExtInfo->mbExternal)
1174     {
1175         // This is an external reference.
1176         bExtDoc = bExtDocInherited = true;
1177     }
1178 
1179     SCCOL   nCol = 0;
1180     SCROW   nRow = 0;
1181     SCTAB   nTab = 0;
1182     ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
1183     ScRefFlags nBits = ScRefFlags::TAB_VALID;
1184     const sal_Unicode* q;
1185     if ( ScGlobal::FindUnquoted( p, '.') )
1186     {
1187         nRes |= ScRefFlags::TAB_3D;
1188         if ( bExtDoc )
1189             nRes |= ScRefFlags::TAB_ABS;
1190         if (*p == '$')
1191         {
1192             nRes |= ScRefFlags::TAB_ABS;
1193             p++;
1194         }
1195 
1196         if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '.')
1197         {
1198             // #REF! particle of an invalidated reference plus sheet separator.
1199             p += pErrRef->getLength() + 1;
1200             nRes &= ~ScRefFlags::TAB_VALID;
1201             nTab = -1;
1202         }
1203         else
1204         {
1205             if (*p == '\'')
1206             {
1207                 // Tokens that start at ' can have anything in them until a final
1208                 // ' but '' marks an escaped '.  We've earlier guaranteed that a
1209                 // string containing '' will be surrounded by '.
1210                 p = parseQuotedName(p, aTab);
1211             }
1212             else
1213             {
1214                 OUStringBuffer aTabAcc;
1215                 while (*p)
1216                 {
1217                     if( *p == '.')
1218                         break;
1219 
1220                     if( *p == '\'' )
1221                     {
1222                         p++; break;
1223                     }
1224                     aTabAcc.append(*p);
1225                     p++;
1226                 }
1227                 aTab = aTabAcc.makeStringAndClear();
1228             }
1229             if( *p++ != '.' )
1230                 nBits = ScRefFlags::ZERO;
1231 
1232             if (!bExtDoc && !rDoc.GetTable( aTab, nTab ))
1233             {
1234                 // Specified table name is not found in this document.  Assume this is an external document.
1235                 aDocName = aTab;
1236                 sal_Int32 n = aDocName.lastIndexOf('.');
1237                 if (n > 0)
1238                 {
1239                     // Extension found.  Strip it.
1240                     aTab = aTab.replaceAt(n, 1, "");
1241                     bExtDoc = true;
1242                 }
1243                 else
1244                     // No extension found.  This is probably not an external document.
1245                     nBits = ScRefFlags::ZERO;
1246             }
1247         }
1248 
1249         if (pSheetEndPos && (nBits & ScRefFlags::TAB_VALID))
1250         {
1251             *pSheetEndPos = p - pStart;
1252             nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
1253         }
1254     }
1255     else
1256     {
1257         if (bExtDoc && !bExtDocInherited)
1258             return nRes;        // After a document a sheet must follow.
1259         nTab = rAddr.Tab();
1260     }
1261     nRes |= nBits;
1262 
1263     q = p;
1264     if (*p)
1265     {
1266         nBits = ScRefFlags::COL_VALID;
1267         if (*p == '$')
1268         {
1269             nBits |= ScRefFlags::COL_ABS;
1270             p++;
1271         }
1272 
1273         if (pErrRef && lcl_isString( p, *pErrRef))
1274         {
1275             // #REF! particle of an invalidated reference.
1276             p += pErrRef->getLength();
1277             nBits &= ~ScRefFlags::COL_VALID;
1278             nCol = -1;
1279         }
1280         else
1281         {
1282             const SCCOL nMaxCol = rDoc.MaxCol();
1283             if (rtl::isAsciiAlpha( *p ))
1284             {
1285                 nCol = sal::static_int_cast<SCCOL>( rtl::toAsciiUpperCase( *p++ ) - 'A' );
1286                 while (nCol < nMaxCol && rtl::isAsciiAlpha(*p))
1287                     nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A' );
1288             }
1289             else
1290                 nBits = ScRefFlags::ZERO;
1291 
1292             if (nCol > nMaxCol || (*p && *p != '$' && !rtl::isAsciiDigit( *p ) &&
1293                         (!pErrRef || !lcl_isString( p, *pErrRef))))
1294                 nBits = ScRefFlags::ZERO;
1295             if( nBits == ScRefFlags::ZERO )
1296                 p = q;
1297         }
1298         nRes |= nBits;
1299     }
1300 
1301     q = p;
1302     if (*p)
1303     {
1304         nBits = ScRefFlags::ROW_VALID;
1305         if (*p == '$')
1306         {
1307             nBits |= ScRefFlags::ROW_ABS;
1308             p++;
1309         }
1310 
1311         if (pErrRef && lcl_isString( p, *pErrRef))
1312         {
1313             // #REF! particle of an invalidated reference.
1314             p += pErrRef->getLength();
1315             // Clearing the ROW_VALID bit here is not possible because of the
1316             // check at the end whether only a valid column was detected in
1317             // which case all bits are cleared because it could be any other
1318             // name. Instead, set to an absolute invalid row value. This will
1319             // display a $#REF! instead of #REF! if the error value was
1320             // relative, but live with it.
1321             nBits |= ScRefFlags::ROW_ABS;
1322             nRow = -1;
1323         }
1324         else
1325         {
1326             if( !rtl::isAsciiDigit( *p ) )
1327             {
1328                 nBits = ScRefFlags::ZERO;
1329                 nRow = SCROW(-1);
1330             }
1331             else
1332             {
1333                 tools::Long n = rtl_ustr_toInt32( p, 10 ) - 1;
1334                 while (rtl::isAsciiDigit( *p ))
1335                     p++;
1336                 const SCROW nMaxRow = rDoc.MaxRow();
1337                 if( n < 0 || n > nMaxRow )
1338                     nBits = ScRefFlags::ZERO;
1339                 nRow = static_cast<SCROW>(n);
1340             }
1341             if( nBits == ScRefFlags::ZERO )
1342                 p = q;
1343         }
1344         nRes |= nBits;
1345     }
1346 
1347     rAddr.Set( nCol, nRow, nTab );
1348 
1349     if (!*p && bExtDoc)
1350     {
1351         ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
1352 
1353         // Need document name if inherited.
1354         if (bExtDocInherited)
1355         {
1356             // The FileId was created using the original file name, so
1357             // obtain that. Otherwise lcl_ScRange_External_TabSpan() would
1358             // retrieve a FileId for the real name and bail out if that
1359             // differed from pExtInfo->mnFileId, as is the case when
1360             // loading documents that refer external files relative to the
1361             // current own document but were saved from a different path
1362             // than loaded.
1363             const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId, true);
1364             if (pFileName)
1365                 aDocName = *pFileName;
1366             else
1367                 nRes = ScRefFlags::ZERO;
1368         }
1369         pRefMgr->convertToAbsName(aDocName);
1370 
1371         if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
1372         {
1373             if (!rDoc.GetTable( aTab, nTab ))
1374                 nRes = ScRefFlags::ZERO;
1375             else
1376             {
1377                 rAddr.SetTab( nTab);
1378                 nRes |= ScRefFlags::TAB_VALID;
1379             }
1380         }
1381         else
1382         {
1383             if (!pExtInfo)
1384                 nRes = ScRefFlags::ZERO;
1385             else
1386             {
1387                 if (!pExtInfo->mbExternal)
1388                 {
1389                     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
1390 
1391                     pExtInfo->mbExternal = true;
1392                     pExtInfo->maTabName = aTab;
1393                     pExtInfo->mnFileId = nFileId;
1394 
1395                     if (pRefMgr->getSingleRefToken(nFileId, aTab,
1396                                 ScAddress(nCol, nRow, 0), nullptr,
1397                                 &nTab))
1398                     {
1399                         rAddr.SetTab( nTab);
1400                         nRes |= ScRefFlags::TAB_VALID;
1401                     }
1402                     else
1403                         nRes = ScRefFlags::ZERO;
1404                 }
1405                 else
1406                 {
1407                     // This is a call for the second part of the reference,
1408                     // we must have the range to adapt tab span.
1409                     if (!pRange)
1410                         nRes = ScRefFlags::ZERO;
1411                     else
1412                     {
1413                         ScRefFlags nFlags = nRes | ScRefFlags::TAB2_VALID;
1414                         if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
1415                                     pExtInfo, aDocName,
1416                                     pExtInfo->maTabName, aTab, rDoc))
1417                             nRes &= ~ScRefFlags::TAB_VALID;
1418                         else
1419                         {
1420                             if (nFlags & ScRefFlags::TAB2_VALID)
1421                             {
1422                                 rAddr.SetTab( pRange->aEnd.Tab());
1423                                 nRes |= ScRefFlags::TAB_VALID;
1424                             }
1425                             else
1426                                 nRes &= ~ScRefFlags::TAB_VALID;
1427                         }
1428                     }
1429                 }
1430             }
1431         }
1432     }
1433     else if (bExtDoc && pExtInfo && !bExtDocInherited && !pExtInfo->mbExternal && pSheetEndPos)
1434     {
1435         // Pass partial info up to caller, there may be an external name
1436         // following, and if after *pSheetEndPos it's external sheet-local.
1437         // For global names aTab is empty and *pSheetEndPos==0.
1438         pExtInfo->mbExternal = true;
1439         pExtInfo->maTabName = aTab;
1440         pExtInfo->mnFileId = rDoc.GetExternalRefManager()->getExternalFileId(aDocName);
1441     }
1442 
1443     rRawRes |= nRes;
1444 
1445     if ( !(nRes & ScRefFlags::ROW_VALID) && (nRes & ScRefFlags::COL_VALID)
1446             && !( (nRes & ScRefFlags::TAB_3D) && (nRes & ScRefFlags::TAB_VALID)) )
1447     {   // no Row, no Tab, but Col => DM (...), B (...) et al
1448         nRes = ScRefFlags::ZERO;
1449     }
1450     if( !*p )
1451     {
1452         ScRefFlags nMask = nRes & ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID );
1453         if( nMask == ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ) )
1454             nRes |= ScRefFlags::VALID;
1455     }
1456     else
1457         nRes = rRawRes = nBailOutFlags;
1458     return nRes;
1459 }
1460 
1461 static ScRefFlags lcl_ScAddress_Parse ( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
1462                                         const ScAddress::Details& rDetails,
1463                                         ScAddress::ExternalInfo* pExtInfo,
1464                                         const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1465                                         sal_Int32* pSheetEndPos,
1466                                         const OUString* pErrRef )
1467 {
1468     if( !*p )
1469         return ScRefFlags::ZERO;
1470 
1471     switch (rDetails.eConv)
1472     {
1473         case formula::FormulaGrammar::CONV_XL_A1:
1474         case formula::FormulaGrammar::CONV_XL_OOX:
1475         {
1476             ScRange rRange = rAddr;
1477             ScRefFlags nFlags = lcl_ScRange_Parse_XL_A1(
1478                     rRange, p, rDoc, true, pExtInfo,
1479                     (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
1480                     pSheetEndPos, pErrRef);
1481             rAddr = rRange.aStart;
1482             return nFlags;
1483         }
1484         case formula::FormulaGrammar::CONV_XL_R1C1:
1485         {
1486             ScRange rRange = rAddr;
1487             ScRefFlags nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, rDoc, rDetails, true, pExtInfo, pSheetEndPos);
1488             rAddr = rRange.aStart;
1489             return nFlags;
1490         }
1491         default :
1492         case formula::FormulaGrammar::CONV_OOO:
1493         {
1494             ScRefFlags nRawRes = ScRefFlags::ZERO;
1495             return lcl_ScAddress_Parse_OOo( p, rDoc, rAddr, nRawRes, pExtInfo, nullptr, pSheetEndPos, pErrRef);
1496         }
1497     }
1498 }
1499 
1500 bool ConvertSingleRef( const ScDocument& rDoc, const OUString& rRefString,
1501                        SCTAB nDefTab, ScRefAddress& rRefAddress,
1502                        const ScAddress::Details& rDetails,
1503                        ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1504 {
1505     bool bRet = false;
1506     if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1507     {
1508         ScAddress aAddr( 0, 0, nDefTab );
1509         ScRefFlags nRes = aAddr.Parse( rRefString, rDoc, rDetails, pExtInfo);
1510         if ( nRes & ScRefFlags::VALID )
1511         {
1512             rRefAddress.Set( aAddr,
1513                     ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
1514                     ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
1515                     ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
1516             bRet = true;
1517         }
1518     }
1519     return bRet;
1520 }
1521 
1522 bool ConvertDoubleRef( const ScDocument& rDoc, const OUString& rRefString, SCTAB nDefTab,
1523                        ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1524                        const ScAddress::Details& rDetails,
1525                        ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1526 {
1527     bool bRet = false;
1528     if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1529     {
1530         ScRange aRange( ScAddress( 0, 0, nDefTab));
1531         ScRefFlags nRes = aRange.Parse( rRefString, rDoc, rDetails, pExtInfo);
1532         if ( nRes & ScRefFlags::VALID )
1533         {
1534             rStartRefAddress.Set( aRange.aStart,
1535                     ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
1536                     ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
1537                     ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
1538             rEndRefAddress.Set( aRange.aEnd,
1539                     ((nRes & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO),
1540                     ((nRes & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO),
1541                     ((nRes & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO));
1542             bRet = true;
1543         }
1544     }
1545     return bRet;
1546 }
1547 
1548 ScRefFlags ScAddress::Parse( const OUString& r, const ScDocument& rDoc,
1549                              const Details& rDetails,
1550                              ExternalInfo* pExtInfo,
1551                              const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1552                              sal_Int32* pSheetEndPos,
1553                              const OUString* pErrRef )
1554 {
1555     return lcl_ScAddress_Parse( r.getStr(), rDoc, *this, rDetails, pExtInfo, pExternalLinks, pSheetEndPos, pErrRef);
1556 }
1557 
1558 bool ScRange::Intersects( const ScRange& rRange ) const
1559 {
1560     return !(
1561         std::min( aEnd.Col(), rRange.aEnd.Col() ) < std::max( aStart.Col(), rRange.aStart.Col() )
1562      || std::min( aEnd.Row(), rRange.aEnd.Row() ) < std::max( aStart.Row(), rRange.aStart.Row() )
1563      || std::min( aEnd.Tab(), rRange.aEnd.Tab() ) < std::max( aStart.Tab(), rRange.aStart.Tab() )
1564         );
1565 }
1566 
1567 ScRange ScRange::Intersection( const ScRange& rOther ) const
1568 {
1569     SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col());
1570     SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col());
1571     SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row());
1572     SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row());
1573     SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab());
1574     SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab());
1575 
1576     if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2)
1577         return ScRange(ScAddress::INITIALIZE_INVALID);
1578 
1579     return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1580 }
1581 
1582 void ScRange::PutInOrder()
1583 {
1584     SCCOL nTempCol;
1585     if ( aEnd.Col() < (nTempCol = aStart.Col()) )
1586     {
1587         aStart.SetCol(aEnd.Col());
1588         aEnd.SetCol(nTempCol);
1589     }
1590     SCROW nTempRow;
1591     if ( aEnd.Row() < (nTempRow = aStart.Row()) )
1592     {
1593         aStart.SetRow(aEnd.Row());
1594         aEnd.SetRow(nTempRow);
1595     }
1596     SCTAB nTempTab;
1597     if ( aEnd.Tab() < (nTempTab = aStart.Tab()) )
1598     {
1599         aStart.SetTab(aEnd.Tab());
1600         aEnd.SetTab(nTempTab);
1601     }
1602 }
1603 
1604 void ScRange::ExtendTo( const ScRange& rRange )
1605 {
1606     OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1607     if( IsValid() )
1608     {
1609         aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) );
1610         aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) );
1611         aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1612         aEnd.SetCol(   std::max( aEnd.Col(),   rRange.aEnd.Col() ) );
1613         aEnd.SetRow(   std::max( aEnd.Row(),   rRange.aEnd.Row() ) );
1614         aEnd.SetTab(   std::max( aEnd.Tab(),   rRange.aEnd.Tab() ) );
1615     }
1616     else
1617         *this = rRange;
1618 }
1619 
1620 static ScRefFlags lcl_ScRange_Parse_OOo( ScRange& rRange,
1621                                          const OUString& r,
1622                                          const ScDocument& rDoc,
1623                                          ScAddress::ExternalInfo* pExtInfo,
1624                                          const OUString* pErrRef )
1625 {
1626     ScRefFlags nRes1 = ScRefFlags::ZERO, nRes2 = ScRefFlags::ZERO;
1627     sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':');
1628     if (nPos != -1)
1629     {
1630         OUStringBuffer aTmp(r);
1631         aTmp[nPos] = 0;
1632         const sal_Unicode* p = aTmp.getStr();
1633         ScRefFlags nRawRes1 = ScRefFlags::ZERO;
1634         nRes1 = lcl_ScAddress_Parse_OOo( p, rDoc, rRange.aStart, nRawRes1, pExtInfo, nullptr, nullptr, pErrRef);
1635         if ((nRes1 != ScRefFlags::ZERO) ||
1636                 ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1637                  (nRawRes1 & ScRefFlags::TAB_VALID)))
1638         {
1639             rRange.aEnd = rRange.aStart;  // sheet must be initialized identical to first sheet
1640             ScRefFlags nRawRes2 = ScRefFlags::ZERO;
1641             nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, rDoc, rRange.aEnd, nRawRes2,
1642                     pExtInfo, &rRange, nullptr, pErrRef);
1643             if (!((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) &&
1644                     // If not fully valid addresses, check if both have a valid
1645                     // column or row, and both have valid (or omitted) sheet references.
1646                     (nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1647                     (nRawRes1 & ScRefFlags::TAB_VALID) &&
1648                     (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1649                     (nRawRes2 & ScRefFlags::TAB_VALID) &&
1650                     // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A
1651                     ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) ==
1652                      (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID))))
1653             {
1654                 nRes1 = nRawRes1 | ScRefFlags::VALID;
1655                 nRes2 = nRawRes2 | ScRefFlags::VALID;
1656                 if (nRawRes1 & ScRefFlags::COL_VALID)
1657                 {
1658                     rRange.aStart.SetRow(0);
1659                     rRange.aEnd.SetRow(rDoc.MaxRow());
1660                     nRes1 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS;
1661                     nRes2 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS;
1662                 }
1663                 else
1664                 {
1665                     rRange.aStart.SetCol(0);
1666                     rRange.aEnd.SetCol( rDoc.MaxCol() );
1667                     nRes1 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
1668                     nRes2 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
1669                 }
1670             }
1671             else if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
1672             {
1673                 // Flag entire column/row references so they can be displayed
1674                 // as such. If the sticky reference parts are not both
1675                 // absolute or relative, assume that the user thought about
1676                 // something we should not touch.
1677                 if (rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() &&
1678                         ((nRes1 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO) &&
1679                         ((nRes2 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO))
1680                 {
1681                     nRes1 |= ScRefFlags::ROW_ABS;
1682                     nRes2 |= ScRefFlags::ROW_ABS;
1683                 }
1684                 else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
1685                         ((nRes1 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO) && ((nRes2 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO))
1686                 {
1687                     nRes1 |= ScRefFlags::COL_ABS;
1688                     nRes2 |= ScRefFlags::COL_ABS;
1689                 }
1690             }
1691             if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
1692             {
1693                 // PutInOrder / Justify
1694                 ScRefFlags nMask, nBits1, nBits2;
1695                 SCCOL nTempCol;
1696                 if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) )
1697                 {
1698                     rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol);
1699                     nMask = (ScRefFlags::COL_VALID | ScRefFlags::COL_ABS);
1700                     nBits1 = nRes1 & nMask;
1701                     nBits2 = nRes2 & nMask;
1702                     nRes1 = (nRes1 & ~nMask) | nBits2;
1703                     nRes2 = (nRes2 & ~nMask) | nBits1;
1704                 }
1705                 SCROW nTempRow;
1706                 if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) )
1707                 {
1708                     rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow);
1709                     nMask = (ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS);
1710                     nBits1 = nRes1 & nMask;
1711                     nBits2 = nRes2 & nMask;
1712                     nRes1 = (nRes1 & ~nMask) | nBits2;
1713                     nRes2 = (nRes2 & ~nMask) | nBits1;
1714                 }
1715                 SCTAB nTempTab;
1716                 if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) )
1717                 {
1718                     rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab);
1719                     nMask = (ScRefFlags::TAB_VALID | ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D);
1720                     nBits1 = nRes1 & nMask;
1721                     nBits2 = nRes2 & nMask;
1722                     nRes1 = (nRes1 & ~nMask) | nBits2;
1723                     nRes2 = (nRes2 & ~nMask) | nBits1;
1724                 }
1725                 if ( ((nRes1 & ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
1726                             == ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
1727                   && !(nRes2 & ScRefFlags::TAB_3D) )
1728                     nRes2 |= ScRefFlags::TAB_ABS;
1729             }
1730             else
1731             {
1732                 // Don't leave around valid half references.
1733                 nRes1 = nRes2 = ScRefFlags::ZERO;
1734             }
1735         }
1736     }
1737     applyStartToEndFlags(nRes1, nRes2 & ScRefFlags::BITS);
1738     nRes1 |= nRes2 & ScRefFlags::VALID;
1739     return nRes1;
1740 }
1741 
1742 ScRefFlags ScRange::Parse( const OUString& rString, const ScDocument& rDoc,
1743                            const ScAddress::Details& rDetails,
1744                            ScAddress::ExternalInfo* pExtInfo,
1745                            const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1746                            const OUString* pErrRef )
1747 {
1748     if (rString.isEmpty())
1749         return ScRefFlags::ZERO;
1750 
1751     switch (rDetails.eConv)
1752     {
1753         case formula::FormulaGrammar::CONV_XL_A1:
1754         case formula::FormulaGrammar::CONV_XL_OOX:
1755         {
1756             return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), rDoc, false, pExtInfo,
1757                     (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
1758                     nullptr, pErrRef );
1759         }
1760 
1761         case formula::FormulaGrammar::CONV_XL_R1C1:
1762         {
1763             return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr );
1764         }
1765 
1766         default:
1767         case formula::FormulaGrammar::CONV_OOO:
1768         {
1769             return lcl_ScRange_Parse_OOo( *this, rString, rDoc, pExtInfo, pErrRef );
1770         }
1771     }
1772 }
1773 
1774 // Accept a full range, or an address
1775 ScRefFlags ScRange::ParseAny( const OUString& rString, const ScDocument& rDoc,
1776                               const ScAddress::Details& rDetails )
1777 {
1778     ScRefFlags nRet = Parse( rString, rDoc, rDetails );
1779     const ScRefFlags nValid = ScRefFlags::VALID | ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID;
1780 
1781     if ( (nRet & nValid) != nValid )
1782     {
1783         ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number
1784         nRet = aAdr.Parse( rString, rDoc, rDetails );
1785         if ( nRet & ScRefFlags::VALID )
1786             aStart = aEnd = aAdr;
1787     }
1788     return nRet;
1789 }
1790 
1791 // Parse only full row references
1792 ScRefFlags ScRange::ParseCols( const ScDocument& rDoc,
1793                                const OUString& rStr,
1794                                const ScAddress::Details& rDetails )
1795 {
1796     if (rStr.isEmpty())
1797         return ScRefFlags::ZERO;
1798 
1799     const sal_Unicode* p = rStr.getStr();
1800     ScRefFlags nRes = ScRefFlags::ZERO;
1801     ScRefFlags ignored = ScRefFlags::ZERO;
1802 
1803     switch (rDetails.eConv)
1804     {
1805     default :
1806     case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1807     case formula::FormulaGrammar::CONV_XL_A1:
1808     case formula::FormulaGrammar::CONV_XL_OOX:
1809         if (nullptr != (p = lcl_a1_get_col( rDoc, p, &aStart, &ignored, nullptr) ) )
1810         {
1811             if( p[0] == ':')
1812             {
1813                 if( nullptr != (p = lcl_a1_get_col( rDoc, p+1, &aEnd, &ignored, nullptr)))
1814                 {
1815                     nRes = ScRefFlags::COL_VALID;
1816                 }
1817             }
1818             else
1819             {
1820                 aEnd = aStart;
1821                 nRes = ScRefFlags::COL_VALID;
1822             }
1823         }
1824         break;
1825 
1826     case formula::FormulaGrammar::CONV_XL_R1C1:
1827         if ((p[0] == 'C' || p[0] == 'c') &&
1828             nullptr != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored )))
1829         {
1830             if( p[0] == ':')
1831             {
1832                 if( (p[1] == 'C' || p[1] == 'c') &&
1833                     nullptr != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored )))
1834                 {
1835                     nRes = ScRefFlags::COL_VALID;
1836                 }
1837             }
1838             else
1839             {
1840                 aEnd = aStart;
1841                 nRes = ScRefFlags::COL_VALID;
1842             }
1843         }
1844         break;
1845     }
1846 
1847     return (p != nullptr && *p == '\0') ? nRes : ScRefFlags::ZERO;
1848 }
1849 
1850 // Parse only full row references
1851 void ScRange::ParseRows( const ScDocument& rDoc,
1852                                const OUString& rStr,
1853                                const ScAddress::Details& rDetails )
1854 {
1855     if (rStr.isEmpty())
1856         return;
1857 
1858     const sal_Unicode* p = rStr.getStr();
1859     ScRefFlags ignored = ScRefFlags::ZERO;
1860 
1861     switch (rDetails.eConv)
1862     {
1863     default :
1864     case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1865     case formula::FormulaGrammar::CONV_XL_A1:
1866     case formula::FormulaGrammar::CONV_XL_OOX:
1867         if (nullptr != (p = lcl_a1_get_row( rDoc, p, &aStart, &ignored, nullptr) ) )
1868         {
1869             if( p[0] == ':')
1870             {
1871                 lcl_a1_get_row( rDoc, p+1, &aEnd, &ignored, nullptr);
1872             }
1873             else
1874             {
1875                 aEnd = aStart;
1876             }
1877         }
1878         break;
1879 
1880     case formula::FormulaGrammar::CONV_XL_R1C1:
1881         if ((p[0] == 'R' || p[0] == 'r') &&
1882             nullptr != (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored )))
1883         {
1884             if( p[0] == ':')
1885             {
1886                 if( p[1] == 'R' || p[1] == 'r' )
1887                 {
1888                     lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored );
1889                 }
1890             }
1891             else
1892             {
1893                 aEnd = aStart;
1894             }
1895         }
1896         break;
1897     }
1898 }
1899 
1900 template<typename T > static void lcl_ScColToAlpha( T& rBuf, SCCOL nCol )
1901 {
1902     if (nCol < 26*26)
1903     {
1904         if (nCol < 26)
1905             rBuf.append( static_cast<char>( 'A' + nCol ));
1906         else
1907         {
1908             rBuf.append( static_cast<char>( 'A' + nCol / 26 - 1 ));
1909             rBuf.append( static_cast<char>( 'A' + nCol % 26 ));
1910         }
1911     }
1912     else
1913     {
1914         sal_Int32 nInsert = rBuf.getLength();
1915         while (nCol >= 26)
1916         {
1917             SCCOL nC = nCol % 26;
1918             rBuf.insert(nInsert, static_cast<char> ( 'A' + nC ));
1919             nCol = sal::static_int_cast<SCCOL>( nCol - nC );
1920             nCol = nCol / 26 - 1;
1921         }
1922         rBuf.insert(nInsert, static_cast<char> ( 'A' + nCol ));
1923     }
1924 }
1925 
1926 void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol)
1927 {
1928     lcl_ScColToAlpha(rBuf, nCol);
1929 }
1930 
1931 template <typename T > static void lcl_a1_append_c ( T &rString, int nCol, bool bIsAbs )
1932 {
1933     if( bIsAbs )
1934         rString.append("$");
1935     lcl_ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) );
1936 }
1937 
1938 template <typename T > static void lcl_a1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs )
1939 {
1940     if ( bIsAbs )
1941         rString.append("$");
1942     rString.append( nRow + 1 );
1943 }
1944 
1945 template <typename T > static void lcl_r1c1_append_c ( T &rString, sal_Int32 nCol, bool bIsAbs,
1946                                        const ScAddress::Details& rDetails )
1947 {
1948     rString.append("C");
1949     if (bIsAbs)
1950     {
1951         rString.append( nCol + 1 );
1952     }
1953     else
1954     {
1955         nCol -= rDetails.nCol;
1956         if (nCol != 0) {
1957             rString.append("[").append(nCol).append("]");
1958         }
1959     }
1960 }
1961 
1962 template <typename T > static void lcl_r1c1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs,
1963                                        const ScAddress::Details& rDetails )
1964 {
1965     rString.append("R");
1966     if (bIsAbs)
1967     {
1968         rString.append( nRow + 1 );
1969     }
1970     else
1971     {
1972         nRow -= rDetails.nRow;
1973         if (nRow != 0) {
1974             rString.append("[").append(nRow).append("]");
1975         }
1976     }
1977 }
1978 
1979 static OUString getFileNameFromDoc( const ScDocument* pDoc )
1980 {
1981     // TODO : er points at ScGlobal::GetAbsDocName()
1982     // as a better template.  Look into it
1983     OUString sFileName;
1984     SfxObjectShell* pShell;
1985 
1986     if( nullptr != pDoc &&
1987         nullptr != (pShell = pDoc->GetDocumentShell() ) )
1988     {
1989         uno::Reference< frame::XModel > xModel = pShell->GetModel();
1990         if( xModel.is() )
1991         {
1992             if( !xModel->getURL().isEmpty() )
1993             {
1994                 INetURLObject aURL( xModel->getURL() );
1995                 sFileName = aURL.GetLastName();
1996             }
1997             else
1998                 sFileName = pShell->GetTitle();
1999         }
2000     }
2001     return sFileName;
2002 }
2003 
2004 
2005 static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString)
2006 {
2007     rString.append(sString);
2008 }
2009 
2010 static void lcl_string_append(OStringBuffer &rString, std::u16string_view sString)
2011 {
2012     rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8  ));
2013 }
2014 
2015 template<typename T > static void lcl_Format( T& r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags,
2016                                   const ScDocument* pDoc,
2017                                   const ScAddress::Details& rDetails)
2018 {
2019     if( nFlags & ScRefFlags::VALID )
2020         nFlags |= ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID;
2021     if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) )
2022     {
2023         if ( nTab < 0 || nTab >= pDoc->GetTableCount() )
2024         {
2025             lcl_string_append(r, ScCompiler::GetNativeSymbol(ocErrRef));
2026             return;
2027         }
2028         if( nFlags & ScRefFlags::TAB_3D )
2029         {
2030             OUString aTabName, aDocName;
2031             pDoc->GetName(nTab, aTabName);
2032             assert( !aTabName.isEmpty() && "empty sheet name");
2033             // External Reference, same as in ScCompiler::MakeTabStr()
2034             if( aTabName[0] == '\'' )
2035             {   // "'Doc'#Tab"
2036                 sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName);
2037                 if (nPos != -1)
2038                 {
2039                     aDocName = aTabName.copy( 0, nPos + 1 );
2040                     aTabName = aTabName.copy( nPos + 1 );
2041                 }
2042             }
2043             else if( nFlags & ScRefFlags::FORCE_DOC )
2044             {
2045                 // VBA has an 'external' flag that forces the addition of the
2046                 // tab name _and_ the doc name.  The VBA code would be
2047                 // needlessly complicated if it constructed an actual external
2048                 // reference so we add this somewhat cheesy kludge to force the
2049                 // addition of the document name even for non-external references
2050                 aDocName = getFileNameFromDoc( pDoc );
2051             }
2052             ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
2053 
2054             switch( rDetails.eConv )
2055             {
2056             default :
2057             case formula::FormulaGrammar::CONV_OOO:
2058                 lcl_string_append(r, aDocName);
2059                 if( nFlags & ScRefFlags::TAB_ABS )
2060                     r.append("$");
2061                 lcl_string_append(r, aTabName);
2062                 r.append(".");
2063                 break;
2064 
2065             case formula::FormulaGrammar::CONV_XL_OOX:
2066                 if (!aTabName.isEmpty() && aTabName[0] == '\'')
2067                 {
2068                     if (!aDocName.isEmpty())
2069                     {
2070                         lcl_string_append(r.append("'["), aDocName);
2071                         r.append("]");
2072                         lcl_string_append(r, aTabName.copy(1));
2073                     }
2074                     else
2075                     {
2076                         lcl_string_append(r, aTabName);
2077                     }
2078                     r.append("!");
2079                     break;
2080                 }
2081                 [[fallthrough]];
2082             case formula::FormulaGrammar::CONV_XL_A1:
2083             case formula::FormulaGrammar::CONV_XL_R1C1:
2084                 if (!aDocName.isEmpty())
2085                 {
2086                     lcl_string_append(r.append("["), aDocName);
2087                     r.append("]");
2088                 }
2089                 lcl_string_append(r, aTabName);
2090                 r.append("!");
2091                 break;
2092             }
2093         }
2094     }
2095     switch( rDetails.eConv )
2096     {
2097     default :
2098     case formula::FormulaGrammar::CONV_OOO:
2099     case formula::FormulaGrammar::CONV_XL_A1:
2100     case formula::FormulaGrammar::CONV_XL_OOX:
2101         if( nFlags & ScRefFlags::COL_VALID )
2102             lcl_a1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2103         if( nFlags & ScRefFlags::ROW_VALID )
2104             lcl_a1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2105         break;
2106 
2107     case formula::FormulaGrammar::CONV_XL_R1C1:
2108         if( nFlags & ScRefFlags::ROW_VALID )
2109             lcl_r1c1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2110         if( nFlags & ScRefFlags::COL_VALID )
2111             lcl_r1c1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2112         break;
2113     }
2114 }
2115 
2116 void ScAddress::Format( OStringBuffer& r, ScRefFlags nFlags,
2117                                   const ScDocument* pDoc,
2118                                   const Details& rDetails) const
2119 {
2120     lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
2121 }
2122 
2123 OUString ScAddress::Format(ScRefFlags nFlags, const ScDocument* pDoc,
2124                            const Details& rDetails) const
2125 {
2126     OUStringBuffer r;
2127     lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
2128     return r.makeStringAndClear();
2129 }
2130 
2131 static void lcl_Split_DocTab( const ScDocument& rDoc,  SCTAB nTab,
2132                               const ScAddress::Details& rDetails,
2133                               ScRefFlags nFlags,
2134                               OUString& rTabName, OUString& rDocName )
2135 {
2136     rDoc.GetName(nTab, rTabName);
2137     rDocName.clear();
2138     // External reference, same as in ScCompiler::MakeTabStr()
2139     if ( rTabName[0] == '\'' )
2140     {   // "'Doc'#Tab"
2141         sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName);
2142         if (nPos != -1)
2143         {
2144             rDocName = rTabName.copy( 0, nPos + 1 );
2145             rTabName = rTabName.copy( nPos + 1 );
2146         }
2147     }
2148     else if( nFlags & ScRefFlags::FORCE_DOC )
2149     {
2150         // VBA has an 'external' flag that forces the addition of the
2151         // tab name _and_ the doc name.  The VBA code would be
2152         // needlessly complicated if it constructed an actual external
2153         // reference so we add this somewhat cheesy kludge to force the
2154         // addition of the document name even for non-external references
2155         rDocName = getFileNameFromDoc(&rDoc);
2156     }
2157     ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
2158 }
2159 
2160 static void lcl_ScRange_Format_XL_Header( OUStringBuffer& rString, const ScRange& rRange,
2161                                           ScRefFlags nFlags, const ScDocument& rDoc,
2162                                           const ScAddress::Details& rDetails )
2163 {
2164     if( !(nFlags & ScRefFlags::TAB_3D) )
2165         return;
2166 
2167     OUString aTabName, aDocName;
2168     lcl_Split_DocTab( rDoc, rRange.aStart.Tab(), rDetails, nFlags, aTabName, aDocName );
2169     switch (rDetails.eConv)
2170     {
2171         case formula::FormulaGrammar::CONV_XL_OOX:
2172             if (!aTabName.isEmpty() && aTabName[0] == '\'')
2173             {
2174                 if (!aDocName.isEmpty())
2175                 {
2176                     rString.append("'[" + aDocName + "]" + aTabName.subView(1));
2177                 }
2178                 else
2179                 {
2180                     rString.append(aTabName);
2181                 }
2182                 break;
2183             }
2184             [[fallthrough]];
2185         default:
2186             if (!aDocName.isEmpty())
2187             {
2188                 rString.append("[").append(aDocName).append("]");
2189             }
2190             rString.append(aTabName);
2191         break;
2192     }
2193     if( nFlags & ScRefFlags::TAB2_3D )
2194     {
2195         lcl_Split_DocTab( rDoc, rRange.aEnd.Tab(), rDetails, nFlags, aTabName, aDocName );
2196         rString.append(":");
2197         rString.append(aTabName);
2198     }
2199     rString.append("!");
2200 }
2201 
2202 // helpers used in ScRange::Format
2203 static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags)
2204 {
2205     return static_cast<bool>(nFlags & ScRefFlags::COL_ABS) != static_cast<bool>(nFlags & ScRefFlags::COL2_ABS);
2206 }
2207 static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags)
2208 {
2209     return static_cast<bool>(nFlags & ScRefFlags::ROW_ABS) != static_cast<bool>(nFlags & ScRefFlags::ROW2_ABS);
2210 }
2211 
2212 OUString ScRange::Format( const ScDocument& rDoc, ScRefFlags nFlags,
2213                           const ScAddress::Details& rDetails, bool bFullAddressNotation ) const
2214 {
2215     if( !( nFlags & ScRefFlags::VALID ) )
2216     {
2217         return ScCompiler::GetNativeSymbol(ocErrRef);
2218     }
2219 
2220     OUStringBuffer r;
2221     switch( rDetails.eConv ) {
2222     default :
2223     case formula::FormulaGrammar::CONV_OOO: {
2224         bool bOneTab = (aStart.Tab() == aEnd.Tab());
2225         if ( !bOneTab )
2226             nFlags |= ScRefFlags::TAB_3D;
2227         r = aStart.Format(nFlags, &rDoc, rDetails);
2228         if( aStart != aEnd ||
2229             lcl_ColAbsFlagDiffer( nFlags ) ||
2230             lcl_RowAbsFlagDiffer( nFlags ))
2231         {
2232             const ScDocument* pDoc = &rDoc;
2233             // move flags of end reference to start reference, mask with BITS to exclude FORCE_DOC flag
2234             nFlags = ScRefFlags::VALID | (ScRefFlags(o3tl::underlyingEnumValue(nFlags) >> 4) & ScRefFlags::BITS);
2235             if ( bOneTab )
2236                 pDoc = nullptr;
2237             else
2238                 nFlags |= ScRefFlags::TAB_3D;
2239             OUString aName(aEnd.Format(nFlags, pDoc, rDetails));
2240             r.append(":");
2241             r.append(aName);
2242         }
2243         break;
2244     }
2245 
2246     case formula::FormulaGrammar::CONV_XL_A1:
2247     case formula::FormulaGrammar::CONV_XL_OOX: {
2248         SCCOL nMaxCol = rDoc.MaxCol();
2249         SCROW nMaxRow = rDoc.MaxRow();
2250 
2251         lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
2252         if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
2253         {
2254             // Full col refs always require 2 rows (2:2)
2255             lcl_a1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2256             r.append(":");
2257             lcl_a1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
2258         }
2259         else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
2260         {
2261             // Full row refs always require 2 cols (A:A)
2262             lcl_a1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2263             r.append(":");
2264             lcl_a1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
2265         }
2266         else
2267         {
2268             lcl_a1_append_c ( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2269             lcl_a1_append_r ( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2270             if( aStart.Col() != aEnd.Col() ||
2271                 lcl_ColAbsFlagDiffer( nFlags ) ||
2272                 aStart.Row() != aEnd.Row() ||
2273                 lcl_RowAbsFlagDiffer( nFlags ) ) {
2274                 r.append(":");
2275                 lcl_a1_append_c ( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
2276                 lcl_a1_append_r ( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
2277             }
2278         }
2279         break;
2280     }
2281 
2282     case formula::FormulaGrammar::CONV_XL_R1C1: {
2283         SCCOL nMaxCol = rDoc.MaxCol();
2284         SCROW nMaxRow = rDoc.MaxRow();
2285 
2286         lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
2287         if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
2288         {
2289             lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2290             if( aStart.Row() != aEnd.Row() ||
2291                 lcl_RowAbsFlagDiffer( nFlags ) ) {
2292                 r.append(":");
2293                 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
2294             }
2295         }
2296         else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
2297         {
2298             lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2299             if( aStart.Col() != aEnd.Col() ||
2300                 lcl_ColAbsFlagDiffer( nFlags )) {
2301                 r.append(":");
2302                 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
2303             }
2304         }
2305         else
2306         {
2307             lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2308             lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2309             if( aStart.Col() != aEnd.Col() ||
2310                 lcl_ColAbsFlagDiffer( nFlags ) ||
2311                 aStart.Row() != aEnd.Row() ||
2312                 lcl_RowAbsFlagDiffer( nFlags ) ) {
2313                 r.append(":");
2314                 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
2315                 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
2316             }
2317         }
2318         break;
2319     }
2320     }
2321     return r.makeStringAndClear();
2322 }
2323 
2324 bool ScAddress::Move( SCCOL dx, SCROW dy, SCTAB dz, ScAddress& rErrorPos, const ScDocument* pDoc )
2325 {
2326     SCTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB;
2327     SCCOL nMaxCol = pDoc ? pDoc->MaxCol() : MAXCOL;
2328     SCROW nMaxRow = pDoc ? pDoc->MaxRow() : MAXROW;
2329     dx = Col() + dx;
2330     dy = Row() + dy;
2331     dz = Tab() + dz;
2332     bool bValid = true;
2333     rErrorPos.SetCol(dx);
2334     if( dx < 0 )
2335     {
2336         dx = 0;
2337         bValid = false;
2338     }
2339     else if( dx > nMaxCol )
2340     {
2341         dx = nMaxCol;
2342         bValid =false;
2343     }
2344     rErrorPos.SetRow(dy);
2345     if( dy < 0 )
2346     {
2347         dy = 0;
2348         bValid = false;
2349     }
2350     else if( dy > nMaxRow )
2351     {
2352         dy = nMaxRow;
2353         bValid =false;
2354     }
2355     rErrorPos.SetTab(dz);
2356     if( dz < 0 )
2357     {
2358         dz = 0;
2359         bValid = false;
2360     }
2361     else if( dz > nMaxTab )
2362     {
2363         // Always set MAXTAB+1 so further checks without ScDocument detect invalid.
2364         rErrorPos.SetTab(MAXTAB+1);
2365         dz = nMaxTab;
2366         bValid =false;
2367     }
2368     Set( dx, dy, dz );
2369     return bValid;
2370 }
2371 
2372 bool ScRange::Move( SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange, const ScDocument* pDoc )
2373 {
2374     SCCOL nMaxCol = pDoc ? pDoc->MaxCol() : MAXCOL;
2375     SCROW nMaxRow = pDoc ? pDoc->MaxRow() : MAXROW;
2376     if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
2377         dy = 0;     // Entire column not to be moved.
2378     if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
2379         dx = 0;     // Entire row not to be moved.
2380     bool b = aStart.Move( dx, dy, dz, rErrorRange.aStart, pDoc );
2381     b &= aEnd.Move( dx, dy, dz, rErrorRange.aEnd, pDoc );
2382     return b;
2383 }
2384 
2385 bool ScRange::MoveSticky( const ScDocument& rDoc, SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange )
2386 {
2387     const SCCOL nMaxCol = rDoc.MaxCol();
2388     const SCROW nMaxRow = rDoc.MaxRow();
2389     bool bColRange = (aStart.Col() < aEnd.Col());
2390     bool bRowRange = (aStart.Row() < aEnd.Row());
2391     if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
2392         dy = 0;     // Entire column not to be moved.
2393     if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
2394         dx = 0;     // Entire row not to be moved.
2395     bool b1 = aStart.Move( dx, dy, dz, rErrorRange.aStart );
2396     if (dx && bColRange && aEnd.Col() == nMaxCol)
2397         dx = 0;     // End column sticky.
2398     if (dy && bRowRange && aEnd.Row() == nMaxRow)
2399         dy = 0;     // End row sticky.
2400     SCTAB nOldTab = aEnd.Tab();
2401     bool b2 = aEnd.Move( dx, dy, dz, rErrorRange.aEnd );
2402     if (!b2)
2403     {
2404         // End column or row of a range may have become sticky.
2405         bColRange = (!dx || (bColRange && aEnd.Col() == nMaxCol));
2406         if (dx && bColRange)
2407             rErrorRange.aEnd.SetCol(nMaxCol);
2408         bRowRange = (!dy || (bRowRange && aEnd.Row() == nMaxRow));
2409         if (dy && bRowRange)
2410             rErrorRange.aEnd.SetRow(nMaxRow);
2411         b2 = bColRange && bRowRange && (aEnd.Tab() - nOldTab == dz);
2412     }
2413     return b1 && b2;
2414 }
2415 
2416 void ScRange::IncColIfNotLessThan(const ScDocument& rDoc, SCCOL nStartCol, SCCOL nOffset)
2417 {
2418     if (aStart.Col() >= nStartCol)
2419     {
2420         aStart.IncCol(nOffset);
2421         if (aStart.Col() < 0)
2422             aStart.SetCol(0);
2423         else if(aStart.Col() > rDoc.MaxCol())
2424             aStart.SetCol(rDoc.MaxCol());
2425     }
2426     if (aEnd.Col() >= nStartCol)
2427     {
2428         aEnd.IncCol(nOffset);
2429         if (aEnd.Col() < 0)
2430             aEnd.SetCol(0);
2431         else if(aEnd.Col() > rDoc.MaxCol())
2432             aEnd.SetCol(rDoc.MaxCol());
2433     }
2434 }
2435 
2436 void ScRange::IncRowIfNotLessThan(const ScDocument& rDoc, SCROW nStartRow, SCROW nOffset)
2437 {
2438     if (aStart.Row() >= nStartRow)
2439     {
2440         aStart.IncRow(nOffset);
2441         if (aStart.Row() < 0)
2442             aStart.SetRow(0);
2443         else if(aStart.Row() > rDoc.MaxRow())
2444             aStart.SetRow(rDoc.MaxRow());
2445     }
2446     if (aEnd.Row() >= nStartRow)
2447     {
2448         aEnd.IncRow(nOffset);
2449         if (aEnd.Row() < 0)
2450             aEnd.SetRow(0);
2451         else if(aEnd.Row() > rDoc.MaxRow())
2452             aEnd.SetRow(rDoc.MaxRow());
2453     }
2454 }
2455 
2456 void ScRange::IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta )
2457 {
2458     SCCOL nCol = aEnd.Col();
2459     if (aStart.Col() >= nCol)
2460     {
2461         // Less than two columns => not sticky.
2462         aEnd.IncCol( nDelta);
2463         return;
2464     }
2465 
2466     const SCCOL nMaxCol = rDoc.MaxCol();
2467     if (nCol == nMaxCol)
2468         // already sticky
2469         return;
2470 
2471     if (nCol < nMaxCol)
2472         aEnd.SetCol( ::std::min( static_cast<SCCOL>(nCol + nDelta), nMaxCol));
2473     else
2474         aEnd.IncCol( nDelta);   // was greater than nMaxCol, caller should know...
2475 }
2476 
2477 void ScRange::IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta )
2478 {
2479     SCROW nRow = aEnd.Row();
2480     if (aStart.Row() >= nRow)
2481     {
2482         // Less than two rows => not sticky.
2483         aEnd.IncRow( nDelta);
2484         return;
2485     }
2486 
2487     if (nRow == rDoc.MaxRow())
2488         // already sticky
2489         return;
2490 
2491     if (nRow < rDoc.MaxRow())
2492         aEnd.SetRow( ::std::min( static_cast<SCROW>(nRow + nDelta), rDoc.MaxRow()));
2493     else
2494         aEnd.IncRow( nDelta);   // was greater than rDoc.MaxRow(), caller should know...
2495 }
2496 
2497 OUString ScAddress::GetColRowString() const
2498 {
2499     OUStringBuffer aString;
2500 
2501     switch( detailsOOOa1.eConv )
2502     {
2503     default :
2504     case formula::FormulaGrammar::CONV_OOO:
2505     case formula::FormulaGrammar::CONV_XL_A1:
2506     case formula::FormulaGrammar::CONV_XL_OOX:
2507         lcl_ScColToAlpha( aString, nCol);
2508         aString.append(OUString::number(nRow+1));
2509         break;
2510 
2511     case formula::FormulaGrammar::CONV_XL_R1C1:
2512         lcl_r1c1_append_r ( aString, nRow, false/*bAbsolute*/, detailsOOOa1 );
2513         lcl_r1c1_append_c ( aString, nCol, false/*bAbsolute*/, detailsOOOa1 );
2514         break;
2515     }
2516 
2517     return aString.makeStringAndClear();
2518 }
2519 
2520 OUString ScRefAddress::GetRefString( const ScDocument& rDoc, SCTAB nActTab,
2521                                      const ScAddress::Details& rDetails ) const
2522 {
2523     if ( Tab()+1 > rDoc.GetTableCount() )
2524         return ScCompiler::GetNativeSymbol(ocErrRef);
2525 
2526     ScRefFlags nFlags = ScRefFlags::VALID;
2527     if ( nActTab != Tab() )
2528     {
2529         nFlags |= ScRefFlags::TAB_3D;
2530         if ( !bRelTab )
2531             nFlags |= ScRefFlags::TAB_ABS;
2532     }
2533     if ( !bRelCol )
2534         nFlags |= ScRefFlags::COL_ABS;
2535     if ( !bRelRow )
2536         nFlags |= ScRefFlags::ROW_ABS;
2537 
2538     return aAdr.Format(nFlags, &rDoc, rDetails);
2539 }
2540 
2541 bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, const OUString& rStr)
2542 {
2543     SCCOL nResult = 0;
2544     sal_Int32 nStop = rStr.getLength();
2545     sal_Int32 nPos = 0;
2546     sal_Unicode c;
2547     const SCCOL nMaxCol = rDoc.MaxCol();
2548     while (nResult <= nMaxCol && nPos < nStop && (c = rStr[nPos]) != 0 &&
2549             rtl::isAsciiAlpha(c))
2550     {
2551         if (nPos > 0)
2552             nResult = (nResult + 1) * 26;
2553         nResult += ScGlobal::ToUpperAlpha(c) - 'A';
2554         ++nPos;
2555     }
2556     bool bOk = (rDoc.ValidCol(nResult) && nPos > 0);
2557     if (bOk)
2558         rCol = nResult;
2559     return bOk;
2560 }
2561 
2562 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2563