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