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& 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  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 ScSheetLimits& rSheetLimits,
706  const sal_Unicode* p,
707  const ScAddress::Details& rDetails,
708  ScAddress* pAddr, ScRefFlags* nFlags )
709 {
710  const sal_Unicode *pEnd;
711  long int 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  long int 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  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 
1434  rRawRes |= nRes;
1435 
1436  if ( !(nRes & ScRefFlags::ROW_VALID) && (nRes & ScRefFlags::COL_VALID)
1437  && !( (nRes & ScRefFlags::TAB_3D) && (nRes & ScRefFlags::TAB_VALID)) )
1438  { // no Row, no Tab, but Col => DM (...), B (...) et al
1439  nRes = ScRefFlags::ZERO;
1440  }
1441  if( !*p )
1442  {
1443  ScRefFlags nMask = nRes & ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID );
1444  if( nMask == ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ) )
1445  nRes |= ScRefFlags::VALID;
1446  }
1447  else
1448  nRes = rRawRes = nBailOutFlags;
1449  return nRes;
1450 }
1451 
1452 static ScRefFlags lcl_ScAddress_Parse ( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
1453  const ScAddress::Details& rDetails,
1454  ScAddress::ExternalInfo* pExtInfo,
1455  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1456  sal_Int32* pSheetEndPos,
1457  const OUString* pErrRef )
1458 {
1459  if( !*p )
1460  return ScRefFlags::ZERO;
1461 
1462  switch (rDetails.eConv)
1463  {
1466  {
1467  ScRange rRange = rAddr;
1469  rRange, p, rDoc, true, pExtInfo,
1470  (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
1471  pSheetEndPos, pErrRef);
1472  rAddr = rRange.aStart;
1473  return nFlags;
1474  }
1476  {
1477  ScRange rRange = rAddr;
1478  ScRefFlags nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, rDoc, rDetails, true, pExtInfo, pSheetEndPos);
1479  rAddr = rRange.aStart;
1480  return nFlags;
1481  }
1482  default :
1484  {
1485  ScRefFlags nRawRes = ScRefFlags::ZERO;
1486  return lcl_ScAddress_Parse_OOo( p, rDoc, rAddr, nRawRes, pExtInfo, nullptr, pSheetEndPos, pErrRef);
1487  }
1488  }
1489 }
1490 
1491 bool ConvertSingleRef( const ScDocument& rDoc, const OUString& rRefString,
1492  SCTAB nDefTab, ScRefAddress& rRefAddress,
1493  const ScAddress::Details& rDetails,
1494  ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1495 {
1496  bool bRet = false;
1497  if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1498  {
1499  ScAddress aAddr( 0, 0, nDefTab );
1500  ScRefFlags nRes = aAddr.Parse( rRefString, rDoc, rDetails, pExtInfo);
1501  if ( nRes & ScRefFlags::VALID )
1502  {
1503  rRefAddress.Set( aAddr,
1504  ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
1505  ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
1506  ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
1507  bRet = true;
1508  }
1509  }
1510  return bRet;
1511 }
1512 
1513 bool ConvertDoubleRef( const ScDocument& rDoc, const OUString& rRefString, SCTAB nDefTab,
1514  ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1515  const ScAddress::Details& rDetails,
1516  ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1517 {
1518  bool bRet = false;
1519  if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1520  {
1521  ScRange aRange( ScAddress( 0, 0, nDefTab));
1522  ScRefFlags nRes = aRange.Parse( rRefString, rDoc, rDetails, pExtInfo);
1523  if ( nRes & ScRefFlags::VALID )
1524  {
1525  rStartRefAddress.Set( aRange.aStart,
1526  ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
1527  ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
1528  ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
1529  rEndRefAddress.Set( aRange.aEnd,
1530  ((nRes & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO),
1531  ((nRes & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO),
1532  ((nRes & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO));
1533  bRet = true;
1534  }
1535  }
1536  return bRet;
1537 }
1538 
1539 ScRefFlags ScAddress::Parse( const OUString& r, const ScDocument& rDoc,
1540  const Details& rDetails,
1541  ExternalInfo* pExtInfo,
1542  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1543  sal_Int32* pSheetEndPos,
1544  const OUString* pErrRef )
1545 {
1546  return lcl_ScAddress_Parse( r.getStr(), rDoc, *this, rDetails, pExtInfo, pExternalLinks, pSheetEndPos, pErrRef);
1547 }
1548 
1549 bool ScRange::Intersects( const ScRange& rRange ) const
1550 {
1551  return !(
1552  std::min( aEnd.Col(), rRange.aEnd.Col() ) < std::max( aStart.Col(), rRange.aStart.Col() )
1553  || std::min( aEnd.Row(), rRange.aEnd.Row() ) < std::max( aStart.Row(), rRange.aStart.Row() )
1554  || std::min( aEnd.Tab(), rRange.aEnd.Tab() ) < std::max( aStart.Tab(), rRange.aStart.Tab() )
1555  );
1556 }
1557 
1558 ScRange ScRange::Intersection( const ScRange& rOther ) const
1559 {
1560  SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col());
1561  SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col());
1562  SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row());
1563  SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row());
1564  SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab());
1565  SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab());
1566 
1567  if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2)
1569 
1570  return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1571 }
1572 
1574 {
1575  SCCOL nTempCol;
1576  if ( aEnd.Col() < (nTempCol = aStart.Col()) )
1577  {
1578  aStart.SetCol(aEnd.Col());
1579  aEnd.SetCol(nTempCol);
1580  }
1581  SCROW nTempRow;
1582  if ( aEnd.Row() < (nTempRow = aStart.Row()) )
1583  {
1584  aStart.SetRow(aEnd.Row());
1585  aEnd.SetRow(nTempRow);
1586  }
1587  SCTAB nTempTab;
1588  if ( aEnd.Tab() < (nTempTab = aStart.Tab()) )
1589  {
1590  aStart.SetTab(aEnd.Tab());
1591  aEnd.SetTab(nTempTab);
1592  }
1593 }
1594 
1595 void ScRange::ExtendTo( const ScRange& rRange )
1596 {
1597  OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1598  if( IsValid() )
1599  {
1600  aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) );
1601  aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) );
1602  aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1603  aEnd.SetCol( std::max( aEnd.Col(), rRange.aEnd.Col() ) );
1604  aEnd.SetRow( std::max( aEnd.Row(), rRange.aEnd.Row() ) );
1605  aEnd.SetTab( std::max( aEnd.Tab(), rRange.aEnd.Tab() ) );
1606  }
1607  else
1608  *this = rRange;
1609 }
1610 
1612  const OUString& r,
1613  const ScDocument& rDoc,
1614  ScAddress::ExternalInfo* pExtInfo,
1615  const OUString* pErrRef )
1616 {
1617  ScRefFlags nRes1 = ScRefFlags::ZERO, nRes2 = ScRefFlags::ZERO;
1618  sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':');
1619  if (nPos != -1)
1620  {
1621  OUStringBuffer aTmp(r);
1622  aTmp[nPos] = 0;
1623  const sal_Unicode* p = aTmp.getStr();
1624  ScRefFlags nRawRes1 = ScRefFlags::ZERO;
1625  nRes1 = lcl_ScAddress_Parse_OOo( p, rDoc, rRange.aStart, nRawRes1, pExtInfo, nullptr, nullptr, pErrRef);
1626  if ((nRes1 != ScRefFlags::ZERO) ||
1627  ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1628  (nRawRes1 & ScRefFlags::TAB_VALID)))
1629  {
1630  rRange.aEnd = rRange.aStart; // sheet must be initialized identical to first sheet
1631  ScRefFlags nRawRes2 = ScRefFlags::ZERO;
1632  nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, rDoc, rRange.aEnd, nRawRes2,
1633  pExtInfo, &rRange, nullptr, pErrRef);
1634  if (!((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) &&
1635  // If not fully valid addresses, check if both have a valid
1636  // column or row, and both have valid (or omitted) sheet references.
1637  (nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1638  (nRawRes1 & ScRefFlags::TAB_VALID) &&
1639  (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
1640  (nRawRes2 & ScRefFlags::TAB_VALID) &&
1641  // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A
1642  ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) ==
1643  (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID))))
1644  {
1645  nRes1 = nRawRes1 | ScRefFlags::VALID;
1646  nRes2 = nRawRes2 | ScRefFlags::VALID;
1647  if (nRawRes1 & ScRefFlags::COL_VALID)
1648  {
1649  rRange.aStart.SetRow(0);
1650  rRange.aEnd.SetRow(rDoc.MaxRow());
1653  }
1654  else
1655  {
1656  rRange.aStart.SetCol(0);
1657  rRange.aEnd.SetCol( rDoc.MaxCol() );
1658  nRes1 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
1659  nRes2 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
1660  }
1661  }
1662  else if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
1663  {
1664  // Flag entire column/row references so they can be displayed
1665  // as such. If the sticky reference parts are not both
1666  // absolute or relative, assume that the user thought about
1667  // something we should not touch.
1668  if (rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() &&
1669  ((nRes1 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO) &&
1670  ((nRes2 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO))
1671  {
1672  nRes1 |= ScRefFlags::ROW_ABS;
1673  nRes2 |= ScRefFlags::ROW_ABS;
1674  }
1675  else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
1677  {
1678  nRes1 |= ScRefFlags::COL_ABS;
1679  nRes2 |= ScRefFlags::COL_ABS;
1680  }
1681  }
1682  if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
1683  {
1684  // PutInOrder / Justify
1685  ScRefFlags nMask, nBits1, nBits2;
1686  SCCOL nTempCol;
1687  if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) )
1688  {
1689  rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol);
1691  nBits1 = nRes1 & nMask;
1692  nBits2 = nRes2 & nMask;
1693  nRes1 = (nRes1 & ~nMask) | nBits2;
1694  nRes2 = (nRes2 & ~nMask) | nBits1;
1695  }
1696  SCROW nTempRow;
1697  if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) )
1698  {
1699  rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow);
1701  nBits1 = nRes1 & nMask;
1702  nBits2 = nRes2 & nMask;
1703  nRes1 = (nRes1 & ~nMask) | nBits2;
1704  nRes2 = (nRes2 & ~nMask) | nBits1;
1705  }
1706  SCTAB nTempTab;
1707  if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) )
1708  {
1709  rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab);
1710  nMask = (ScRefFlags::TAB_VALID | ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D);
1711  nBits1 = nRes1 & nMask;
1712  nBits2 = nRes2 & nMask;
1713  nRes1 = (nRes1 & ~nMask) | nBits2;
1714  nRes2 = (nRes2 & ~nMask) | nBits1;
1715  }
1716  if ( ((nRes1 & ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
1718  && !(nRes2 & ScRefFlags::TAB_3D) )
1719  nRes2 |= ScRefFlags::TAB_ABS;
1720  }
1721  else
1722  {
1723  // Don't leave around valid half references.
1724  nRes1 = nRes2 = ScRefFlags::ZERO;
1725  }
1726  }
1727  }
1728  applyStartToEndFlags(nRes1, nRes2 & ScRefFlags::BITS);
1729  nRes1 |= nRes2 & ScRefFlags::VALID;
1730  return nRes1;
1731 }
1732 
1733 ScRefFlags ScRange::Parse( const OUString& rString, const ScDocument& rDoc,
1734  const ScAddress::Details& rDetails,
1735  ScAddress::ExternalInfo* pExtInfo,
1736  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
1737  const OUString* pErrRef )
1738 {
1739  if (rString.isEmpty())
1740  return ScRefFlags::ZERO;
1741 
1742  switch (rDetails.eConv)
1743  {
1746  {
1747  return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), rDoc, false, pExtInfo,
1748  (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
1749  nullptr, pErrRef );
1750  }
1751 
1753  {
1754  return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr );
1755  }
1756 
1757  default:
1759  {
1760  return lcl_ScRange_Parse_OOo( *this, rString, rDoc, pExtInfo, pErrRef );
1761  }
1762  }
1763 }
1764 
1765 // Accept a full range, or an address
1766 ScRefFlags ScRange::ParseAny( const OUString& rString, const ScDocument& rDoc,
1767  const ScAddress::Details& rDetails )
1768 {
1769  ScRefFlags nRet = Parse( rString, rDoc, rDetails );
1771 
1772  if ( (nRet & nValid) != nValid )
1773  {
1774  ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number
1775  nRet = aAdr.Parse( rString, rDoc, rDetails );
1776  if ( nRet & ScRefFlags::VALID )
1777  aStart = aEnd = aAdr;
1778  }
1779  return nRet;
1780 }
1781 
1782 // Parse only full row references
1784  const OUString& rStr,
1785  const ScAddress::Details& rDetails )
1786 {
1787  if (rStr.isEmpty())
1788  return ScRefFlags::ZERO;
1789 
1790  const sal_Unicode* p = rStr.getStr();
1792  ScRefFlags ignored = ScRefFlags::ZERO;
1793 
1794  switch (rDetails.eConv)
1795  {
1796  default :
1797  case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1800  if (nullptr != (p = lcl_a1_get_col( rDoc, p, &aStart, &ignored, nullptr) ) )
1801  {
1802  if( p[0] == ':')
1803  {
1804  if( nullptr != (p = lcl_a1_get_col( rDoc, p+1, &aEnd, &ignored, nullptr)))
1805  {
1806  nRes = ScRefFlags::COL_VALID;
1807  }
1808  }
1809  else
1810  {
1811  aEnd = aStart;
1812  nRes = ScRefFlags::COL_VALID;
1813  }
1814  }
1815  break;
1816 
1818  if ((p[0] == 'C' || p[0] == 'c') &&
1819  nullptr != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored )))
1820  {
1821  if( p[0] == ':')
1822  {
1823  if( (p[1] == 'C' || p[1] == 'c') &&
1824  nullptr != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored )))
1825  {
1826  nRes = ScRefFlags::COL_VALID;
1827  }
1828  }
1829  else
1830  {
1831  aEnd = aStart;
1832  nRes = ScRefFlags::COL_VALID;
1833  }
1834  }
1835  break;
1836  }
1837 
1838  return (p != nullptr && *p == '\0') ? nRes : ScRefFlags::ZERO;
1839 }
1840 
1841 // Parse only full row references
1842 void ScRange::ParseRows( const ScDocument& rDoc,
1843  const OUString& rStr,
1844  const ScAddress::Details& rDetails )
1845 {
1846  if (rStr.isEmpty())
1847  return;
1848 
1849  const sal_Unicode* p = rStr.getStr();
1850  ScRefFlags ignored = ScRefFlags::ZERO;
1851 
1852  switch (rDetails.eConv)
1853  {
1854  default :
1855  case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1858  if (nullptr != (p = lcl_a1_get_row( rDoc, p, &aStart, &ignored, nullptr) ) )
1859  {
1860  if( p[0] == ':')
1861  {
1862  lcl_a1_get_row( rDoc, p+1, &aEnd, &ignored, nullptr);
1863  }
1864  else
1865  {
1866  aEnd = aStart;
1867  }
1868  }
1869  break;
1870 
1872  if ((p[0] == 'R' || p[0] == 'r') &&
1873  nullptr != (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored )))
1874  {
1875  if( p[0] == ':')
1876  {
1877  if( p[1] == 'R' || p[1] == 'r' )
1878  {
1879  lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored );
1880  }
1881  }
1882  else
1883  {
1884  aEnd = aStart;
1885  }
1886  }
1887  break;
1888  }
1889 }
1890 
1891 template<typename T > static void lcl_ScColToAlpha( T& rBuf, SCCOL nCol )
1892 {
1893  if (nCol < 26*26)
1894  {
1895  if (nCol < 26)
1896  rBuf.append( static_cast<char>( 'A' + nCol ));
1897  else
1898  {
1899  rBuf.append( static_cast<char>( 'A' + nCol / 26 - 1 ));
1900  rBuf.append( static_cast<char>( 'A' + nCol % 26 ));
1901  }
1902  }
1903  else
1904  {
1905  sal_Int32 nInsert = rBuf.getLength();
1906  while (nCol >= 26)
1907  {
1908  SCCOL nC = nCol % 26;
1909  rBuf.insert(nInsert, static_cast<char> ( 'A' + nC ));
1910  nCol = sal::static_int_cast<SCCOL>( nCol - nC );
1911  nCol = nCol / 26 - 1;
1912  }
1913  rBuf.insert(nInsert, static_cast<char> ( 'A' + nCol ));
1914  }
1915 }
1916 
1917 void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol)
1918 {
1919  lcl_ScColToAlpha(rBuf, nCol);
1920 }
1921 
1922 template <typename T > static void lcl_a1_append_c ( T &rString, int nCol, bool bIsAbs )
1923 {
1924  if( bIsAbs )
1925  rString.append("$");
1926  lcl_ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) );
1927 }
1928 
1929 template <typename T > static void lcl_a1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs )
1930 {
1931  if ( bIsAbs )
1932  rString.append("$");
1933  rString.append( nRow + 1 );
1934 }
1935 
1936 template <typename T > static void lcl_r1c1_append_c ( T &rString, sal_Int32 nCol, bool bIsAbs,
1937  const ScAddress::Details& rDetails )
1938 {
1939  rString.append("C");
1940  if (bIsAbs)
1941  {
1942  rString.append( nCol + 1 );
1943  }
1944  else
1945  {
1946  nCol -= rDetails.nCol;
1947  if (nCol != 0) {
1948  rString.append("[").append(nCol).append("]");
1949  }
1950  }
1951 }
1952 
1953 template <typename T > static void lcl_r1c1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs,
1954  const ScAddress::Details& rDetails )
1955 {
1956  rString.append("R");
1957  if (bIsAbs)
1958  {
1959  rString.append( nRow + 1 );
1960  }
1961  else
1962  {
1963  nRow -= rDetails.nRow;
1964  if (nRow != 0) {
1965  rString.append("[").append(nRow).append("]");
1966  }
1967  }
1968 }
1969 
1970 static OUString getFileNameFromDoc( const ScDocument* pDoc )
1971 {
1972  // TODO : er points at ScGlobal::GetAbsDocName()
1973  // as a better template. Look into it
1974  OUString sFileName;
1975  SfxObjectShell* pShell;
1976 
1977  if( nullptr != pDoc &&
1978  nullptr != (pShell = pDoc->GetDocumentShell() ) )
1979  {
1980  uno::Reference< frame::XModel > xModel = pShell->GetModel();
1981  if( xModel.is() )
1982  {
1983  if( !xModel->getURL().isEmpty() )
1984  {
1985  INetURLObject aURL( xModel->getURL() );
1986  sFileName = aURL.GetLastName();
1987  }
1988  else
1989  sFileName = pShell->GetTitle();
1990  }
1991  }
1992  return sFileName;
1993 }
1994 
1995 
1996 static void lcl_string_append(OUStringBuffer &rString, const OUString &sString)
1997 {
1998  rString.append(sString);
1999 }
2000 
2001 static void lcl_string_append(OStringBuffer &rString, const OUString &sString)
2002 {
2003  rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8 ));
2004 }
2005 
2006 template<typename T > static void lcl_Format( T& r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags,
2007  const ScDocument* pDoc,
2008  const ScAddress::Details& rDetails)
2009 {
2010  if( nFlags & ScRefFlags::VALID )
2012  if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) )
2013  {
2014  if ( nTab < 0 || nTab >= pDoc->GetTableCount() )
2015  {
2017  return;
2018  }
2019  if( nFlags & ScRefFlags::TAB_3D )
2020  {
2021  OUString aTabName, aDocName;
2022  pDoc->GetName(nTab, aTabName);
2023  assert( !aTabName.isEmpty() && "empty sheet name");
2024  // External Reference, same as in ScCompiler::MakeTabStr()
2025  if( aTabName[0] == '\'' )
2026  { // "'Doc'#Tab"
2027  sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName);
2028  if (nPos != -1)
2029  {
2030  aDocName = aTabName.copy( 0, nPos + 1 );
2031  aTabName = aTabName.copy( nPos + 1 );
2032  }
2033  }
2034  else if( nFlags & ScRefFlags::FORCE_DOC )
2035  {
2036  // VBA has an 'external' flag that forces the addition of the
2037  // tab name _and_ the doc name. The VBA code would be
2038  // needlessly complicated if it constructed an actual external
2039  // reference so we add this somewhat cheesy kludge to force the
2040  // addition of the document name even for non-external references
2041  aDocName = getFileNameFromDoc( pDoc );
2042  }
2043  ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
2044 
2045  switch( rDetails.eConv )
2046  {
2047  default :
2049  lcl_string_append(r, aDocName);
2050  if( nFlags & ScRefFlags::TAB_ABS )
2051  r.append("$");
2052  lcl_string_append(r, aTabName);
2053  r.append(".");
2054  break;
2055 
2057  if (!aTabName.isEmpty() && aTabName[0] == '\'')
2058  {
2059  if (!aDocName.isEmpty())
2060  {
2061  lcl_string_append(r.append("'["), aDocName);
2062  r.append("]");
2063  lcl_string_append(r, aTabName.copy(1));
2064  }
2065  else
2066  {
2067  lcl_string_append(r, aTabName);
2068  }
2069  r.append("!");
2070  break;
2071  }
2072  [[fallthrough]];
2075  if (!aDocName.isEmpty())
2076  {
2077  lcl_string_append(r.append("["), aDocName);
2078  r.append("]");
2079  }
2080  lcl_string_append(r, aTabName);
2081  r.append("!");
2082  break;
2083  }
2084  }
2085  }
2086  switch( rDetails.eConv )
2087  {
2088  default :
2092  if( nFlags & ScRefFlags::COL_VALID )
2093  lcl_a1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2094  if( nFlags & ScRefFlags::ROW_VALID )
2095  lcl_a1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2096  break;
2097 
2099  if( nFlags & ScRefFlags::ROW_VALID )
2100  lcl_r1c1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2101  if( nFlags & ScRefFlags::COL_VALID )
2102  lcl_r1c1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2103  break;
2104  }
2105 }
2106 
2107 void ScAddress::Format( OStringBuffer& r, ScRefFlags nFlags,
2108  const ScDocument* pDoc,
2109  const Details& rDetails) const
2110 {
2111  lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
2112 }
2113 
2114 OUString ScAddress::Format(ScRefFlags nFlags, const ScDocument* pDoc,
2115  const Details& rDetails) const
2116 {
2117  OUStringBuffer r;
2118  lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
2119  return r.makeStringAndClear();
2120 }
2121 
2122 static void lcl_Split_DocTab( const ScDocument& rDoc, SCTAB nTab,
2123  const ScAddress::Details& rDetails,
2124  ScRefFlags nFlags,
2125  OUString& rTabName, OUString& rDocName )
2126 {
2127  rDoc.GetName(nTab, rTabName);
2128  rDocName.clear();
2129  // External reference, same as in ScCompiler::MakeTabStr()
2130  if ( rTabName[0] == '\'' )
2131  { // "'Doc'#Tab"
2132  sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName);
2133  if (nPos != -1)
2134  {
2135  rDocName = rTabName.copy( 0, nPos + 1 );
2136  rTabName = rTabName.copy( nPos + 1 );
2137  }
2138  }
2139  else if( nFlags & ScRefFlags::FORCE_DOC )
2140  {
2141  // VBA has an 'external' flag that forces the addition of the
2142  // tab name _and_ the doc name. The VBA code would be
2143  // needlessly complicated if it constructed an actual external
2144  // reference so we add this somewhat cheesy kludge to force the
2145  // addition of the document name even for non-external references
2146  rDocName = getFileNameFromDoc(&rDoc);
2147  }
2148  ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
2149 }
2150 
2151 static void lcl_ScRange_Format_XL_Header( OUStringBuffer& rString, const ScRange& rRange,
2152  ScRefFlags nFlags, const ScDocument& rDoc,
2153  const ScAddress::Details& rDetails )
2154 {
2155  if( !(nFlags & ScRefFlags::TAB_3D) )
2156  return;
2157 
2158  OUString aTabName, aDocName;
2159  lcl_Split_DocTab( rDoc, rRange.aStart.Tab(), rDetails, nFlags, aTabName, aDocName );
2160  switch (rDetails.eConv)
2161  {
2163  if (!aTabName.isEmpty() && aTabName[0] == '\'')
2164  {
2165  if (!aDocName.isEmpty())
2166  {
2167  rString.append("'[").append(aDocName).append("]").append(std::u16string_view(aTabName).substr(1));
2168  }
2169  else
2170  {
2171  rString.append(aTabName);
2172  }
2173  break;
2174  }
2175  [[fallthrough]];
2176  default:
2177  if (!aDocName.isEmpty())
2178  {
2179  rString.append("[").append(aDocName).append("]");
2180  }
2181  rString.append(aTabName);
2182  break;
2183  }
2184  if( nFlags & ScRefFlags::TAB2_3D )
2185  {
2186  lcl_Split_DocTab( rDoc, rRange.aEnd.Tab(), rDetails, nFlags, aTabName, aDocName );
2187  rString.append(":");
2188  rString.append(aTabName);
2189  }
2190  rString.append("!");
2191 }
2192 
2193 // helpers used in ScRange::Format
2194 static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags)
2195 {
2196  return static_cast<bool>(nFlags & ScRefFlags::COL_ABS) != static_cast<bool>(nFlags & ScRefFlags::COL2_ABS);
2197 }
2198 static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags)
2199 {
2200  return static_cast<bool>(nFlags & ScRefFlags::ROW_ABS) != static_cast<bool>(nFlags & ScRefFlags::ROW2_ABS);
2201 }
2202 
2203 OUString ScRange::Format( const ScDocument& rDoc, ScRefFlags nFlags,
2204  const ScAddress::Details& rDetails, bool bFullAddressNotation ) const
2205 {
2206  if( !( nFlags & ScRefFlags::VALID ) )
2207  {
2209  }
2210 
2211  OUStringBuffer r;
2212  switch( rDetails.eConv ) {
2213  default :
2215  bool bOneTab = (aStart.Tab() == aEnd.Tab());
2216  if ( !bOneTab )
2217  nFlags |= ScRefFlags::TAB_3D;
2218  r = aStart.Format(nFlags, &rDoc, rDetails);
2219  if( aStart != aEnd ||
2220  lcl_ColAbsFlagDiffer( nFlags ) ||
2221  lcl_RowAbsFlagDiffer( nFlags ))
2222  {
2223  const ScDocument* pDoc = &rDoc;
2224  // move flags of end reference to start reference, mask with BITS to exclude FORCE_DOC flag
2225  nFlags = ScRefFlags::VALID | (ScRefFlags(o3tl::underlyingEnumValue(nFlags) >> 4) & ScRefFlags::BITS);
2226  if ( bOneTab )
2227  pDoc = nullptr;
2228  else
2229  nFlags |= ScRefFlags::TAB_3D;
2230  OUString aName(aEnd.Format(nFlags, pDoc, rDetails));
2231  r.append(":");
2232  r.append(aName);
2233  }
2234  break;
2235  }
2236 
2239  SCCOL nMaxCol = rDoc.MaxCol();
2240  SCROW nMaxRow = rDoc.MaxRow();
2241 
2242  lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
2243  if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
2244  {
2245  // Full col refs always require 2 rows (2:2)
2246  lcl_a1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2247  r.append(":");
2248  lcl_a1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
2249  }
2250  else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
2251  {
2252  // Full row refs always require 2 cols (A:A)
2253  lcl_a1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2254  r.append(":");
2255  lcl_a1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
2256  }
2257  else
2258  {
2259  lcl_a1_append_c ( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
2260  lcl_a1_append_r ( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
2261  if( aStart.Col() != aEnd.Col() ||
2262  lcl_ColAbsFlagDiffer( nFlags ) ||
2263  aStart.Row() != aEnd.Row() ||
2264  lcl_RowAbsFlagDiffer( nFlags ) ) {
2265  r.append(":");
2266  lcl_a1_append_c ( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
2267  lcl_a1_append_r ( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
2268  }
2269  }
2270  break;
2271  }
2272 
2274  SCCOL nMaxCol = rDoc.MaxCol();
2275  SCROW nMaxRow = rDoc.MaxRow();
2276 
2277  lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
2278  if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
2279  {
2280  lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2281  if( aStart.Row() != aEnd.Row() ||
2282  lcl_RowAbsFlagDiffer( nFlags ) ) {
2283  r.append(":");
2284  lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
2285  }
2286  }
2287  else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
2288  {
2289  lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2290  if( aStart.Col() != aEnd.Col() ||
2291  lcl_ColAbsFlagDiffer( nFlags )) {
2292  r.append(":");
2293  lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
2294  }
2295  }
2296  else
2297  {
2298  lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
2299  lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
2300  if( aStart.Col() != aEnd.Col() ||
2301  lcl_ColAbsFlagDiffer( nFlags ) ||
2302  aStart.Row() != aEnd.Row() ||
2303  lcl_RowAbsFlagDiffer( nFlags ) ) {
2304  r.append(":");
2305  lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
2306  lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
2307  }
2308  }
2309  break;
2310  }
2311  }
2312  return r.makeStringAndClear();
2313 }
2314 
2315 bool ScAddress::Move( SCCOL dx, SCROW dy, SCTAB dz, ScAddress& rErrorPos, const ScDocument* pDoc )
2316 {
2317  SCTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB;
2318  SCCOL nMaxCol = pDoc ? pDoc->MaxCol() : MAXCOL;
2319  SCROW nMaxRow = pDoc ? pDoc->MaxRow() : MAXROW;
2320  dx = Col() + dx;
2321  dy = Row() + dy;
2322  dz = Tab() + dz;
2323  bool bValid = true;
2324  rErrorPos.SetCol(dx);
2325  if( dx < 0 )
2326  {
2327  dx = 0;
2328  bValid = false;
2329  }
2330  else if( dx > nMaxCol )
2331  {
2332  dx = nMaxCol;
2333  bValid =false;
2334  }
2335  rErrorPos.SetRow(dy);
2336  if( dy < 0 )
2337  {
2338  dy = 0;
2339  bValid = false;
2340  }
2341  else if( dy > nMaxRow )
2342  {
2343  dy = nMaxRow;
2344  bValid =false;
2345  }
2346  rErrorPos.SetTab(dz);
2347  if( dz < 0 )
2348  {
2349  dz = 0;
2350  bValid = false;
2351  }
2352  else if( dz > nMaxTab )
2353  {
2354  // Always set MAXTAB+1 so further checks without ScDocument detect invalid.
2355  rErrorPos.SetTab(MAXTAB+1);
2356  dz = nMaxTab;
2357  bValid =false;
2358  }
2359  Set( dx, dy, dz );
2360  return bValid;
2361 }
2362 
2363 bool ScRange::Move( SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange, const ScDocument* pDoc )
2364 {
2365  SCCOL nMaxCol = pDoc ? pDoc->MaxCol() : MAXCOL;
2366  SCROW nMaxRow = pDoc ? pDoc->MaxRow() : MAXROW;
2367  if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
2368  dy = 0; // Entire column not to be moved.
2369  if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
2370  dx = 0; // Entire row not to be moved.
2371  bool b = aStart.Move( dx, dy, dz, rErrorRange.aStart, pDoc );
2372  b &= aEnd.Move( dx, dy, dz, rErrorRange.aEnd, pDoc );
2373  return b;
2374 }
2375 
2376 bool ScRange::MoveSticky( const ScDocument& rDoc, SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange )
2377 {
2378  const SCCOL nMaxCol = rDoc.MaxCol();
2379  const SCROW nMaxRow = rDoc.MaxRow();
2380  bool bColRange = (aStart.Col() < aEnd.Col());
2381  bool bRowRange = (aStart.Row() < aEnd.Row());
2382  if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
2383  dy = 0; // Entire column not to be moved.
2384  if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
2385  dx = 0; // Entire row not to be moved.
2386  bool b1 = aStart.Move( dx, dy, dz, rErrorRange.aStart );
2387  if (dx && bColRange && aEnd.Col() == nMaxCol)
2388  dx = 0; // End column sticky.
2389  if (dy && bRowRange && aEnd.Row() == nMaxRow)
2390  dy = 0; // End row sticky.
2391  SCTAB nOldTab = aEnd.Tab();
2392  bool b2 = aEnd.Move( dx, dy, dz, rErrorRange.aEnd );
2393  if (!b2)
2394  {
2395  // End column or row of a range may have become sticky.
2396  bColRange = (!dx || (bColRange && aEnd.Col() == nMaxCol));
2397  if (dx && bColRange)
2398  rErrorRange.aEnd.SetCol(nMaxCol);
2399  bRowRange = (!dy || (bRowRange && aEnd.Row() == nMaxRow));
2400  if (dy && bRowRange)
2401  rErrorRange.aEnd.SetRow(nMaxRow);
2402  b2 = bColRange && bRowRange && (aEnd.Tab() - nOldTab == dz);
2403  }
2404  return b1 && b2;
2405 }
2406 
2407 void ScRange::IncColIfNotLessThan(const ScDocument& rDoc, SCCOL nStartCol, SCCOL nOffset)
2408 {
2409  if (aStart.Col() >= nStartCol)
2410  {
2411  aStart.IncCol(nOffset);
2412  if (aStart.Col() < 0)
2413  aStart.SetCol(0);
2414  else if(aStart.Col() > rDoc.MaxCol())
2415  aStart.SetCol(rDoc.MaxCol());
2416  }
2417  if (aEnd.Col() >= nStartCol)
2418  {
2419  aEnd.IncCol(nOffset);
2420  if (aEnd.Col() < 0)
2421  aEnd.SetCol(0);
2422  else if(aEnd.Col() > rDoc.MaxCol())
2423  aEnd.SetCol(rDoc.MaxCol());
2424  }
2425 }
2426 
2427 void ScRange::IncRowIfNotLessThan(const ScDocument& rDoc, SCROW nStartRow, SCROW nOffset)
2428 {
2429  if (aStart.Row() >= nStartRow)
2430  {
2431  aStart.IncRow(nOffset);
2432  if (aStart.Row() < 0)
2433  aStart.SetRow(0);
2434  else if(aStart.Row() > rDoc.MaxRow())
2435  aStart.SetRow(rDoc.MaxRow());
2436  }
2437  if (aEnd.Row() >= nStartRow)
2438  {
2439  aEnd.IncRow(nOffset);
2440  if (aEnd.Row() < 0)
2441  aEnd.SetRow(0);
2442  else if(aEnd.Row() > rDoc.MaxRow())
2443  aEnd.SetRow(rDoc.MaxRow());
2444  }
2445 }
2446 
2447 void ScRange::IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta )
2448 {
2449  SCCOL nCol = aEnd.Col();
2450  if (aStart.Col() >= nCol)
2451  {
2452  // Less than two columns => not sticky.
2453  aEnd.IncCol( nDelta);
2454  return;
2455  }
2456 
2457  const SCCOL nMaxCol = rDoc.MaxCol();
2458  if (nCol == nMaxCol)
2459  // already sticky
2460  return;
2461 
2462  if (nCol < nMaxCol)
2463  aEnd.SetCol( ::std::min( static_cast<SCCOL>(nCol + nDelta), nMaxCol));
2464  else
2465  aEnd.IncCol( nDelta); // was greater than nMaxCol, caller should know...
2466 }
2467 
2468 void ScRange::IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta )
2469 {
2470  SCROW nRow = aEnd.Row();
2471  if (aStart.Row() >= nRow)
2472  {
2473  // Less than two rows => not sticky.
2474  aEnd.IncRow( nDelta);
2475  return;
2476  }
2477 
2478  if (nRow == rDoc.MaxRow())
2479  // already sticky
2480  return;
2481 
2482  if (nRow < rDoc.MaxRow())
2483  aEnd.SetRow( ::std::min( static_cast<SCROW>(nRow + nDelta), rDoc.MaxRow()));
2484  else
2485  aEnd.IncRow( nDelta); // was greater than rDoc.MaxRow(), caller should know...
2486 }
2487 
2489 {
2490  OUStringBuffer aString;
2491 
2492  switch( detailsOOOa1.eConv )
2493  {
2494  default :
2498  lcl_ScColToAlpha( aString, nCol);
2499  aString.append(OUString::number(nRow+1));
2500  break;
2501 
2503  lcl_r1c1_append_r ( aString, nRow, false/*bAbsolute*/, detailsOOOa1 );
2504  lcl_r1c1_append_c ( aString, nCol, false/*bAbsolute*/, detailsOOOa1 );
2505  break;
2506  }
2507 
2508  return aString.makeStringAndClear();
2509 }
2510 
2511 OUString ScRefAddress::GetRefString( const ScDocument& rDoc, SCTAB nActTab,
2512  const ScAddress::Details& rDetails ) const
2513 {
2514  if ( Tab()+1 > rDoc.GetTableCount() )
2516 
2517  ScRefFlags nFlags = ScRefFlags::VALID;
2518  if ( nActTab != Tab() )
2519  {
2520  nFlags |= ScRefFlags::TAB_3D;
2521  if ( !bRelTab )
2522  nFlags |= ScRefFlags::TAB_ABS;
2523  }
2524  if ( !bRelCol )
2525  nFlags |= ScRefFlags::COL_ABS;
2526  if ( !bRelRow )
2527  nFlags |= ScRefFlags::ROW_ABS;
2528 
2529  return aAdr.Format(nFlags, &rDoc, rDetails);
2530 }
2531 
2532 bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, const OUString& rStr)
2533 {
2534  SCCOL nResult = 0;
2535  sal_Int32 nStop = rStr.getLength();
2536  sal_Int32 nPos = 0;
2537  sal_Unicode c;
2538  const SCCOL nMaxCol = rDoc.MaxCol();
2539  while (nResult <= nMaxCol && nPos < nStop && (c = rStr[nPos]) != 0 &&
2540  rtl::isAsciiAlpha(c))
2541  {
2542  if (nPos > 0)
2543  nResult = (nResult + 1) * 26;
2544  nResult += ScGlobal::ToUpperAlpha(c) - 'A';
2545  ++nPos;
2546  }
2547  bool bOk = (rDoc.ValidCol(nResult) && nPos > 0);
2548  if (bOk)
2549  rCol = nResult;
2550  return bOk;
2551 }
2552 
2553 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SC_DLLPUBLIC ScRefFlags ParseAny(const OUString &, const ScDocument &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1766
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:1936
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2107
OUString GetColRowString() const
Create a human-readable string representation of the cell address.
Definition: address.cxx:2488
URL aURL
ScAddress aStart
Definition: address.hxx:500
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:2122
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:1549
static void lcl_r1c1_append_r(T &rString, sal_Int32 nRow, bool bIsAbs, const ScAddress::Details &rDetails)
Definition: address.cxx:1953
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
bool ConvertSingleRef(const ScDocument &rDoc, const OUString &rRefString, SCTAB nDefTab, ScRefAddress &rRefAddress, const ScAddress::Details &rDetails, ScAddress::ExternalInfo *pExtInfo)
Definition: address.cxx:1491
SC_DLLPUBLIC void ExtendTo(const ScRange &rRange)
Definition: address.cxx:1595
static void lcl_ScColToAlpha(T &rBuf, SCCOL nCol)
Definition: address.cxx:1891
sal_Int64 n
void ScColToAlpha(OUStringBuffer &rBuf, SCCOL nCol)
append alpha representation of column to buffer
Definition: address.cxx:1917
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:2363
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:1558
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:1783
static void lcl_ScRange_Format_XL_Header(OUStringBuffer &rString, const ScRange &rRange, ScRefFlags nFlags, const ScDocument &rDoc, const ScAddress::Details &rDetails)
Definition: address.cxx:2151
static sal_Int32 GetDocTabPos(const OUString &rString)
Analyzes a string for a 'Doc'Tab construct, or 'Do''c'Tab etc...
Definition: compiler.cxx:1972
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:2198
SC_DLLPUBLIC void ParseRows(const ScDocument &rDoc, const OUString &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1842
SC_DLLPUBLIC void IncColIfNotLessThan(const ScDocument &rDoc, SCCOL nStartCol, SCCOL nOffset)
Definition: address.cxx:2407
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:1452
formula::FormulaGrammar::AddressConvention eConv
Definition: address.hxx:213
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:873
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:314
const SCROW MAXROW
Definition: address.hxx:69
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:260
SC_DLLPUBLIC ScExternalRefManager * GetExternalRefManager() const
Definition: documen3.cxx:618
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
bool IsValid() const
Definition: address.hxx:293
SCROW nRow
Definition: address.hxx:202
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:872
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:700
void IncEndColSticky(const ScDocument &rDoc, SCCOL nDelta)
Increment or decrement end column unless sticky or until it becomes sticky.
Definition: address.cxx:2447
int i
static void lcl_a1_append_r(T &rString, sal_Int32 nRow, bool bIsAbs)
Definition: address.cxx:1929
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:875
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
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:874
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:2203
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:1611
static void lcl_Format(T &r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags, const ScDocument *pDoc, const ScAddress::Details &rDetails)
Definition: address.cxx:2006
SC_DLLPUBLIC void IncRowIfNotLessThan(const ScDocument &rDoc, SCROW nStartRow, SCROW nOffset)
Definition: address.cxx:2427
static void lcl_a1_append_c(T &rString, int nCol, bool bIsAbs)
Definition: address.cxx:1922
const SCTAB MAXTAB
Definition: address.hxx:71
bool IsValid() const
Definition: address.hxx:547
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: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:2315
OString OUStringToOString(const OUString &str, ConnectionSettings const *settings)
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:2532
sal_Int32 SCROW
Definition: types.hxx:18
SC_DLLPUBLIC void PutInOrder()
Definition: address.cxx:1573
void IncEndRowSticky(const ScDocument &rDoc, SCROW nDelta)
Increment or decrement end row unless sticky or until it becomes sticky.
Definition: address.cxx:2468
static sal_Unicode ToUpperAlpha(sal_Unicode c)
Definition: global.hxx:618
#define SAL_INFO(area, stream)
OUString aName
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:1733
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:642
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:83
bool ConvertDoubleRef(const ScDocument &rDoc, const OUString &rRefString, SCTAB nDefTab, ScRefAddress &rStartRefAddress, ScRefAddress &rEndRefAddress, const ScAddress::Details &rDetails, ScAddress::ExternalInfo *pExtInfo)
Definition: address.cxx:1513
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1058
SCTAB nTab
Definition: address.hxx:204
ocErrRef
Reference< XModel > xModel
static void lcl_string_append(OUStringBuffer &rString, const OUString &sString)
Definition: address.cxx:1996
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:1539
OUString GetRefString(const ScDocument &rDocument, SCTAB nActTab, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1) const
Definition: address.cxx:2511
ScRefFlags
Definition: address.hxx:145
static OUString getFileNameFromDoc(const ScDocument *pDoc)
Definition: address.cxx:1970
static void CheckTabQuotes(OUString &aTabName, const formula::FormulaGrammar::AddressConvention eConv=formula::FormulaGrammar::CONV_OOO)
all
Definition: compiler.cxx:1933
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:2194
bool isOwnDocument(const OUString &rFile) const
static const sal_Unicode * lcl_eatWhiteSpace(const sal_Unicode *p)
Definition: address.cxx:165
aStr
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:213
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:2376
sal_uInt16 nPos
sal_Int16 SCTAB
Definition: types.hxx:23