1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <string_view>
24 #include <address.hxx>
26 #include <compiler.hxx>
27 #include <document.hxx>
29 #include <externalrefmgr.hxx>
31 #include <osl/diagnose.h>
32 #include <o3tl/underlyingenumvalue.hxx>
33 #include <com/sun/star/frame/XModel.hpp>
34 #include <com/sun/star/sheet/ExternalLinkInfo.hpp>
35 #include <com/sun/star/sheet/ExternalLinkType.hpp>
36 #include <sfx2/objsh.hxx>
37 #include <tools/urlobj.hxx>
38 #include <sal/log.hxx>
39 #include <rtl/character.hxx>
40 #include <unotools/charclass.hxx>
44 const ScAddress::Details
ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO
, 0, 0 );
46 ScAddress::Details::Details ( const ScDocument
& rDoc
,
47 const ScAddress
& rAddr
) :
48 eConv( rDoc
.GetAddressConvention() ),
55 const sal_Unicode
* parseQuotedNameWithBuffer( const sal_Unicode
* pStart
, const sal_Unicode
* p
, OUString
& rName
)
57 // The current character must be on the 2nd quote.
59 // Push all the characters up to the current, but skip the very first
60 // character which is the opening quote.
61 OUStringBuffer
aBuf(std::u16string_view(pStart
+1, p
-pStart
-1));
63 ++p
; // Skip the 2nd quote.
64 sal_Unicode cPrev
= 0;
71 // double single-quote equals one single quote.
77 else if (cPrev
== '\'')
79 // We are past the closing quote. We're done!
80 rName
= aBuf
.makeStringAndClear();
92 * Parse from the opening single quote to the closing single quote. Inside
93 * the quotes, a single quote character is encoded by double single-quote
96 * @param p pointer to the first character to begin parsing.
97 * @param rName (reference) parsed name within the quotes. If the name is
98 * empty, either the parsing failed or it's an empty quote.
100 * @return pointer to the character immediately after the closing single
103 const sal_Unicode
* parseQuotedName( const sal_Unicode
* p
, OUString
& rName
)
108 const sal_Unicode
* pStart
= p
;
109 sal_Unicode cPrev
= 0;
116 // double single-quote equals one single quote.
117 return parseQuotedNameWithBuffer(pStart
, p
, rName
);
120 else if (cPrev
== '\'')
122 // We are past the closing quote. We're done! Skip the opening
123 // and closing quotes.
124 rName
= OUString(pStart
+1, p
- pStart
-2);
137 static sal_Int64
sal_Unicode_strtol ( const sal_Unicode
* p
, const sal_Unicode
** pEnd
)
150 const sal_Int64 cutoff
= is_neg
? std::numeric_limits
<sal_Int64
>::min() / 10
151 : -(std::numeric_limits
<sal_Int64
>::max() / 10);
152 const int cutlim
= is_neg
? -(std::numeric_limits
<sal_Int64
>::min() % 10)
153 : std::numeric_limits
<sal_Int64
>::max() % 10;
155 while (rtl::isAsciiDigit( *p
))
158 if (accum
< cutoff
|| (accum
== cutoff
&& val
> cutlim
))
163 accum
= accum
* 10 - val
;
168 return is_neg
? accum
: -accum
;
171 static const sal_Unicode
* lcl_eatWhiteSpace( const sal_Unicode
* p
)
181 // Compare ignore case ASCII.
182 static bool lcl_isString( const sal_Unicode
* p1
, const OUString
& rStr
)
184 const size_t n
= rStr
.getLength();
187 const sal_Unicode
* p2
= rStr
.getStr();
188 for (size_t i
=0; i
<n
; ++i
)
194 sal_Unicode c1
= p1
[i
];
195 if ('A' <= c1
&& c1
<= 'Z')
197 if (c1
< 'a' || 'z' < c1
)
198 return false; // not a letter
200 sal_Unicode c2
= p2
[i
];
201 if ('A' <= c2
&& c2
<= 'Z')
203 if (c2
< 'a' || 'z' < c2
)
204 return false; // not a letter to match
207 return false; // lower case doesn't match either
213 /** Determines the number of sheets an external reference spans and sets
214 rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
215 bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
216 cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
217 is set to rEndTabName.
218 @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
219 result in the identical file ID. Else <TRUE/>.
221 static bool lcl_ScRange_External_TabSpan(
224 ScAddress::ExternalInfo
* pExtInfo
,
225 const OUString
& rExternDocName
,
226 const OUString
& rStartTabName
,
227 const OUString
& rEndTabName
,
228 const ScDocument
& rDoc
)
230 if (rExternDocName
.isEmpty())
231 return !pExtInfo
|| !pExtInfo
->mbExternal
;
233 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
234 if (pRefMgr
->isOwnDocument( rExternDocName
))
236 // This is an internal document. Get the sheet positions from the
237 // ScDocument instance.
238 if (!rStartTabName
.isEmpty())
241 if (rDoc
.GetTable(rStartTabName
, nTab
))
242 rRange
.aStart
.SetTab(nTab
);
245 if (!rEndTabName
.isEmpty())
248 if (rDoc
.GetTable(rEndTabName
, nTab
))
249 rRange
.aEnd
.SetTab(nTab
);
251 return !pExtInfo
|| !pExtInfo
->mbExternal
;
254 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId( rExternDocName
);
258 if (pExtInfo
->mbExternal
)
260 if (pExtInfo
->mnFileId
!= nFileId
)
265 pExtInfo
->mbExternal
= true;
266 pExtInfo
->maTabName
= rStartTabName
;
267 pExtInfo
->mnFileId
= nFileId
;
271 if (rEndTabName
.isEmpty() || rStartTabName
== rEndTabName
)
273 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab());
277 SCTAB nSpan
= pRefMgr
->getCachedTabSpan( nFileId
, rStartTabName
, rEndTabName
);
279 rFlags
&= ~ScRefFlags(ScRefFlags::TAB_VALID
| ScRefFlags::TAB2_VALID
);
281 rFlags
&= ~ScRefFlags::TAB2_VALID
;
283 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab() + nSpan
- 1);
286 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab() - nSpan
- 1);
288 pExtInfo
->maTabName
= rEndTabName
;
293 /** Returns NULL if the string should be a sheet name, but is invalid.
294 Returns a pointer to the first character after the sheet name, if there was
295 any, else pointer to start.
296 @param pMsoxlQuoteStop
297 Starting _within_ a quoted name, but still may be 3D; quoted name stops
300 static const sal_Unicode
* lcl_XL_ParseSheetRef( const sal_Unicode
* start
,
301 OUString
& rExternTabName
,
303 const sal_Unicode
* pMsoxlQuoteStop
,
304 const OUString
* pErrRef
)
307 const sal_Unicode
*p
= start
;
309 // XL only seems to use single quotes for sheet names.
312 const sal_Unicode
* pCurrentStart
= p
;
313 while (p
< pMsoxlQuoteStop
)
317 // We pre-analyzed the quoting, no checks needed here.
320 aTabName
+= std::u16string_view( pCurrentStart
,
321 sal::static_int_cast
<sal_Int32
>( p
- pCurrentStart
));
332 if (pCurrentStart
< p
)
333 aTabName
+= std::u16string_view( pCurrentStart
, sal::static_int_cast
<sal_Int32
>( p
- pCurrentStart
));
334 if (aTabName
.isEmpty())
336 if (p
== pMsoxlQuoteStop
)
337 ++p
; // position on ! of ...'!...
338 if( *p
!= '!' && ( !bAllow3D
|| *p
!= ':' ) )
339 return (!bAllow3D
&& *p
== ':') ? p
: start
;
343 p
= parseQuotedName(p
, aTabName
);
344 if (aTabName
.isEmpty())
347 else if (pErrRef
&& lcl_isString( p
, *pErrRef
) && p
[pErrRef
->getLength()] == '!')
349 p
+= pErrRef
->getLength(); // position after "#REF!" on '!'
350 // XXX NOTE: caller has to check the name and that it wasn't quoted.
355 bool only_digits
= true;
362 * Some names starting with digits are actually valid, but
363 * unparse quoted. Things are quite tricky: most sheet names
364 * starting with a digit are ok, but not those starting with
365 * "[0-9]*\." or "[0-9]+[eE]".
374 const sal_Unicode uc
= *p
;
375 if( rtl::isAsciiAlpha( uc
) || uc
== '_' )
377 if( only_digits
&& p
!= start
&&
378 (uc
== 'e' || uc
== 'E' ) )
386 else if( rtl::isAsciiDigit( uc
))
392 if( only_digits
) // Valid, except after only digits.
401 // non ASCII character is allowed.
408 if( *p
!= '!' && ( !bAllow3D
|| *p
!= ':' ) )
409 return (!bAllow3D
&& *p
== ':') ? p
: start
;
411 aTabName
+= std::u16string_view( start
, sal::static_int_cast
<sal_Int32
>( p
- start
) );
414 rExternTabName
= aTabName
;
418 /** Tries to obtain the external document index and replace by actual document
422 Contains the default pointer the caller would return if this method
423 returns FALSE, may be replaced by NULL for type or data errors.
425 @returns FALSE only if the input name is numeric and not within the index
426 sequence, or the link type cannot be determined or data mismatch. Returns
427 TRUE in all other cases, also when there is no index sequence or the input
430 static bool lcl_XL_getExternalDoc( const sal_Unicode
** ppErrRet
, OUString
& rExternDocName
,
431 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
433 // 1-based, sequence starts with an empty element.
434 if (pExternalLinks
&& pExternalLinks
->hasElements())
436 // A numeric "document name" is an index into the sequence.
437 if (CharClass::isAsciiNumeric( rExternDocName
))
439 sal_Int32 i
= rExternDocName
.toInt32();
440 if (i
< 0 || i
>= pExternalLinks
->getLength())
441 return false; // with default *ppErrRet
442 const sheet::ExternalLinkInfo
& rInfo
= (*pExternalLinks
)[i
];
445 case sheet::ExternalLinkType::DOCUMENT
:
448 if (!(rInfo
.Data
>>= aStr
))
452 "Data type mismatch for ExternalLinkInfo "
457 rExternDocName
= aStr
;
460 case sheet::ExternalLinkType::SELF
:
462 case sheet::ExternalLinkType::SPECIAL
:
463 // silently return nothing (do not assert), caller has to handle this
469 "unhandled ExternalLinkType " << rInfo
.Type
470 << " for index " << i
);
479 const sal_Unicode
* ScRange::Parse_XL_Header(
480 const sal_Unicode
* p
,
481 const ScDocument
& rDoc
,
482 OUString
& rExternDocName
,
483 OUString
& rStartTabName
,
484 OUString
& rEndTabName
,
486 bool bOnlyAcceptSingle
,
487 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
,
488 const OUString
* pErrRef
)
490 const sal_Unicode
* startTabs
, *start
= p
;
491 ScRefFlags nSaveFlags
= nFlags
;
493 // Is this an external reference ?
494 rStartTabName
.clear();
496 rExternDocName
.clear();
497 const sal_Unicode
* pMsoxlQuoteStop
= nullptr;
501 // Only single quotes are correct, and a double single quote escapes a
502 // single quote text inside the quoted text.
505 p
= parseQuotedName(p
, rExternDocName
);
506 if (*p
!= ']' || rExternDocName
.isEmpty())
508 rExternDocName
.clear();
514 // non-quoted file name.
515 p
= ScGlobal::UnicodeStrChr( start
+1, ']' );
518 rExternDocName
+= std::u16string_view( start
+1, sal::static_int_cast
<sal_Int32
>( p
-(start
+1) ) );
522 const sal_Unicode
* pErrRet
= start
;
523 if (!lcl_XL_getExternalDoc( &pErrRet
, rExternDocName
, pExternalLinks
))
526 rExternDocName
= ScGlobal::GetAbsDocName(rExternDocName
, rDoc
.GetDocumentShell());
530 // Sickness in Excel's ODF msoxl namespace:
531 // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or
532 // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
533 // But, 'Sheet1'!B3 would also be a valid!
534 // Excel does not allow [ and ] characters in sheet names though.
535 // But, more sickness comes with MOOXML as there may be
536 // '[1]Sheet 4'!$A$1 where [1] is the external doc's index.
537 p
= parseQuotedName(p
, rExternDocName
);
540 rExternDocName
.clear();
543 if (!rExternDocName
.isEmpty())
545 sal_Int32 nOpen
= rExternDocName
.indexOf( '[');
547 rExternDocName
.clear();
550 sal_Int32 nClose
= rExternDocName
.indexOf( ']', nOpen
+1);
552 rExternDocName
.clear();
555 rExternDocName
= rExternDocName
.copy(0, nClose
);
556 rExternDocName
= rExternDocName
.replaceAt( nOpen
, 1, u
"");
557 pMsoxlQuoteStop
= p
- 1; // the ' quote char
558 // There may be embedded escaped quotes, just matching the
559 // doc name's length may not work.
560 for (p
= start
; *p
!= '['; ++p
)
562 for ( ; *p
!= ']'; ++p
)
566 // Handle '[1]Sheet 4'!$A$1
569 const sal_Unicode
* pErrRet
= start
;
570 if (!lcl_XL_getExternalDoc( &pErrRet
, rExternDocName
, pExternalLinks
))
576 if (rExternDocName
.isEmpty())
581 p
= lcl_XL_ParseSheetRef( p
, rStartTabName
, !bOnlyAcceptSingle
, pMsoxlQuoteStop
, pErrRef
);
583 return start
; // invalid tab
584 if (bOnlyAcceptSingle
&& *p
== ':')
585 return nullptr; // 3D
586 const sal_Unicode
* startEndTabs
= nullptr;
589 nFlags
|= ScRefFlags::TAB_VALID
| ScRefFlags::TAB_3D
| ScRefFlags::TAB_ABS
;
590 if( *p
== ':' ) // 3d ref
592 startEndTabs
= p
+ 1;
593 p
= lcl_XL_ParseSheetRef( startEndTabs
, rEndTabName
, false, pMsoxlQuoteStop
, pErrRef
);
597 return start
; // invalid tab
599 nFlags
|= ScRefFlags::TAB2_VALID
| ScRefFlags::TAB2_3D
| ScRefFlags::TAB2_ABS
;
603 // If only one sheet is given, the full reference is still valid,
604 // only the second 3D flag is not set.
605 nFlags
|= ScRefFlags::TAB2_VALID
| ScRefFlags::TAB2_ABS
;
606 aEnd
.SetTab( aStart
.Tab() );
612 return start
; // syntax error
615 p
= lcl_eatWhiteSpace( p
);
619 nFlags
|= ScRefFlags::TAB_VALID
| ScRefFlags::TAB2_VALID
;
620 // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
623 if (!rExternDocName
.isEmpty())
625 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
626 pRefMgr
->convertToAbsName(rExternDocName
);
630 // Internal reference.
631 if (rStartTabName
.isEmpty())
638 if ((pErrRef
&& *startTabs
!= '\'' && rStartTabName
== *pErrRef
) || !rDoc
.GetTable(rStartTabName
, nTab
))
640 // invalid table name.
641 nFlags
&= ~ScRefFlags::TAB_VALID
;
648 if (!rEndTabName
.isEmpty())
650 if ((pErrRef
&& startEndTabs
&& *startEndTabs
!= '\'' && rEndTabName
== *pErrRef
) ||
651 !rDoc
.GetTable(rEndTabName
, nTab
))
653 // invalid table name.
654 nFlags
&= ~ScRefFlags::TAB2_VALID
;
664 static const sal_Unicode
* lcl_r1c1_get_col( const ScSheetLimits
& rSheetLimits
,
665 const sal_Unicode
* p
,
666 const ScAddress::Details
& rDetails
,
667 ScAddress
* pAddr
, ScRefFlags
* nFlags
)
669 const sal_Unicode
*pEnd
;
677 isRelative
= *p
== '[';
680 n
= sal_Unicode_strtol( p
, &pEnd
);
681 if( nullptr == pEnd
)
684 if( p
== pEnd
) // C is a relative ref with offset 0
690 else if( isRelative
)
699 *nFlags
|= ScRefFlags::COL_ABS
;
703 if( n
< 0 || n
>= rSheetLimits
.GetMaxColCount())
705 pAddr
->SetCol( static_cast<SCCOL
>( n
) );
706 *nFlags
|= ScRefFlags::COL_VALID
;
711 static const sal_Unicode
* lcl_r1c1_get_row(
712 const ScSheetLimits
& rSheetLimits
,
713 const sal_Unicode
* p
,
714 const ScAddress::Details
& rDetails
,
715 ScAddress
* pAddr
, ScRefFlags
* nFlags
)
717 const sal_Unicode
*pEnd
;
724 isRelative
= *p
== '[';
727 sal_Int64 n
= sal_Unicode_strtol( p
, &pEnd
);
728 if( nullptr == pEnd
)
731 if( p
== pEnd
) // R is a relative ref with offset 0
737 else if( isRelative
)
746 *nFlags
|= ScRefFlags::ROW_ABS
;
750 if( n
< 0 || n
>= rSheetLimits
.GetMaxRowCount() )
752 pAddr
->SetRow( static_cast<SCROW
>( n
) );
753 *nFlags
|= ScRefFlags::ROW_VALID
;
758 static ScRefFlags
lcl_ScRange_Parse_XL_R1C1( ScRange
& r
,
759 const sal_Unicode
* p
,
760 const ScDocument
& rDoc
,
761 const ScAddress::Details
& rDetails
,
762 bool bOnlyAcceptSingle
,
763 ScAddress::ExternalInfo
* pExtInfo
,
764 sal_Int32
* pSheetEndPos
)
766 const sal_Unicode
* const pStart
= p
;
769 const sal_Unicode
* pTmp
= nullptr;
770 OUString aExternDocName
, aStartTabName
, aEndTabName
;
771 ScRefFlags nFlags
= ScRefFlags::VALID
| ScRefFlags::TAB_VALID
;
772 // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
773 ScRefFlags nFlags2
= ScRefFlags::TAB_VALID
;
775 p
= r
.Parse_XL_Header( p
, rDoc
, aExternDocName
, aStartTabName
,
776 aEndTabName
, nFlags
, bOnlyAcceptSingle
);
778 ScRefFlags nBailOutFlags
= ScRefFlags::ZERO
;
779 if (pSheetEndPos
&& pStart
< p
&& (nFlags
& ScRefFlags::TAB_VALID
) && (nFlags
& ScRefFlags::TAB_3D
))
781 *pSheetEndPos
= p
- pStart
;
782 nBailOutFlags
= ScRefFlags::TAB_VALID
| ScRefFlags::TAB_3D
;
785 if (!aExternDocName
.isEmpty())
786 lcl_ScRange_External_TabSpan( r
, nFlags
, pExtInfo
, aExternDocName
,
787 aStartTabName
, aEndTabName
, rDoc
);
790 return ScRefFlags::ZERO
;
792 if( *p
== 'R' || *p
== 'r' )
794 if( nullptr == (p
= lcl_r1c1_get_row( rDoc
.GetSheetLimits(), p
, rDetails
, &r
.aStart
, &nFlags
)) )
795 return nBailOutFlags
;
797 if( *p
!= 'C' && *p
!= 'c' ) // full row R#
799 if( p
[0] != ':' || (p
[1] != 'R' && p
[1] != 'r' ) ||
800 nullptr == (pTmp
= lcl_r1c1_get_row( rDoc
.GetSheetLimits(), p
+1, rDetails
, &r
.aEnd
, &nFlags2
)))
802 // Only the initial row number is given, or the second row
803 // number is invalid. Fallback to just the initial R
804 applyStartToEndFlags(nFlags
);
805 r
.aEnd
.SetRow( r
.aStart
.Row() );
807 else // pTmp != nullptr
809 // Full row range successfully parsed.
810 applyStartToEndFlags(nFlags
, nFlags2
);
816 // any trailing invalid character must invalidate the whole address.
817 nFlags
&= ~ScRefFlags(ScRefFlags::VALID
| ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
818 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
);
823 ScRefFlags::COL_VALID
| ScRefFlags::COL2_VALID
|
824 ScRefFlags::COL_ABS
| ScRefFlags::COL2_ABS
;
825 r
.aStart
.SetCol( 0 );
826 r
.aEnd
.SetCol( rDoc
.MaxCol() );
828 return bOnlyAcceptSingle
? ScRefFlags::ZERO
: nFlags
;
830 else if( nullptr == (p
= lcl_r1c1_get_col( rDoc
.GetSheetLimits(), p
, rDetails
, &r
.aStart
, &nFlags
)))
832 return ScRefFlags::ZERO
;
836 (p
[1] != 'R' && p
[1] != 'r') ||
837 nullptr == (pTmp
= lcl_r1c1_get_row( rDoc
.GetSheetLimits(), p
+1, rDetails
, &r
.aEnd
, &nFlags2
)) ||
838 (*pTmp
!= 'C' && *pTmp
!= 'c') ||
839 nullptr == (pTmp
= lcl_r1c1_get_col( rDoc
.GetSheetLimits(), pTmp
, rDetails
, &r
.aEnd
, &nFlags2
)))
841 // single cell reference
845 // any trailing invalid character must invalidate the whole address.
846 nFlags
&= ~ScRefFlags(ScRefFlags::VALID
| ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
);
850 return bOnlyAcceptSingle
? nFlags
: ScRefFlags::ZERO
;
859 // any trailing invalid character must invalidate the whole range.
860 nFlags
&= ~ScRefFlags(ScRefFlags::VALID
| ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
861 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
);
865 applyStartToEndFlags(nFlags
, nFlags2
);
866 return bOnlyAcceptSingle
? ScRefFlags::ZERO
: nFlags
;
868 else if( *p
== 'C' || *p
== 'c' ) // full col C#
870 if( nullptr == (p
= lcl_r1c1_get_col( rDoc
.GetSheetLimits(), p
, rDetails
, &r
.aStart
, &nFlags
)))
871 return nBailOutFlags
;
873 if( p
[0] != ':' || (p
[1] != 'C' && p
[1] != 'c') ||
874 nullptr == (pTmp
= lcl_r1c1_get_col( rDoc
.GetSheetLimits(), p
+1, rDetails
, &r
.aEnd
, &nFlags2
)))
875 { // Fallback to just the initial C
876 applyStartToEndFlags(nFlags
);
877 r
.aEnd
.SetCol( r
.aStart
.Col() );
879 else // pTmp != nullptr
881 applyStartToEndFlags(nFlags
, nFlags2
);
887 // any trailing invalid character must invalidate the whole address.
888 nFlags
&= ~ScRefFlags(ScRefFlags::VALID
| ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
889 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
);
894 ScRefFlags::ROW_VALID
| ScRefFlags::ROW2_VALID
|
895 ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
;
896 r
.aStart
.SetRow( 0 );
897 r
.aEnd
.SetRow( rDoc
.MaxRow() );
899 return bOnlyAcceptSingle
? ScRefFlags::ZERO
: nFlags
;
902 return nBailOutFlags
;
905 static const sal_Unicode
* lcl_a1_get_col( const ScDocument
& rDoc
,
906 const sal_Unicode
* p
,
909 const OUString
* pErrRef
)
913 *nFlags
|= ScRefFlags::COL_ABS
;
917 if (pErrRef
&& lcl_isString( p
, *pErrRef
))
919 p
+= pErrRef
->getLength();
920 *nFlags
&= ~ScRefFlags::COL_VALID
;
925 if( !rtl::isAsciiAlpha( *p
) )
928 sal_Int64 nCol
= rtl::toAsciiUpperCase( *p
++ ) - 'A';
929 const SCCOL nMaxCol
= rDoc
.MaxCol();
930 while (nCol
<= nMaxCol
&& rtl::isAsciiAlpha(*p
))
931 nCol
= ((nCol
+ 1) * 26) + rtl::toAsciiUpperCase( *p
++ ) - 'A';
932 if( nCol
> nMaxCol
|| nCol
< 0 || rtl::isAsciiAlpha( *p
) )
935 *nFlags
|= ScRefFlags::COL_VALID
;
936 pAddr
->SetCol( sal::static_int_cast
<SCCOL
>( nCol
));
941 static const sal_Unicode
* lcl_a1_get_row( const ScDocument
& rDoc
,
942 const sal_Unicode
* p
,
945 const OUString
* pErrRef
)
947 const sal_Unicode
*pEnd
;
951 *nFlags
|= ScRefFlags::ROW_ABS
;
955 if (pErrRef
&& lcl_isString( p
, *pErrRef
))
957 p
+= pErrRef
->getLength();
958 *nFlags
&= ~ScRefFlags::ROW_VALID
;
963 sal_Int64 n
= sal_Unicode_strtol(p
, &pEnd
);
964 if (nullptr == pEnd
|| p
== pEnd
|| n
< 1)
967 if (n
> rDoc
.MaxRow())
970 *nFlags
|= ScRefFlags::ROW_VALID
;
971 pAddr
->SetRow( sal::static_int_cast
<SCROW
>(n
) );
976 /// B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ...
977 static bool isValidSingleton( ScRefFlags nFlags
, ScRefFlags nFlags2
)
979 bool bCols
= (nFlags
& ScRefFlags::COL_VALID
) && ((nFlags
& ScRefFlags::COL2_VALID
) || (nFlags2
& ScRefFlags::COL_VALID
));
980 bool bRows
= (nFlags
& ScRefFlags::ROW_VALID
) && ((nFlags
& ScRefFlags::ROW2_VALID
) || (nFlags2
& ScRefFlags::ROW_VALID
));
981 return bCols
!= bRows
;
984 static ScRefFlags
lcl_ScRange_Parse_XL_A1( ScRange
& r
,
985 const sal_Unicode
* p
,
986 const ScDocument
& rDoc
,
987 bool bOnlyAcceptSingle
,
988 ScAddress::ExternalInfo
* pExtInfo
,
989 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
,
990 sal_Int32
* pSheetEndPos
,
991 const OUString
* pErrRef
)
993 const sal_Unicode
* const pStart
= p
;
996 const sal_Unicode
* tmp1
, *tmp2
;
997 OUString aExternDocName
, aStartTabName
, aEndTabName
; // for external link table
998 ScRefFlags nFlags
= ScRefFlags::VALID
| ScRefFlags::TAB_VALID
, nFlags2
= ScRefFlags::TAB_VALID
;
1000 p
= r
.Parse_XL_Header( p
, rDoc
, aExternDocName
, aStartTabName
,
1001 aEndTabName
, nFlags
, bOnlyAcceptSingle
, pExternalLinks
, pErrRef
);
1003 ScRefFlags nBailOutFlags
= ScRefFlags::ZERO
;
1004 if (pSheetEndPos
&& pStart
< p
&& (nFlags
& ScRefFlags::TAB_VALID
) && (nFlags
& ScRefFlags::TAB_3D
))
1006 *pSheetEndPos
= p
- pStart
;
1007 nBailOutFlags
= ScRefFlags::TAB_VALID
| ScRefFlags::TAB_3D
;
1010 if (!aExternDocName
.isEmpty())
1011 lcl_ScRange_External_TabSpan( r
, nFlags
, pExtInfo
, aExternDocName
,
1012 aStartTabName
, aEndTabName
, rDoc
);
1015 return nBailOutFlags
;
1017 tmp1
= lcl_a1_get_col( rDoc
, p
, &r
.aStart
, &nFlags
, pErrRef
);
1018 if( tmp1
== nullptr ) // Is it a row only reference 3:5
1020 if( bOnlyAcceptSingle
) // by definition full row refs are ranges
1021 return nBailOutFlags
;
1023 tmp1
= lcl_a1_get_row( rDoc
, p
, &r
.aStart
, &nFlags
, pErrRef
);
1025 tmp1
= lcl_eatWhiteSpace( tmp1
);
1026 if( !tmp1
|| *tmp1
++ != ':' ) // Even a singleton requires ':' (eg 2:2)
1027 return nBailOutFlags
;
1029 tmp1
= lcl_eatWhiteSpace( tmp1
);
1030 tmp2
= lcl_a1_get_row( rDoc
, tmp1
, &r
.aEnd
, &nFlags2
, pErrRef
);
1031 if( !tmp2
|| *tmp2
!= 0 ) // Must have fully parsed a singleton.
1032 return nBailOutFlags
;
1034 r
.aStart
.SetCol( 0 ); r
.aEnd
.SetCol( rDoc
.MaxCol() );
1036 ScRefFlags::COL_VALID
| ScRefFlags::COL2_VALID
|
1037 ScRefFlags::COL_ABS
| ScRefFlags::COL2_ABS
;
1038 applyStartToEndFlags(nFlags
, nFlags2
);
1042 tmp2
= lcl_a1_get_row( rDoc
, tmp1
, &r
.aStart
, &nFlags
, pErrRef
);
1043 if( tmp2
== nullptr ) // check for col only reference F:H
1045 if( bOnlyAcceptSingle
) // by definition full col refs are ranges
1046 return nBailOutFlags
;
1048 tmp1
= lcl_eatWhiteSpace( tmp1
);
1049 if( *tmp1
++ != ':' ) // Even a singleton requires ':' (eg F:F)
1050 return nBailOutFlags
;
1052 tmp1
= lcl_eatWhiteSpace( tmp1
);
1053 tmp2
= lcl_a1_get_col( rDoc
, tmp1
, &r
.aEnd
, &nFlags2
, pErrRef
);
1054 if( !tmp2
|| *tmp2
!= 0 ) // Must have fully parsed a singleton.
1055 return nBailOutFlags
;
1057 r
.aStart
.SetRow( 0 ); r
.aEnd
.SetRow( rDoc
.MaxRow() );
1059 ScRefFlags::ROW_VALID
| ScRefFlags::ROW2_VALID
|
1060 ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
;
1061 applyStartToEndFlags(nFlags
, nFlags2
);
1065 // prepare as if it's a singleton, in case we want to fall back */
1066 r
.aEnd
.SetCol( r
.aStart
.Col() );
1067 r
.aEnd
.SetRow( r
.aStart
.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header()
1069 if ( bOnlyAcceptSingle
)
1075 // any trailing invalid character must invalidate the address.
1076 nFlags
&= ~ScRefFlags(ScRefFlags::VALID
| ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
);
1081 tmp2
= lcl_eatWhiteSpace( tmp2
);
1084 // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
1085 // not. Any trailing invalid character invalidates the range.
1086 if (*tmp2
== 0 && (nFlags
& ScRefFlags::TAB2_3D
))
1088 if (nFlags
& ScRefFlags::COL_ABS
)
1089 nFlags
|= ScRefFlags::COL2_ABS
;
1090 if (nFlags
& ScRefFlags::ROW_ABS
)
1091 nFlags
|= ScRefFlags::ROW2_ABS
;
1094 nFlags
&= ~ScRefFlags(ScRefFlags::VALID
|
1095 ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
1096 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
);
1100 p
= lcl_eatWhiteSpace( tmp2
+1 ); // after ':'
1101 tmp1
= lcl_a1_get_col( rDoc
, p
, &r
.aEnd
, &nFlags2
, pErrRef
);
1102 if( !tmp1
&& aEndTabName
.isEmpty() ) // Probably the aEndTabName was specified after the first range
1104 p
= lcl_XL_ParseSheetRef( p
, aEndTabName
, false, nullptr, pErrRef
);
1108 if( !aEndTabName
.isEmpty() && rDoc
.GetTable( aEndTabName
, nTab
) )
1110 r
.aEnd
.SetTab( nTab
);
1111 nFlags
|= ScRefFlags::TAB2_VALID
| ScRefFlags::TAB2_3D
| ScRefFlags::TAB2_ABS
;
1113 if (*p
== '!' || *p
== ':')
1114 p
= lcl_eatWhiteSpace( p
+1 );
1115 tmp1
= lcl_a1_get_col( rDoc
, p
, &r
.aEnd
, &nFlags2
, pErrRef
);
1118 if( !tmp1
) // strange, but maybe valid singleton
1119 return isValidSingleton( nFlags
, nFlags2
) ? nFlags
: (nFlags
& ~ScRefFlags::VALID
);
1121 tmp2
= lcl_a1_get_row( rDoc
, tmp1
, &r
.aEnd
, &nFlags2
, pErrRef
);
1122 if( !tmp2
) // strange, but maybe valid singleton
1123 return isValidSingleton( nFlags
, nFlags2
) ? nFlags
: (nFlags
& ~ScRefFlags::VALID
);
1127 // any trailing invalid character must invalidate the range.
1128 nFlags
&= ~ScRefFlags(ScRefFlags::VALID
| ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
1129 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
);
1133 applyStartToEndFlags(nFlags
, nFlags2
);
1138 @param p pointer to null-terminated sal_Unicode string
1139 @param rRawRes returns ScRefFlags::... flags without the final check for full
1140 validity that is applied to the return value, with which
1141 two addresses that form a column or row singleton range,
1142 e.g. A:A or 1:1, can be detected. Used in
1143 lcl_ScRange_Parse_OOo().
1144 @param pRange pointer to range where rAddr effectively is *pRange->aEnd,
1145 used in conjunction with pExtInfo to determine the tab span
1148 static ScRefFlags
lcl_ScAddress_Parse_OOo( const sal_Unicode
* p
, const ScDocument
& rDoc
, ScAddress
& rAddr
,
1149 ScRefFlags
& rRawRes
,
1150 ScAddress::ExternalInfo
* pExtInfo
,
1152 sal_Int32
* pSheetEndPos
,
1153 const OUString
* pErrRef
)
1155 const sal_Unicode
* const pStart
= p
;
1158 ScRefFlags nRes
= ScRefFlags::ZERO
;
1159 rRawRes
= ScRefFlags::ZERO
;
1160 OUString aDocName
; // the pure Document Name
1162 bool bExtDoc
= false;
1163 bool bExtDocInherited
= false;
1165 // Lets see if this is a reference to something in an external file. A
1166 // document name is always quoted and has a trailing #.
1170 p
= parseQuotedName(p
, aTmp
);
1172 if (*p
++ == SC_COMPILER_FILE_TAB_SEP
)
1175 // This is not a document name. Perhaps a quoted relative table
1179 else if (pExtInfo
&& pExtInfo
->mbExternal
)
1181 // This is an external reference.
1182 bExtDoc
= bExtDocInherited
= true;
1188 ScRefFlags nBailOutFlags
= ScRefFlags::ZERO
;
1189 ScRefFlags nBits
= ScRefFlags::TAB_VALID
;
1190 const sal_Unicode
* q
;
1191 if ( ScGlobal::FindUnquoted( p
, '.') )
1193 nRes
|= ScRefFlags::TAB_3D
;
1195 nRes
|= ScRefFlags::TAB_ABS
;
1198 nRes
|= ScRefFlags::TAB_ABS
;
1202 if (pErrRef
&& lcl_isString( p
, *pErrRef
) && p
[pErrRef
->getLength()] == '.')
1204 // #REF! particle of an invalidated reference plus sheet separator.
1205 p
+= pErrRef
->getLength() + 1;
1206 nRes
&= ~ScRefFlags::TAB_VALID
;
1213 // Tokens that start at ' can have anything in them until a final
1214 // ' but '' marks an escaped '. We've earlier guaranteed that a
1215 // string containing '' will be surrounded by '.
1216 p
= parseQuotedName(p
, aTab
);
1220 OUStringBuffer aTabAcc
;
1233 aTab
= aTabAcc
.makeStringAndClear();
1236 nBits
= ScRefFlags::ZERO
;
1240 if (!bExtDoc
&& !rDoc
.GetTable( aTab
, nTab
))
1241 nBits
= ScRefFlags::ZERO
;
1245 if (pSheetEndPos
&& (nBits
& ScRefFlags::TAB_VALID
))
1247 *pSheetEndPos
= p
- pStart
;
1248 nBailOutFlags
= ScRefFlags::TAB_VALID
| ScRefFlags::TAB_3D
;
1253 if (bExtDoc
&& !bExtDocInherited
)
1254 return nRes
; // After a document a sheet must follow.
1262 nBits
= ScRefFlags::COL_VALID
;
1265 nBits
|= ScRefFlags::COL_ABS
;
1269 if (pErrRef
&& lcl_isString( p
, *pErrRef
))
1271 // #REF! particle of an invalidated reference.
1272 p
+= pErrRef
->getLength();
1273 nBits
&= ~ScRefFlags::COL_VALID
;
1278 if (rtl::isAsciiAlpha( *p
))
1280 const SCCOL nMaxCol
= rDoc
.MaxCol();
1281 sal_Int64 n
= rtl::toAsciiUpperCase( *p
++ ) - 'A';
1282 while (n
< nMaxCol
&& rtl::isAsciiAlpha(*p
))
1283 n
= ((n
+ 1) * 26) + rtl::toAsciiUpperCase( *p
++ ) - 'A';
1284 if (n
> nMaxCol
|| n
< 0 || (*p
&& *p
!= '$' && !rtl::isAsciiDigit( *p
) &&
1285 (!pErrRef
|| !lcl_isString( p
, *pErrRef
))))
1286 nBits
= ScRefFlags::ZERO
;
1288 nCol
= sal::static_int_cast
<SCCOL
>( n
);
1291 nBits
= ScRefFlags::ZERO
;
1293 if( nBits
== ScRefFlags::ZERO
)
1302 nBits
= ScRefFlags::ROW_VALID
;
1305 nBits
|= ScRefFlags::ROW_ABS
;
1309 if (pErrRef
&& lcl_isString( p
, *pErrRef
))
1311 // #REF! particle of an invalidated reference.
1312 p
+= pErrRef
->getLength();
1313 // Clearing the ROW_VALID bit here is not possible because of the
1314 // check at the end whether only a valid column was detected in
1315 // which case all bits are cleared because it could be any other
1316 // name. Instead, set to an absolute invalid row value. This will
1317 // display a $#REF! instead of #REF! if the error value was
1318 // relative, but live with it.
1319 nBits
|= ScRefFlags::ROW_ABS
;
1324 if( !rtl::isAsciiDigit( *p
) )
1326 nBits
= ScRefFlags::ZERO
;
1331 sal_Int64 n
= rtl_ustr_toInt32( p
, 10 ) - 1;
1332 while (rtl::isAsciiDigit( *p
))
1334 const SCROW nMaxRow
= rDoc
.MaxRow();
1335 if( n
< 0 || n
> nMaxRow
)
1336 nBits
= ScRefFlags::ZERO
;
1337 nRow
= sal::static_int_cast
<SCROW
>(n
);
1339 if( nBits
== ScRefFlags::ZERO
)
1345 rAddr
.Set( nCol
, nRow
, nTab
);
1349 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
1351 // Need document name if inherited.
1352 if (bExtDocInherited
)
1354 // The FileId was created using the original file name, so
1355 // obtain that. Otherwise lcl_ScRange_External_TabSpan() would
1356 // retrieve a FileId for the real name and bail out if that
1357 // differed from pExtInfo->mnFileId, as is the case when
1358 // loading documents that refer external files relative to the
1359 // current own document but were saved from a different path
1361 const OUString
* pFileName
= pRefMgr
->getExternalFileName( pExtInfo
->mnFileId
, true);
1363 aDocName
= *pFileName
;
1365 nRes
= ScRefFlags::ZERO
;
1367 pRefMgr
->convertToAbsName(aDocName
);
1369 if ((!pExtInfo
|| !pExtInfo
->mbExternal
) && pRefMgr
->isOwnDocument(aDocName
))
1371 if (!rDoc
.GetTable( aTab
, nTab
))
1372 nRes
= ScRefFlags::ZERO
;
1375 rAddr
.SetTab( nTab
);
1376 nRes
|= ScRefFlags::TAB_VALID
;
1382 nRes
= ScRefFlags::ZERO
;
1385 if (!pExtInfo
->mbExternal
)
1387 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId(aDocName
);
1389 pExtInfo
->mbExternal
= true;
1390 pExtInfo
->maTabName
= aTab
;
1391 pExtInfo
->mnFileId
= nFileId
;
1393 if (pRefMgr
->getSingleRefToken(nFileId
, aTab
,
1394 ScAddress(nCol
, nRow
, 0), nullptr,
1397 rAddr
.SetTab( nTab
);
1398 nRes
|= ScRefFlags::TAB_VALID
;
1401 nRes
= ScRefFlags::ZERO
;
1405 // This is a call for the second part of the reference,
1406 // we must have the range to adapt tab span.
1408 nRes
= ScRefFlags::ZERO
;
1411 ScRefFlags nFlags
= nRes
| ScRefFlags::TAB2_VALID
;
1412 if (!lcl_ScRange_External_TabSpan( *pRange
, nFlags
,
1414 pExtInfo
->maTabName
, aTab
, rDoc
))
1415 nRes
&= ~ScRefFlags::TAB_VALID
;
1418 if (nFlags
& ScRefFlags::TAB2_VALID
)
1420 rAddr
.SetTab( pRange
->aEnd
.Tab());
1421 nRes
|= ScRefFlags::TAB_VALID
;
1424 nRes
&= ~ScRefFlags::TAB_VALID
;
1431 else if (bExtDoc
&& pExtInfo
&& !bExtDocInherited
&& !pExtInfo
->mbExternal
&& pSheetEndPos
)
1433 // Pass partial info up to caller, there may be an external name
1434 // following, and if after *pSheetEndPos it's external sheet-local.
1435 // For global names aTab is empty and *pSheetEndPos==0.
1436 pExtInfo
->mbExternal
= true;
1437 pExtInfo
->maTabName
= aTab
;
1438 pExtInfo
->mnFileId
= rDoc
.GetExternalRefManager()->getExternalFileId(aDocName
);
1443 if ( !(nRes
& ScRefFlags::ROW_VALID
) && (nRes
& ScRefFlags::COL_VALID
)
1444 && !( (nRes
& ScRefFlags::TAB_3D
) && (nRes
& ScRefFlags::TAB_VALID
)) )
1445 { // no Row, no Tab, but Col => DM (...), B (...) et al
1446 nRes
= ScRefFlags::ZERO
;
1450 ScRefFlags nMask
= nRes
& ( ScRefFlags::ROW_VALID
| ScRefFlags::COL_VALID
| ScRefFlags::TAB_VALID
);
1451 if( nMask
== ( ScRefFlags::ROW_VALID
| ScRefFlags::COL_VALID
| ScRefFlags::TAB_VALID
) )
1452 nRes
|= ScRefFlags::VALID
;
1455 nRes
= rRawRes
= nBailOutFlags
;
1459 static ScRefFlags
lcl_ScAddress_Parse ( const sal_Unicode
* p
, const ScDocument
& rDoc
, ScAddress
& rAddr
,
1460 const ScAddress::Details
& rDetails
,
1461 ScAddress::ExternalInfo
* pExtInfo
,
1462 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
,
1463 sal_Int32
* pSheetEndPos
,
1464 const OUString
* pErrRef
)
1467 return ScRefFlags::ZERO
;
1469 switch (rDetails
.eConv
)
1471 case formula::FormulaGrammar::CONV_XL_A1
:
1472 case formula::FormulaGrammar::CONV_XL_OOX
:
1474 ScRange rRange
= rAddr
;
1475 ScRefFlags nFlags
= lcl_ScRange_Parse_XL_A1(
1476 rRange
, p
, rDoc
, true, pExtInfo
,
1477 (rDetails
.eConv
== formula::FormulaGrammar::CONV_XL_OOX
? pExternalLinks
: nullptr),
1478 pSheetEndPos
, pErrRef
);
1479 rAddr
= rRange
.aStart
;
1482 case formula::FormulaGrammar::CONV_XL_R1C1
:
1484 ScRange rRange
= rAddr
;
1485 ScRefFlags nFlags
= lcl_ScRange_Parse_XL_R1C1( rRange
, p
, rDoc
, rDetails
, true, pExtInfo
, pSheetEndPos
);
1486 rAddr
= rRange
.aStart
;
1490 case formula::FormulaGrammar::CONV_OOO
:
1492 ScRefFlags nRawRes
= ScRefFlags::ZERO
;
1493 return lcl_ScAddress_Parse_OOo( p
, rDoc
, rAddr
, nRawRes
, pExtInfo
, nullptr, pSheetEndPos
, pErrRef
);
1498 bool ConvertSingleRef( const ScDocument
& rDoc
, const OUString
& rRefString
,
1499 SCTAB nDefTab
, ScRefAddress
& rRefAddress
,
1500 const ScAddress::Details
& rDetails
,
1501 ScAddress::ExternalInfo
* pExtInfo
/* = NULL */ )
1504 if (pExtInfo
|| (ScGlobal::FindUnquoted( rRefString
, SC_COMPILER_FILE_TAB_SEP
) == -1))
1506 ScAddress
aAddr( 0, 0, nDefTab
);
1507 ScRefFlags nRes
= aAddr
.Parse( rRefString
, rDoc
, rDetails
, pExtInfo
);
1508 if ( nRes
& ScRefFlags::VALID
)
1510 rRefAddress
.Set( aAddr
,
1511 ((nRes
& ScRefFlags::COL_ABS
) == ScRefFlags::ZERO
),
1512 ((nRes
& ScRefFlags::ROW_ABS
) == ScRefFlags::ZERO
),
1513 ((nRes
& ScRefFlags::TAB_ABS
) == ScRefFlags::ZERO
));
1520 bool ConvertDoubleRef( const ScDocument
& rDoc
, const OUString
& rRefString
, SCTAB nDefTab
,
1521 ScRefAddress
& rStartRefAddress
, ScRefAddress
& rEndRefAddress
,
1522 const ScAddress::Details
& rDetails
,
1523 ScAddress::ExternalInfo
* pExtInfo
/* = NULL */ )
1526 if (pExtInfo
|| (ScGlobal::FindUnquoted( rRefString
, SC_COMPILER_FILE_TAB_SEP
) == -1))
1528 ScRange
aRange( ScAddress( 0, 0, nDefTab
));
1529 ScRefFlags nRes
= aRange
.Parse( rRefString
, rDoc
, rDetails
, pExtInfo
);
1530 if ( nRes
& ScRefFlags::VALID
)
1532 rStartRefAddress
.Set( aRange
.aStart
,
1533 ((nRes
& ScRefFlags::COL_ABS
) == ScRefFlags::ZERO
),
1534 ((nRes
& ScRefFlags::ROW_ABS
) == ScRefFlags::ZERO
),
1535 ((nRes
& ScRefFlags::TAB_ABS
) == ScRefFlags::ZERO
));
1536 rEndRefAddress
.Set( aRange
.aEnd
,
1537 ((nRes
& ScRefFlags::COL2_ABS
) == ScRefFlags::ZERO
),
1538 ((nRes
& ScRefFlags::ROW2_ABS
) == ScRefFlags::ZERO
),
1539 ((nRes
& ScRefFlags::TAB2_ABS
) == ScRefFlags::ZERO
));
1546 ScRefFlags
ScAddress::Parse( const OUString
& r
, const ScDocument
& rDoc
,
1547 const Details
& rDetails
,
1548 ExternalInfo
* pExtInfo
,
1549 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
,
1550 sal_Int32
* pSheetEndPos
,
1551 const OUString
* pErrRef
)
1553 return lcl_ScAddress_Parse( r
.getStr(), rDoc
, *this, rDetails
, pExtInfo
, pExternalLinks
, pSheetEndPos
, pErrRef
);
1556 ScRange
ScRange::Intersection( const ScRange
& rOther
) const
1558 SCCOL nCol1
= std::max(aStart
.Col(), rOther
.aStart
.Col());
1559 SCCOL nCol2
= std::min(aEnd
.Col(), rOther
.aEnd
.Col());
1560 SCROW nRow1
= std::max(aStart
.Row(), rOther
.aStart
.Row());
1561 SCROW nRow2
= std::min(aEnd
.Row(), rOther
.aEnd
.Row());
1562 SCTAB nTab1
= std::max(aStart
.Tab(), rOther
.aStart
.Tab());
1563 SCTAB nTab2
= std::min(aEnd
.Tab(), rOther
.aEnd
.Tab());
1565 if (nCol1
> nCol2
|| nRow1
> nRow2
|| nTab1
> nTab2
)
1566 return ScRange(ScAddress::INITIALIZE_INVALID
);
1568 return ScRange(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
1571 void ScRange::ExtendTo( const ScRange
& rRange
)
1573 OSL_ENSURE( rRange
.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1576 aStart
.SetCol( std::min( aStart
.Col(), rRange
.aStart
.Col() ) );
1577 aStart
.SetRow( std::min( aStart
.Row(), rRange
.aStart
.Row() ) );
1578 aStart
.SetTab( std::min( aStart
.Tab(), rRange
.aStart
.Tab() ) );
1579 aEnd
.SetCol( std::max( aEnd
.Col(), rRange
.aEnd
.Col() ) );
1580 aEnd
.SetRow( std::max( aEnd
.Row(), rRange
.aEnd
.Row() ) );
1581 aEnd
.SetTab( std::max( aEnd
.Tab(), rRange
.aEnd
.Tab() ) );
1587 static ScRefFlags
lcl_ScRange_Parse_OOo( ScRange
& rRange
,
1589 const ScDocument
& rDoc
,
1590 ScAddress::ExternalInfo
* pExtInfo
,
1591 const OUString
* pErrRef
)
1593 ScRefFlags nRes1
= ScRefFlags::ZERO
, nRes2
= ScRefFlags::ZERO
;
1594 sal_Int32 nPos
= ScGlobal::FindUnquoted( r
, ':');
1597 OUStringBuffer
aTmp(r
);
1599 const sal_Unicode
* p
= aTmp
.getStr();
1600 ScRefFlags nRawRes1
= ScRefFlags::ZERO
;
1601 nRes1
= lcl_ScAddress_Parse_OOo( p
, rDoc
, rRange
.aStart
, nRawRes1
, pExtInfo
, nullptr, nullptr, pErrRef
);
1602 if ((nRes1
!= ScRefFlags::ZERO
) ||
1603 ((nRawRes1
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
)) &&
1604 (nRawRes1
& ScRefFlags::TAB_VALID
)))
1606 rRange
.aEnd
= rRange
.aStart
; // sheet must be initialized identical to first sheet
1607 ScRefFlags nRawRes2
= ScRefFlags::ZERO
;
1608 nRes2
= lcl_ScAddress_Parse_OOo( p
+ nPos
+ 1, rDoc
, rRange
.aEnd
, nRawRes2
,
1609 pExtInfo
, &rRange
, nullptr, pErrRef
);
1610 if (!((nRes1
& ScRefFlags::VALID
) && (nRes2
& ScRefFlags::VALID
)) &&
1611 // If not fully valid addresses, check if both have a valid
1612 // column or row, and both have valid (or omitted) sheet references.
1613 (nRawRes1
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
)) &&
1614 (nRawRes1
& ScRefFlags::TAB_VALID
) &&
1615 (nRawRes2
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
)) &&
1616 (nRawRes2
& ScRefFlags::TAB_VALID
) &&
1617 // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A
1618 ((nRawRes1
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
)) ==
1619 (nRawRes2
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
))))
1621 nRes1
= nRawRes1
| ScRefFlags::VALID
;
1622 nRes2
= nRawRes2
| ScRefFlags::VALID
;
1623 if (nRawRes1
& ScRefFlags::COL_VALID
)
1625 rRange
.aStart
.SetRow(0);
1626 rRange
.aEnd
.SetRow(rDoc
.MaxRow());
1627 nRes1
|= ScRefFlags::ROW_VALID
| ScRefFlags::ROW_ABS
;
1628 nRes2
|= ScRefFlags::ROW_VALID
| ScRefFlags::ROW_ABS
;
1632 rRange
.aStart
.SetCol(0);
1633 rRange
.aEnd
.SetCol( rDoc
.MaxCol() );
1634 nRes1
|= ScRefFlags::COL_VALID
| ScRefFlags::COL_ABS
;
1635 nRes2
|= ScRefFlags::COL_VALID
| ScRefFlags::COL_ABS
;
1638 else if ((nRes1
& ScRefFlags::VALID
) && (nRes2
& ScRefFlags::VALID
))
1640 // Flag entire column/row references so they can be displayed
1641 // as such. If the sticky reference parts are not both
1642 // absolute or relative, assume that the user thought about
1643 // something we should not touch.
1644 if (rRange
.aStart
.Row() == 0 && rRange
.aEnd
.Row() == rDoc
.MaxRow() &&
1645 ((nRes1
& ScRefFlags::ROW_ABS
) == ScRefFlags::ZERO
) &&
1646 ((nRes2
& ScRefFlags::ROW_ABS
) == ScRefFlags::ZERO
))
1648 nRes1
|= ScRefFlags::ROW_ABS
;
1649 nRes2
|= ScRefFlags::ROW_ABS
;
1651 else if (rRange
.aStart
.Col() == 0 && rRange
.aEnd
.Col() == rDoc
.MaxCol() &&
1652 ((nRes1
& ScRefFlags::COL_ABS
) == ScRefFlags::ZERO
) && ((nRes2
& ScRefFlags::COL_ABS
) == ScRefFlags::ZERO
))
1654 nRes1
|= ScRefFlags::COL_ABS
;
1655 nRes2
|= ScRefFlags::COL_ABS
;
1658 if ((nRes1
& ScRefFlags::VALID
) && (nRes2
& ScRefFlags::VALID
))
1660 // PutInOrder / Justify
1661 ScRefFlags nMask
, nBits1
, nBits2
;
1663 if ( rRange
.aEnd
.Col() < (nTempCol
= rRange
.aStart
.Col()) )
1665 rRange
.aStart
.SetCol(rRange
.aEnd
.Col()); rRange
.aEnd
.SetCol(nTempCol
);
1666 nMask
= (ScRefFlags::COL_VALID
| ScRefFlags::COL_ABS
);
1667 nBits1
= nRes1
& nMask
;
1668 nBits2
= nRes2
& nMask
;
1669 nRes1
= (nRes1
& ~nMask
) | nBits2
;
1670 nRes2
= (nRes2
& ~nMask
) | nBits1
;
1673 if ( rRange
.aEnd
.Row() < (nTempRow
= rRange
.aStart
.Row()) )
1675 rRange
.aStart
.SetRow(rRange
.aEnd
.Row()); rRange
.aEnd
.SetRow(nTempRow
);
1676 nMask
= (ScRefFlags::ROW_VALID
| ScRefFlags::ROW_ABS
);
1677 nBits1
= nRes1
& nMask
;
1678 nBits2
= nRes2
& nMask
;
1679 nRes1
= (nRes1
& ~nMask
) | nBits2
;
1680 nRes2
= (nRes2
& ~nMask
) | nBits1
;
1683 if ( rRange
.aEnd
.Tab() < (nTempTab
= rRange
.aStart
.Tab()) )
1685 rRange
.aStart
.SetTab(rRange
.aEnd
.Tab()); rRange
.aEnd
.SetTab(nTempTab
);
1686 nMask
= (ScRefFlags::TAB_VALID
| ScRefFlags::TAB_ABS
| ScRefFlags::TAB_3D
);
1687 nBits1
= nRes1
& nMask
;
1688 nBits2
= nRes2
& nMask
;
1689 nRes1
= (nRes1
& ~nMask
) | nBits2
;
1690 nRes2
= (nRes2
& ~nMask
) | nBits1
;
1692 if ( ((nRes1
& ( ScRefFlags::TAB_ABS
| ScRefFlags::TAB_3D
))
1693 == ( ScRefFlags::TAB_ABS
| ScRefFlags::TAB_3D
))
1694 && !(nRes2
& ScRefFlags::TAB_3D
) )
1695 nRes2
|= ScRefFlags::TAB_ABS
;
1699 // Don't leave around valid half references.
1700 nRes1
= nRes2
= ScRefFlags::ZERO
;
1704 applyStartToEndFlags(nRes1
, nRes2
& ScRefFlags::BITS
);
1705 nRes1
|= nRes2
& ScRefFlags::VALID
;
1709 ScRefFlags
ScRange::Parse( const OUString
& rString
, const ScDocument
& rDoc
,
1710 const ScAddress::Details
& rDetails
,
1711 ScAddress::ExternalInfo
* pExtInfo
,
1712 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
,
1713 const OUString
* pErrRef
)
1715 if (rString
.isEmpty())
1716 return ScRefFlags::ZERO
;
1718 switch (rDetails
.eConv
)
1720 case formula::FormulaGrammar::CONV_XL_A1
:
1721 case formula::FormulaGrammar::CONV_XL_OOX
:
1723 return lcl_ScRange_Parse_XL_A1( *this, rString
.getStr(), rDoc
, false, pExtInfo
,
1724 (rDetails
.eConv
== formula::FormulaGrammar::CONV_XL_OOX
? pExternalLinks
: nullptr),
1728 case formula::FormulaGrammar::CONV_XL_R1C1
:
1730 return lcl_ScRange_Parse_XL_R1C1( *this, rString
.getStr(), rDoc
, rDetails
, false, pExtInfo
, nullptr );
1734 case formula::FormulaGrammar::CONV_OOO
:
1736 return lcl_ScRange_Parse_OOo( *this, rString
, rDoc
, pExtInfo
, pErrRef
);
1741 // Accept a full range, or an address
1742 ScRefFlags
ScRange::ParseAny( const OUString
& rString
, const ScDocument
& rDoc
,
1743 const ScAddress::Details
& rDetails
)
1745 ScRefFlags nRet
= Parse( rString
, rDoc
, rDetails
);
1746 const ScRefFlags nValid
= ScRefFlags::VALID
| ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
;
1748 if ( (nRet
& nValid
) != nValid
)
1750 ScAddress
aAdr(aStart
);//initialize with currentPos as fallback for table number
1751 nRet
= aAdr
.Parse( rString
, rDoc
, rDetails
);
1752 if ( nRet
& ScRefFlags::VALID
)
1753 aStart
= aEnd
= aAdr
;
1758 // Parse only full row references
1759 ScRefFlags
ScRange::ParseCols( const ScDocument
& rDoc
,
1760 const OUString
& rStr
,
1761 const ScAddress::Details
& rDetails
)
1764 return ScRefFlags::ZERO
;
1766 const sal_Unicode
* p
= rStr
.getStr();
1767 ScRefFlags nRes
= ScRefFlags::ZERO
;
1768 ScRefFlags ignored
= ScRefFlags::ZERO
;
1770 switch (rDetails
.eConv
)
1773 case formula::FormulaGrammar::CONV_OOO
: // No full col refs in OOO yet, assume XL notation
1774 case formula::FormulaGrammar::CONV_XL_A1
:
1775 case formula::FormulaGrammar::CONV_XL_OOX
:
1776 if (nullptr != (p
= lcl_a1_get_col( rDoc
, p
, &aStart
, &ignored
, nullptr) ) )
1780 if( nullptr != (p
= lcl_a1_get_col( rDoc
, p
+1, &aEnd
, &ignored
, nullptr)))
1782 nRes
= ScRefFlags::COL_VALID
;
1788 nRes
= ScRefFlags::COL_VALID
;
1793 case formula::FormulaGrammar::CONV_XL_R1C1
:
1794 if ((p
[0] == 'C' || p
[0] == 'c') &&
1795 nullptr != (p
= lcl_r1c1_get_col( rDoc
.GetSheetLimits(), p
, rDetails
, &aStart
, &ignored
)))
1799 if( (p
[1] == 'C' || p
[1] == 'c') &&
1800 nullptr != (p
= lcl_r1c1_get_col( rDoc
.GetSheetLimits(), p
+1, rDetails
, &aEnd
, &ignored
)))
1802 nRes
= ScRefFlags::COL_VALID
;
1808 nRes
= ScRefFlags::COL_VALID
;
1814 return (p
!= nullptr && *p
== '\0') ? nRes
: ScRefFlags::ZERO
;
1817 // Parse only full row references
1818 void ScRange::ParseRows( const ScDocument
& rDoc
,
1819 const OUString
& rStr
,
1820 const ScAddress::Details
& rDetails
)
1825 const sal_Unicode
* p
= rStr
.getStr();
1826 ScRefFlags ignored
= ScRefFlags::ZERO
;
1828 switch (rDetails
.eConv
)
1831 case formula::FormulaGrammar::CONV_OOO
: // No full row refs in OOO yet, assume XL notation
1832 case formula::FormulaGrammar::CONV_XL_A1
:
1833 case formula::FormulaGrammar::CONV_XL_OOX
:
1834 if (nullptr != (p
= lcl_a1_get_row( rDoc
, p
, &aStart
, &ignored
, nullptr) ) )
1838 lcl_a1_get_row( rDoc
, p
+1, &aEnd
, &ignored
, nullptr);
1847 case formula::FormulaGrammar::CONV_XL_R1C1
:
1848 if ((p
[0] == 'R' || p
[0] == 'r') &&
1849 nullptr != (p
= lcl_r1c1_get_row( rDoc
.GetSheetLimits(), p
, rDetails
, &aStart
, &ignored
)))
1853 if( p
[1] == 'R' || p
[1] == 'r' )
1855 lcl_r1c1_get_row( rDoc
.GetSheetLimits(), p
+1, rDetails
, &aEnd
, &ignored
);
1867 template<typename T
> static void lcl_ScColToAlpha( T
& rBuf
, SCCOL nCol
)
1872 rBuf
.append( static_cast<char>( 'A' + nCol
));
1875 rBuf
.append( static_cast<char>( 'A' + nCol
/ 26 - 1 ));
1876 rBuf
.append( static_cast<char>( 'A' + nCol
% 26 ));
1881 sal_Int32 nInsert
= rBuf
.getLength();
1884 SCCOL nC
= nCol
% 26;
1885 rBuf
.insert(nInsert
, static_cast<char> ( 'A' + nC
));
1886 nCol
= sal::static_int_cast
<SCCOL
>( nCol
- nC
);
1887 nCol
= nCol
/ 26 - 1;
1889 rBuf
.insert(nInsert
, static_cast<char> ( 'A' + nCol
));
1893 void ScColToAlpha( OUStringBuffer
& rBuf
, SCCOL nCol
)
1895 lcl_ScColToAlpha(rBuf
, nCol
);
1898 template <typename T
> static void lcl_a1_append_c ( T
&rString
, int nCol
, bool bIsAbs
)
1901 rString
.append("$");
1902 lcl_ScColToAlpha( rString
, sal::static_int_cast
<SCCOL
>(nCol
) );
1905 template <typename T
> static void lcl_a1_append_r ( T
&rString
, sal_Int32 nRow
, bool bIsAbs
)
1908 rString
.append("$");
1909 rString
.append( nRow
+ 1 );
1912 template <typename T
> static void lcl_r1c1_append_c ( T
&rString
, sal_Int32 nCol
, bool bIsAbs
,
1913 const ScAddress::Details
& rDetails
)
1915 rString
.append("C");
1918 rString
.append( nCol
+ 1 );
1922 nCol
-= rDetails
.nCol
;
1924 rString
.append("[").append(nCol
).append("]");
1929 template <typename T
> static void lcl_r1c1_append_r ( T
&rString
, sal_Int32 nRow
, bool bIsAbs
,
1930 const ScAddress::Details
& rDetails
)
1932 rString
.append("R");
1935 rString
.append( nRow
+ 1 );
1939 nRow
-= rDetails
.nRow
;
1941 rString
.append("[").append(nRow
).append("]");
1946 static OUString
getFileNameFromDoc( const ScDocument
* pDoc
)
1948 // TODO : er points at ScGlobal::GetAbsDocName()
1949 // as a better template. Look into it
1951 SfxObjectShell
* pShell
;
1953 if( nullptr != pDoc
&&
1954 nullptr != (pShell
= pDoc
->GetDocumentShell() ) )
1956 uno::Reference
< frame::XModel
> xModel
= pShell
->GetModel();
1959 if( !xModel
->getURL().isEmpty() )
1961 INetURLObject
aURL( xModel
->getURL() );
1962 sFileName
= aURL
.GetLastName();
1965 sFileName
= pShell
->GetTitle();
1972 static void lcl_string_append(OUStringBuffer
&rString
, std::u16string_view sString
)
1974 rString
.append(sString
);
1977 static void lcl_string_append(OStringBuffer
&rString
, std::u16string_view sString
)
1979 rString
.append(OUStringToOString( sString
, RTL_TEXTENCODING_UTF8
));
1982 template<typename T
> static void lcl_Format( T
& r
, SCTAB nTab
, SCROW nRow
, SCCOL nCol
, ScRefFlags nFlags
,
1983 const ScDocument
* pDoc
,
1984 const ScAddress::Details
& rDetails
)
1986 if( nFlags
& ScRefFlags::VALID
)
1987 nFlags
|= ScRefFlags::ROW_VALID
| ScRefFlags::COL_VALID
| ScRefFlags::TAB_VALID
;
1988 if( pDoc
&& (nFlags
& ScRefFlags::TAB_VALID
) )
1990 if ( nTab
< 0 || nTab
>= pDoc
->GetTableCount() )
1992 lcl_string_append(r
, ScCompiler::GetNativeSymbol(ocErrRef
));
1995 if( nFlags
& ScRefFlags::TAB_3D
)
1997 OUString aTabName
, aDocName
;
1998 pDoc
->GetName(nTab
, aTabName
);
1999 assert( !aTabName
.isEmpty() && "empty sheet name");
2000 // External Reference, same as in ScCompiler::MakeTabStr()
2001 if( aTabName
[0] == '\'' )
2003 sal_Int32 nPos
= ScCompiler::GetDocTabPos( aTabName
);
2006 aDocName
= aTabName
.copy( 0, nPos
+ 1 );
2007 aTabName
= aTabName
.copy( nPos
+ 1 );
2010 else if( nFlags
& ScRefFlags::FORCE_DOC
)
2012 // VBA has an 'external' flag that forces the addition of the
2013 // tab name _and_ the doc name. The VBA code would be
2014 // needlessly complicated if it constructed an actual external
2015 // reference so we add this somewhat cheesy kludge to force the
2016 // addition of the document name even for non-external references
2017 aDocName
= getFileNameFromDoc( pDoc
);
2019 ScCompiler::CheckTabQuotes( aTabName
, rDetails
.eConv
);
2021 switch( rDetails
.eConv
)
2024 case formula::FormulaGrammar::CONV_OOO
:
2025 lcl_string_append(r
, aDocName
);
2026 if( nFlags
& ScRefFlags::TAB_ABS
)
2028 lcl_string_append(r
, aTabName
);
2032 case formula::FormulaGrammar::CONV_XL_OOX
:
2033 if (!aTabName
.isEmpty() && aTabName
[0] == '\'')
2035 if (!aDocName
.isEmpty())
2037 lcl_string_append(r
.append("'["), aDocName
);
2039 lcl_string_append(r
, aTabName
.subView(1));
2043 lcl_string_append(r
, aTabName
);
2049 case formula::FormulaGrammar::CONV_XL_A1
:
2050 case formula::FormulaGrammar::CONV_XL_R1C1
:
2051 if (!aDocName
.isEmpty())
2053 lcl_string_append(r
.append("["), aDocName
);
2056 lcl_string_append(r
, aTabName
);
2062 switch( rDetails
.eConv
)
2065 case formula::FormulaGrammar::CONV_OOO
:
2066 case formula::FormulaGrammar::CONV_XL_A1
:
2067 case formula::FormulaGrammar::CONV_XL_OOX
:
2068 if( nFlags
& ScRefFlags::COL_VALID
)
2069 lcl_a1_append_c ( r
, nCol
, (nFlags
& ScRefFlags::COL_ABS
) != ScRefFlags::ZERO
);
2070 if( nFlags
& ScRefFlags::ROW_VALID
)
2071 lcl_a1_append_r ( r
, nRow
, (nFlags
& ScRefFlags::ROW_ABS
) != ScRefFlags::ZERO
);
2074 case formula::FormulaGrammar::CONV_XL_R1C1
:
2075 if( nFlags
& ScRefFlags::ROW_VALID
)
2076 lcl_r1c1_append_r ( r
, nRow
, (nFlags
& ScRefFlags::ROW_ABS
) != ScRefFlags::ZERO
, rDetails
);
2077 if( nFlags
& ScRefFlags::COL_VALID
)
2078 lcl_r1c1_append_c ( r
, nCol
, (nFlags
& ScRefFlags::COL_ABS
) != ScRefFlags::ZERO
, rDetails
);
2083 void ScAddress::Format( OStringBuffer
& r
, ScRefFlags nFlags
,
2084 const ScDocument
* pDoc
,
2085 const Details
& rDetails
) const
2087 lcl_Format(r
, nTab
, nRow
, nCol
, nFlags
, pDoc
, rDetails
);
2090 OUString
ScAddress::Format(ScRefFlags nFlags
, const ScDocument
* pDoc
,
2091 const Details
& rDetails
) const
2094 lcl_Format(r
, nTab
, nRow
, nCol
, nFlags
, pDoc
, rDetails
);
2095 return r
.makeStringAndClear();
2098 static void lcl_Split_DocTab( const ScDocument
& rDoc
, SCTAB nTab
,
2099 const ScAddress::Details
& rDetails
,
2101 OUString
& rTabName
, OUString
& rDocName
)
2103 rDoc
.GetName(nTab
, rTabName
);
2105 // External reference, same as in ScCompiler::MakeTabStr()
2106 if (!rTabName
.isEmpty() && rTabName
[0] == '\'')
2108 sal_Int32 nPos
= ScCompiler::GetDocTabPos( rTabName
);
2111 rDocName
= rTabName
.copy( 0, nPos
+ 1 );
2112 rTabName
= rTabName
.copy( nPos
+ 1 );
2115 else if( nFlags
& ScRefFlags::FORCE_DOC
)
2117 // VBA has an 'external' flag that forces the addition of the
2118 // tab name _and_ the doc name. The VBA code would be
2119 // needlessly complicated if it constructed an actual external
2120 // reference so we add this somewhat cheesy kludge to force the
2121 // addition of the document name even for non-external references
2122 rDocName
= getFileNameFromDoc(&rDoc
);
2124 ScCompiler::CheckTabQuotes( rTabName
, rDetails
.eConv
);
2127 static void lcl_ScRange_Format_XL_Header( OUStringBuffer
& rString
, const ScRange
& rRange
,
2128 ScRefFlags nFlags
, const ScDocument
& rDoc
,
2129 const ScAddress::Details
& rDetails
)
2131 if( !(nFlags
& ScRefFlags::TAB_3D
) )
2134 OUString aTabName
, aDocName
;
2135 lcl_Split_DocTab( rDoc
, rRange
.aStart
.Tab(), rDetails
, nFlags
, aTabName
, aDocName
);
2136 switch (rDetails
.eConv
)
2138 case formula::FormulaGrammar::CONV_XL_OOX
:
2139 if (!aTabName
.isEmpty() && aTabName
[0] == '\'')
2141 if (!aDocName
.isEmpty())
2143 rString
.append("'[" + aDocName
+ "]" + aTabName
.subView(1));
2147 rString
.append(aTabName
);
2153 if (!aDocName
.isEmpty())
2155 rString
.append("[" + aDocName
+ "]");
2157 rString
.append(aTabName
);
2160 if( nFlags
& ScRefFlags::TAB2_3D
)
2162 lcl_Split_DocTab( rDoc
, rRange
.aEnd
.Tab(), rDetails
, nFlags
, aTabName
, aDocName
);
2163 rString
.append(":");
2164 rString
.append(aTabName
);
2166 rString
.append("!");
2169 // helpers used in ScRange::Format
2170 static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags
)
2172 return static_cast<bool>(nFlags
& ScRefFlags::COL_ABS
) != static_cast<bool>(nFlags
& ScRefFlags::COL2_ABS
);
2174 static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags
)
2176 return static_cast<bool>(nFlags
& ScRefFlags::ROW_ABS
) != static_cast<bool>(nFlags
& ScRefFlags::ROW2_ABS
);
2179 OUString
ScRange::Format( const ScDocument
& rDoc
, ScRefFlags nFlags
,
2180 const ScAddress::Details
& rDetails
, bool bFullAddressNotation
) const
2182 if( !( nFlags
& ScRefFlags::VALID
) )
2184 return ScCompiler::GetNativeSymbol(ocErrRef
);
2188 switch( rDetails
.eConv
) {
2190 case formula::FormulaGrammar::CONV_OOO
: {
2191 bool bOneTab
= (aStart
.Tab() == aEnd
.Tab());
2193 nFlags
|= ScRefFlags::TAB_3D
;
2194 r
= aStart
.Format(nFlags
, &rDoc
, rDetails
);
2195 if( aStart
!= aEnd
||
2196 lcl_ColAbsFlagDiffer( nFlags
) ||
2197 lcl_RowAbsFlagDiffer( nFlags
))
2199 const ScDocument
* pDoc
= &rDoc
;
2200 // move flags of end reference to start reference, mask with BITS to exclude FORCE_DOC flag
2201 nFlags
= ScRefFlags::VALID
| (ScRefFlags(o3tl::to_underlying(nFlags
) >> 4) & ScRefFlags::BITS
);
2205 nFlags
|= ScRefFlags::TAB_3D
;
2206 r
.append(":" + aEnd
.Format(nFlags
, pDoc
, rDetails
));
2211 case formula::FormulaGrammar::CONV_XL_A1
:
2212 case formula::FormulaGrammar::CONV_XL_OOX
: {
2213 SCCOL nMaxCol
= rDoc
.MaxCol();
2214 SCROW nMaxRow
= rDoc
.MaxRow();
2216 lcl_ScRange_Format_XL_Header( r
, *this, nFlags
, rDoc
, rDetails
);
2217 if( aStart
.Col() == 0 && aEnd
.Col() >= nMaxCol
&& !bFullAddressNotation
)
2219 // Full col refs always require 2 rows (2:2)
2220 lcl_a1_append_r( r
, aStart
.Row(), (nFlags
& ScRefFlags::ROW_ABS
) != ScRefFlags::ZERO
);
2222 lcl_a1_append_r( r
, aEnd
.Row(), (nFlags
& ScRefFlags::ROW2_ABS
) != ScRefFlags::ZERO
);
2224 else if( aStart
.Row() == 0 && aEnd
.Row() >= nMaxRow
&& !bFullAddressNotation
)
2226 // Full row refs always require 2 cols (A:A)
2227 lcl_a1_append_c( r
, aStart
.Col(), (nFlags
& ScRefFlags::COL_ABS
) != ScRefFlags::ZERO
);
2229 lcl_a1_append_c( r
, aEnd
.Col(), (nFlags
& ScRefFlags::COL2_ABS
) != ScRefFlags::ZERO
);
2233 lcl_a1_append_c ( r
, aStart
.Col(), (nFlags
& ScRefFlags::COL_ABS
) != ScRefFlags::ZERO
);
2234 lcl_a1_append_r ( r
, aStart
.Row(), (nFlags
& ScRefFlags::ROW_ABS
) != ScRefFlags::ZERO
);
2235 if( aStart
.Col() != aEnd
.Col() ||
2236 lcl_ColAbsFlagDiffer( nFlags
) ||
2237 aStart
.Row() != aEnd
.Row() ||
2238 lcl_RowAbsFlagDiffer( nFlags
) ) {
2240 lcl_a1_append_c ( r
, aEnd
.Col(), (nFlags
& ScRefFlags::COL2_ABS
) != ScRefFlags::ZERO
);
2241 lcl_a1_append_r ( r
, aEnd
.Row(), (nFlags
& ScRefFlags::ROW2_ABS
) != ScRefFlags::ZERO
);
2247 case formula::FormulaGrammar::CONV_XL_R1C1
: {
2248 SCCOL nMaxCol
= rDoc
.MaxCol();
2249 SCROW nMaxRow
= rDoc
.MaxRow();
2251 lcl_ScRange_Format_XL_Header( r
, *this, nFlags
, rDoc
, rDetails
);
2252 if( aStart
.Col() == 0 && aEnd
.Col() >= nMaxCol
&& !bFullAddressNotation
)
2254 lcl_r1c1_append_r( r
, aStart
.Row(), (nFlags
& ScRefFlags::ROW_ABS
) != ScRefFlags::ZERO
, rDetails
);
2255 if( aStart
.Row() != aEnd
.Row() ||
2256 lcl_RowAbsFlagDiffer( nFlags
) ) {
2258 lcl_r1c1_append_r( r
, aEnd
.Row(), (nFlags
& ScRefFlags::ROW2_ABS
) != ScRefFlags::ZERO
, rDetails
);
2261 else if( aStart
.Row() == 0 && aEnd
.Row() >= nMaxRow
&& !bFullAddressNotation
)
2263 lcl_r1c1_append_c( r
, aStart
.Col(), (nFlags
& ScRefFlags::COL_ABS
) != ScRefFlags::ZERO
, rDetails
);
2264 if( aStart
.Col() != aEnd
.Col() ||
2265 lcl_ColAbsFlagDiffer( nFlags
)) {
2267 lcl_r1c1_append_c( r
, aEnd
.Col(), (nFlags
& ScRefFlags::COL2_ABS
) != ScRefFlags::ZERO
, rDetails
);
2272 lcl_r1c1_append_r( r
, aStart
.Row(), (nFlags
& ScRefFlags::ROW_ABS
) != ScRefFlags::ZERO
, rDetails
);
2273 lcl_r1c1_append_c( r
, aStart
.Col(), (nFlags
& ScRefFlags::COL_ABS
) != ScRefFlags::ZERO
, rDetails
);
2274 if( aStart
.Col() != aEnd
.Col() ||
2275 lcl_ColAbsFlagDiffer( nFlags
) ||
2276 aStart
.Row() != aEnd
.Row() ||
2277 lcl_RowAbsFlagDiffer( nFlags
) ) {
2279 lcl_r1c1_append_r( r
, aEnd
.Row(), (nFlags
& ScRefFlags::ROW2_ABS
) != ScRefFlags::ZERO
, rDetails
);
2280 lcl_r1c1_append_c( r
, aEnd
.Col(), (nFlags
& ScRefFlags::COL2_ABS
) != ScRefFlags::ZERO
, rDetails
);
2286 return r
.makeStringAndClear();
2289 bool ScAddress::Move( SCCOL dx
, SCROW dy
, SCTAB dz
, ScAddress
& rErrorPos
, const ScDocument
& rDoc
)
2291 SCTAB nMaxTab
= rDoc
.GetTableCount();
2292 SCCOL nMaxCol
= rDoc
.MaxCol();
2293 SCROW nMaxRow
= rDoc
.MaxRow();
2298 rErrorPos
.SetCol(dx
);
2304 else if( dx
> nMaxCol
)
2309 rErrorPos
.SetRow(dy
);
2315 else if( dy
> nMaxRow
)
2320 rErrorPos
.SetTab(dz
);
2326 else if( dz
> nMaxTab
)
2328 // Always set MAXTAB+1 so further checks without ScDocument detect invalid.
2329 rErrorPos
.SetTab(MAXTAB
+1);
2337 bool ScRange::Move( SCCOL dx
, SCROW dy
, SCTAB dz
, ScRange
& rErrorRange
, const ScDocument
& rDoc
)
2339 SCCOL nMaxCol
= rDoc
.MaxCol();
2340 SCROW nMaxRow
= rDoc
.MaxRow();
2341 if (dy
&& aStart
.Row() == 0 && aEnd
.Row() == nMaxRow
)
2342 dy
= 0; // Entire column not to be moved.
2343 if (dx
&& aStart
.Col() == 0 && aEnd
.Col() == nMaxCol
)
2344 dx
= 0; // Entire row not to be moved.
2345 bool b
= aStart
.Move( dx
, dy
, dz
, rErrorRange
.aStart
, rDoc
);
2346 b
&= aEnd
.Move( dx
, dy
, dz
, rErrorRange
.aEnd
, rDoc
);
2350 bool ScRange::MoveSticky( const ScDocument
& rDoc
, SCCOL dx
, SCROW dy
, SCTAB dz
, ScRange
& rErrorRange
)
2352 const SCCOL nMaxCol
= rDoc
.MaxCol();
2353 const SCROW nMaxRow
= rDoc
.MaxRow();
2354 bool bColRange
= (aStart
.Col() < aEnd
.Col());
2355 bool bRowRange
= (aStart
.Row() < aEnd
.Row());
2356 if (dy
&& aStart
.Row() == 0 && aEnd
.Row() == nMaxRow
)
2357 dy
= 0; // Entire column not to be moved.
2358 if (dx
&& aStart
.Col() == 0 && aEnd
.Col() == nMaxCol
)
2359 dx
= 0; // Entire row not to be moved.
2360 bool b1
= aStart
.Move( dx
, dy
, dz
, rErrorRange
.aStart
, rDoc
);
2361 if (dx
&& bColRange
&& aEnd
.Col() == nMaxCol
)
2362 dx
= 0; // End column sticky.
2363 if (dy
&& bRowRange
&& aEnd
.Row() == nMaxRow
)
2364 dy
= 0; // End row sticky.
2365 SCTAB nOldTab
= aEnd
.Tab();
2366 bool b2
= aEnd
.Move( dx
, dy
, dz
, rErrorRange
.aEnd
, rDoc
);
2369 // End column or row of a range may have become sticky.
2370 bColRange
= (!dx
|| (bColRange
&& aEnd
.Col() == nMaxCol
));
2371 if (dx
&& bColRange
)
2372 rErrorRange
.aEnd
.SetCol(nMaxCol
);
2373 bRowRange
= (!dy
|| (bRowRange
&& aEnd
.Row() == nMaxRow
));
2374 if (dy
&& bRowRange
)
2375 rErrorRange
.aEnd
.SetRow(nMaxRow
);
2376 b2
= bColRange
&& bRowRange
&& (aEnd
.Tab() - nOldTab
== dz
);
2381 void ScRange::IncColIfNotLessThan(const ScDocument
& rDoc
, SCCOL nStartCol
, SCCOL nOffset
)
2384 if (aStart
.Col() > nStartCol
)
2387 if (nStartCol
+ nOffset
> aStart
.Col())
2388 offset
= aStart
.Col() - nStartCol
;
2389 else if (nStartCol
- nOffset
> aStart
.Col())
2390 offset
= -1 * (aStart
.Col() - nStartCol
);
2392 aStart
.IncCol(offset
);
2393 if (aStart
.Col() < 0)
2395 else if(aStart
.Col() > rDoc
.MaxCol())
2396 aStart
.SetCol(rDoc
.MaxCol());
2398 if (aEnd
.Col() > nStartCol
)
2401 if (nStartCol
+ nOffset
> aEnd
.Col())
2402 offset
= aEnd
.Col() - nStartCol
;
2403 else if (nStartCol
- nOffset
> aEnd
.Col())
2404 offset
= -1 * (aEnd
.Col() - nStartCol
);
2406 aEnd
.IncCol(offset
);
2409 else if(aEnd
.Col() > rDoc
.MaxCol())
2410 aEnd
.SetCol(rDoc
.MaxCol());
2414 void ScRange::IncRowIfNotLessThan(const ScDocument
& rDoc
, SCROW nStartRow
, SCROW nOffset
)
2417 if (aStart
.Row() > nStartRow
)
2420 if (nStartRow
+ nOffset
> aStart
.Row())
2421 offset
= aStart
.Row() - nStartRow
;
2422 else if (nStartRow
- nOffset
> aStart
.Row())
2423 offset
= -1 * (aStart
.Row() - nStartRow
);
2425 aStart
.IncRow(offset
);
2426 if (aStart
.Row() < 0)
2428 else if(aStart
.Row() > rDoc
.MaxRow())
2429 aStart
.SetRow(rDoc
.MaxRow());
2431 if (aEnd
.Row() > nStartRow
)
2434 if (nStartRow
+ nOffset
> aEnd
.Row())
2435 offset
= aEnd
.Row() - nStartRow
;
2436 else if (nStartRow
- nOffset
> aEnd
.Row())
2437 offset
= -1 * (aEnd
.Row() - nStartRow
);
2439 aEnd
.IncRow(offset
);
2442 else if(aEnd
.Row() > rDoc
.MaxRow())
2443 aEnd
.SetRow(rDoc
.MaxRow());
2447 bool ScRange::IsEndColSticky( const ScDocument
& rDoc
) const
2449 // Only in an actual column range, i.e. not if both columns are MAXCOL.
2450 return aEnd
.Col() == rDoc
.MaxCol() && aStart
.Col() < aEnd
.Col();
2453 bool ScRange::IsEndRowSticky( const ScDocument
& rDoc
) const
2455 // Only in an actual row range, i.e. not if both rows are MAXROW.
2456 return aEnd
.Row() == rDoc
.MaxRow() && aStart
.Row() < aEnd
.Row();
2459 void ScRange::IncEndColSticky( const ScDocument
& rDoc
, SCCOL nDelta
)
2461 SCCOL nCol
= aEnd
.Col();
2462 if (aStart
.Col() >= nCol
)
2464 // Less than two columns => not sticky.
2465 aEnd
.IncCol( nDelta
);
2469 const SCCOL nMaxCol
= rDoc
.MaxCol();
2470 if (nCol
== nMaxCol
)
2475 aEnd
.SetCol( ::std::min( static_cast<SCCOL
>(nCol
+ nDelta
), nMaxCol
));
2477 aEnd
.IncCol( nDelta
); // was greater than nMaxCol, caller should know...
2480 void ScRange::IncEndRowSticky( const ScDocument
& rDoc
, SCROW nDelta
)
2482 SCROW nRow
= aEnd
.Row();
2483 if (aStart
.Row() >= nRow
)
2485 // Less than two rows => not sticky.
2486 aEnd
.IncRow( nDelta
);
2490 if (nRow
== rDoc
.MaxRow())
2494 if (nRow
< rDoc
.MaxRow())
2495 aEnd
.SetRow( ::std::min( static_cast<SCROW
>(nRow
+ nDelta
), rDoc
.MaxRow()));
2497 aEnd
.IncRow( nDelta
); // was greater than rDoc.MaxRow(), caller should know...
2500 OUString
ScAddress::GetColRowString() const
2502 OUStringBuffer aString
;
2504 switch( detailsOOOa1
.eConv
)
2507 case formula::FormulaGrammar::CONV_OOO
:
2508 case formula::FormulaGrammar::CONV_XL_A1
:
2509 case formula::FormulaGrammar::CONV_XL_OOX
:
2510 lcl_ScColToAlpha( aString
, nCol
);
2511 aString
.append(nRow
+1);
2514 case formula::FormulaGrammar::CONV_XL_R1C1
:
2515 lcl_r1c1_append_r ( aString
, nRow
, false/*bAbsolute*/, detailsOOOa1
);
2516 lcl_r1c1_append_c ( aString
, nCol
, false/*bAbsolute*/, detailsOOOa1
);
2520 return aString
.makeStringAndClear();
2523 OUString
ScRefAddress::GetRefString( const ScDocument
& rDoc
, SCTAB nActTab
,
2524 const ScAddress::Details
& rDetails
) const
2526 if ( Tab()+1 > rDoc
.GetTableCount() )
2527 return ScCompiler::GetNativeSymbol(ocErrRef
);
2529 ScRefFlags nFlags
= ScRefFlags::VALID
;
2530 if ( nActTab
!= Tab() )
2532 nFlags
|= ScRefFlags::TAB_3D
;
2534 nFlags
|= ScRefFlags::TAB_ABS
;
2537 nFlags
|= ScRefFlags::COL_ABS
;
2539 nFlags
|= ScRefFlags::ROW_ABS
;
2541 return aAdr
.Format(nFlags
, &rDoc
, rDetails
);
2544 bool AlphaToCol(const ScDocument
& rDoc
, SCCOL
& rCol
, std::u16string_view rStr
)
2547 sal_Int32 nStop
= rStr
.size();
2550 const SCCOL nMaxCol
= rDoc
.MaxCol();
2551 while (nResult
<= nMaxCol
&& nPos
< nStop
&& (c
= rStr
[nPos
]) != 0 &&
2552 rtl::isAsciiAlpha(c
))
2555 nResult
= (nResult
+ 1) * 26;
2556 nResult
+= ScGlobal::ToUpperAlpha(c
) - 'A';
2559 bool bOk
= (rDoc
.ValidCol(nResult
) && nPos
> 0);
2565 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */