LibreOffice Module sc (master)  1
address.cxx
Go to the documentation of this file.
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>
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 
44 
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 
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 
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 
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)
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 
294 static const sal_Unicode * lcl_XL_ParseSheetRef( const sal_Unicode* start,
295  OUString& rExternTabName,
296  bool bAllow3D,
297  const sal_Unicode* pMsoxlQuoteStop,
298  const OUString* pErrRef )
299 {
300  OUString aTabName;
301  const sal_Unicode *p = start;
302 
303  // XL only seems to use single quotes for sheet names.
304  if (pMsoxlQuoteStop)
305  {
306  const sal_Unicode* pCurrentStart = p;
307  while (p < pMsoxlQuoteStop)
308  {
309  if (*p == '\'')
310  {
311  // We pre-analyzed the quoting, no checks needed here.
312  if (*++p == '\'')
313  {
314  aTabName += std::u16string_view( pCurrentStart,
315  sal::static_int_cast<sal_Int32>( p - pCurrentStart));
316  pCurrentStart = ++p;
317  }
318  }
319  else if (*p == ':')
320  {
321  break; // while
322  }
323  else
324  ++p;
325  }
326  if (pCurrentStart < p)
327  aTabName += std::u16string_view( pCurrentStart, sal::static_int_cast<sal_Int32>( p - pCurrentStart));
328  if (aTabName.isEmpty())
329  return nullptr;
330  if (p == pMsoxlQuoteStop)
331  ++p; // position on ! of ...'!...
332  if( *p != '!' && ( !bAllow3D || *p != ':' ) )
333  return (!bAllow3D && *p == ':') ? p : start;
334  }
335  else if( *p == '\'')
336  {
337  p = parseQuotedName(p, aTabName);
338  if (aTabName.isEmpty())
339  return nullptr;
340  }
341  else if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '!')
342  {
343  p += pErrRef->getLength(); // position after "#REF!" on '!'
344  // XXX NOTE: caller has to check the name and that it wasn't quoted.
345  aTabName = *pErrRef;
346  }
347  else
348  {
349  bool only_digits = true;
350 
351  /*
352  * Valid: Normal!a1
353  * Valid: x.y!a1
354  * Invalid: .y!a1
355  *
356  * Some names starting with digits are actually valid, but
357  * unparse quoted. Things are quite tricky: most sheet names
358  * starting with a digit are ok, but not those starting with
359  * "[0-9]*\." or "[0-9]+[eE]".
360  *
361  * Valid: 42!a1
362  * Valid: 4x!a1
363  * Invalid: 1.!a1
364  * Invalid: 1e!a1
365  */
366  while( true )
367  {
368  const sal_Unicode uc = *p;
369  if( rtl::isAsciiAlpha( uc ) || uc == '_' )
370  {
371  if( only_digits && p != start &&
372  (uc == 'e' || uc == 'E' ) )
373  {
374  p = start;
375  break;
376  }
377  only_digits = false;
378  p++;
379  }
380  else if( rtl::isAsciiDigit( uc ))
381  {
382  p++;
383  }
384  else if( uc == '.' )
385  {
386  if( only_digits ) // Valid, except after only digits.
387  {
388  p = start;
389  break;
390  }
391  p++;
392  }
393  else if (uc > 127)
394  {
395  // non ASCII character is allowed.
396  ++p;
397  }
398  else
399  break;
400  }
401 
402  if( *p != '!' && ( !bAllow3D || *p != ':' ) )
403  return (!bAllow3D && *p == ':') ? p : start;
404 
405  aTabName += std::u16string_view( start, sal::static_int_cast<sal_Int32>( p - start ) );
406  }
407 
408  rExternTabName = aTabName;
409  return p;
410 }
411 
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 
474  const sal_Unicode* p,
475  const ScDocument& rDoc,
476  OUString& rExternDocName,
477  OUString& rStartTabName,
478  OUString& rEndTabName,
479  ScRefFlags& nFlags,
480  bool bOnlyAcceptSingle,
481  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
482  const OUString* pErrRef )
483 {
484  const sal_Unicode* startTabs, *start = p;
485  ScRefFlags nSaveFlags = nFlags;
486 
487  // Is this an external reference ?
488  rStartTabName.clear();
489  rEndTabName.clear();
490  rExternDocName.clear();
491  const sal_Unicode* pMsoxlQuoteStop = nullptr;
492  if (*p == '[')
493  {
494  ++p;
495  // Only single quotes are correct, and a double single quote escapes a
496  // single quote text inside the quoted text.
497  if (*p == '\'')
498  {
499  p = parseQuotedName(p, rExternDocName);
500  if (*p != ']' || rExternDocName.isEmpty())
501  {
502  rExternDocName.clear();
503  return start;
504  }
505  }
506  else
507  {
508  // non-quoted file name.
509  p = ScGlobal::UnicodeStrChr( start+1, ']' );
510  if( p == nullptr )
511  return start;
512  rExternDocName += std::u16string_view( start+1, sal::static_int_cast<sal_Int32>( p-(start+1) ) );
513  }
514  ++p;
515 
516  const sal_Unicode* pErrRet = start;
517  if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
518  return pErrRet;
519 
520  rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, rDoc.GetDocumentShell());
521  }
522  else if (*p == '\'')
523  {
524  // Sickness in Excel's ODF msoxl namespace:
525  // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or
526  // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
527  // But, 'Sheet1'!B3 would also be a valid!
528  // Excel does not allow [ and ] characters in sheet names though.
529  // But, more sickness comes with MOOXML as there may be
530  // '[1]Sheet 4'!$A$1 where [1] is the external doc's index.
531  p = parseQuotedName(p, rExternDocName);
532  if (*p != '!')
533  {
534  rExternDocName.clear();
535  return start;
536  }
537  if (!rExternDocName.isEmpty())
538  {
539  sal_Int32 nOpen = rExternDocName.indexOf( '[');
540  if (nOpen == -1)
541  rExternDocName.clear();
542  else
543  {
544  sal_Int32 nClose = rExternDocName.indexOf( ']', nOpen+1);
545  if (nClose == -1)
546  rExternDocName.clear();
547  else
548  {
549  rExternDocName = rExternDocName.copy(0, nClose);
550  rExternDocName = rExternDocName.replaceAt( nOpen, 1, u"");
551  pMsoxlQuoteStop = p - 1; // the ' quote char
552  // There may be embedded escaped quotes, just matching the
553  // doc name's length may not work.
554  for (p = start; *p != '['; ++p)
555  ;
556  for ( ; *p != ']'; ++p)
557  ;
558  ++p;
559 
560  // Handle '[1]Sheet 4'!$A$1
561  if (nOpen == 0)
562  {
563  const sal_Unicode* pErrRet = start;
564  if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
565  return pErrRet;
566  }
567  }
568  }
569  }
570  if (rExternDocName.isEmpty())
571  p = start;
572  }
573 
574  startTabs = p;
575  p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop, pErrRef);
576  if( nullptr == p )
577  return start; // invalid tab
578  if (bOnlyAcceptSingle && *p == ':')
579  return nullptr; // 3D
580  const sal_Unicode* startEndTabs = nullptr;
581  if( p != startTabs )
582  {
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  }
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.
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  {
614  // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
615  }
616 
617  if (!rExternDocName.isEmpty())
618  {
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 
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 
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;
766  // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
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 |
813  return nFlags;
814  }
815 
816  nFlags |=
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 |
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 |
884  return nFlags;
885  }
886 
887  nFlags |=
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 
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 
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
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 |=
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 |=
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 |
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 );
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 |
1124  return nFlags;
1125  }
1126 
1127  applyStartToEndFlags(nFlags, nFlags2);
1128  return nFlags;
1129 }
1130 
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;
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;
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  // Assume that common filename extensions are not more than 4 characters.
1238  if (n > 0 && aTab.getLength() - n <= 4)
1239  {
1240  // Extension found. Strip it.
1241  aTab = aTab.copy(0, n);
1242  bExtDoc = true;
1243  }
1244  else
1245  // No extension found. This is probably not an external document.
1246  nBits = ScRefFlags::ZERO;
1247  }
1248  }
1249 
1250  if (pSheetEndPos && (nBits & ScRefFlags::TAB_VALID))
1251  {
1252  *pSheetEndPos = p - pStart;
1253  nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
1254  }
1255  }
1256  else
1257  {
1258  if (bExtDoc && !bExtDocInherited)
1259  return nRes; // After a document a sheet must follow.
1260  nTab = rAddr.Tab();
1261  }
1262  nRes |= nBits;
1263 
1264  q = p;
1265  if (*p)
1266  {
1267  nBits = ScRefFlags::COL_VALID;
1268  if (*p == '$')
1269  {
1270  nBits |= ScRefFlags::COL_ABS;
1271  p++;
1272  }
1273 
1274  if (pErrRef && lcl_isString( p, *pErrRef))
1275  {
1276  // #REF! particle of an invalidated reference.
1277  p += pErrRef->getLength();
1278  nBits &= ~ScRefFlags::COL_VALID;
1279  nCol = -1;
1280  }
1281  else
1282  {
1283  const SCCOL nMaxCol = rDoc.MaxCol();
1284  if (rtl::isAsciiAlpha( *p ))
1285  {
1286  nCol = sal::static_int_cast<SCCOL>( rtl::toAsciiUpperCase( *p++ ) - 'A' );
1287  while (nCol < nMaxCol && rtl::isAsciiAlpha(*p))
1288  nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A' );
1289  }
1290  else
1291  nBits = ScRefFlags::ZERO;
1292 
1293  if (nCol > nMaxCol || (*p && *p != '$' && !rtl::isAsciiDigit( *p ) &&
1294  (!pErrRef || !lcl_isString( p, *pErrRef))))
1295  nBits = ScRefFlags::ZERO;
1296  if( nBits == ScRefFlags::ZERO )
1297  p = q;
1298  }
1299  nRes |= nBits;
1300  }
1301 
1302  q = p;
1303  if (*p)
1304  {
1305  nBits = ScRefFlags::ROW_VALID;
1306  if (*p == '$')
1307  {
1308  nBits |= ScRefFlags::ROW_ABS;
1309  p++;
1310  }
1311 
1312  if (pErrRef && lcl_isString( p, *pErrRef))
1313  {
1314  // #REF! particle of an invalidated reference.
1315  p += pErrRef->getLength();
1316  // Clearing the ROW_VALID bit here is not possible because of the
1317  // check at the end whether only a valid column was detected in
1318  // which case all bits are cleared because it could be any other
1319  // name. Instead, set to an absolute invalid row value. This will
1320  // display a $#REF! instead of #REF! if the error value was
1321  // relative, but live with it.
1322  nBits |= ScRefFlags::ROW_ABS;
1323  nRow = -1;
1324  }
1325  else
1326  {
1327  if( !rtl::isAsciiDigit( *p ) )
1328  {
1329  nBits = ScRefFlags::ZERO;
1330  nRow = SCROW(-1);
1331  }
1332  else
1333  {
1334  tools::Long n = rtl_ustr_toInt32( p, 10 ) - 1;
1335  while (rtl::isAsciiDigit( *p ))
1336  p++;
1337  const SCROW nMaxRow = rDoc.MaxRow();
1338  if( n < 0 || n > nMaxRow )
1339  nBits = ScRefFlags::ZERO;
1340  nRow = static_cast<SCROW>(n);
1341  }
1342  if( nBits == ScRefFlags::ZERO )
1343  p = q;
1344  }
1345  nRes |= nBits;
1346  }
1347 
1348  rAddr.Set( nCol, nRow, nTab );
1349 
1350  if (!*p && bExtDoc)
1351  {
1352  ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
1353 
1354  // Need document name if inherited.
1355  if (bExtDocInherited)
1356  {
1357  // The FileId was created using the original file name, so
1358  // obtain that. Otherwise lcl_ScRange_External_TabSpan() would
1359  // retrieve a FileId for the real name and bail out if that
1360  // differed from pExtInfo->mnFileId, as is the case when
1361  // loading documents that refer external files relative to the
1362  // current own document but were saved from a different path
1363  // than loaded.
1364  const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId, true);
1365  if (pFileName)
1366  aDocName = *pFileName;
1367  else
1368  nRes = ScRefFlags::ZERO;
1369  }
1370  pRefMgr->convertToAbsName(aDocName);
1371 
1372  if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
1373  {
1374  if (!rDoc.GetTable( aTab, nTab ))
1375  nRes = ScRefFlags::ZERO;
1376  else
1377  {
1378  rAddr.SetTab( nTab);
1379  nRes |= ScRefFlags::TAB_VALID;
1380  }
1381  }
1382  else
1383  {
1384  if (!pExtInfo)
1385  nRes = ScRefFlags::ZERO;
1386  else
1387  {
1388  if (!pExtInfo->mbExternal)
1389  {
1390  sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
1391 
1392  pExtInfo->mbExternal = true;
1393  pExtInfo->maTabName = aTab;
1394  pExtInfo->mnFileId = nFileId;
1395 
1396  if (pRefMgr->getSingleRefToken(nFileId, aTab,
1397  ScAddress(nCol, nRow, 0), nullptr,
1398  &nTab))
1399  {
1400  rAddr.SetTab( nTab);
1401  nRes |= ScRefFlags::TAB_VALID;
1402  }
1403  else
1404  nRes = ScRefFlags::ZERO;
1405  }
1406  else
1407  {
1408  // This is a call for the second part of the reference,
1409  // we must have the range to adapt tab span.
1410  if (!pRange)
1411  nRes = ScRefFlags::ZERO;
1412  else
1413  {
1414  ScRefFlags nFlags = nRes | ScRefFlags::TAB2_VALID;
1415  if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
1416  pExtInfo, aDocName,
1417  pExtInfo->maTabName, aTab, rDoc))
1418  nRes &= ~ScRefFlags::TAB_VALID;
1419  else
1420  {
1421  if (nFlags & ScRefFlags::TAB2_VALID)
1422  {
1423  rAddr.SetTab( pRange->aEnd.Tab());
1424  nRes |= ScRefFlags::TAB_VALID;
1425  }
1426  else
1427  nRes &= ~ScRefFlags::TAB_VALID;
1428  }
1429  }
1430  }
1431  }
1432  }
1433  }
1434  else if (bExtDoc && pExtInfo && !bExtDocInherited && !pExtInfo->mbExternal && pSheetEndPos)
1435  {
1436  // Pass partial info up to caller, there may be an external name
1437  // following, and if after *pSheetEndPos it's external sheet-local.
1438  // For global names aTab is empty and *pSheetEndPos==0.
1439  pExtInfo->mbExternal = true;
1440  pExtInfo->maTabName = aTab;
1441  pExtInfo->mnFileId = rDoc.GetExternalRefManager()->getExternalFileId(aDocName);
1442  }
1443 
1444  rRawRes |= nRes;
1445 
1446  if ( !(nRes & ScRefFlags::ROW_VALID) && (nRes & ScRefFlags::COL_VALID)
1447  && !( (nRes & ScRefFlags::TAB_3D) && (nRes & ScRefFlags::TAB_VALID)) )
1448  { // no Row, no Tab, but Col => DM (...), B (...) et al
1449  nRes = ScRefFlags::ZERO;
1450  }
1451  if( !*p )
1452  {
1453  ScRefFlags nMask = nRes & ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID );
1454  if( nMask == ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ) )
1455  nRes |= ScRefFlags::VALID;
1456  }
1457  else
1458  nRes = rRawRes = nBailOutFlags;
1459  return nRes;
1460 }
1461 
1462 static ScRefFlags lcl_ScAddress_Parse ( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
1463  const ScAddress::Details& rDetails,
1464  ScAddress::ExternalInfo* pExtInfo,
1465  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1466  sal_Int32* pSheetEndPos,
1467  const OUString* pErrRef )
1468 {
1469  if( !*p )
1470  return ScRefFlags::ZERO;
1471 
1472  switch (rDetails.eConv)
1473  {
1476  {
1477  ScRange rRange = rAddr;
1479  rRange, p, rDoc, true, pExtInfo,
1480  (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
1481  pSheetEndPos, pErrRef);
1482  rAddr = rRange.aStart;
1483  return nFlags;
1484  }
1486  {
1487  ScRange rRange = rAddr;
1488  ScRefFlags nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, rDoc, rDetails, true, pExtInfo, pSheetEndPos);
1489  rAddr = rRange.aStart;
1490  return nFlags;
1491  }
1492  default :
1494  {
1495  ScRefFlags nRawRes = ScRefFlags::ZERO;
1496  return lcl_ScAddress_Parse_OOo( p, rDoc, rAddr, nRawRes, pExtInfo, nullptr, pSheetEndPos, pErrRef);
1497  }
1498  }
1499 }
1500 
1501 bool ConvertSingleRef( const ScDocument& rDoc, const OUString& rRefString,
1502  SCTAB nDefTab, ScRefAddress& rRefAddress,
1503  const ScAddress::Details& rDetails,
1504  ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1505 {
1506  bool bRet = false;
1507  if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1508  {
1509  ScAddress aAddr( 0, 0, nDefTab );
1510  ScRefFlags nRes = aAddr.Parse( rRefString, rDoc, rDetails, pExtInfo);
1511  if ( nRes & ScRefFlags::VALID )
1512  {
1513  rRefAddress.Set( aAddr,
1514  ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
1515  ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
1516  ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
1517  bRet = true;
1518  }
1519  }
1520  return bRet;
1521 }
1522 
1523 bool ConvertDoubleRef( const ScDocument& rDoc, const OUString& rRefString, SCTAB nDefTab,
1524  ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1525  const ScAddress::Details& rDetails,
1526  ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1527 {
1528  bool bRet = false;
1529  if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1530  {
1531  ScRange aRange( ScAddress( 0, 0, nDefTab));
1532  ScRefFlags nRes = aRange.Parse( rRefString, rDoc, rDetails, pExtInfo);
1533  if ( nRes & ScRefFlags::VALID )
1534  {
1535  rStartRefAddress.Set( aRange.aStart,
1536  ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
1537  ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
1538  ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
1539  rEndRefAddress.Set( aRange.aEnd,
1540  ((nRes & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO),
1541  ((nRes & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO),
1542  ((nRes & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO));
1543  bRet = true;
1544  }
1545  }
1546  return bRet;
1547 }
1548 
1549 ScRefFlags ScAddress::Parse( const OUString& r, const ScDocument& rDoc,
1550  const Details& rDetails,
1551  ExternalInfo* pExtInfo,
1552  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1553  sal_Int32* pSheetEndPos,
1554  const OUString* pErrRef )
1555 {
1556  return lcl_ScAddress_Parse( r.getStr(), rDoc, *this, rDetails, pExtInfo, pExternalLinks, pSheetEndPos, pErrRef);
1557 }
1558 
1559 ScRange ScRange::Intersection( const ScRange& rOther ) const
1560 {
1561  SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col());
1562  SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col());
1563  SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row());
1564  SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row());
1565  SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab());
1566  SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab());
1567 
1568  if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2)
1570 
1571  return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1572 }
1573 
1575 {
1576  SCCOL nTempCol;
1577  if ( aEnd.Col() < (nTempCol = aStart.Col()) )
1578  {
1579  aStart.SetCol(aEnd.Col());
1580  aEnd.SetCol(nTempCol);
1581  }
1582  SCROW nTempRow;
1583  if ( aEnd.Row() < (nTempRow = aStart.Row()) )
1584  {
1585  aStart.SetRow(aEnd.Row());
1586  aEnd.SetRow(nTempRow);
1587  }
1588  SCTAB nTempTab;
1589  if ( aEnd.Tab() < (nTempTab = aStart.Tab()) )
1590  {
1591  aStart.SetTab(aEnd.Tab());
1592  aEnd.SetTab(nTempTab);
1593  }
1594 }
1595 
1596 void ScRange::ExtendTo( const ScRange& rRange )
1597 {
1598  OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1599  if( IsValid() )
1600  {
1601  aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) );
1602  aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) );
1603  aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1604  aEnd.SetCol( std::max( aEnd.Col(), rRange.aEnd.Col() ) );
1605  aEnd.SetRow( std::max( aEnd.Row(), rRange.aEnd.Row() ) );
1606  aEnd.SetTab( std::max( aEnd.Tab(), rRange.aEnd.Tab() ) );
1607  }
1608  else
1609  *this = rRange;
1610 }
1611 
1613  const OUString& r,
1614  const ScDocument& rDoc,
1615  ScAddress::ExternalInfo* pExtInfo,
1616  const OUString* pErrRef )
1617 {
1618  ScRefFlags nRes1 = ScRefFlags::ZERO, nRes2 = ScRefFlags::ZERO;
1619  sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':');
1620  if (nPos != -1)
1621  {
1622  OUStringBuffer aTmp(r);
1623  aTmp[nPos] = 0;
1624  const sal_Unicode* p = aTmp.getStr();
1625  ScRefFlags nRawRes1 = ScRefFlags::ZERO;
1626  nRes1 = lcl_ScAddress_Parse_OOo( p, rDoc, rRange.aStart, nRawRes1, pExtInfo, nullptr, nullptr, pErrRef);
1627  if ((nRes1 != ScRefFlags::ZERO) ||
1628  ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1629  (nRawRes1 & ScRefFlags::TAB_VALID)))
1630  {
1631  rRange.aEnd = rRange.aStart; // sheet must be initialized identical to first sheet
1632  ScRefFlags nRawRes2 = ScRefFlags::ZERO;
1633  nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, rDoc, rRange.aEnd, nRawRes2,
1634  pExtInfo, &rRange, nullptr, pErrRef);
1635  if (!((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) &&
1636  // If not fully valid addresses, check if both have a valid
1637  // column or row, and both have valid (or omitted) sheet references.
1638  (nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1639  (nRawRes1 & ScRefFlags::TAB_VALID) &&
1640  (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1641  (nRawRes2 & ScRefFlags::TAB_VALID) &&
1642  // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A
1643  ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) ==
1644  (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID))))
1645  {
1646  nRes1 = nRawRes1 | ScRefFlags::VALID;
1647  nRes2 = nRawRes2 | ScRefFlags::VALID;
1648  if (nRawRes1 & ScRefFlags::COL_VALID)
1649  {
1650  rRange.aStart.SetRow(0);
1651  rRange.aEnd.SetRow(rDoc.MaxRow());
1654  }
1655  else
1656  {
1657  rRange.aStart.SetCol(0);
1658  rRange.aEnd.SetCol( rDoc.MaxCol() );
1659  nRes1 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
1660  nRes2 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
1661  }
1662  }
1663  else if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
1664  {
1665  // Flag entire column/row references so they can be displayed
1666  // as such. If the sticky reference parts are not both
1667  // absolute or relative, assume that the user thought about
1668  // something we should not touch.
1669  if (rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() &&
1670  ((nRes1 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO) &&
1671  ((nRes2 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO))
1672  {
1673  nRes1 |= ScRefFlags::ROW_ABS;
1674  nRes2 |= ScRefFlags::ROW_ABS;
1675  }
1676  else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
1678  {
1679  nRes1 |= ScRefFlags::COL_ABS;
1680  nRes2 |= ScRefFlags::COL_ABS;
1681  }
1682  }
1683  if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
1684  {
1685  // PutInOrder / Justify
1686  ScRefFlags nMask, nBits1, nBits2;
1687  SCCOL nTempCol;
1688  if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) )
1689  {
1690  rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol);
1692  nBits1 = nRes1 & nMask;
1693  nBits2 = nRes2 & nMask;
1694  nRes1 = (nRes1 & ~nMask) | nBits2;
1695  nRes2 = (nRes2 & ~nMask) | nBits1;
1696  }
1697  SCROW nTempRow;
1698  if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) )
1699  {
1700  rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow);
1702  nBits1 = nRes1 & nMask;
1703  nBits2 = nRes2 & nMask;
1704  nRes1 = (nRes1 & ~nMask) | nBits2;
1705  nRes2 = (nRes2 & ~nMask) | nBits1;
1706  }
1707  SCTAB nTempTab;
1708  if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) )
1709  {
1710  rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab);
1711  nMask = (ScRefFlags::TAB_VALID | ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D);
1712  nBits1 = nRes1 & nMask;
1713  nBits2 = nRes2 & nMask;
1714  nRes1 = (nRes1 & ~nMask) | nBits2;
1715  nRes2 = (nRes2 & ~nMask) | nBits1;
1716  }
1717  if ( ((nRes1 & ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
1719  && !(nRes2 & ScRefFlags::TAB_3D) )
1720  nRes2 |= ScRefFlags::TAB_ABS;
1721  }
1722  else
1723  {
1724  // Don't leave around valid half references.
1725  nRes1 = nRes2 = ScRefFlags::ZERO;
1726  }
1727  }
1728  }
1729  applyStartToEndFlags(nRes1, nRes2 & ScRefFlags::BITS);
1730  nRes1 |= nRes2 & ScRefFlags::VALID;
1731  return nRes1;
1732 }
1733 
1734 ScRefFlags ScRange::Parse( const OUString& rString, const ScDocument& rDoc,
1735  const ScAddress::Details& rDetails,
1736  ScAddress::ExternalInfo* pExtInfo,
1737  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1738  const OUString* pErrRef )
1739 {
1740  if (rString.isEmpty())
1741  return ScRefFlags::ZERO;
1742 
1743  switch (rDetails.eConv)
1744  {
1747  {
1748  return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), rDoc, false, pExtInfo,
1749  (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
1750  nullptr, pErrRef );
1751  }
1752 
1754  {
1755  return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr );
1756  }
1757 
1758  default:
1760  {
1761  return lcl_ScRange_Parse_OOo( *this, rString, rDoc, pExtInfo, pErrRef );
1762  }
1763  }
1764 }
1765 
1766 // Accept a full range, or an address
1767 ScRefFlags ScRange::ParseAny( const OUString& rString, const ScDocument& rDoc,
1768  const ScAddress::Details& rDetails )
1769 {
1770  ScRefFlags nRet = Parse( rString, rDoc, rDetails );
1772 
1773  if ( (nRet & nValid) != nValid )
1774  {
1775  ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number
1776  nRet = aAdr.Parse( rString, rDoc, rDetails );
1777  if ( nRet & ScRefFlags::VALID )
1778  aStart = aEnd = aAdr;
1779  }
1780  return nRet;
1781 }
1782 
1783 // Parse only full row references
1785  const OUString& rStr,
1786  const ScAddress::Details& rDetails )
1787 {
1788  if (rStr.isEmpty())
1789  return ScRefFlags::ZERO;
1790 
1791  const sal_Unicode* p = rStr.getStr();
1793  ScRefFlags ignored = ScRefFlags::ZERO;
1794 
1795  switch (rDetails.eConv)
1796  {
1797  default :
1798  case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1801  if (nullptr != (p = lcl_a1_get_col( rDoc, p, &aStart, &ignored, nullptr) ) )
1802  {
1803  if( p[0] == ':')
1804  {
1805  if( nullptr != (p = lcl_a1_get_col( rDoc, p+1, &aEnd, &ignored, nullptr)))
1806  {
1807  nRes = ScRefFlags::COL_VALID;
1808  }
1809  }
1810  else
1811  {
1812  aEnd = aStart;
1813  nRes = ScRefFlags::COL_VALID;
1814  }
1815  }
1816  break;
1817 
1819  if ((p[0] == 'C' || p[0] == 'c') &&
1820  nullptr != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored )))
1821  {
1822  if( p[0] == ':')
1823  {
1824  if( (p[1] == 'C' || p[1] == 'c') &&
1825  nullptr != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored )))
1826  {
1827  nRes = ScRefFlags::COL_VALID;
1828  }
1829  }
1830  else
1831  {
1832  aEnd = aStart;
1833  nRes = ScRefFlags::COL_VALID;
1834  }
1835  }
1836  break;
1837  }
1838 
1839  return (p != nullptr && *p == '\0') ? nRes : ScRefFlags::ZERO;
1840 }
1841 
1842 // Parse only full row references
1843 void ScRange::ParseRows( const ScDocument& rDoc,
1844  const OUString& rStr,
1845  const ScAddress::Details& rDetails )
1846 {
1847  if (rStr.isEmpty())
1848  return;
1849 
1850  const sal_Unicode* p = rStr.getStr();
1851  ScRefFlags ignored = ScRefFlags::ZERO;
1852 
1853  switch (rDetails.eConv)
1854  {
1855  default :
1856  case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1859  if (nullptr != (p = lcl_a1_get_row( rDoc, p, &aStart, &ignored, nullptr) ) )
1860  {
1861  if( p[0] == ':')
1862  {
1863  lcl_a1_get_row( rDoc, p+1, &aEnd, &ignored, nullptr);
1864  }
1865  else
1866  {
1867  aEnd = aStart;
1868  }
1869  }
1870  break;
1871 
1873  if ((p[0] == 'R' || p[0] == 'r') &&
1874  nullptr != (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored )))
1875  {
1876  if( p[0] == ':')
1877  {
1878  if( p[1] == 'R' || p[1] == 'r' )
1879  {
1880  lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored );
1881  }
1882  }
1883  else
1884  {
1885  aEnd = aStart;
1886  }
1887  }
1888  break;
1889  }
1890 }
1891 
1892 template<typename T > static void lcl_ScColToAlpha( T& rBuf, SCCOL nCol )
1893 {
1894  if (nCol < 26*26)
1895  {
1896  if (nCol < 26)
1897  rBuf.append( static_cast<char>( 'A' + nCol ));
1898  else
1899  {
1900  rBuf.append( static_cast<char>( 'A' + nCol / 26 - 1 ));
1901  rBuf.append( static_cast<char>( 'A' + nCol % 26 ));
1902  }
1903  }
1904  else
1905  {
1906  sal_Int32 nInsert = rBuf.getLength();
1907  while (nCol >= 26)
1908  {
1909  SCCOL nC = nCol % 26;
1910  rBuf.insert(nInsert, static_cast<char> ( 'A' + nC ));
1911  nCol = sal::static_int_cast<SCCOL>( nCol - nC );
1912  nCol = nCol / 26 - 1;
1913  }
1914  rBuf.insert(nInsert, static_cast<char> ( 'A' + nCol ));
1915  }
1916 }
1917 
1918 void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol)
1919 {
1920  lcl_ScColToAlpha(rBuf, nCol);
1921 }
1922 
1923 template <typename T > static void lcl_a1_append_c ( T &rString, int nCol, bool bIsAbs )
1924 {
1925  if( bIsAbs )
1926  rString.append("$");
1927  lcl_ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) );
1928 }
1929 
1930 template <typename T > static void lcl_a1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs )
1931 {
1932  if ( bIsAbs )
1933  rString.append("$");
1934  rString.append( nRow + 1 );
1935 }
1936 
1937 template <typename T > static void lcl_r1c1_append_c ( T &rString, sal_Int32 nCol, bool bIsAbs,
1938  const ScAddress::Details& rDetails )
1939 {
1940  rString.append("C");
1941  if (bIsAbs)
1942  {
1943  rString.append( nCol + 1 );
1944  }
1945  else
1946  {
1947  nCol -= rDetails.nCol;
1948  if (nCol != 0) {
1949  rString.append("[").append(nCol).append("]");
1950  }
1951  }
1952 }
1953 
1954 template <typename T > static void lcl_r1c1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs,
1955  const ScAddress::Details& rDetails )
1956 {
1957  rString.append("R");
1958  if (bIsAbs)
1959  {
1960  rString.append( nRow + 1 );
1961  }
1962  else
1963  {
1964  nRow -= rDetails.nRow;
1965  if (nRow != 0) {
1966  rString.append("[").append(nRow).append("]");
1967  }
1968  }
1969 }
1970 
1971 static OUString getFileNameFromDoc( const ScDocument* pDoc )
1972 {
1973  // TODO : er points at ScGlobal::GetAbsDocName()
1974  // as a better template. Look into it
1975  OUString sFileName;
1976  SfxObjectShell* pShell;
1977 
1978  if( nullptr != pDoc &&
1979  nullptr != (pShell = pDoc->GetDocumentShell() ) )
1980  {
1981  uno::Reference< frame::XModel > xModel = pShell->GetModel();
1982  if( xModel.is() )
1983  {
1984  if( !xModel->getURL().isEmpty() )
1985  {
1986  INetURLObject aURL( xModel->getURL() );
1987  sFileName = aURL.GetLastName();
1988  }
1989  else
1990  sFileName = pShell->GetTitle();
1991  }
1992  }
1993  return sFileName;
1994 }
1995 
1996 
1997 static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString)
1998 {
1999  rString.append(sString);
2000 }
2001 
2002 static void lcl_string_append(OStringBuffer &rString, std::u16string_view sString)
2003 {
2004  rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8 ));
2005 }
2006 
2007 template<typename T > static void lcl_Format( T& r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags,
2008  const ScDocument* pDoc,
2009  const ScAddress::Details& rDetails)
2010 {
2011  if( nFlags & ScRefFlags::VALID )
2013  if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) )
2014  {
2015  if ( nTab < 0 || nTab >= pDoc->GetTableCount() )
2016  {
2018  return;
2019  }
2020  if( nFlags & ScRefFlags::TAB_3D )
2021  {
2022  OUString aTabName, aDocName;
2023  pDoc->GetName(nTab, aTabName);
2024  assert( !aTabName.isEmpty() && "empty sheet name");
2025  // External Reference, same as in ScCompiler::MakeTabStr()
2026  if( aTabName[0] == '\'' )
2027  { // "'Doc'#Tab"
2028  sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName);
2029  if (nPos != -1)
2030  {
2031  aDocName = aTabName.copy( 0, nPos + 1 );
2032  aTabName = aTabName.copy( nPos + 1 );
2033  }
2034  }
2035  else if( nFlags & ScRefFlags::FORCE_DOC )
2036  {
2037  // VBA has an 'external' flag that forces the addition of the
2038  // tab name _and_ the doc name. The VBA code would be
2039  // needlessly complicated if it constructed an actual external
2040  // reference so we add this somewhat cheesy kludge to force the
2041  // addition of the document name even for non-external references
2042  aDocName = getFileNameFromDoc( pDoc );
2043  }
2044  ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
2045 
2046  switch( rDetails.eConv )
2047  {
2048  default :
2050  lcl_string_append(r, aDocName);
2051  if( nFlags & ScRefFlags::TAB_ABS )
2052  r.append("$");
2053  lcl_string_append(r, aTabName);
2054  r.append(".");
2055  break;
2056 
2058  if (!aTabName.isEmpty() && aTabName[0] == '\'')
2059  {
2060  if (!aDocName.isEmpty())
2061  {
2062  lcl_string_append(r.append("'["), aDocName);
2063  r.append("]");
2064  lcl_string_append(r, aTabName.copy(1));
2065  }
2066  else
2067  {
2068  lcl_string_append(r, aTabName);
2069  }
2070  r.append("!");
2071  break;
2072  }
2073  [[fallthrough]];
2076  if (!aDocName.isEmpty())
2077  {
2078  lcl_string_append(r.append("["), aDocName);
2079  r.append("]");
2080  }
2081  lcl_string_append(r, aTabName);
2082  r.append("!");
2083  break;
2084  }
2085  }
2086  }
2087  switch( rDetails.eConv )
2088  {
2089  default :
2093  if( nFlags & ScRefFlags::COL_VALID )
2094  lcl_a1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2095  if( nFlags & ScRefFlags::ROW_VALID )
2096  lcl_a1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2097  break;
2098 
2100  if( nFlags & ScRefFlags::ROW_VALID )
2101  lcl_r1c1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2102  if( nFlags & ScRefFlags::COL_VALID )
2103  lcl_r1c1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2104  break;
2105  }
2106 }
2107 
2108 void ScAddress::Format( OStringBuffer& r, ScRefFlags nFlags,
2109  const ScDocument* pDoc,
2110  const Details& rDetails) const
2111 {
2112  lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
2113 }
2114 
2115 OUString ScAddress::Format(ScRefFlags nFlags, const ScDocument* pDoc,
2116  const Details& rDetails) const
2117 {
2118  OUStringBuffer r;
2119  lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
2120  return r.makeStringAndClear();
2121 }
2122 
2123 static void lcl_Split_DocTab( const ScDocument& rDoc, SCTAB nTab,
2124  const ScAddress::Details& rDetails,
2125  ScRefFlags nFlags,
2126  OUString& rTabName, OUString& rDocName )
2127 {
2128  rDoc.GetName(nTab, rTabName);
2129  rDocName.clear();
2130  // External reference, same as in ScCompiler::MakeTabStr()
2131  if ( rTabName[0] == '\'' )
2132  { // "'Doc'#Tab"
2133  sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName);
2134  if (nPos != -1)
2135  {
2136  rDocName = rTabName.copy( 0, nPos + 1 );
2137  rTabName = rTabName.copy( nPos + 1 );
2138  }
2139  }
2140  else if( nFlags & ScRefFlags::FORCE_DOC )
2141  {
2142  // VBA has an 'external' flag that forces the addition of the
2143  // tab name _and_ the doc name. The VBA code would be
2144  // needlessly complicated if it constructed an actual external
2145  // reference so we add this somewhat cheesy kludge to force the
2146  // addition of the document name even for non-external references
2147  rDocName = getFileNameFromDoc(&rDoc);
2148  }
2149  ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
2150 }
2151 
2152 static void lcl_ScRange_Format_XL_Header( OUStringBuffer& rString, const ScRange& rRange,
2153  ScRefFlags nFlags, const ScDocument& rDoc,
2154  const ScAddress::Details& rDetails )
2155 {
2156  if( !(nFlags & ScRefFlags::TAB_3D) )
2157  return;
2158 
2159  OUString aTabName, aDocName;
2160  lcl_Split_DocTab( rDoc, rRange.aStart.Tab(), rDetails, nFlags, aTabName, aDocName );
2161  switch (rDetails.eConv)
2162  {
2164  if (!aTabName.isEmpty() && aTabName[0] == '\'')
2165  {
2166  if (!aDocName.isEmpty())
2167  {
2168  rString.append("'[" + aDocName + "]" + aTabName.subView(1));
2169  }
2170  else
2171  {
2172  rString.append(aTabName);
2173  }
2174  break;
2175  }
2176  [[fallthrough]];
2177  default:
2178  if (!aDocName.isEmpty())
2179  {
2180  rString.append("[" + aDocName + "]");
2181  }
2182  rString.append(aTabName);
2183  break;
2184  }
2185  if( nFlags & ScRefFlags::TAB2_3D )
2186  {
2187  lcl_Split_DocTab( rDoc, rRange.aEnd.Tab(), rDetails, nFlags, aTabName, aDocName );
2188  rString.append(":");
2189  rString.append(aTabName);
2190  }
2191  rString.append("!");
2192 }
2193 
2194 // helpers used in ScRange::Format
2195 static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags)
2196 {
2197  return static_cast<bool>(nFlags & ScRefFlags::COL_ABS) != static_cast<bool>(nFlags & ScRefFlags::COL2_ABS);
2198 }
2199 static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags)
2200 {
2201  return static_cast<bool>(nFlags & ScRefFlags::ROW_ABS) != static_cast<bool>(nFlags & ScRefFlags::ROW2_ABS);
2202 }
2203 
2204 OUString ScRange::Format( const ScDocument& rDoc, ScRefFlags nFlags,
2205  const ScAddress::Details& rDetails, bool bFullAddressNotation ) const
2206 {
2207  if( !( nFlags & ScRefFlags::VALID ) )
2208  {
2210  }
2211 
2212  OUStringBuffer r;
2213  switch( rDetails.eConv ) {
2214  default :
2216  bool bOneTab = (aStart.Tab() == aEnd.Tab());
2217  if ( !bOneTab )
2218  nFlags |= ScRefFlags::TAB_3D;
2219  r = aStart.Format(nFlags, &rDoc, rDetails);
2220  if( aStart != aEnd ||
2221  lcl_ColAbsFlagDiffer( nFlags ) ||
2222  lcl_RowAbsFlagDiffer( nFlags ))
2223  {
2224  const ScDocument* pDoc = &rDoc;
2225  // move flags of end reference to start reference, mask with BITS to exclude FORCE_DOC flag
2226  nFlags = ScRefFlags::VALID | (ScRefFlags(o3tl::to_underlying(nFlags) >> 4) & ScRefFlags::BITS);
2227  if ( bOneTab )
2228  pDoc = nullptr;
2229  else
2230  nFlags |= ScRefFlags::TAB_3D;
2231  OUString aName(aEnd.Format(nFlags, pDoc, rDetails));
2232  r.append(":");
2233  r.append(aName);
2234  }
2235  break;
2236  }
2237 
2240  SCCOL nMaxCol = rDoc.MaxCol();
2241  SCROW nMaxRow = rDoc.MaxRow();
2242 
2243  lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
2244  if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
2245  {
2246  // Full col refs always require 2 rows (2:2)
2247  lcl_a1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2248  r.append(":");
2249  lcl_a1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
2250  }
2251  else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
2252  {
2253  // Full row refs always require 2 cols (A:A)
2254  lcl_a1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2255  r.append(":");
2256  lcl_a1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
2257  }
2258  else
2259  {
2260  lcl_a1_append_c ( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2261  lcl_a1_append_r ( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2262  if( aStart.Col() != aEnd.Col() ||
2263  lcl_ColAbsFlagDiffer( nFlags ) ||
2264  aStart.Row() != aEnd.Row() ||
2265  lcl_RowAbsFlagDiffer( nFlags ) ) {
2266  r.append(":");
2267  lcl_a1_append_c ( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
2268  lcl_a1_append_r ( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
2269  }
2270  }
2271  break;
2272  }
2273 
2275  SCCOL nMaxCol = rDoc.MaxCol();
2276  SCROW nMaxRow = rDoc.MaxRow();
2277 
2278  lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
2279  if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
2280  {
2281  lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2282  if( aStart.Row() != aEnd.Row() ||
2283  lcl_RowAbsFlagDiffer( nFlags ) ) {
2284  r.append(":");
2285  lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
2286  }
2287  }
2288  else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
2289  {
2290  lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2291  if( aStart.Col() != aEnd.Col() ||
2292  lcl_ColAbsFlagDiffer( nFlags )) {
2293  r.append(":");
2294  lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
2295  }
2296  }
2297  else
2298  {
2299  lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2300  lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2301  if( aStart.Col() != aEnd.Col() ||
2302  lcl_ColAbsFlagDiffer( nFlags ) ||
2303  aStart.Row() != aEnd.Row() ||
2304  lcl_RowAbsFlagDiffer( nFlags ) ) {
2305  r.append(":");
2306  lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
2307  lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
2308  }
2309  }
2310  break;
2311  }
2312  }
2313  return r.makeStringAndClear();
2314 }
2315 
2316 bool ScAddress::Move( SCCOL dx, SCROW dy, SCTAB dz, ScAddress& rErrorPos, const ScDocument* pDoc )
2317 {
2318  SCTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB;
2319  SCCOL nMaxCol = pDoc ? pDoc->MaxCol() : MAXCOL;
2320  SCROW nMaxRow = pDoc ? pDoc->MaxRow() : MAXROW;
2321  dx = Col() + dx;
2322  dy = Row() + dy;
2323  dz = Tab() + dz;
2324  bool bValid = true;
2325  rErrorPos.SetCol(dx);
2326  if( dx < 0 )
2327  {
2328  dx = 0;
2329  bValid = false;
2330  }
2331  else if( dx > nMaxCol )
2332  {
2333  dx = nMaxCol;
2334  bValid =false;
2335  }
2336  rErrorPos.SetRow(dy);
2337  if( dy < 0 )
2338  {
2339  dy = 0;
2340  bValid = false;
2341  }
2342  else if( dy > nMaxRow )
2343  {
2344  dy = nMaxRow;
2345  bValid =false;
2346  }
2347  rErrorPos.SetTab(dz);
2348  if( dz < 0 )
2349  {
2350  dz = 0;
2351  bValid = false;
2352  }
2353  else if( dz > nMaxTab )
2354  {
2355  // Always set MAXTAB+1 so further checks without ScDocument detect invalid.
2356  rErrorPos.SetTab(MAXTAB+1);
2357  dz = nMaxTab;
2358  bValid =false;
2359  }
2360  Set( dx, dy, dz );
2361  return bValid;
2362 }
2363 
2364 bool ScRange::Move( SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange, const ScDocument* pDoc )
2365 {
2366  SCCOL nMaxCol = pDoc ? pDoc->MaxCol() : MAXCOL;
2367  SCROW nMaxRow = pDoc ? pDoc->MaxRow() : MAXROW;
2368  if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
2369  dy = 0; // Entire column not to be moved.
2370  if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
2371  dx = 0; // Entire row not to be moved.
2372  bool b = aStart.Move( dx, dy, dz, rErrorRange.aStart, pDoc );
2373  b &= aEnd.Move( dx, dy, dz, rErrorRange.aEnd, pDoc );
2374  return b;
2375 }
2376 
2377 bool ScRange::MoveSticky( const ScDocument& rDoc, SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange )
2378 {
2379  const SCCOL nMaxCol = rDoc.MaxCol();
2380  const SCROW nMaxRow = rDoc.MaxRow();
2381  bool bColRange = (aStart.Col() < aEnd.Col());
2382  bool bRowRange = (aStart.Row() < aEnd.Row());
2383  if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
2384  dy = 0; // Entire column not to be moved.
2385  if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
2386  dx = 0; // Entire row not to be moved.
2387  bool b1 = aStart.Move( dx, dy, dz, rErrorRange.aStart );
2388  if (dx && bColRange && aEnd.Col() == nMaxCol)
2389  dx = 0; // End column sticky.
2390  if (dy && bRowRange && aEnd.Row() == nMaxRow)
2391  dy = 0; // End row sticky.
2392  SCTAB nOldTab = aEnd.Tab();
2393  bool b2 = aEnd.Move( dx, dy, dz, rErrorRange.aEnd );
2394  if (!b2)
2395  {
2396  // End column or row of a range may have become sticky.
2397  bColRange = (!dx || (bColRange && aEnd.Col() == nMaxCol));
2398  if (dx && bColRange)
2399  rErrorRange.aEnd.SetCol(nMaxCol);
2400  bRowRange = (!dy || (bRowRange && aEnd.Row() == nMaxRow));
2401  if (dy && bRowRange)
2402  rErrorRange.aEnd.SetRow(nMaxRow);
2403  b2 = bColRange && bRowRange && (aEnd.Tab() - nOldTab == dz);
2404  }
2405  return b1 && b2;
2406 }
2407 
2408 void ScRange::IncColIfNotLessThan(const ScDocument& rDoc, SCCOL nStartCol, SCCOL nOffset)
2409 {
2410  if (aStart.Col() >= nStartCol)
2411  {
2412  aStart.IncCol(nOffset);
2413  if (aStart.Col() < 0)
2414  aStart.SetCol(0);
2415  else if(aStart.Col() > rDoc.MaxCol())
2416  aStart.SetCol(rDoc.MaxCol());
2417  }
2418  if (aEnd.Col() >= nStartCol)
2419  {
2420  aEnd.IncCol(nOffset);
2421  if (aEnd.Col() < 0)
2422  aEnd.SetCol(0);
2423  else if(aEnd.Col() > rDoc.MaxCol())
2424  aEnd.SetCol(rDoc.MaxCol());
2425  }
2426 }
2427 
2428 void ScRange::IncRowIfNotLessThan(const ScDocument& rDoc, SCROW nStartRow, SCROW nOffset)
2429 {
2430  if (aStart.Row() >= nStartRow)
2431  {
2432  aStart.IncRow(nOffset);
2433  if (aStart.Row() < 0)
2434  aStart.SetRow(0);
2435  else if(aStart.Row() > rDoc.MaxRow())
2436  aStart.SetRow(rDoc.MaxRow());
2437  }
2438  if (aEnd.Row() >= nStartRow)
2439  {
2440  aEnd.IncRow(nOffset);
2441  if (aEnd.Row() < 0)
2442  aEnd.SetRow(0);
2443  else if(aEnd.Row() > rDoc.MaxRow())
2444  aEnd.SetRow(rDoc.MaxRow());
2445  }
2446 }
2447 
2448 void ScRange::IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta )
2449 {
2450  SCCOL nCol = aEnd.Col();
2451  if (aStart.Col() >= nCol)
2452  {
2453  // Less than two columns => not sticky.
2454  aEnd.IncCol( nDelta);
2455  return;
2456  }
2457 
2458  const SCCOL nMaxCol = rDoc.MaxCol();
2459  if (nCol == nMaxCol)
2460  // already sticky
2461  return;
2462 
2463  if (nCol < nMaxCol)
2464  aEnd.SetCol( ::std::min( static_cast<SCCOL>(nCol + nDelta), nMaxCol));
2465  else
2466  aEnd.IncCol( nDelta); // was greater than nMaxCol, caller should know...
2467 }
2468 
2469 void ScRange::IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta )
2470 {
2471  SCROW nRow = aEnd.Row();
2472  if (aStart.Row() >= nRow)
2473  {
2474  // Less than two rows => not sticky.
2475  aEnd.IncRow( nDelta);
2476  return;
2477  }
2478 
2479  if (nRow == rDoc.MaxRow())
2480  // already sticky
2481  return;
2482 
2483  if (nRow < rDoc.MaxRow())
2484  aEnd.SetRow( ::std::min( static_cast<SCROW>(nRow + nDelta), rDoc.MaxRow()));
2485  else
2486  aEnd.IncRow( nDelta); // was greater than rDoc.MaxRow(), caller should know...
2487 }
2488 
2490 {
2491  OUStringBuffer aString;
2492 
2493  switch( detailsOOOa1.eConv )
2494  {
2495  default :
2499  lcl_ScColToAlpha( aString, nCol);
2500  aString.append(nRow+1);
2501  break;
2502 
2504  lcl_r1c1_append_r ( aString, nRow, false/*bAbsolute*/, detailsOOOa1 );
2505  lcl_r1c1_append_c ( aString, nCol, false/*bAbsolute*/, detailsOOOa1 );
2506  break;
2507  }
2508 
2509  return aString.makeStringAndClear();
2510 }
2511 
2512 OUString ScRefAddress::GetRefString( const ScDocument& rDoc, SCTAB nActTab,
2513  const ScAddress::Details& rDetails ) const
2514 {
2515  if ( Tab()+1 > rDoc.GetTableCount() )
2517 
2518  ScRefFlags nFlags = ScRefFlags::VALID;
2519  if ( nActTab != Tab() )
2520  {
2521  nFlags |= ScRefFlags::TAB_3D;
2522  if ( !bRelTab )
2523  nFlags |= ScRefFlags::TAB_ABS;
2524  }
2525  if ( !bRelCol )
2526  nFlags |= ScRefFlags::COL_ABS;
2527  if ( !bRelRow )
2528  nFlags |= ScRefFlags::ROW_ABS;
2529 
2530  return aAdr.Format(nFlags, &rDoc, rDetails);
2531 }
2532 
2533 bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, const OUString& rStr)
2534 {
2535  SCCOL nResult = 0;
2536  sal_Int32 nStop = rStr.getLength();
2537  sal_Int32 nPos = 0;
2538  sal_Unicode c;
2539  const SCCOL nMaxCol = rDoc.MaxCol();
2540  while (nResult <= nMaxCol && nPos < nStop && (c = rStr[nPos]) != 0 &&
2541  rtl::isAsciiAlpha(c))
2542  {
2543  if (nPos > 0)
2544  nResult = (nResult + 1) * 26;
2545  nResult += ScGlobal::ToUpperAlpha(c) - 'A';
2546  ++nPos;
2547  }
2548  bool bOk = (rDoc.ValidCol(nResult) && nPos > 0);
2549  if (bOk)
2550  rCol = nResult;
2551  return bOk;
2552 }
2553 
2554 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
ScRefFlags ParseAny(const OUString &, const ScDocument &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1767
void Set(const ScAddress &rAdr, bool bNewRelCol, bool bNewRelRow, bool bNewRelTab)
Definition: address.hxx:928
static void lcl_r1c1_append_c(T &rString, sal_Int32 nCol, bool bIsAbs, const ScAddress::Details &rDetails)
Definition: address.cxx:1937
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2108
OUString GetColRowString() const
Create a human-readable string representation of the cell address.
Definition: address.cxx:2489
URL aURL
ScAddress aStart
Definition: address.hxx:499
SCROW Row() const
Definition: address.hxx:261
static void lcl_Split_DocTab(const ScDocument &rDoc, SCTAB nTab, const ScAddress::Details &rDetails, ScRefFlags nFlags, OUString &rTabName, OUString &rDocName)
Definition: address.cxx:2123
OUString GetTitle(sal_uInt16 nMaxLen=0) const
static const sal_Unicode * lcl_a1_get_row(const ScDocument &rDoc, const sal_Unicode *p, ScAddress *pAddr, ScRefFlags *nFlags, const OUString *pErrRef)
Definition: address.cxx:937
static const sal_Unicode * lcl_r1c1_get_row(const ScSheetLimits &rSheetLimits, const sal_Unicode *p, const ScAddress::Details &rDetails, ScAddress *pAddr, ScRefFlags *nFlags)
Definition: address.cxx:704
constexpr std::underlying_type_t< T > to_underlying(T e)
static void lcl_r1c1_append_r(T &rString, sal_Int32 nRow, bool bIsAbs, const ScAddress::Details &rDetails)
Definition: address.cxx:1954
static const sal_Unicode * lcl_a1_get_col(const ScDocument &rDoc, const sal_Unicode *p, ScAddress *pAddr, ScRefFlags *nFlags, const OUString *pErrRef)
Definition: address.cxx:899
static bool isValidSingleton(ScRefFlags nFlags, ScRefFlags nFlags2)
B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ...
Definition: address.cxx:971
long Long
bool ConvertSingleRef(const ScDocument &rDoc, const OUString &rRefString, SCTAB nDefTab, ScRefAddress &rRefAddress, const ScAddress::Details &rDetails, ScAddress::ExternalInfo *pExtInfo)
Definition: address.cxx:1501
void ExtendTo(const ScRange &rRange)
Definition: address.cxx:1596
static void lcl_ScColToAlpha(T &rBuf, SCCOL nCol)
Definition: address.cxx:1892
sal_Int64 n
void ScColToAlpha(OUStringBuffer &rBuf, SCCOL nCol)
append alpha representation of column to buffer
Definition: address.cxx:1918
css::uno::Reference< css::frame::XModel3 > GetModel() const
aBuf
bool Move(SCCOL aDeltaX, SCROW aDeltaY, SCTAB aDeltaZ, ScRange &rErrorRange, const ScDocument *pDocument=nullptr)
Definition: address.cxx:2364
static const sal_Unicode * lcl_r1c1_get_col(const sal_Unicode *p, const ScAddress::Details &rDetails, ScAddress *pAddr, ScRefFlags *nFlags)
Definition: address.cxx:658
ScAddress aEnd
Definition: address.hxx:500
sal_uInt16 getExternalFileId(const OUString &rFile)
const SCCOL MAXCOLCOUNT
Definition: address.hxx:63
ScRange Intersection(const ScRange &rOther) const
Definition: address.cxx:1559
SCROW GetMaxRowCount() const
Definition: sheetlimits.hxx:61
static ScRefFlags lcl_ScRange_Parse_XL_A1(ScRange &r, const sal_Unicode *p, const ScDocument &rDoc, bool bOnlyAcceptSingle, ScAddress::ExternalInfo *pExtInfo, const uno::Sequence< sheet::ExternalLinkInfo > *pExternalLinks, sal_Int32 *pSheetEndPos, const OUString *pErrRef)
Definition: address.cxx:978
ScRefFlags ParseCols(const ScDocument &rDoc, const OUString &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1784
static void lcl_ScRange_Format_XL_Header(OUStringBuffer &rString, const ScRange &rRange, ScRefFlags nFlags, const ScDocument &rDoc, const ScAddress::Details &rDetails)
Definition: address.cxx:2152
static SC_DLLPUBLIC sal_Int32 FindUnquoted(const OUString &rString, sal_Unicode cChar, sal_Int32 nStart=0)
Finds an unquoted instance of cChar in rString, starting at offset nStart.
Definition: global.cxx:698
static sal_Int32 GetDocTabPos(const OUString &rString)
Analyzes a string for a 'Doc'Tab construct, or 'Do''c'Tab etc...
Definition: compiler.cxx:1970
const OUString * getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal=false)
It returns a pointer to the name of the URI associated with a given external file ID...
sal_uInt16 sal_Unicode
static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags)
Definition: address.cxx:2199
void ParseRows(const ScDocument &rDoc, const OUString &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1843
void IncColIfNotLessThan(const ScDocument &rDoc, SCCOL nStartCol, SCCOL nOffset)
Definition: address.cxx:2408
static ScRefFlags lcl_ScAddress_Parse(const sal_Unicode *p, const ScDocument &rDoc, ScAddress &rAddr, const ScAddress::Details &rDetails, ScAddress::ExternalInfo *pExtInfo, const uno::Sequence< sheet::ExternalLinkInfo > *pExternalLinks, sal_Int32 *pSheetEndPos, const OUString *pErrRef)
Definition: address.cxx:1462
formula::FormulaGrammar::AddressConvention eConv
Definition: address.hxx:212
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:878
void convertToAbsName(OUString &rFile) const
Takes a flat file name, and convert it to an absolute URL path.
SC_DLLPUBLIC SCTAB GetTableCount() const
Definition: document.cxx:314
const SCROW MAXROW
Definition: address.hxx:68
SC_DLLPUBLIC bool GetTable(const OUString &rName, SCTAB &rTab) const
Definition: document.cxx:260
SC_DLLPUBLIC ScExternalRefManager * GetExternalRefManager() const
Definition: documen3.cxx:627
static SC_DLLPUBLIC const Details detailsOOOa1
Definition: address.hxx:228
SCTAB Tab() const
Definition: address.hxx:270
void SetRow(SCROW nRowP)
Definition: address.hxx:274
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
SCCOL nCol
Definition: address.hxx:202
void SetCol(SCCOL nColP)
Definition: address.hxx:278
bool IsValid() const
Definition: address.hxx:292
SCROW nRow
Definition: address.hxx:201
static SC_DLLPUBLIC OUString GetAbsDocName(const OUString &rFileName, const SfxObjectShell *pShell)
Definition: global2.cxx:286
void SetTab(SCTAB nTabP)
Definition: address.hxx:282
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:877
static const OUString & GetNativeSymbol(OpCode eOp)
void IncEndColSticky(const ScDocument &rDoc, SCCOL nDelta)
Increment or decrement end column unless sticky or until it becomes sticky.
Definition: address.cxx:2448
int i
static void lcl_a1_append_r(T &rString, sal_Int32 nRow, bool bIsAbs)
Definition: address.cxx:1930
static bool lcl_isString(const sal_Unicode *p1, const OUString &rStr)
Definition: address.cxx:176
void Set(SCCOL nCol, SCROW nRow, SCTAB nTab)
Definition: address.hxx:405
sal_Int16 SCCOL
Definition: types.hxx:21
bool ValidCol(SCCOL nCol) const
Definition: document.hxx:880
Details(formula::FormulaGrammar::AddressConvention eConvP, SCROW nRowP, SCCOL nColP)
Definition: address.hxx:216
void applyStartToEndFlags(ScRefFlags &target, const ScRefFlags source)
Definition: address.hxx:183
const SCCOL MAXCOL
Definition: address.hxx:69
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:879
OUString Format(const ScDocument &rDocument, ScRefFlags nFlags=ScRefFlags::ZERO, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1, bool bFullAddressNotation=false) const
Returns string with formatted cell range from aStart to aEnd, according to provided address conventio...
Definition: address.cxx:2204
static bool lcl_ScRange_External_TabSpan(ScRange &rRange, ScRefFlags &rFlags, ScAddress::ExternalInfo *pExtInfo, const OUString &rExternDocName, const OUString &rStartTabName, const OUString &rEndTabName, const ScDocument &rDoc)
Determines the number of sheets an external reference spans and sets rRange.aEnd.nTab accordingly...
Definition: address.cxx:215
const sal_Unicode * Parse_XL_Header(const sal_Unicode *pString, const ScDocument &rDocument, OUString &rExternDocName, OUString &rStartTabName, OUString &rEndTabName, ScRefFlags &nFlags, bool bOnlyAcceptSingle, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, const OUString *pErrRef=nullptr)
Parse an Excel style reference up to and including the sheet name separator '!', including detection ...
Definition: address.cxx:473
float u
static ScRefFlags lcl_ScRange_Parse_OOo(ScRange &rRange, const OUString &r, const ScDocument &rDoc, ScAddress::ExternalInfo *pExtInfo, const OUString *pErrRef)
Definition: address.cxx:1612
static void lcl_Format(T &r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags, const ScDocument *pDoc, const ScAddress::Details &rDetails)
Definition: address.cxx:2007
static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString)
Definition: address.cxx:1997
void IncRowIfNotLessThan(const ScDocument &rDoc, SCROW nStartRow, SCROW nOffset)
Definition: address.cxx:2428
static void lcl_a1_append_c(T &rString, int nCol, bool bIsAbs)
Definition: address.cxx:1923
const SCTAB MAXTAB
Definition: address.hxx:70
bool IsValid() const
Definition: address.hxx:546
ScExternalRefCache::TokenRef getSingleRefToken(sal_uInt16 nFileId, const OUString &rTabName, const ScAddress &rCell, const ScAddress *pCurPos, SCTAB *pTab, ScExternalRefCache::CellFormat *pFmt=nullptr)
SCCOL Col() const
Definition: address.hxx:266
static bool lcl_XL_getExternalDoc(const sal_Unicode **ppErrRet, OUString &rExternDocName, const uno::Sequence< sheet::ExternalLinkInfo > *pExternalLinks)
Tries to obtain the external document index and replace by actual document name.
Definition: address.cxx:424
SC_DLLPUBLIC bool Move(SCCOL nDeltaX, SCROW nDeltaY, SCTAB nDeltaZ, ScAddress &rErrorPos, const ScDocument *pDocument=nullptr)
Definition: address.cxx:2316
static ScRefFlags lcl_ScRange_Parse_XL_R1C1(ScRange &r, const sal_Unicode *p, const ScDocument &rDoc, const ScAddress::Details &rDetails, bool bOnlyAcceptSingle, ScAddress::ExternalInfo *pExtInfo, sal_Int32 *pSheetEndPos)
Definition: address.cxx:752
bool AlphaToCol(const ScDocument &rDoc, SCCOL &rCol, const OUString &rStr)
get column number of A..IV... string
Definition: address.cxx:2533
sal_Int32 SCROW
Definition: types.hxx:17
void PutInOrder()
Definition: address.cxx:1574
void IncEndRowSticky(const ScDocument &rDoc, SCROW nDelta)
Increment or decrement end row unless sticky or until it becomes sticky.
Definition: address.cxx:2469
static sal_Unicode ToUpperAlpha(sal_Unicode c)
Definition: global.hxx:608
#define SAL_INFO(area, stream)
OUString aName
bool isOwnDocument(std::u16string_view rFile) const
ScRefFlags Parse(const OUString &, const ScDocument &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1, ScAddress::ExternalInfo *pExtInfo=nullptr, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, const OUString *pErrRef=nullptr)
Definition: address.cxx:1734
static bool isAsciiNumeric(const OUString &rStr)
static const sal_Unicode * UnicodeStrChr(const sal_Unicode *pStr, sal_Unicode c)
strchr() functionality on unicode, as long as we need it for FormulaToken etc.
Definition: global.cxx:640
static ScRefFlags lcl_ScAddress_Parse_OOo(const sal_Unicode *p, const ScDocument &rDoc, ScAddress &rAddr, ScRefFlags &rRawRes, ScAddress::ExternalInfo *pExtInfo, ScRange *pRange, sal_Int32 *pSheetEndPos, const OUString *pErrRef)
Definition: address.cxx:1142
#define SC_COMPILER_FILE_TAB_SEP
Definition: compiler.hxx:82
bool ConvertDoubleRef(const ScDocument &rDoc, const OUString &rRefString, SCTAB nDefTab, ScRefAddress &rStartRefAddress, ScRefAddress &rEndRefAddress, const ScAddress::Details &rDetails, ScAddress::ExternalInfo *pExtInfo)
Definition: address.cxx:1523
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1063
SCTAB nTab
Definition: address.hxx:203
ocErrRef
Reference< XModel > xModel
SC_DLLPUBLIC ScRefFlags Parse(const OUString &, const ScDocument &, const Details &rDetails=detailsOOOa1, ExternalInfo *pExtInfo=nullptr, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, sal_Int32 *pSheetEndPos=nullptr, const OUString *pErrRef=nullptr)
Definition: address.cxx:1549
OUString GetRefString(const ScDocument &rDocument, SCTAB nActTab, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1) const
Definition: address.cxx:2512
ScRefFlags
Definition: address.hxx:144
static OUString getFileNameFromDoc(const ScDocument *pDoc)
Definition: address.cxx:1971
static void CheckTabQuotes(OUString &aTabName, const formula::FormulaGrammar::AddressConvention eConv=formula::FormulaGrammar::CONV_OOO)
all
Definition: compiler.cxx:1931
static tools::Long sal_Unicode_strtol(const sal_Unicode *p, const sal_Unicode **pEnd)
Definition: address.cxx:136
static const sal_Unicode * lcl_XL_ParseSheetRef(const sal_Unicode *start, OUString &rExternTabName, bool bAllow3D, const sal_Unicode *pMsoxlQuoteStop, const OUString *pErrRef)
Returns NULL if the string should be a sheet name, but is invalid.
Definition: address.cxx:294
static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags)
Definition: address.cxx:2195
static const sal_Unicode * lcl_eatWhiteSpace(const sal_Unicode *p)
Definition: address.cxx:165
aStr
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:213
bool MoveSticky(const ScDocument &rDoc, SCCOL aDeltaX, SCROW aDeltaY, SCTAB aDeltaZ, ScRange &rErrorRange)
Same as Move() but with sticky end col/row anchors.
Definition: address.cxx:2377
sal_uInt16 nPos
sal_Int16 SCTAB
Definition: types.hxx:22