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 
MakeArea(const OUString & rAreaStr,ScArea & rArea,const ScDocument & rDoc,SCTAB nTab,ScAddress::Details const & rDetails)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 
CutPosString(const OUString & theAreaStr,OUString & thePosStr)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 
IsAbsTabArea(const OUString & rAreaStr,const ScDocument * pDoc,std::unique_ptr<ScArea[]> * ppAreas,sal_uInt16 * pAreaCount,bool,ScAddress::Details const & rDetails)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 
IsAbsArea(const OUString & rAreaStr,const ScDocument & rDoc,SCTAB nTab,OUString * pCompleteStr,ScRefAddress * pStartPos,ScRefAddress * pEndPos,ScAddress::Details const & rDetails)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 
IsAbsPos(const OUString & rPosStr,const ScDocument & rDoc,SCTAB nTab,OUString * pCompleteStr,ScRefAddress * pPosTripel,ScAddress::Details const & rDetails)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 
MakeRangeFromName(const OUString & rName,const ScDocument & rDoc,SCTAB nCurTab,ScRange & rRange,RutlNameScope eScope,ScAddress::Details const & rDetails,bool bUseDetailsPos)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     bool bUseDetailsPos )
241 {
242     bool bResult = false;
243     if (rName.isEmpty())
244         return bResult;
245 
246     SCTAB nTab = 0;
247     SCCOL nColStart = 0;
248     SCCOL nColEnd = 0;
249     SCROW nRowStart = 0;
250     SCROW nRowEnd = 0;
251 
252     if (eScope == RUTL_NAMES || eScope == RUTL_NAMES_LOCAL || eScope == RUTL_NAMES_GLOBAL)
253     {
254         OUString aName(rName);
255         SCTAB nTable = nCurTab;
256 
257         if (eScope != RUTL_NAMES_GLOBAL)
258         {
259             // First handle UI names like "local1 (Sheet1)", which point to a
260             // local range name.
261             const sal_Int32 nEndPos = aName.getLength() - 1;
262             if (rName[nEndPos] == ')')
263             {
264                 const sal_Int32 nStartPos = aName.indexOf(" (");
265                 if (nStartPos != -1)
266                 {
267                     OUString aSheetName = aName.copy(nStartPos+2, nEndPos-nStartPos-2);
268                     if (rDoc.GetTable(aSheetName, nTable))
269                     {
270                         aName = aName.copy(0, nStartPos);
271                         eScope = RUTL_NAMES_LOCAL;
272                     }
273                     else
274                         nTable = nCurTab;
275                 }
276             }
277         }
278 
279         aName = ScGlobal::getCharClassPtr()->uppercase(aName);
280         ScRangeData* pData = nullptr;
281         if (eScope != RUTL_NAMES_GLOBAL)
282         {
283             // Check for local range names.
284             ScRangeName* pRangeNames = rDoc.GetRangeName( nTable );
285             if ( pRangeNames )
286                 pData = pRangeNames->findByUpperName(aName);
287         }
288         if (!pData && eScope != RUTL_NAMES_LOCAL)
289             pData = rDoc.GetRangeName()->findByUpperName(aName);
290         if (pData)
291         {
292             OUString         aStrArea;
293             ScRefAddress     aStartPos;
294             ScRefAddress     aEndPos;
295 
296             // tdf#138646: use the current grammar of the document and passed
297             // address convention.
298             // tdf#145077: create range string according to current cell cursor
299             // position if expression has relative references and details say so.
300             if (bUseDetailsPos)
301                 pData->GetSymbol( aStrArea, ScAddress( rDetails.nCol, rDetails.nRow, nCurTab),
302                         FormulaGrammar::mergeToGrammar(rDoc.GetGrammar(), rDetails.eConv));
303             else
304                 pData->GetSymbol( aStrArea,
305                         FormulaGrammar::mergeToGrammar(rDoc.GetGrammar(), rDetails.eConv));
306 
307             if ( IsAbsArea( aStrArea, rDoc, nTable,
308                             nullptr, &aStartPos, &aEndPos, rDetails ) )
309             {
310                 nTab       = aStartPos.Tab();
311                 nColStart  = aStartPos.Col();
312                 nRowStart  = aStartPos.Row();
313                 nColEnd    = aEndPos.Col();
314                 nRowEnd    = aEndPos.Row();
315                 bResult    = true;
316             }
317             else
318             {
319                 CutPosString( aStrArea, aStrArea );
320 
321                 if ( IsAbsPos( aStrArea, rDoc, nTable,
322                                           nullptr, &aStartPos, rDetails ) )
323                 {
324                     nTab       = aStartPos.Tab();
325                     nColStart  = nColEnd = aStartPos.Col();
326                     nRowStart  = nRowEnd = aStartPos.Row();
327                     bResult    = true;
328                 }
329             }
330         }
331     }
332     else if( eScope==RUTL_DBASE )
333     {
334         ScDBCollection::NamedDBs& rDbNames = rDoc.GetDBCollection()->getNamedDBs();
335         ScDBData* pData = rDbNames.findByUpperName(ScGlobal::getCharClassPtr()->uppercase(rName));
336         if (pData)
337         {
338             pData->GetArea(nTab, nColStart, nRowStart, nColEnd, nRowEnd);
339             bResult = true;
340         }
341     }
342     else
343     {
344         OSL_FAIL( "ScRangeUtil::MakeRangeFromName" );
345     }
346 
347     if( bResult )
348     {
349         rRange = ScRange( nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab );
350     }
351 
352     return bResult;
353 }
354 
AssignString(OUString & rString,const OUString & rNewStr,bool bAppendStr,sal_Unicode cSeparator)355 void ScRangeStringConverter::AssignString(
356         OUString& rString,
357         const OUString& rNewStr,
358         bool bAppendStr,
359         sal_Unicode cSeparator)
360 {
361     if( bAppendStr )
362     {
363         if( !rNewStr.isEmpty() )
364         {
365             if( !rString.isEmpty() )
366                 rString += OUStringChar(cSeparator);
367             rString += rNewStr;
368         }
369     }
370     else
371         rString = rNewStr;
372 }
373 
IndexOf(const OUString & rString,sal_Unicode cSearchChar,sal_Int32 nOffset,sal_Unicode cQuote)374 sal_Int32 ScRangeStringConverter::IndexOf(
375         const OUString& rString,
376         sal_Unicode cSearchChar,
377         sal_Int32 nOffset,
378         sal_Unicode cQuote )
379 {
380     sal_Int32       nLength     = rString.getLength();
381     sal_Int32       nIndex      = nOffset;
382     bool            bQuoted     = false;
383     bool        bExitLoop   = false;
384 
385     while( !bExitLoop && (nIndex >= 0 && nIndex < nLength) )
386     {
387         sal_Unicode cCode = rString[ nIndex ];
388         bExitLoop = (cCode == cSearchChar) && !bQuoted;
389         bQuoted = (bQuoted != (cCode == cQuote));
390         if( !bExitLoop )
391             nIndex++;
392     }
393     return (nIndex < nLength) ? nIndex : -1;
394 }
395 
IndexOfDifferent(const OUString & rString,sal_Unicode cSearchChar,sal_Int32 nOffset)396 sal_Int32 ScRangeStringConverter::IndexOfDifferent(
397         const OUString& rString,
398         sal_Unicode cSearchChar,
399         sal_Int32 nOffset )
400 {
401     sal_Int32       nLength     = rString.getLength();
402     sal_Int32       nIndex      = nOffset;
403     bool        bExitLoop   = false;
404 
405     while( !bExitLoop && (nIndex >= 0 && nIndex < nLength) )
406     {
407         bExitLoop = (rString[ nIndex ] != cSearchChar);
408         if( !bExitLoop )
409             nIndex++;
410     }
411     return (nIndex < nLength) ? nIndex : -1;
412 }
413 
GetTokenByOffset(OUString & rToken,const OUString & rString,sal_Int32 & nOffset,sal_Unicode cSeparator,sal_Unicode cQuote)414 void ScRangeStringConverter::GetTokenByOffset(
415         OUString& rToken,
416         const OUString& rString,
417         sal_Int32& nOffset,
418         sal_Unicode cSeparator,
419         sal_Unicode cQuote)
420 {
421     sal_Int32 nLength = rString.getLength();
422     if( nOffset == -1 || nOffset >= nLength )
423     {
424         rToken.clear();
425         nOffset = -1;
426     }
427     else
428     {
429         sal_Int32 nTokenEnd = IndexOf( rString, cSeparator, nOffset, cQuote );
430         if( nTokenEnd < 0 )
431             nTokenEnd = nLength;
432         rToken = rString.copy( nOffset, nTokenEnd - nOffset );
433 
434         sal_Int32 nNextBegin = IndexOfDifferent( rString, cSeparator, nTokenEnd );
435         nOffset = (nNextBegin < 0) ? nLength : nNextBegin;
436     }
437 }
438 
AppendTableName(OUStringBuffer & rBuf,const OUString & rTabName)439 void ScRangeStringConverter::AppendTableName(OUStringBuffer& rBuf, const OUString& rTabName)
440 {
441     // quote character is always "'"
442     OUString aQuotedTab(rTabName);
443     ScCompiler::CheckTabQuotes(aQuotedTab);
444     rBuf.append(aQuotedTab);
445 }
446 
GetTokenCount(const OUString & rString,sal_Unicode cSeparator)447 sal_Int32 ScRangeStringConverter::GetTokenCount( const OUString& rString, sal_Unicode cSeparator )
448 {
449     OUString    sToken;
450     sal_Int32   nCount = 0;
451     sal_Int32   nOffset = 0;
452     while( nOffset >= 0 )
453     {
454         GetTokenByOffset( sToken, rString, nOffset, '\'', cSeparator );
455         if( nOffset >= 0 )
456             nCount++;
457     }
458     return nCount;
459 }
460 
GetAddressFromString(ScAddress & rAddress,const OUString & rAddressStr,const ScDocument & rDocument,FormulaGrammar::AddressConvention eConv,sal_Int32 & nOffset,sal_Unicode cSeparator,sal_Unicode cQuote)461 bool ScRangeStringConverter::GetAddressFromString(
462         ScAddress& rAddress,
463         const OUString& rAddressStr,
464         const ScDocument& rDocument,
465         FormulaGrammar::AddressConvention eConv,
466         sal_Int32& nOffset,
467         sal_Unicode cSeparator,
468         sal_Unicode cQuote )
469 {
470     OUString sToken;
471     GetTokenByOffset( sToken, rAddressStr, nOffset, cSeparator, cQuote );
472     if( nOffset >= 0 )
473     {
474         if ((rAddress.Parse( sToken, rDocument, eConv ) & ScRefFlags::VALID) == ScRefFlags::VALID)
475             return true;
476         ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention();
477         if (eConv != eConvUI)
478             return ((rAddress.Parse(sToken, rDocument, eConvUI) & ScRefFlags::VALID) == ScRefFlags::VALID);
479     }
480     return false;
481 }
482 
GetRangeFromString(ScRange & rRange,const OUString & rRangeStr,const ScDocument & rDocument,FormulaGrammar::AddressConvention eConv,sal_Int32 & nOffset,sal_Unicode cSeparator,sal_Unicode cQuote)483 bool ScRangeStringConverter::GetRangeFromString(
484         ScRange& rRange,
485         const OUString& rRangeStr,
486         const ScDocument& rDocument,
487         FormulaGrammar::AddressConvention eConv,
488         sal_Int32& nOffset,
489         sal_Unicode cSeparator,
490         sal_Unicode cQuote )
491 {
492     OUString sToken;
493     bool bResult(false);
494     GetTokenByOffset( sToken, rRangeStr, nOffset, cSeparator, cQuote );
495     if( nOffset >= 0 )
496     {
497         sal_Int32 nIndex = IndexOf( sToken, ':', 0, cQuote );
498         OUString aUIString(sToken);
499 
500         if( nIndex < 0 )
501         {
502             if ( aUIString[0] == '.' )
503                 aUIString = aUIString.copy( 1 );
504             bResult = (rRange.aStart.Parse( aUIString, rDocument, eConv) & ScRefFlags::VALID) ==
505                                                                                                      ScRefFlags::VALID;
506             ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention();
507             if (!bResult && eConv != eConvUI)
508                 bResult = (rRange.aStart.Parse(aUIString, rDocument, eConvUI) & ScRefFlags::VALID) ==
509                                                                                                          ScRefFlags::VALID;
510             rRange.aEnd = rRange.aStart;
511         }
512         else
513         {
514             if ( aUIString[0] == '.' )
515             {
516                 aUIString = aUIString.copy( 1 );
517                 --nIndex;
518             }
519 
520             if ( nIndex < aUIString.getLength() - 1 &&
521                     aUIString[ nIndex + 1 ] == '.' )
522                 aUIString = aUIString.replaceAt( nIndex + 1, 1, "" );
523 
524             bResult = ((rRange.Parse(aUIString, rDocument, eConv) & ScRefFlags::VALID) ==
525                                                                                               ScRefFlags::VALID);
526 
527             // #i77703# chart ranges in the file format contain both sheet names, even for an external reference sheet.
528             // This isn't parsed by ScRange, so try to parse the two Addresses then.
529             if (!bResult)
530             {
531                 bResult = ((rRange.aStart.Parse( aUIString.copy(0, nIndex), rDocument, eConv)
532                                & ScRefFlags::VALID) == ScRefFlags::VALID)
533                           &&
534                           ((rRange.aEnd.Parse( aUIString.copy(nIndex+1), rDocument, eConv)
535                                & ScRefFlags::VALID) == ScRefFlags::VALID);
536 
537                 ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention();
538                 if (!bResult && eConv != eConvUI)
539                 {
540                     bResult = ((rRange.aStart.Parse( aUIString.copy(0, nIndex), rDocument, eConvUI)
541                                    & ScRefFlags::VALID) == ScRefFlags::VALID)
542                               &&
543                               ((rRange.aEnd.Parse( aUIString.copy(nIndex+1), rDocument, eConvUI)
544                                    & ScRefFlags::VALID) == ScRefFlags::VALID);
545                 }
546             }
547         }
548     }
549     return bResult;
550 }
551 
GetRangeListFromString(ScRangeList & rRangeList,const OUString & rRangeListStr,const ScDocument & rDocument,FormulaGrammar::AddressConvention eConv,sal_Unicode cSeparator,sal_Unicode cQuote)552 bool ScRangeStringConverter::GetRangeListFromString(
553         ScRangeList& rRangeList,
554         const OUString& rRangeListStr,
555         const ScDocument& rDocument,
556         FormulaGrammar::AddressConvention eConv,
557         sal_Unicode cSeparator,
558         sal_Unicode cQuote )
559 {
560     bool bRet = true;
561     OSL_ENSURE( !rRangeListStr.isEmpty(), "ScXMLConverter::GetRangeListFromString - empty string!" );
562     sal_Int32 nOffset = 0;
563     while( nOffset >= 0 )
564     {
565         ScRange aRange;
566         if (
567              GetRangeFromString( aRange, rRangeListStr, rDocument, eConv, nOffset, cSeparator, cQuote ) &&
568              (nOffset >= 0)
569            )
570         {
571             rRangeList.push_back( aRange );
572         }
573         else if (nOffset > -1)
574             bRet = false;
575     }
576     return bRet;
577 }
578 
GetAreaFromString(ScArea & rArea,const OUString & rRangeStr,const ScDocument & rDocument,FormulaGrammar::AddressConvention eConv,sal_Int32 & nOffset,sal_Unicode cSeparator)579 bool ScRangeStringConverter::GetAreaFromString(
580         ScArea& rArea,
581         const OUString& rRangeStr,
582         const ScDocument& rDocument,
583         FormulaGrammar::AddressConvention eConv,
584         sal_Int32& nOffset,
585         sal_Unicode cSeparator )
586 {
587     ScRange aScRange;
588     bool bResult(false);
589     if( GetRangeFromString( aScRange, rRangeStr, rDocument, eConv, nOffset, cSeparator ) && (nOffset >= 0) )
590     {
591         rArea.nTab = aScRange.aStart.Tab();
592         rArea.nColStart = aScRange.aStart.Col();
593         rArea.nRowStart = aScRange.aStart.Row();
594         rArea.nColEnd = aScRange.aEnd.Col();
595         rArea.nRowEnd = aScRange.aEnd.Row();
596         bResult = true;
597     }
598     return bResult;
599 }
600 
GetRangeFromString(table::CellRangeAddress & rRange,const OUString & rRangeStr,const ScDocument & rDocument,FormulaGrammar::AddressConvention eConv,sal_Int32 & nOffset,sal_Unicode cSeparator)601 bool ScRangeStringConverter::GetRangeFromString(
602         table::CellRangeAddress& rRange,
603         const OUString& rRangeStr,
604         const ScDocument& rDocument,
605         FormulaGrammar::AddressConvention eConv,
606         sal_Int32& nOffset,
607         sal_Unicode cSeparator )
608 {
609     ScRange aScRange;
610     bool bResult(false);
611     if( GetRangeFromString( aScRange, rRangeStr, rDocument, eConv, nOffset, cSeparator ) && (nOffset >= 0) )
612     {
613         ScUnoConversion::FillApiRange( rRange, aScRange );
614         bResult = true;
615     }
616     return bResult;
617 }
618 
GetStringFromAddress(OUString & rString,const ScAddress & rAddress,const ScDocument * pDocument,FormulaGrammar::AddressConvention eConv,sal_Unicode cSeparator,bool bAppendStr,ScRefFlags nFormatFlags)619 void ScRangeStringConverter::GetStringFromAddress(
620         OUString& rString,
621         const ScAddress& rAddress,
622         const ScDocument* pDocument,
623         FormulaGrammar::AddressConvention eConv,
624         sal_Unicode cSeparator,
625         bool bAppendStr,
626         ScRefFlags nFormatFlags )
627 {
628     if (pDocument && pDocument->HasTable(rAddress.Tab()))
629     {
630         OUString sAddress(rAddress.Format(nFormatFlags, pDocument, eConv));
631         AssignString( rString, sAddress, bAppendStr, cSeparator );
632     }
633 }
634 
GetStringFromRange(OUString & rString,const ScRange & rRange,const ScDocument * pDocument,FormulaGrammar::AddressConvention eConv,sal_Unicode cSeparator,bool bAppendStr,ScRefFlags nFormatFlags)635 void ScRangeStringConverter::GetStringFromRange(
636         OUString& rString,
637         const ScRange& rRange,
638         const ScDocument* pDocument,
639         FormulaGrammar::AddressConvention eConv,
640         sal_Unicode cSeparator,
641         bool bAppendStr,
642         ScRefFlags nFormatFlags )
643 {
644     if (pDocument && pDocument->HasTable(rRange.aStart.Tab()))
645     {
646         ScAddress aStartAddress( rRange.aStart );
647         ScAddress aEndAddress( rRange.aEnd );
648         OUString sStartAddress(aStartAddress.Format(nFormatFlags, pDocument, eConv));
649         OUString sEndAddress(aEndAddress.Format(nFormatFlags, pDocument, eConv));
650         AssignString(
651             rString, sStartAddress + ":" + sEndAddress, bAppendStr, cSeparator);
652     }
653 }
654 
GetStringFromRangeList(OUString & rString,const ScRangeList * pRangeList,const ScDocument * pDocument,FormulaGrammar::AddressConvention eConv,sal_Unicode cSeparator)655 void ScRangeStringConverter::GetStringFromRangeList(
656         OUString& rString,
657         const ScRangeList* pRangeList,
658         const ScDocument* pDocument,
659         FormulaGrammar::AddressConvention eConv,
660         sal_Unicode cSeparator )
661 {
662     OUString sRangeListStr;
663     if( pRangeList )
664     {
665         for( size_t nIndex = 0, nCount = pRangeList->size(); nIndex < nCount; nIndex++ )
666         {
667             const ScRange & rRange = (*pRangeList)[nIndex];
668             GetStringFromRange( sRangeListStr, rRange, pDocument, eConv, cSeparator, true );
669         }
670     }
671     rString = sRangeListStr;
672 }
673 
GetStringFromArea(OUString & rString,const ScArea & rArea,const ScDocument * pDocument,FormulaGrammar::AddressConvention eConv,sal_Unicode cSeparator,bool bAppendStr,ScRefFlags nFormatFlags)674 void ScRangeStringConverter::GetStringFromArea(
675         OUString& rString,
676         const ScArea& rArea,
677         const ScDocument* pDocument,
678         FormulaGrammar::AddressConvention eConv,
679         sal_Unicode cSeparator,
680         bool bAppendStr,
681         ScRefFlags nFormatFlags )
682 {
683     ScRange aRange( rArea.nColStart, rArea.nRowStart, rArea.nTab, rArea.nColEnd, rArea.nRowEnd, rArea.nTab );
684     GetStringFromRange( rString, aRange, pDocument, eConv, cSeparator, bAppendStr, nFormatFlags );
685 }
686 
GetStringFromAddress(OUString & rString,const table::CellAddress & rAddress,const ScDocument * pDocument,FormulaGrammar::AddressConvention eConv,sal_Unicode cSeparator,bool bAppendStr)687 void ScRangeStringConverter::GetStringFromAddress(
688         OUString& rString,
689         const table::CellAddress& rAddress,
690         const ScDocument* pDocument,
691         FormulaGrammar::AddressConvention eConv,
692         sal_Unicode cSeparator,
693         bool bAppendStr )
694 {
695     ScAddress aScAddress( static_cast<SCCOL>(rAddress.Column), static_cast<SCROW>(rAddress.Row), rAddress.Sheet );
696     GetStringFromAddress( rString, aScAddress, pDocument, eConv, cSeparator, bAppendStr );
697 }
698 
GetStringFromRange(OUString & rString,const table::CellRangeAddress & rRange,const ScDocument * pDocument,FormulaGrammar::AddressConvention eConv,sal_Unicode cSeparator,bool bAppendStr,ScRefFlags nFormatFlags)699 void ScRangeStringConverter::GetStringFromRange(
700         OUString& rString,
701         const table::CellRangeAddress& rRange,
702         const ScDocument* pDocument,
703         FormulaGrammar::AddressConvention eConv,
704         sal_Unicode cSeparator,
705         bool bAppendStr,
706         ScRefFlags nFormatFlags )
707 {
708     ScRange aScRange( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), rRange.Sheet,
709         static_cast<SCCOL>(rRange.EndColumn), static_cast<SCROW>(rRange.EndRow), rRange.Sheet );
710     GetStringFromRange( rString, aScRange, pDocument, eConv, cSeparator, bAppendStr, nFormatFlags );
711 }
712 
GetStringFromRangeList(OUString & rString,const uno::Sequence<table::CellRangeAddress> & rRangeSeq,const ScDocument * pDocument,FormulaGrammar::AddressConvention eConv,sal_Unicode cSeparator)713 void ScRangeStringConverter::GetStringFromRangeList(
714         OUString& rString,
715         const uno::Sequence< table::CellRangeAddress >& rRangeSeq,
716         const ScDocument* pDocument,
717         FormulaGrammar::AddressConvention eConv,
718         sal_Unicode cSeparator )
719 {
720     OUString sRangeListStr;
721     for( const table::CellRangeAddress& rRange : rRangeSeq )
722     {
723         GetStringFromRange( sRangeListStr, rRange, pDocument, eConv, cSeparator, true );
724     }
725     rString = sRangeListStr;
726 }
727 
lcl_appendCellAddress(OUStringBuffer & rBuf,const ScDocument & rDoc,const ScAddress & rCell,const ScAddress::ExternalInfo & rExtInfo)728 static void lcl_appendCellAddress(
729     OUStringBuffer& rBuf, const ScDocument& rDoc, const ScAddress& rCell,
730     const ScAddress::ExternalInfo& rExtInfo)
731 {
732     if (rExtInfo.mbExternal)
733     {
734         ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
735         const OUString* pFilePath = pRefMgr->getExternalFileName(rExtInfo.mnFileId, true);
736         if (!pFilePath)
737             return;
738 
739         sal_Unicode cQuote = '\'';
740         rBuf.append(cQuote);
741         rBuf.append(*pFilePath);
742         rBuf.append(cQuote);
743         rBuf.append('#');
744         rBuf.append('$');
745         ScRangeStringConverter::AppendTableName(rBuf, rExtInfo.maTabName);
746         rBuf.append('.');
747 
748         OUString aAddr(rCell.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()));
749         rBuf.append(aAddr);
750     }
751     else
752     {
753         OUString aAddr(rCell.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention()));
754         rBuf.append(aAddr);
755     }
756 }
757 
lcl_appendCellRangeAddress(OUStringBuffer & rBuf,const ScDocument & rDoc,const ScAddress & rCell1,const ScAddress & rCell2,const ScAddress::ExternalInfo & rExtInfo1,const ScAddress::ExternalInfo & rExtInfo2)758 static void lcl_appendCellRangeAddress(
759     OUStringBuffer& rBuf, const ScDocument& rDoc, const ScAddress& rCell1, const ScAddress& rCell2,
760     const ScAddress::ExternalInfo& rExtInfo1, const ScAddress::ExternalInfo& rExtInfo2)
761 {
762     if (rExtInfo1.mbExternal)
763     {
764         OSL_ENSURE(rExtInfo2.mbExternal, "2nd address is not external!?");
765         OSL_ENSURE(rExtInfo1.mnFileId == rExtInfo2.mnFileId, "File IDs do not match between 1st and 2nd addresses.");
766 
767         ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
768         const OUString* pFilePath = pRefMgr->getExternalFileName(rExtInfo1.mnFileId, true);
769         if (!pFilePath)
770             return;
771 
772         sal_Unicode cQuote = '\'';
773         rBuf.append(cQuote);
774         rBuf.append(*pFilePath);
775         rBuf.append(cQuote);
776         rBuf.append('#');
777         rBuf.append('$');
778         ScRangeStringConverter::AppendTableName(rBuf, rExtInfo1.maTabName);
779         rBuf.append('.');
780 
781         OUString aAddr(rCell1.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()));
782         rBuf.append(aAddr);
783 
784         rBuf.append(":");
785 
786         if (rExtInfo1.maTabName != rExtInfo2.maTabName)
787         {
788             rBuf.append('$');
789             ScRangeStringConverter::AppendTableName(rBuf, rExtInfo2.maTabName);
790             rBuf.append('.');
791         }
792 
793         aAddr = rCell2.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention());
794         rBuf.append(aAddr);
795     }
796     else
797     {
798         ScRange aRange;
799         aRange.aStart = rCell1;
800         aRange.aEnd   = rCell2;
801         OUString aAddr(aRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
802         rBuf.append(aAddr);
803     }
804 }
805 
GetStringFromXMLRangeString(OUString & rString,const OUString & rXMLRange,const ScDocument & rDoc)806 void ScRangeStringConverter::GetStringFromXMLRangeString( OUString& rString, const OUString& rXMLRange, const ScDocument& rDoc )
807 {
808     FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
809     const sal_Unicode cSepNew = ScCompiler::GetNativeSymbolChar(ocSep);
810 
811     OUStringBuffer aRetStr;
812     sal_Int32 nOffset = 0;
813     bool bFirst = true;
814 
815     while (nOffset >= 0)
816     {
817         OUString aToken;
818         GetTokenByOffset(aToken, rXMLRange, nOffset);
819         if (nOffset < 0)
820             break;
821 
822         sal_Int32 nSepPos = IndexOf(aToken, ':', 0);
823         if (nSepPos >= 0)
824         {
825             // Cell range
826             OUString aBeginCell = aToken.copy(0, nSepPos);
827             OUString aEndCell   = aToken.copy(nSepPos+1);
828 
829             if (aBeginCell.isEmpty() || aEndCell.isEmpty())
830                 // both cell addresses must exist for this to work.
831                 continue;
832 
833             sal_Int32 nEndCellDotPos = aEndCell.indexOf('.');
834             if (nEndCellDotPos <= 0)
835             {
836                 // initialize buffer with table name...
837                 sal_Int32 nDotPos = IndexOf(aBeginCell, '.', 0);
838                 OUStringBuffer aBuf(aBeginCell.subView(0, nDotPos));
839 
840                 if (nEndCellDotPos == 0)
841                 {
842                     // workaround for old syntax (probably pre-chart2 age?)
843                     // e.g. Sheet1.A1:.B2
844                     aBuf.append(aEndCell);
845                 }
846                 else if (nEndCellDotPos < 0)
847                 {
848                     // sheet name in the end cell is omitted (e.g. Sheet2.A1:B2).
849                     aBuf.append('.');
850                     aBuf.append(aEndCell);
851                 }
852                 aEndCell = aBuf.makeStringAndClear();
853             }
854 
855             ScAddress::ExternalInfo aExtInfo1, aExtInfo2;
856             ScAddress aCell1, aCell2;
857             ScRefFlags nRet = aCell1.Parse(aBeginCell, rDoc, FormulaGrammar::CONV_OOO, &aExtInfo1);
858             if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
859             {
860                 // first cell is invalid.
861                 if (eConv == FormulaGrammar::CONV_OOO)
862                     continue;
863 
864                 nRet = aCell1.Parse(aBeginCell, rDoc, eConv, &aExtInfo1);
865                 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
866                     // first cell is really invalid.
867                     continue;
868             }
869 
870             nRet = aCell2.Parse(aEndCell, rDoc, FormulaGrammar::CONV_OOO, &aExtInfo2);
871             if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
872             {
873                 // second cell is invalid.
874                 if (eConv == FormulaGrammar::CONV_OOO)
875                     continue;
876 
877                 nRet = aCell2.Parse(aEndCell, rDoc, eConv, &aExtInfo2);
878                 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
879                     // second cell is really invalid.
880                     continue;
881             }
882 
883             if (aExtInfo1.mnFileId != aExtInfo2.mnFileId || aExtInfo1.mbExternal != aExtInfo2.mbExternal)
884                 // external info inconsistency.
885                 continue;
886 
887             // All looks good!
888 
889             if (bFirst)
890                 bFirst = false;
891             else
892                 aRetStr.append(cSepNew);
893 
894             lcl_appendCellRangeAddress(aRetStr, rDoc, aCell1, aCell2, aExtInfo1, aExtInfo2);
895         }
896         else
897         {
898             // Chart always saves ranges using CONV_OOO convention.
899             ScAddress::ExternalInfo aExtInfo;
900             ScAddress aCell;
901             ScRefFlags nRet = aCell.Parse(aToken, rDoc, ::formula::FormulaGrammar::CONV_OOO, &aExtInfo);
902             if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO )
903             {
904                 nRet = aCell.Parse(aToken, rDoc, eConv, &aExtInfo);
905                 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
906                     continue;
907             }
908 
909             // Looks good!
910 
911             if (bFirst)
912                 bFirst = false;
913             else
914                 aRetStr.append(cSepNew);
915 
916             lcl_appendCellAddress(aRetStr, rDoc, aCell, aExtInfo);
917         }
918     }
919 
920     rString = aRetStr.makeStringAndClear();
921 }
922 
GetRangeDataFromString(const OUString & rString,const SCTAB nTab,const ScDocument & rDoc,formula::FormulaGrammar::AddressConvention eConv)923 ScRangeData* ScRangeStringConverter::GetRangeDataFromString( const OUString& rString, const SCTAB nTab,
924         const ScDocument& rDoc, formula::FormulaGrammar::AddressConvention eConv )
925 {
926     // This may be called with an external 'doc'#name but wouldn't find any.
927 
928     // Dot '.' is not allowed in range names, if present only lookup if it's a
929     // sheet-local name. Same for '!' Excel syntax.
930     // If eConv == FormulaGrammar::CONV_A1_XL_A1 then try both, first our own.
931     sal_Int32 nIndex = -1;
932     if (eConv == FormulaGrammar::CONV_OOO || eConv == FormulaGrammar::CONV_A1_XL_A1)
933         nIndex = ScGlobal::FindUnquoted( rString, '.');
934     if (nIndex < 0 && (eConv == FormulaGrammar::CONV_A1_XL_A1
935                 || eConv == FormulaGrammar::CONV_XL_A1
936                 || eConv == FormulaGrammar::CONV_XL_R1C1
937                 || eConv == FormulaGrammar::CONV_XL_OOX))
938         nIndex = ScGlobal::FindUnquoted( rString, '!');
939 
940     if (nIndex >= 0)
941     {
942         if (nIndex == 0)
943             return nullptr;     // Can't be a name.
944 
945         OUString aTab( rString.copy( 0, nIndex));
946         ScGlobal::EraseQuotes( aTab, '\'');
947         SCTAB nLocalTab;
948         if (!rDoc.GetTable( aTab, nLocalTab))
949             return nullptr;
950 
951         ScRangeName* pLocalRangeName = rDoc.GetRangeName(nLocalTab);
952         if (!pLocalRangeName)
953             return nullptr;
954 
955         const OUString aName( rString.copy( nIndex+1));
956         return pLocalRangeName->findByUpperName( ScGlobal::getCharClassPtr()->uppercase( aName));
957     }
958 
959     ScRangeName* pLocalRangeName = rDoc.GetRangeName(nTab);
960     ScRangeData* pData = nullptr;
961     OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rString);
962     if(pLocalRangeName)
963     {
964         pData = pLocalRangeName->findByUpperName(aUpperName);
965     }
966     if (!pData)
967     {
968         ScRangeName* pGlobalRangeName = rDoc.GetRangeName();
969         if (pGlobalRangeName)
970         {
971             pData = pGlobalRangeName->findByUpperName(aUpperName);
972         }
973     }
974     return pData;
975 }
976 
ScArea(SCTAB tab,SCCOL colStart,SCROW rowStart,SCCOL colEnd,SCROW rowEnd)977 ScArea::ScArea( SCTAB tab,
978                 SCCOL colStart, SCROW rowStart,
979                 SCCOL colEnd,   SCROW rowEnd ) :
980         nTab     ( tab ),
981         nColStart( colStart ),  nRowStart( rowStart ),
982         nColEnd  ( colEnd ),    nRowEnd  ( rowEnd )
983 {
984 }
985 
operator ==(const ScArea & r) const986 bool ScArea::operator==( const ScArea& r ) const
987 {
988     return (   (nTab        == r.nTab)
989             && (nColStart   == r.nColStart)
990             && (nRowStart   == r.nRowStart)
991             && (nColEnd     == r.nColEnd)
992             && (nRowEnd     == r.nRowEnd) );
993 }
994 
ScAreaNameIterator(const ScDocument & rDoc)995 ScAreaNameIterator::ScAreaNameIterator( const ScDocument& rDoc ) :
996     pRangeName(rDoc.GetRangeName()),
997     pDBCollection(rDoc.GetDBCollection()),
998     bFirstPass(true)
999 {
1000     if (pRangeName)
1001     {
1002         maRNPos = pRangeName->begin();
1003         maRNEnd = pRangeName->end();
1004     }
1005 }
1006 
Next(OUString & rName,ScRange & rRange)1007 bool ScAreaNameIterator::Next( OUString& rName, ScRange& rRange )
1008 {
1009     for (;;)
1010     {
1011         if ( bFirstPass )                                   // first the area names
1012         {
1013             if ( pRangeName && maRNPos != maRNEnd )
1014             {
1015                 const ScRangeData& rData = *maRNPos->second;
1016                 ++maRNPos;
1017                 bool bValid = rData.IsValidReference(rRange);
1018                 if (bValid)
1019                 {
1020                     rName = rData.GetName();
1021                     return true;                            // found
1022                 }
1023             }
1024             else
1025             {
1026                 bFirstPass = false;
1027                 if (pDBCollection)
1028                 {
1029                     const ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs();
1030                     maDBPos = rDBs.begin();
1031                     maDBEnd = rDBs.end();
1032                 }
1033             }
1034         }
1035 
1036         if ( !bFirstPass )                                  // then the DB areas
1037         {
1038             if (pDBCollection && maDBPos != maDBEnd)
1039             {
1040                 const ScDBData& rData = **maDBPos;
1041                 ++maDBPos;
1042                 rData.GetArea(rRange);
1043                 rName = rData.GetName();
1044                 return true;                                // found
1045             }
1046             else
1047                 return false;                               // nothing left
1048         }
1049     }
1050 }
1051 
UpdateInsertTab(ScAddress & rAddr,const sc::RefUpdateInsertTabContext & rCxt)1052 void ScRangeUpdater::UpdateInsertTab(ScAddress& rAddr, const sc::RefUpdateInsertTabContext& rCxt)
1053 {
1054     if (rCxt.mnInsertPos <= rAddr.Tab())
1055     {
1056         rAddr.IncTab(rCxt.mnSheets);
1057     }
1058 }
1059 
UpdateDeleteTab(ScAddress & rAddr,const sc::RefUpdateDeleteTabContext & rCxt)1060 void ScRangeUpdater::UpdateDeleteTab(ScAddress& rAddr, const sc::RefUpdateDeleteTabContext& rCxt)
1061 {
1062     if (rCxt.mnDeletePos <= rAddr.Tab())
1063     {
1064         rAddr.IncTab(-rCxt.mnSheets);
1065     }
1066 }
1067 
1068 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1069