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 += 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 
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 += 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  {
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  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  {
1475  {
1476  ScRange rRange = rAddr;
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  }
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 :
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)
1578 
1579  return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1580 }
1581 
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 
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());
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() &&
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);
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);
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 ))
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  {
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 
1762  {
1763  return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr );
1764  }
1765 
1766  default:
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 );
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
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();
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
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 
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
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 
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 )
2021  if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) )
2022  {
2023  if ( nTab < 0 || nTab >= pDoc->GetTableCount() )
2024  {
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 :
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 
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]];
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 :
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 
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  {
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  {
2218  }
2219 
2220  OUStringBuffer r;
2221  switch( rDetails.eConv ) {
2222  default :
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 
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 
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 
2498 {
2499  OUStringBuffer aString;
2500 
2501  switch( detailsOOOa1.eConv )
2502  {
2503  default :
2507  lcl_ScColToAlpha( aString, nCol);
2508  aString.append(OUString::number(nRow+1));
2509  break;
2510 
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() )
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: */
SC_DLLPUBLIC ScRefFlags ParseAny(const OUString &, const ScDocument &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1775
void Set(const ScAddress &rAdr, bool bNewRelCol, bool bNewRelRow, bool bNewRelTab)
Definition: address.hxx:920
static void lcl_r1c1_append_c(T &rString, sal_Int32 nCol, bool bIsAbs, const ScAddress::Details &rDetails)
Definition: address.cxx:1945
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2116
OUString GetColRowString() const
Create a human-readable string representation of the cell address.
Definition: address.cxx:2497
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:2131
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
SC_DLLPUBLIC bool Intersects(const ScRange &rRange) const
Definition: address.cxx:1558
static void lcl_r1c1_append_r(T &rString, sal_Int32 nRow, bool bIsAbs, const ScAddress::Details &rDetails)
Definition: address.cxx:1962
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:1500
SC_DLLPUBLIC void ExtendTo(const ScRange &rRange)
Definition: address.cxx:1604
static void lcl_ScColToAlpha(T &rBuf, SCCOL nCol)
Definition: address.cxx:1900
sal_Int64 n
void ScColToAlpha(OUStringBuffer &rBuf, SCCOL nCol)
append alpha representation of column to buffer
Definition: address.cxx:1926
constexpr std::underlying_type_t< T > underlyingEnumValue(T e)
aBuf
SC_DLLPUBLIC bool Move(SCCOL aDeltaX, SCROW aDeltaY, SCTAB aDeltaZ, ScRange &rErrorRange, const ScDocument *pDocument=nullptr)
Definition: address.cxx:2372
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)
css::uno::Reference< css::frame::XModel > GetModel() const
const SCCOL MAXCOLCOUNT
Definition: address.hxx:63
ScRange Intersection(const ScRange &rOther) const
Definition: address.cxx:1567
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
SC_DLLPUBLIC ScRefFlags ParseCols(const ScDocument &rDoc, const OUString &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1792
static void lcl_ScRange_Format_XL_Header(OUStringBuffer &rString, const ScRange &rRange, ScRefFlags nFlags, const ScDocument &rDoc, const ScAddress::Details &rDetails)
Definition: address.cxx:2160
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:1968
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:2207
SC_DLLPUBLIC void ParseRows(const ScDocument &rDoc, const OUString &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1851
SC_DLLPUBLIC void IncColIfNotLessThan(const ScDocument &rDoc, SCCOL nStartCol, SCCOL nOffset)
Definition: address.cxx:2416
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:1461
formula::FormulaGrammar::AddressConvention eConv
Definition: address.hxx:212
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:871
void convertToAbsName(OUString &rFile) const
Takes a flat file name, and convert it to an absolute URL path.
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
SC_DLLPUBLIC SCTAB GetTableCount() const
Definition: document.cxx:313
const SCROW MAXROW
Definition: address.hxx:68
SC_DLLPUBLIC bool GetTable(const OUString &rName, SCTAB &rTab) const
Definition: document.cxx:259
SC_DLLPUBLIC ScExternalRefManager * GetExternalRefManager() const
Definition: documen3.cxx:623
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:284
void SetTab(SCTAB nTabP)
Definition: address.hxx:282
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:870
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:2456
int i
static void lcl_a1_append_r(T &rString, sal_Int32 nRow, bool bIsAbs)
Definition: address.cxx:1938
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:873
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:872
SC_DLLPUBLIC 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:2212
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
static ScRefFlags lcl_ScRange_Parse_OOo(ScRange &rRange, const OUString &r, const ScDocument &rDoc, ScAddress::ExternalInfo *pExtInfo, const OUString *pErrRef)
Definition: address.cxx:1620
static void lcl_Format(T &r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags, const ScDocument *pDoc, const ScAddress::Details &rDetails)
Definition: address.cxx:2015
static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString)
Definition: address.cxx:2005
SC_DLLPUBLIC void IncRowIfNotLessThan(const ScDocument &rDoc, SCROW nStartRow, SCROW nOffset)
Definition: address.cxx:2436
static void lcl_a1_append_c(T &rString, int nCol, bool bIsAbs)
Definition: address.cxx:1931
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:2324
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:2541
sal_Int32 SCROW
Definition: types.hxx:17
SC_DLLPUBLIC void PutInOrder()
Definition: address.cxx:1582
void IncEndRowSticky(const ScDocument &rDoc, SCROW nDelta)
Increment or decrement end row unless sticky or until it becomes sticky.
Definition: address.cxx:2477
static sal_Unicode ToUpperAlpha(sal_Unicode c)
Definition: global.hxx:618
#define SAL_INFO(area, stream)
OUString aName
bool isOwnDocument(std::u16string_view rFile) const
SC_DLLPUBLIC 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:1742
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:1522
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1056
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:1548
OUString GetRefString(const ScDocument &rDocument, SCTAB nActTab, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1) const
Definition: address.cxx:2520
ScRefFlags
Definition: address.hxx:144
static OUString getFileNameFromDoc(const ScDocument *pDoc)
Definition: address.cxx:1979
static void CheckTabQuotes(OUString &aTabName, const formula::FormulaGrammar::AddressConvention eConv=formula::FormulaGrammar::CONV_OOO)
all
Definition: compiler.cxx:1929
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:2203
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:212
SC_DLLPUBLIC 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:2385
sal_uInt16 nPos
sal_Int16 SCTAB
Definition: types.hxx:22