xref: /core/sc/source/core/tool/rangeutl.cxx (revision 1da69081)
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 <memory>
21 #include <osl/diagnose.h>
22 #include <unotools/charclass.hxx>
23 #include <rangeutl.hxx>
24 #include <document.hxx>
25 #include <global.hxx>
26 #include <dbdata.hxx>
27 #include <rangenam.hxx>
28 #include <convuno.hxx>
29 #include <externalrefmgr.hxx>
30 #include <compiler.hxx>
31 #include <refupdatecontext.hxx>
32 
33 using ::formula::FormulaGrammar;
34 using namespace ::com::sun::star;
35 
36 bool ScRangeUtil::MakeArea( const OUString&   rAreaStr,
37                             ScArea&         rArea,
38                             const ScDocument& rDoc,
39                             SCTAB           nTab,
40                             ScAddress::Details const & rDetails )
41 {
42     // Input in rAreaStr: "$Tabelle1.$A1:$D17"
43 
44     // BROKEN BROKEN BROKEN
45     // but it is only used in the consolidate dialog.  Ignore for now.
46 
47     bool        bSuccess    = false;
48     sal_Int32       nPointPos   = rAreaStr.indexOf('.');
49     sal_Int32       nColonPos   = rAreaStr.indexOf(':');
50     OUString      aStrArea( rAreaStr );
51     ScRefAddress    startPos;
52     ScRefAddress    endPos;
53 
54     if ( nColonPos == -1  && nPointPos != -1 )
55     {
56         aStrArea += OUString::Concat(":") + rAreaStr.subView( nPointPos+1 ); // do not include '.' in copy
57     }
58 
59     bSuccess = ConvertDoubleRef( rDoc, aStrArea, nTab, startPos, endPos, rDetails );
60 
61     if ( bSuccess )
62         rArea = ScArea( startPos.Tab(),
63                         startPos.Col(), startPos.Row(),
64                         endPos.Col(),   endPos.Row() );
65 
66     return bSuccess;
67 }
68 
69 void ScRangeUtil::CutPosString( const OUString&   theAreaStr,
70                                 OUString&         thePosStr )
71 {
72     OUString  aPosStr;
73     // BROKEN BROKEN BROKEN
74     // but it is only used in the consolidate dialog.  Ignore for now.
75 
76     sal_Int32  nColonPos = theAreaStr.indexOf(':');
77 
78     if ( nColonPos != -1 )
79         aPosStr = theAreaStr.copy( 0, nColonPos ); // do not include ':' in copy
80     else
81         aPosStr = theAreaStr;
82 
83     thePosStr = aPosStr;
84 }
85 
86 bool ScRangeUtil::IsAbsTabArea( const OUString&   rAreaStr,
87                                 const ScDocument* pDoc,
88                                 std::unique_ptr<ScArea[]>*  ppAreas,
89                                 sal_uInt16*         pAreaCount,
90                                 bool            /* bAcceptCellRef */,
91                                 ScAddress::Details const & rDetails )
92 {
93     OSL_ENSURE( pDoc, "No document given!" );
94     if ( !pDoc )
95         return false;
96 
97     // BROKEN BROKEN BROKEN
98     // but it is only used in the consolidate dialog.  Ignore for now.
99 
100     /*
101      * Expects strings like:
102      *      "$Tabelle1.$A$1:$Tabelle3.$D$17"
103      * If bAcceptCellRef == sal_True then also accept strings like:
104      *      "$Tabelle1.$A$1"
105      *
106      * as result a ScArea-Array is created,
107      * which is published via ppAreas and also has to be deleted this route.
108      */
109 
110     bool    bStrOk = false;
111     OUString  aTempAreaStr(rAreaStr);
112 
113     if ( -1 == aTempAreaStr.indexOf(':') )
114     {
115         aTempAreaStr += ":" + rAreaStr;
116     }
117 
118     sal_Int32   nColonPos = aTempAreaStr.indexOf(':');
119 
120     if (   -1 != nColonPos
121         && -1 != aTempAreaStr.indexOf('.') )
122     {
123         ScRefAddress    aStartPos;
124 
125         OUString aStartPosStr = aTempAreaStr.copy( 0,           nColonPos  );
126         OUString aEndPosStr   = aTempAreaStr.copy( nColonPos+1 );
127 
128         if ( ConvertSingleRef( *pDoc, aStartPosStr, 0, aStartPos, rDetails ) )
129         {
130             ScRefAddress aEndPos;
131             if ( ConvertSingleRef( *pDoc, aEndPosStr, aStartPos.Tab(), aEndPos, rDetails ) )
132             {
133                 aStartPos.SetRelCol( false );
134                 aStartPos.SetRelRow( false );
135                 aStartPos.SetRelTab( false );
136                 aEndPos.SetRelCol( false );
137                 aEndPos.SetRelRow( false );
138                 aEndPos.SetRelTab( false );
139 
140                 bStrOk = true;
141 
142                 if ( ppAreas && pAreaCount ) // Array returned ?
143                 {
144                     SCTAB       nStartTab   = aStartPos.Tab();
145                     SCTAB       nEndTab     = aEndPos.Tab();
146                     sal_uInt16      nTabCount   = static_cast<sal_uInt16>(nEndTab-nStartTab+1);
147                     ppAreas->reset(new ScArea[nTabCount]);
148                     SCTAB       nTab        = 0;
149                     sal_uInt16      i           = 0;
150                     ScArea      theArea( 0, aStartPos.Col(), aStartPos.Row(),
151                                             aEndPos.Col(), aEndPos.Row() );
152 
153                     nTab = nStartTab;
154                     for ( i=0; i<nTabCount; i++ )
155                     {
156                         (*ppAreas)[i] = theArea;
157                         (*ppAreas)[i].nTab = nTab;
158                         nTab++;
159                     }
160                     *pAreaCount = nTabCount;
161                 }
162             }
163         }
164     }
165 
166     return bStrOk;
167 }
168 
169 bool ScRangeUtil::IsAbsArea( const OUString&  rAreaStr,
170                              const ScDocument& rDoc,
171                              SCTAB          nTab,
172                              OUString*      pCompleteStr,
173                              ScRefAddress*  pStartPos,
174                              ScRefAddress*  pEndPos,
175                              ScAddress::Details const & rDetails )
176 {
177     ScRefAddress    startPos;
178     ScRefAddress    endPos;
179 
180     bool bIsAbsArea = ConvertDoubleRef( rDoc, rAreaStr, nTab, startPos, endPos, rDetails );
181 
182     if ( bIsAbsArea )
183     {
184         startPos.SetRelCol( false );
185         startPos.SetRelRow( false );
186         startPos.SetRelTab( false );
187         endPos  .SetRelCol( false );
188         endPos  .SetRelRow( false );
189         endPos  .SetRelTab( false );
190 
191         if ( pCompleteStr )
192         {
193             *pCompleteStr  = startPos.GetRefString( rDoc, MAXTAB+1, rDetails );
194             *pCompleteStr += ":";
195             *pCompleteStr += endPos.GetRefString( rDoc, nTab, rDetails );
196         }
197 
198         if ( pStartPos && pEndPos )
199         {
200             *pStartPos = startPos;
201             *pEndPos   = endPos;
202         }
203     }
204 
205     return bIsAbsArea;
206 }
207 
208 bool ScRangeUtil::IsAbsPos( const OUString&   rPosStr,
209                             const ScDocument& rDoc,
210                             SCTAB           nTab,
211                             OUString*       pCompleteStr,
212                             ScRefAddress*   pPosTripel,
213                             ScAddress::Details const & rDetails )
214 {
215     ScRefAddress    thePos;
216 
217     bool bIsAbsPos = ConvertSingleRef( rDoc, rPosStr, nTab, thePos, rDetails );
218     thePos.SetRelCol( false );
219     thePos.SetRelRow( false );
220     thePos.SetRelTab( false );
221 
222     if ( bIsAbsPos )
223     {
224         if ( pPosTripel )
225             *pPosTripel = thePos;
226         if ( pCompleteStr )
227             *pCompleteStr = thePos.GetRefString( rDoc, MAXTAB+1, rDetails );
228     }
229 
230     return bIsAbsPos;
231 }
232 
233 bool ScRangeUtil::MakeRangeFromName (
234     const OUString& rName,
235     const ScDocument& rDoc,
236     SCTAB           nCurTab,
237     ScRange&        rRange,
238     RutlNameScope   eScope,
239     ScAddress::Details const & rDetails )
240 {
241     bool bResult = false;
242     SCTAB nTab = 0;
243     SCCOL nColStart = 0;
244     SCCOL nColEnd = 0;
245     SCROW nRowStart = 0;
246     SCROW nRowEnd = 0;
247 
248     if( eScope==RUTL_NAMES )
249     {
250         //first handle ui names like local1 (Sheet1), which point to a local range name
251         OUString aName(rName);
252         sal_Int32 nEndPos = aName.lastIndexOf(')');
253         sal_Int32 nStartPos = aName.lastIndexOf(" (");
254         SCTAB nTable = nCurTab;
255         if (nEndPos != -1 && nStartPos != -1)
256         {
257             OUString aSheetName = aName.copy(nStartPos+2, nEndPos-nStartPos-2);
258             if (rDoc.GetTable(aSheetName, nTable))
259             {
260                 aName = aName.copy(0, nStartPos);
261             }
262             else
263                 nTable = nCurTab;
264         }
265         //then check for local range names
266         ScRangeName* pRangeNames = rDoc.GetRangeName( nTable );
267         ScRangeData* pData = nullptr;
268         aName = ScGlobal::getCharClassPtr()->uppercase(aName);
269         if ( pRangeNames )
270             pData = pRangeNames->findByUpperName(aName);
271         if (!pData)
272             pData = rDoc.GetRangeName()->findByUpperName(aName);
273         if (pData)
274         {
275             OUString         aStrArea;
276             ScRefAddress     aStartPos;
277             ScRefAddress     aEndPos;
278 
279             pData->GetSymbol( aStrArea );
280 
281             if ( IsAbsArea( aStrArea, rDoc, nTable,
282                             nullptr, &aStartPos, &aEndPos, rDetails ) )
283             {
284                 nTab       = aStartPos.Tab();
285                 nColStart  = aStartPos.Col();
286                 nRowStart  = aStartPos.Row();
287                 nColEnd    = aEndPos.Col();
288                 nRowEnd    = aEndPos.Row();
289                 bResult    = true;
290             }
291             else
292             {
293                 CutPosString( aStrArea, aStrArea );
294 
295                 if ( IsAbsPos( aStrArea, rDoc, nTable,
296                                           nullptr, &aStartPos, rDetails ) )
297                 {
298                     nTab       = aStartPos.Tab();
299                     nColStart  = nColEnd = aStartPos.Col();
300                     nRowStart  = nRowEnd = aStartPos.Row();
301                     bResult    = true;
302                 }
303             }
304         }
305     }
306     else if( eScope==RUTL_DBASE )
307     {
308         ScDBCollection::NamedDBs& rDbNames = rDoc.GetDBCollection()->getNamedDBs();
309         ScDBData* pData = rDbNames.findByUpperName(ScGlobal::getCharClassPtr()->uppercase(rName));
310         if (pData)
311         {
312             pData->GetArea(nTab, nColStart, nRowStart, nColEnd, nRowEnd);
313             bResult = true;
314         }
315     }
316     else
317     {
318         OSL_FAIL( "ScRangeUtil::MakeRangeFromName" );
319     }
320 
321     if( bResult )
322     {
323         rRange = ScRange( nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab );
324     }
325 
326     return bResult;
327 }
328 
329 void ScRangeStringConverter::AssignString(
330         OUString& rString,
331         const OUString& rNewStr,
332         bool bAppendStr,
333         sal_Unicode cSeparator)
334 {
335     if( bAppendStr )
336     {
337         if( !rNewStr.isEmpty() )
338         {
339             if( !rString.isEmpty() )
340                 rString += OUStringChar(cSeparator);
341             rString += rNewStr;
342         }
343     }
344     else
345         rString = rNewStr;
346 }
347 
348 sal_Int32 ScRangeStringConverter::IndexOf(
349         const OUString& rString,
350         sal_Unicode cSearchChar,
351         sal_Int32 nOffset,
352         sal_Unicode cQuote )
353 {
354     sal_Int32       nLength     = rString.getLength();
355     sal_Int32       nIndex      = nOffset;
356     bool            bQuoted     = false;
357     bool        bExitLoop   = false;
358 
359     while( !bExitLoop && (nIndex >= 0 && nIndex < nLength) )
360     {
361         sal_Unicode cCode = rString[ nIndex ];
362         bExitLoop = (cCode == cSearchChar) && !bQuoted;
363         bQuoted = (bQuoted != (cCode == cQuote));
364         if( !bExitLoop )
365             nIndex++;
366     }
367     return (nIndex < nLength) ? nIndex : -1;
368 }
369 
370 sal_Int32 ScRangeStringConverter::IndexOfDifferent(
371         const OUString& rString,
372         sal_Unicode cSearchChar,
373         sal_Int32 nOffset )
374 {
375     sal_Int32       nLength     = rString.getLength();
376     sal_Int32       nIndex      = nOffset;
377     bool        bExitLoop   = false;
378 
379     while( !bExitLoop && (nIndex >= 0 && nIndex < nLength) )
380     {
381         bExitLoop = (rString[ nIndex ] != cSearchChar);
382         if( !bExitLoop )
383             nIndex++;
384     }
385     return (nIndex < nLength) ? nIndex : -1;
386 }
387 
388 void ScRangeStringConverter::GetTokenByOffset(
389         OUString& rToken,
390         const OUString& rString,
391         sal_Int32& nOffset,
392         sal_Unicode cSeparator,
393         sal_Unicode cQuote)
394 {
395     sal_Int32 nLength = rString.getLength();
396     if( nOffset == -1 || nOffset >= nLength )
397     {
398         rToken.clear();
399         nOffset = -1;
400     }
401     else
402     {
403         sal_Int32 nTokenEnd = IndexOf( rString, cSeparator, nOffset, cQuote );
404         if( nTokenEnd < 0 )
405             nTokenEnd = nLength;
406         rToken = rString.copy( nOffset, nTokenEnd - nOffset );
407 
408         sal_Int32 nNextBegin = IndexOfDifferent( rString, cSeparator, nTokenEnd );
409         nOffset = (nNextBegin < 0) ? nLength : nNextBegin;
410     }
411 }
412 
413 void ScRangeStringConverter::AppendTableName(OUStringBuffer& rBuf, const OUString& rTabName)
414 {
415     // quote character is always "'"
416     OUString aQuotedTab(rTabName);
417     ScCompiler::CheckTabQuotes(aQuotedTab);
418     rBuf.append(aQuotedTab);
419 }
420 
421 sal_Int32 ScRangeStringConverter::GetTokenCount( const OUString& rString, sal_Unicode cSeparator )
422 {
423     OUString    sToken;
424     sal_Int32   nCount = 0;
425     sal_Int32   nOffset = 0;
426     while( nOffset >= 0 )
427     {
428         GetTokenByOffset( sToken, rString, nOffset, '\'', cSeparator );
429         if( nOffset >= 0 )
430             nCount++;
431     }
432     return nCount;
433 }
434 
435 bool ScRangeStringConverter::GetAddressFromString(
436         ScAddress& rAddress,
437         const OUString& rAddressStr,
438         const ScDocument& rDocument,
439         FormulaGrammar::AddressConvention eConv,
440         sal_Int32& nOffset,
441         sal_Unicode cSeparator,
442         sal_Unicode cQuote )
443 {
444     OUString sToken;
445     GetTokenByOffset( sToken, rAddressStr, nOffset, cSeparator, cQuote );
446     if( nOffset >= 0 )
447     {
448         if ((rAddress.Parse( sToken, rDocument, eConv ) & ScRefFlags::VALID) == ScRefFlags::VALID)
449             return true;
450         ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention();
451         if (eConv != eConvUI)
452             return ((rAddress.Parse(sToken, rDocument, eConvUI) & ScRefFlags::VALID) == ScRefFlags::VALID);
453     }
454     return false;
455 }
456 
457 bool ScRangeStringConverter::GetRangeFromString(
458         ScRange& rRange,
459         const OUString& rRangeStr,
460         const ScDocument& rDocument,
461         FormulaGrammar::AddressConvention eConv,
462         sal_Int32& nOffset,
463         sal_Unicode cSeparator,
464         sal_Unicode cQuote )
465 {
466     OUString sToken;
467     bool bResult(false);
468     GetTokenByOffset( sToken, rRangeStr, nOffset, cSeparator, cQuote );
469     if( nOffset >= 0 )
470     {
471         sal_Int32 nIndex = IndexOf( sToken, ':', 0, cQuote );
472         OUString aUIString(sToken);
473 
474         if( nIndex < 0 )
475         {
476             if ( aUIString[0] == '.' )
477                 aUIString = aUIString.copy( 1 );
478             bResult = (rRange.aStart.Parse( aUIString, rDocument, eConv) & ScRefFlags::VALID) ==
479                                                                                                      ScRefFlags::VALID;
480             ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention();
481             if (!bResult && eConv != eConvUI)
482                 bResult = (rRange.aStart.Parse(aUIString, rDocument, eConvUI) & ScRefFlags::VALID) ==
483                                                                                                          ScRefFlags::VALID;
484             rRange.aEnd = rRange.aStart;
485         }
486         else
487         {
488             if ( aUIString[0] == '.' )
489             {
490                 aUIString = aUIString.copy( 1 );
491                 --nIndex;
492             }
493 
494             if ( nIndex < aUIString.getLength() - 1 &&
495                     aUIString[ nIndex + 1 ] == '.' )
496                 aUIString = aUIString.replaceAt( nIndex + 1, 1, "" );
497 
498             bResult = ((rRange.Parse(aUIString, rDocument, eConv) & ScRefFlags::VALID) ==
499                                                                                               ScRefFlags::VALID);
500 
501             // #i77703# chart ranges in the file format contain both sheet names, even for an external reference sheet.
502             // This isn't parsed by ScRange, so try to parse the two Addresses then.
503             if (!bResult)
504             {
505                 bResult = ((rRange.aStart.Parse( aUIString.copy(0, nIndex), rDocument, eConv)
506                                & ScRefFlags::VALID) == ScRefFlags::VALID)
507                           &&
508                           ((rRange.aEnd.Parse( aUIString.copy(nIndex+1), rDocument, eConv)
509                                & ScRefFlags::VALID) == ScRefFlags::VALID);
510 
511                 ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention();
512                 if (!bResult && eConv != eConvUI)
513                 {
514                     bResult = ((rRange.aStart.Parse( aUIString.copy(0, nIndex), rDocument, eConvUI)
515                                    & ScRefFlags::VALID) == ScRefFlags::VALID)
516                               &&
517                               ((rRange.aEnd.Parse( aUIString.copy(nIndex+1), rDocument, eConvUI)
518                                    & ScRefFlags::VALID) == ScRefFlags::VALID);
519                 }
520             }
521         }
522     }
523     return bResult;
524 }
525 
526 bool ScRangeStringConverter::GetRangeListFromString(
527         ScRangeList& rRangeList,
528         const OUString& rRangeListStr,
529         const ScDocument& rDocument,
530         FormulaGrammar::AddressConvention eConv,
531         sal_Unicode cSeparator,
532         sal_Unicode cQuote )
533 {
534     bool bRet = true;
535     OSL_ENSURE( !rRangeListStr.isEmpty(), "ScXMLConverter::GetRangeListFromString - empty string!" );
536     sal_Int32 nOffset = 0;
537     while( nOffset >= 0 )
538     {
539         ScRange aRange;
540         if (
541              GetRangeFromString( aRange, rRangeListStr, rDocument, eConv, nOffset, cSeparator, cQuote ) &&
542              (nOffset >= 0)
543            )
544         {
545             rRangeList.push_back( aRange );
546         }
547         else if (nOffset > -1)
548             bRet = false;
549     }
550     return bRet;
551 }
552 
553 bool ScRangeStringConverter::GetAreaFromString(
554         ScArea& rArea,
555         const OUString& rRangeStr,
556         const ScDocument& rDocument,
557         FormulaGrammar::AddressConvention eConv,
558         sal_Int32& nOffset,
559         sal_Unicode cSeparator )
560 {
561     ScRange aScRange;
562     bool bResult(false);
563     if( GetRangeFromString( aScRange, rRangeStr, rDocument, eConv, nOffset, cSeparator ) && (nOffset >= 0) )
564     {
565         rArea.nTab = aScRange.aStart.Tab();
566         rArea.nColStart = aScRange.aStart.Col();
567         rArea.nRowStart = aScRange.aStart.Row();
568         rArea.nColEnd = aScRange.aEnd.Col();
569         rArea.nRowEnd = aScRange.aEnd.Row();
570         bResult = true;
571     }
572     return bResult;
573 }
574 
575 bool ScRangeStringConverter::GetRangeFromString(
576         table::CellRangeAddress& rRange,
577         const OUString& rRangeStr,
578         const ScDocument& rDocument,
579         FormulaGrammar::AddressConvention eConv,
580         sal_Int32& nOffset,
581         sal_Unicode cSeparator )
582 {
583     ScRange aScRange;
584     bool bResult(false);
585     if( GetRangeFromString( aScRange, rRangeStr, rDocument, eConv, nOffset, cSeparator ) && (nOffset >= 0) )
586     {
587         ScUnoConversion::FillApiRange( rRange, aScRange );
588         bResult = true;
589     }
590     return bResult;
591 }
592 
593 void ScRangeStringConverter::GetStringFromAddress(
594         OUString& rString,
595         const ScAddress& rAddress,
596         const ScDocument* pDocument,
597         FormulaGrammar::AddressConvention eConv,
598         sal_Unicode cSeparator,
599         bool bAppendStr,
600         ScRefFlags nFormatFlags )
601 {
602     if (pDocument && pDocument->HasTable(rAddress.Tab()))
603     {
604         OUString sAddress(rAddress.Format(nFormatFlags, pDocument, eConv));
605         AssignString( rString, sAddress, bAppendStr, cSeparator );
606     }
607 }
608 
609 void ScRangeStringConverter::GetStringFromRange(
610         OUString& rString,
611         const ScRange& rRange,
612         const ScDocument* pDocument,
613         FormulaGrammar::AddressConvention eConv,
614         sal_Unicode cSeparator,
615         bool bAppendStr,
616         ScRefFlags nFormatFlags )
617 {
618     if (pDocument && pDocument->HasTable(rRange.aStart.Tab()))
619     {
620         ScAddress aStartAddress( rRange.aStart );
621         ScAddress aEndAddress( rRange.aEnd );
622         OUString sStartAddress(aStartAddress.Format(nFormatFlags, pDocument, eConv));
623         OUString sEndAddress(aEndAddress.Format(nFormatFlags, pDocument, eConv));
624         AssignString(
625             rString, sStartAddress + ":" + sEndAddress, bAppendStr, cSeparator);
626     }
627 }
628 
629 void ScRangeStringConverter::GetStringFromRangeList(
630         OUString& rString,
631         const ScRangeList* pRangeList,
632         const ScDocument* pDocument,
633         FormulaGrammar::AddressConvention eConv,
634         sal_Unicode cSeparator )
635 {
636     OUString sRangeListStr;
637     if( pRangeList )
638     {
639         for( size_t nIndex = 0, nCount = pRangeList->size(); nIndex < nCount; nIndex++ )
640         {
641             const ScRange & rRange = (*pRangeList)[nIndex];
642             GetStringFromRange( sRangeListStr, rRange, pDocument, eConv, cSeparator, true );
643         }
644     }
645     rString = sRangeListStr;
646 }
647 
648 void ScRangeStringConverter::GetStringFromArea(
649         OUString& rString,
650         const ScArea& rArea,
651         const ScDocument* pDocument,
652         FormulaGrammar::AddressConvention eConv,
653         sal_Unicode cSeparator,
654         bool bAppendStr,
655         ScRefFlags nFormatFlags )
656 {
657     ScRange aRange( rArea.nColStart, rArea.nRowStart, rArea.nTab, rArea.nColEnd, rArea.nRowEnd, rArea.nTab );
658     GetStringFromRange( rString, aRange, pDocument, eConv, cSeparator, bAppendStr, nFormatFlags );
659 }
660 
661 void ScRangeStringConverter::GetStringFromAddress(
662         OUString& rString,
663         const table::CellAddress& rAddress,
664         const ScDocument* pDocument,
665         FormulaGrammar::AddressConvention eConv,
666         sal_Unicode cSeparator,
667         bool bAppendStr )
668 {
669     ScAddress aScAddress( static_cast<SCCOL>(rAddress.Column), static_cast<SCROW>(rAddress.Row), rAddress.Sheet );
670     GetStringFromAddress( rString, aScAddress, pDocument, eConv, cSeparator, bAppendStr );
671 }
672 
673 void ScRangeStringConverter::GetStringFromRange(
674         OUString& rString,
675         const table::CellRangeAddress& rRange,
676         const ScDocument* pDocument,
677         FormulaGrammar::AddressConvention eConv,
678         sal_Unicode cSeparator,
679         bool bAppendStr,
680         ScRefFlags nFormatFlags )
681 {
682     ScRange aScRange( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), rRange.Sheet,
683         static_cast<SCCOL>(rRange.EndColumn), static_cast<SCROW>(rRange.EndRow), rRange.Sheet );
684     GetStringFromRange( rString, aScRange, pDocument, eConv, cSeparator, bAppendStr, nFormatFlags );
685 }
686 
687 void ScRangeStringConverter::GetStringFromRangeList(
688         OUString& rString,
689         const uno::Sequence< table::CellRangeAddress >& rRangeSeq,
690         const ScDocument* pDocument,
691         FormulaGrammar::AddressConvention eConv,
692         sal_Unicode cSeparator )
693 {
694     OUString sRangeListStr;
695     for( const table::CellRangeAddress& rRange : rRangeSeq )
696     {
697         GetStringFromRange( sRangeListStr, rRange, pDocument, eConv, cSeparator, true );
698     }
699     rString = sRangeListStr;
700 }
701 
702 static void lcl_appendCellAddress(
703     OUStringBuffer& rBuf, const ScDocument& rDoc, const ScAddress& rCell,
704     const ScAddress::ExternalInfo& rExtInfo)
705 {
706     if (rExtInfo.mbExternal)
707     {
708         ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
709         const OUString* pFilePath = pRefMgr->getExternalFileName(rExtInfo.mnFileId, true);
710         if (!pFilePath)
711             return;
712 
713         sal_Unicode cQuote = '\'';
714         rBuf.append(cQuote);
715         rBuf.append(*pFilePath);
716         rBuf.append(cQuote);
717         rBuf.append('#');
718         rBuf.append('$');
719         ScRangeStringConverter::AppendTableName(rBuf, rExtInfo.maTabName);
720         rBuf.append('.');
721 
722         OUString aAddr(rCell.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()));
723         rBuf.append(aAddr);
724     }
725     else
726     {
727         OUString aAddr(rCell.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention()));
728         rBuf.append(aAddr);
729     }
730 }
731 
732 static void lcl_appendCellRangeAddress(
733     OUStringBuffer& rBuf, const ScDocument& rDoc, const ScAddress& rCell1, const ScAddress& rCell2,
734     const ScAddress::ExternalInfo& rExtInfo1, const ScAddress::ExternalInfo& rExtInfo2)
735 {
736     if (rExtInfo1.mbExternal)
737     {
738         OSL_ENSURE(rExtInfo2.mbExternal, "2nd address is not external!?");
739         OSL_ENSURE(rExtInfo1.mnFileId == rExtInfo2.mnFileId, "File IDs do not match between 1st and 2nd addresses.");
740 
741         ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
742         const OUString* pFilePath = pRefMgr->getExternalFileName(rExtInfo1.mnFileId, true);
743         if (!pFilePath)
744             return;
745 
746         sal_Unicode cQuote = '\'';
747         rBuf.append(cQuote);
748         rBuf.append(*pFilePath);
749         rBuf.append(cQuote);
750         rBuf.append('#');
751         rBuf.append('$');
752         ScRangeStringConverter::AppendTableName(rBuf, rExtInfo1.maTabName);
753         rBuf.append('.');
754 
755         OUString aAddr(rCell1.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()));
756         rBuf.append(aAddr);
757 
758         rBuf.append(":");
759 
760         if (rExtInfo1.maTabName != rExtInfo2.maTabName)
761         {
762             rBuf.append('$');
763             ScRangeStringConverter::AppendTableName(rBuf, rExtInfo2.maTabName);
764             rBuf.append('.');
765         }
766 
767         aAddr = rCell2.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention());
768         rBuf.append(aAddr);
769     }
770     else
771     {
772         ScRange aRange;
773         aRange.aStart = rCell1;
774         aRange.aEnd   = rCell2;
775         OUString aAddr(aRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
776         rBuf.append(aAddr);
777     }
778 }
779 
780 void ScRangeStringConverter::GetStringFromXMLRangeString( OUString& rString, const OUString& rXMLRange, const ScDocument& rDoc )
781 {
782     FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
783     const sal_Unicode cSepNew = ScCompiler::GetNativeSymbolChar(ocSep);
784 
785     OUStringBuffer aRetStr;
786     sal_Int32 nOffset = 0;
787     bool bFirst = true;
788 
789     while (nOffset >= 0)
790     {
791         OUString aToken;
792         GetTokenByOffset(aToken, rXMLRange, nOffset);
793         if (nOffset < 0)
794             break;
795 
796         sal_Int32 nSepPos = IndexOf(aToken, ':', 0);
797         if (nSepPos >= 0)
798         {
799             // Cell range
800             OUString aBeginCell = aToken.copy(0, nSepPos);
801             OUString aEndCell   = aToken.copy(nSepPos+1);
802 
803             if (aBeginCell.isEmpty() || aEndCell.isEmpty())
804                 // both cell addresses must exist for this to work.
805                 continue;
806 
807             sal_Int32 nEndCellDotPos = aEndCell.indexOf('.');
808             if (nEndCellDotPos <= 0)
809             {
810                 // initialize buffer with table name...
811                 sal_Int32 nDotPos = IndexOf(aBeginCell, '.', 0);
812                 OUStringBuffer aBuf(aBeginCell.subView(0, nDotPos));
813 
814                 if (nEndCellDotPos == 0)
815                 {
816                     // workaround for old syntax (probably pre-chart2 age?)
817                     // e.g. Sheet1.A1:.B2
818                     aBuf.append(aEndCell);
819                 }
820                 else if (nEndCellDotPos < 0)
821                 {
822                     // sheet name in the end cell is omitted (e.g. Sheet2.A1:B2).
823                     aBuf.append('.');
824                     aBuf.append(aEndCell);
825                 }
826                 aEndCell = aBuf.makeStringAndClear();
827             }
828 
829             ScAddress::ExternalInfo aExtInfo1, aExtInfo2;
830             ScAddress aCell1, aCell2;
831             ScRefFlags nRet = aCell1.Parse(aBeginCell, rDoc, FormulaGrammar::CONV_OOO, &aExtInfo1);
832             if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
833             {
834                 // first cell is invalid.
835                 if (eConv == FormulaGrammar::CONV_OOO)
836                     continue;
837 
838                 nRet = aCell1.Parse(aBeginCell, rDoc, eConv, &aExtInfo1);
839                 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
840                     // first cell is really invalid.
841                     continue;
842             }
843 
844             nRet = aCell2.Parse(aEndCell, rDoc, FormulaGrammar::CONV_OOO, &aExtInfo2);
845             if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
846             {
847                 // second cell is invalid.
848                 if (eConv == FormulaGrammar::CONV_OOO)
849                     continue;
850 
851                 nRet = aCell2.Parse(aEndCell, rDoc, eConv, &aExtInfo2);
852                 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
853                     // second cell is really invalid.
854                     continue;
855             }
856 
857             if (aExtInfo1.mnFileId != aExtInfo2.mnFileId || aExtInfo1.mbExternal != aExtInfo2.mbExternal)
858                 // external info inconsistency.
859                 continue;
860 
861             // All looks good!
862 
863             if (bFirst)
864                 bFirst = false;
865             else
866                 aRetStr.append(cSepNew);
867 
868             lcl_appendCellRangeAddress(aRetStr, rDoc, aCell1, aCell2, aExtInfo1, aExtInfo2);
869         }
870         else
871         {
872             // Chart always saves ranges using CONV_OOO convention.
873             ScAddress::ExternalInfo aExtInfo;
874             ScAddress aCell;
875             ScRefFlags nRet = aCell.Parse(aToken, rDoc, ::formula::FormulaGrammar::CONV_OOO, &aExtInfo);
876             if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO )
877             {
878                 nRet = aCell.Parse(aToken, rDoc, eConv, &aExtInfo);
879                 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
880                     continue;
881             }
882 
883             // Looks good!
884 
885             if (bFirst)
886                 bFirst = false;
887             else
888                 aRetStr.append(cSepNew);
889 
890             lcl_appendCellAddress(aRetStr, rDoc, aCell, aExtInfo);
891         }
892     }
893 
894     rString = aRetStr.makeStringAndClear();
895 }
896 
897 ScRangeData* ScRangeStringConverter::GetRangeDataFromString( const OUString& rString, const SCTAB nTab,
898         const ScDocument& rDoc, formula::FormulaGrammar::AddressConvention eConv )
899 {
900     // This may be called with an external 'doc'#name but wouldn't find any.
901 
902     // Dot '.' is not allowed in range names, if present only lookup if it's a
903     // sheet-local name. Same for '!' Excel syntax.
904     // If eConv == FormulaGrammar::CONV_A1_XL_A1 then try both, first our own.
905     sal_Int32 nIndex = -1;
906     if (eConv == FormulaGrammar::CONV_OOO || eConv == FormulaGrammar::CONV_A1_XL_A1)
907         nIndex = ScGlobal::FindUnquoted( rString, '.');
908     if (nIndex < 0 && (eConv == FormulaGrammar::CONV_A1_XL_A1
909                 || eConv == FormulaGrammar::CONV_XL_A1
910                 || eConv == FormulaGrammar::CONV_XL_R1C1
911                 || eConv == FormulaGrammar::CONV_XL_OOX))
912         nIndex = ScGlobal::FindUnquoted( rString, '!');
913 
914     if (nIndex >= 0)
915     {
916         if (nIndex == 0)
917             return nullptr;     // Can't be a name.
918 
919         OUString aTab( rString.copy( 0, nIndex));
920         ScGlobal::EraseQuotes( aTab, '\'');
921         SCTAB nLocalTab;
922         if (!rDoc.GetTable( aTab, nLocalTab))
923             return nullptr;
924 
925         ScRangeName* pLocalRangeName = rDoc.GetRangeName(nLocalTab);
926         if (!pLocalRangeName)
927             return nullptr;
928 
929         const OUString aName( rString.copy( nIndex+1));
930         return pLocalRangeName->findByUpperName( ScGlobal::getCharClassPtr()->uppercase( aName));
931     }
932 
933     ScRangeName* pLocalRangeName = rDoc.GetRangeName(nTab);
934     ScRangeData* pData = nullptr;
935     OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rString);
936     if(pLocalRangeName)
937     {
938         pData = pLocalRangeName->findByUpperName(aUpperName);
939     }
940     if (!pData)
941     {
942         ScRangeName* pGlobalRangeName = rDoc.GetRangeName();
943         if (pGlobalRangeName)
944         {
945             pData = pGlobalRangeName->findByUpperName(aUpperName);
946         }
947     }
948     return pData;
949 }
950 
951 ScArea::ScArea( SCTAB tab,
952                 SCCOL colStart, SCROW rowStart,
953                 SCCOL colEnd,   SCROW rowEnd ) :
954         nTab     ( tab ),
955         nColStart( colStart ),  nRowStart( rowStart ),
956         nColEnd  ( colEnd ),    nRowEnd  ( rowEnd )
957 {
958 }
959 
960 bool ScArea::operator==( const ScArea& r ) const
961 {
962     return (   (nTab        == r.nTab)
963             && (nColStart   == r.nColStart)
964             && (nRowStart   == r.nRowStart)
965             && (nColEnd     == r.nColEnd)
966             && (nRowEnd     == r.nRowEnd) );
967 }
968 
969 ScAreaNameIterator::ScAreaNameIterator( const ScDocument& rDoc ) :
970     pRangeName(rDoc.GetRangeName()),
971     pDBCollection(rDoc.GetDBCollection()),
972     bFirstPass(true)
973 {
974     if (pRangeName)
975     {
976         maRNPos = pRangeName->begin();
977         maRNEnd = pRangeName->end();
978     }
979 }
980 
981 bool ScAreaNameIterator::Next( OUString& rName, ScRange& rRange )
982 {
983     for (;;)
984     {
985         if ( bFirstPass )                                   // first the area names
986         {
987             if ( pRangeName && maRNPos != maRNEnd )
988             {
989                 const ScRangeData& rData = *maRNPos->second;
990                 ++maRNPos;
991                 bool bValid = rData.IsValidReference(rRange);
992                 if (bValid)
993                 {
994                     rName = rData.GetName();
995                     return true;                            // found
996                 }
997             }
998             else
999             {
1000                 bFirstPass = false;
1001                 if (pDBCollection)
1002                 {
1003                     const ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs();
1004                     maDBPos = rDBs.begin();
1005                     maDBEnd = rDBs.end();
1006                 }
1007             }
1008         }
1009 
1010         if ( !bFirstPass )                                  // then the DB areas
1011         {
1012             if (pDBCollection && maDBPos != maDBEnd)
1013             {
1014                 const ScDBData& rData = **maDBPos;
1015                 ++maDBPos;
1016                 rData.GetArea(rRange);
1017                 rName = rData.GetName();
1018                 return true;                                // found
1019             }
1020             else
1021                 return false;                               // nothing left
1022         }
1023     }
1024 }
1025 
1026 void ScRangeUpdater::UpdateInsertTab(ScAddress& rAddr, const sc::RefUpdateInsertTabContext& rCxt)
1027 {
1028     if (rCxt.mnInsertPos <= rAddr.Tab())
1029     {
1030         rAddr.IncTab(rCxt.mnSheets);
1031     }
1032 }
1033 
1034 void ScRangeUpdater::UpdateDeleteTab(ScAddress& rAddr, const sc::RefUpdateDeleteTabContext& rCxt)
1035 {
1036     if (rCxt.mnDeletePos <= rAddr.Tab())
1037     {
1038         rAddr.IncTab(-rCxt.mnSheets);
1039     }
1040 }
1041 
1042 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1043