xref: /trunk/main/sc/source/core/tool/address.cxx (revision b3f79822)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26 
27 #include "address.hxx"
28 #include "global.hxx"
29 #include "compiler.hxx"
30 #include "document.hxx"
31 #include "externalrefmgr.hxx"
32 
33 #include "globstr.hrc"
34 #include <sal/alloca.h>
35 
36 #include <com/sun/star/frame/XModel.hpp>
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include <com/sun/star/sheet/ExternalLinkInfo.hpp>
39 #include <com/sun/star/sheet/ExternalLinkType.hpp>
40 #include <sfx2/objsh.hxx>
41 #include <tools/urlobj.hxx>
42 using namespace ::com::sun::star;
43 
44 ////////////////////////////////////////////////////////////////////////////
45 const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 );
46 
Details(const ScDocument * pDoc,const ScAddress & rAddr)47 ScAddress::Details::Details ( const ScDocument* pDoc,
48                               const ScAddress & rAddr ) :
49     eConv( pDoc->GetAddressConvention() ),
50     nRow( rAddr.Row() ),
51     nCol( rAddr.Col() )
52 {
53 }
54 
55 //UNUSED2009-05 void ScAddress::Details::SetPos ( const ScDocument* pDoc,
56 //UNUSED2009-05                                   const ScAddress & rAddr )
57 //UNUSED2009-05 {
58 //UNUSED2009-05     nRow  = rAddr.Row();
59 //UNUSED2009-05     nCol  = rAddr.Col();
60 //UNUSED2009-05     eConv = pDoc->GetAddressConvention();
61 //UNUSED2009-05 }
62 
63 ////////////////////////////////////////////////////////////////////////////
64 
65 #include <iostream>
66 
67 /**
68  * Parse from the opening single quote to the closing single quote.  Inside
69  * the quotes, a single quote character is encoded by double single-quote
70  * characters.
71  *
72  * @param p pointer to the first character to begin parsing.
73  * @param rName (reference) parsed name within the quotes.  If the name is
74  *              empty, either the parsing failed or it's an empty quote.
75  *
76  * @return pointer to the character immediately after the closing single
77  *         quote.
78  */
lcl_ParseQuotedName(const sal_Unicode * p,String & rName)79 static const sal_Unicode* lcl_ParseQuotedName( const sal_Unicode* p, String& rName )
80 {
81     rName.Erase();
82     if (*p != '\'')
83         return p;
84 
85     const sal_Unicode* pStart = p;
86     sal_Unicode cPrev = 0;
87     for (++p; *p; ++p)
88     {
89         if (*p == '\'')
90         {
91             if (cPrev == '\'')
92             {
93                 // double single-quote equals one single quote.
94                 rName += *p;
95                 cPrev = 0;
96                 continue;
97             }
98         }
99         else if (cPrev == '\'')
100             // We are past the closing quote.  We're done!
101             return p;
102         else
103             rName += *p;
104         cPrev = *p;
105     }
106     rName.Erase();
107     return pStart;
108 }
109 
110 static long int
sal_Unicode_strtol(const sal_Unicode * p,const sal_Unicode ** pEnd)111 sal_Unicode_strtol ( const sal_Unicode*  p,
112                      const sal_Unicode** pEnd )
113 {
114     long int accum = 0, prev = 0;
115     bool is_neg = false;
116 
117     if( *p == '-' )
118     {
119         is_neg = true;
120         p++;
121     }
122     else if( *p == '+' )
123         p++;
124 
125     while (CharClass::isAsciiDigit( *p ))
126     {
127         accum = accum * 10 + *p - '0';
128         if( accum < prev )
129         {
130             *pEnd = NULL;
131             return 0;
132         }
133         prev = accum;
134         p++;
135     }
136 
137     *pEnd = p;
138     return is_neg ? -accum : accum;
139 }
140 
lcl_eatWhiteSpace(const sal_Unicode * p)141 const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p )
142 {
143     if ( p )
144     {
145         while(  *p == ' ' )
146             ++p;
147     }
148     return p;
149 }
150 
151 /** Determines the number of sheets an external reference spans and sets
152     rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
153     bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
154     cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
155     is set to rEndTabName.
156     @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
157              result in the identical file ID. Else <TRUE/>.
158  */
lcl_ScRange_External_TabSpan(ScRange & rRange,sal_uInt16 & rFlags,ScAddress::ExternalInfo * pExtInfo,const String & rExternDocName,const String & rStartTabName,const String & rEndTabName,ScDocument * pDoc)159 static bool lcl_ScRange_External_TabSpan(
160         ScRange & rRange,
161         sal_uInt16 & rFlags,
162         ScAddress::ExternalInfo* pExtInfo,
163         const String & rExternDocName,
164         const String & rStartTabName,
165         const String & rEndTabName,
166         ScDocument* pDoc )
167 {
168     if (!rExternDocName.Len())
169         return !pExtInfo || !pExtInfo->mbExternal;
170 
171     ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
172     if (pRefMgr->isOwnDocument( rExternDocName))
173         return !pExtInfo || !pExtInfo->mbExternal;
174 
175     sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName);
176 
177     if (pExtInfo)
178     {
179         if (pExtInfo->mbExternal)
180         {
181             if (pExtInfo->mnFileId != nFileId)
182                 return false;
183         }
184         else
185         {
186             pExtInfo->mbExternal = true;
187             pExtInfo->maTabName = rStartTabName;
188             pExtInfo->mnFileId = nFileId;
189         }
190     }
191 
192     if (!rEndTabName.Len() || rStartTabName == rEndTabName)
193     {
194         rRange.aEnd.SetTab( rRange.aStart.Tab());
195         return true;
196     }
197 
198     SCsTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName);
199     if (nSpan == -1)
200         rFlags &= ~(SCA_VALID_TAB | SCA_VALID_TAB2);
201     else if (nSpan == 0)
202         rFlags &= ~SCA_VALID_TAB2;
203     else if (nSpan >= 1)
204         rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1);
205     else // (nSpan < -1)
206     {
207         rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1);
208         if (pExtInfo)
209             pExtInfo->maTabName = rEndTabName;
210     }
211     return true;
212 }
213 
214 /** Returns NULL if the string should be a sheet name, but is invalid.
215     Returns a pointer to the first character after the sheet name, if there was
216     any, else pointer to start.
217     @param pMsoxlQuoteStop
218         Starting _within_ a quoted name, but still may be 3D; quoted name stops
219         at pMsoxlQuoteStop
220  */
221 static const sal_Unicode *
lcl_XL_ParseSheetRef(const sal_Unicode * start,String & rExternTabName,bool allow_3d,const sal_Unicode * pMsoxlQuoteStop)222 lcl_XL_ParseSheetRef( const sal_Unicode* start,
223                       String& rExternTabName,
224                       bool allow_3d,
225                       const sal_Unicode* pMsoxlQuoteStop )
226 {
227     String aTabName;
228     const sal_Unicode *p = start;
229 
230     // XL only seems to use single quotes for sheet names.
231     if (pMsoxlQuoteStop)
232     {
233         const sal_Unicode* pCurrentStart = p;
234         while (p < pMsoxlQuoteStop)
235         {
236             if (*p == '\'')
237             {
238                 // We pre-analyzed the quoting, no checks needed here.
239                 if (*++p == '\'')
240                 {
241                     aTabName.Append( pCurrentStart,
242                             sal::static_int_cast<xub_StrLen>( p - pCurrentStart));
243                     pCurrentStart = ++p;
244                 }
245             }
246             else if (*p == ':')
247             {
248                 break;  // while
249             }
250             else
251                 ++p;
252         }
253         if (pCurrentStart < p)
254             aTabName.Append( pCurrentStart, sal::static_int_cast<xub_StrLen>( p - pCurrentStart));
255         if (!aTabName.Len())
256             return NULL;
257         if (p == pMsoxlQuoteStop)
258             ++p;    // position on ! of ...'!...
259         if( *p != '!' && ( !allow_3d || *p != ':' ) )
260             return (!allow_3d && *p == ':') ? p : start;
261     }
262     else if( *p == '\'')
263     {
264         p = lcl_ParseQuotedName(p, aTabName);
265         if (!aTabName.Len())
266             return NULL;
267     }
268     else
269     {
270         bool only_digits = sal_True;
271 
272         /*
273          * Valid: Normal!a1
274          * Valid: x.y!a1
275          * Invalid: .y!a1
276          *
277          * Some names starting with digits are actually valid, but
278          * unparse quoted. Things are quite tricky: most sheet names
279          * starting with a digit are ok, but not those starting with
280          * "[0-9]*\." or "[0-9]+[eE]".
281          *
282          * Valid: 42!a1
283          * Valid: 4x!a1
284          * Invalid: 1.!a1
285          * Invalid: 1e!a1
286          */
287         while( 1 )
288         {
289             const sal_Unicode uc = *p;
290             if( CharClass::isAsciiAlpha( uc ) || uc == '_' )
291             {
292                 if( only_digits && p != start &&
293                    (uc == 'e' || uc == 'E' ) )
294                 {
295                     p = start;
296                     break;
297                 }
298                 only_digits = sal_False;
299                 p++;
300             }
301             else if( CharClass::isAsciiDigit( uc ))
302             {
303                 p++;
304             }
305             else if( uc == '.' )
306             {
307                 if( only_digits ) // Valid, except after only digits.
308                 {
309                     p = start;
310                     break;
311                 }
312                 p++;
313             }
314             else if (uc > 127)
315             {
316                 // non ASCII character is allowed.
317                 ++p;
318             }
319             else
320                 break;
321         }
322 
323         if( *p != '!' && ( !allow_3d || *p != ':' ) )
324             return (!allow_3d && *p == ':') ? p : start;
325 
326         aTabName.Append( start, sal::static_int_cast<xub_StrLen>( p - start ) );
327     }
328 
329     rExternTabName = aTabName;
330     return p;
331 }
332 
333 
Parse_XL_Header(const sal_Unicode * p,const ScDocument * pDoc,String & rExternDocName,String & rStartTabName,String & rEndTabName,sal_uInt16 & nFlags,bool bOnlyAcceptSingle,const uno::Sequence<const sheet::ExternalLinkInfo> * pExternalLinks)334 const sal_Unicode* ScRange::Parse_XL_Header(
335         const sal_Unicode* p,
336         const ScDocument* pDoc,
337         String& rExternDocName,
338         String& rStartTabName,
339         String& rEndTabName,
340         sal_uInt16& nFlags,
341         bool bOnlyAcceptSingle,
342         const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
343 {
344     const sal_Unicode* startTabs, *start = p;
345     sal_uInt16 nSaveFlags = nFlags;
346 
347     // Is this an external reference ?
348     rStartTabName.Erase();
349     rEndTabName.Erase();
350     rExternDocName.Erase();
351     const sal_Unicode* pMsoxlQuoteStop = NULL;
352     if (*p == '[')
353     {
354         ++p;
355         // Only single quotes are correct, and a double single quote escapes a
356         // single quote text inside the quoted text.
357         if (*p == '\'')
358         {
359             p = lcl_ParseQuotedName(p, rExternDocName);
360             if (!*p || *p != ']' || !rExternDocName.Len())
361             {
362                 rExternDocName.Erase();
363                 return start;
364             }
365         }
366         else
367         {
368             // non-quoted file name.
369             p = ScGlobal::UnicodeStrChr( start+1, ']' );
370             if( p == NULL )
371                 return start;
372             rExternDocName.Append( start+1, sal::static_int_cast<xub_StrLen>( p-(start+1) ) );
373         }
374         ++p;
375 
376         if (pExternalLinks && pExternalLinks->hasElements())
377         {
378             // A numeric "document name" is an index into the sequence.
379             if (CharClass::isAsciiNumeric( rExternDocName))
380             {
381                 sal_Int32 i = rExternDocName.ToInt32();
382                 if (i < 0 || i >= pExternalLinks->getLength())
383                     return start;
384                 const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i];
385                 switch (rInfo.Type)
386                 {
387                     case sheet::ExternalLinkType::DOCUMENT :
388                         {
389                             rtl::OUString aStr;
390                             if (!(rInfo.Data >>= aStr))
391                             {
392                                 DBG_ERROR1( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i);
393                                 return NULL;
394                             }
395                             rExternDocName = aStr;
396                         }
397                         break;
398                     case sheet::ExternalLinkType::SELF :
399                         return start;   // ???
400                     case sheet::ExternalLinkType::SPECIAL :
401                         // silently return nothing (do not assert), caller has to handle this
402                         return NULL;
403                     default:
404                         DBG_ERROR2( "ScRange::Parse_XL_Header: unhandled ExternalLinkType %d for index %d",
405                                 rInfo.Type, i);
406                         return NULL;
407                 }
408             }
409         }
410         rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, pDoc->GetDocumentShell());
411     }
412     else if (*p == '\'')
413     {
414         // Sickness in Excel's ODF msoxl namespace:
415         // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7  or
416         // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
417         // But, 'Sheet1'!B3 would also be a valid!
418         // Excel does not allow [ and ] characters in sheet names though.
419         p = lcl_ParseQuotedName(p, rExternDocName);
420         if (!*p || *p != '!')
421         {
422             rExternDocName.Erase();
423             return start;
424         }
425         if (rExternDocName.Len())
426         {
427             xub_StrLen nOpen = rExternDocName.Search( '[');
428             if (nOpen == STRING_NOTFOUND)
429                 rExternDocName.Erase();
430             else
431             {
432                 xub_StrLen nClose = rExternDocName.Search( ']', nOpen+1);
433                 if (nClose == STRING_NOTFOUND)
434                     rExternDocName.Erase();
435                 else
436                 {
437                     rExternDocName.Erase( nClose);
438                     rExternDocName.Erase( nOpen, 1);
439                     pMsoxlQuoteStop = p - 1;    // the ' quote char
440                     // There may be embedded escaped quotes, just matching the
441                     // doc name's length may not work.
442                     for (p = start; *p != '['; ++p)
443                         ;
444                     for ( ; *p != ']'; ++p)
445                         ;
446                     ++p;
447                 }
448             }
449         }
450         if (!rExternDocName.Len())
451             p = start;
452     }
453 
454     startTabs = p;
455     p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop);
456     if( NULL == p )
457         return start;       // invalid tab
458     if (bOnlyAcceptSingle && *p == ':')
459         return NULL;        // 3D
460     if( p != startTabs )
461     {
462         nFlags |= SCA_VALID_TAB | SCA_TAB_3D | SCA_TAB_ABSOLUTE;
463         if( *p == ':' ) // 3d ref
464         {
465             p = lcl_XL_ParseSheetRef( p+1, rEndTabName, false, pMsoxlQuoteStop);
466             if( p == NULL )
467             {
468                 nFlags = nSaveFlags;
469                 return start; // invalid tab
470             }
471             nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE;
472         }
473         else
474         {
475             // If only one sheet is given, the full reference is still valid,
476             // only the second 3D flag is not set.
477             nFlags |= SCA_VALID_TAB2 | SCA_TAB2_ABSOLUTE;
478             aEnd.SetTab( aStart.Tab() );
479         }
480 
481         if( *p++ != '!' )
482         {
483             nFlags = nSaveFlags;
484             return start;   // syntax error
485         }
486         else
487             p = lcl_eatWhiteSpace( p );
488     }
489     else
490     {
491         nFlags |= SCA_VALID_TAB | SCA_VALID_TAB2;
492         // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
493     }
494 
495     if (rExternDocName.Len())
496     {
497         ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
498         pRefMgr->convertToAbsName( rExternDocName);
499     }
500     else
501     {
502         // Internal reference.
503         if (!rStartTabName.Len())
504         {
505             nFlags = nSaveFlags;
506             return start;
507         }
508 
509         SCTAB nTab;
510         if (!pDoc->GetTable(rStartTabName, nTab))
511         {
512             // invalid table name.
513             nFlags &= ~SCA_VALID_TAB;
514             nTab = -1;
515         }
516 
517         aStart.SetTab(nTab);
518         aEnd.SetTab(nTab);
519 
520         if (rEndTabName.Len())
521         {
522             if (!pDoc->GetTable(rEndTabName, nTab))
523             {
524                 // invalid table name.
525                 nFlags &= ~SCA_VALID_TAB2;
526                 nTab = -1;
527             }
528 
529             aEnd.SetTab(nTab);
530         }
531     }
532     return p;
533 }
534 
535 
536 static const sal_Unicode*
lcl_r1c1_get_col(const sal_Unicode * p,const ScAddress::Details & rDetails,ScAddress * pAddr,sal_uInt16 * nFlags)537 lcl_r1c1_get_col( const sal_Unicode* p,
538                   const ScAddress::Details& rDetails,
539                   ScAddress* pAddr, sal_uInt16* nFlags )
540 {
541     const sal_Unicode *pEnd;
542     long int n;
543     bool isRelative;
544 
545     if( p[0] == '\0' )
546         return NULL;
547 
548     p++;
549     if( (isRelative = (*p == '[') ) != false )
550         p++;
551     n = sal_Unicode_strtol( p, &pEnd );
552     if( NULL == pEnd )
553         return NULL;
554 
555     if( p == pEnd ) // C is a relative ref with offset 0
556     {
557         if( isRelative )
558             return NULL;
559         n = rDetails.nCol;
560     }
561     else if( isRelative )
562     {
563         if( *pEnd != ']' )
564             return NULL;
565         n += rDetails.nCol;
566         pEnd++;
567     }
568     else
569     {
570         *nFlags |= SCA_COL_ABSOLUTE;
571         n--;
572     }
573 
574     if( n < 0 || n >= MAXCOLCOUNT )
575         return NULL;
576     pAddr->SetCol( static_cast<SCCOL>( n ) );
577     *nFlags |= SCA_VALID_COL;
578 
579     return pEnd;
580 }
581 static inline const sal_Unicode*
lcl_r1c1_get_row(const sal_Unicode * p,const ScAddress::Details & rDetails,ScAddress * pAddr,sal_uInt16 * nFlags)582 lcl_r1c1_get_row( const sal_Unicode* p,
583                   const ScAddress::Details& rDetails,
584                   ScAddress* pAddr, sal_uInt16* nFlags )
585 {
586     const sal_Unicode *pEnd;
587     long int n;
588     bool isRelative;
589 
590     if( p[0] == '\0' )
591         return NULL;
592 
593     p++;
594     if( (isRelative = (*p == '[') ) != false )
595         p++;
596     n = sal_Unicode_strtol( p, &pEnd );
597     if( NULL == pEnd )
598         return NULL;
599 
600     if( p == pEnd ) // R is a relative ref with offset 0
601     {
602         if( isRelative )
603             return NULL;
604         n = rDetails.nRow;
605     }
606     else if( isRelative )
607     {
608         if( *pEnd != ']' )
609             return NULL;
610         n += rDetails.nRow;
611         pEnd++;
612     }
613     else
614     {
615         *nFlags |= SCA_ROW_ABSOLUTE;
616         n--;
617     }
618 
619     if( n < 0 || n >= MAXROWCOUNT )
620         return NULL;
621     pAddr->SetRow( static_cast<SCROW>( n ) );
622     *nFlags |= SCA_VALID_ROW;
623 
624     return pEnd;
625 }
626 
627 static sal_uInt16
lcl_ScRange_Parse_XL_R1C1(ScRange & r,const sal_Unicode * p,ScDocument * pDoc,const ScAddress::Details & rDetails,bool bOnlyAcceptSingle,ScAddress::ExternalInfo * pExtInfo)628 lcl_ScRange_Parse_XL_R1C1( ScRange& r,
629                            const sal_Unicode* p,
630                            ScDocument* pDoc,
631                            const ScAddress::Details& rDetails,
632                            bool bOnlyAcceptSingle,
633                            ScAddress::ExternalInfo* pExtInfo )
634 {
635     const sal_Unicode* pTmp = NULL;
636     String aExternDocName, aStartTabName, aEndTabName;
637     sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB;
638     // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
639     sal_uInt16 nFlags2 = SCA_VALID_TAB;
640 
641 #if 0
642     {
643         ByteString  aStr(p, RTL_TEXTENCODING_UTF8);
644         aStr.Append(static_cast< char >(0));
645         std::cerr << "parse::XL::R1C1 \'" << aStr.GetBuffer() << '\'' << std::endl;
646     }
647 #endif
648     p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
649             aEndTabName, nFlags, bOnlyAcceptSingle, NULL );
650 
651     if (aExternDocName.Len() > 0)
652         lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
653                 aStartTabName, aEndTabName, pDoc);
654 
655     if( NULL == p )
656         return 0;
657 
658     if( *p == 'R' || *p == 'r' )
659     {
660         if( NULL == (p = lcl_r1c1_get_row( p, rDetails, &r.aStart, &nFlags )) )
661             goto failed;
662 
663         if( *p != 'C' && *p != 'c' )    // full row R#
664         {
665             if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) ||
666                 NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )))
667             {
668                 // Only the initial row number is given, or the second row
669                 // number is invalid. Fallback to just the initial R
670                 nFlags |= (nFlags << 4);
671                 r.aEnd.SetRow( r.aStart.Row() );
672             }
673             else
674             {
675                 // Full row range successfully parsed.
676                 nFlags |= (nFlags2 << 4);
677                 p = pTmp;
678             }
679 
680             if (p && p[0] != 0)
681             {
682                 // any trailing invalid character must invalidate the whole address.
683                 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
684                             SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
685                 return nFlags;
686             }
687 
688             nFlags |=
689                 SCA_VALID_COL | SCA_VALID_COL2 |
690                 SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
691             r.aStart.SetCol( 0 );
692             r.aEnd.SetCol( MAXCOL );
693 
694             return bOnlyAcceptSingle ? 0 : nFlags;
695         }
696         else if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
697             goto failed;
698 
699         if( p[0] != ':' ||
700             (p[1] != 'R' && p[1] != 'r') ||
701             NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )) ||
702             (*pTmp != 'C' && *pTmp != 'c') ||
703             NULL == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 )))
704         {
705             // single cell reference
706 
707             if (p && p[0] != 0)
708             {
709                 // any trailing invalid character must invalidate the whole address.
710                 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
711                 return nFlags;
712             }
713 
714             return bOnlyAcceptSingle ? nFlags : 0;
715         }
716         p = pTmp;
717 
718         // double reference
719 
720         if (p && p[0] != 0)
721         {
722             // any trailing invalid character must invalidate the whole range.
723             nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
724                         SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
725             return nFlags;
726         }
727 
728         nFlags |= (nFlags2 << 4);
729         return bOnlyAcceptSingle ? 0 : nFlags;
730     }
731     else if( *p == 'C' || *p == 'c' )   // full col C#
732     {
733         if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
734             goto failed;
735 
736         if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') ||
737             NULL == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, &nFlags2 )))
738         {    // Fallback to just the initial C
739             nFlags |= (nFlags << 4);
740             r.aEnd.SetCol( r.aStart.Col() );
741         }
742         else
743         {
744             nFlags |= (nFlags2 << 4);
745             p = pTmp;
746         }
747 
748         if (p && p[0] != 0)
749         {
750             // any trailing invalid character must invalidate the whole address.
751             nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
752                         SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
753             return nFlags;
754         }
755 
756         nFlags |=
757             SCA_VALID_ROW | SCA_VALID_ROW2 |
758             SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
759         r.aStart.SetRow( 0 );
760         r.aEnd.SetRow( MAXROW );
761 
762         return bOnlyAcceptSingle ? 0 : nFlags;
763     }
764 
765 failed :
766     return 0;
767 }
768 
769 static inline const sal_Unicode*
lcl_a1_get_col(const sal_Unicode * p,ScAddress * pAddr,sal_uInt16 * nFlags)770 lcl_a1_get_col( const sal_Unicode* p, ScAddress* pAddr, sal_uInt16* nFlags )
771 {
772     SCCOL nCol;
773 
774     if( *p == '$' )
775         *nFlags |= SCA_COL_ABSOLUTE, p++;
776 
777     if( !CharClass::isAsciiAlpha( *p ) )
778         return NULL;
779 
780     nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
781     while (nCol <= MAXCOL && CharClass::isAsciiAlpha(*p))
782         nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
783     if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) )
784         return NULL;
785 
786     *nFlags |= SCA_VALID_COL;
787     pAddr->SetCol( nCol );
788 
789     return p;
790 }
791 
792 static inline const sal_Unicode*
lcl_a1_get_row(const sal_Unicode * p,ScAddress * pAddr,sal_uInt16 * nFlags)793 lcl_a1_get_row( const sal_Unicode* p, ScAddress* pAddr, sal_uInt16* nFlags )
794 {
795     const sal_Unicode *pEnd;
796     long int n;
797 
798     if( *p == '$' )
799         *nFlags |= SCA_ROW_ABSOLUTE, p++;
800 
801     n = sal_Unicode_strtol( p, &pEnd ) - 1;
802     if( NULL == pEnd || p == pEnd || n < 0 || n > MAXROW )
803         return NULL;
804 
805     *nFlags |= SCA_VALID_ROW;
806     pAddr->SetRow( static_cast<SCROW>(n) );
807 
808     return pEnd;
809 }
810 
811 static sal_uInt16
lcl_ScRange_Parse_XL_A1(ScRange & r,const sal_Unicode * p,ScDocument * pDoc,bool bOnlyAcceptSingle,ScAddress::ExternalInfo * pExtInfo,const uno::Sequence<const sheet::ExternalLinkInfo> * pExternalLinks)812 lcl_ScRange_Parse_XL_A1( ScRange& r,
813                          const sal_Unicode* p,
814                          ScDocument* pDoc,
815                          bool bOnlyAcceptSingle,
816                          ScAddress::ExternalInfo* pExtInfo,
817                          const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
818 {
819     const sal_Unicode* tmp1, *tmp2;
820     String aExternDocName, aStartTabName, aEndTabName; // for external link table
821     sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB;
822 
823 #if 0
824     {
825         ByteString  aStr(p, RTL_TEXTENCODING_UTF8);
826         aStr.Append(static_cast< char >(0));
827         std::cerr << "parse::XL::A1 \'" << aStr.GetBuffer() << '\'' << std::endl;
828     }
829 #endif
830     p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
831             aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks );
832 
833     if (aExternDocName.Len() > 0)
834         lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
835                 aStartTabName, aEndTabName, pDoc);
836 
837     if( NULL == p )
838         return 0;
839 
840     tmp1 = lcl_a1_get_col( p, &r.aStart, &nFlags );
841     if( tmp1 == NULL )          // Is it a row only reference 3:5
842     {
843         if( bOnlyAcceptSingle ) // by definition full row refs are ranges
844             return 0;
845 
846         tmp1 = lcl_a1_get_row( p, &r.aStart, &nFlags );
847 
848         tmp1 = lcl_eatWhiteSpace( tmp1 );
849         if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
850             return 0;
851 
852         tmp1 = lcl_eatWhiteSpace( tmp1 );
853         tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
854         if( !tmp2 )
855             return 0;
856 
857         r.aStart.SetCol( 0 ); r.aEnd.SetCol( MAXCOL );
858         nFlags |=
859             SCA_VALID_COL | SCA_VALID_COL2 |
860             SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
861         nFlags |= (nFlags2 << 4);
862         return nFlags;
863     }
864 
865     tmp2 = lcl_a1_get_row( tmp1, &r.aStart, &nFlags );
866     if( tmp2 == NULL )          // check for col only reference F:H
867     {
868         if( bOnlyAcceptSingle ) // by definition full col refs are ranges
869             return 0;
870 
871         tmp1 = lcl_eatWhiteSpace( tmp1 );
872         if( *tmp1++ != ':' )    // Even a singleton requires ':' (eg F:F)
873             return 0;
874 
875         tmp1 = lcl_eatWhiteSpace( tmp1 );
876         tmp2 = lcl_a1_get_col( tmp1, &r.aEnd, &nFlags2 );
877         if( !tmp2 )
878             return 0;
879 
880         r.aStart.SetRow( 0 ); r.aEnd.SetRow( MAXROW );
881         nFlags |=
882             SCA_VALID_ROW | SCA_VALID_ROW2 |
883             SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
884         nFlags |= (nFlags2 << 4);
885         return nFlags;
886     }
887 
888     // prepare as if it's a singleton, in case we want to fall back */
889     r.aEnd.SetCol( r.aStart.Col() );
890     r.aEnd.SetRow( r.aStart.Row() );    // don't overwrite sheet number as parsed in Parse_XL_Header()
891 
892     if ( bOnlyAcceptSingle )
893     {
894         if ( *tmp2 == 0 )
895             return nFlags;
896         else
897         {
898             // any trailing invalid character must invalidate the address.
899             nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
900             return nFlags;
901         }
902     }
903 
904     tmp2 = lcl_eatWhiteSpace( tmp2 );
905     if( *tmp2 != ':' )
906     {
907         // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
908         // not. Any trailing invalid character invalidates the range.
909         if (*tmp2 == 0 && (nFlags & SCA_TAB2_3D))
910         {
911             if (nFlags & SCA_COL_ABSOLUTE)
912                 nFlags |= SCA_COL2_ABSOLUTE;
913             if (nFlags & SCA_ROW_ABSOLUTE)
914                 nFlags |= SCA_ROW2_ABSOLUTE;
915         }
916         else
917             nFlags &= ~(SCA_VALID |
918                     SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
919                     SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
920         return nFlags;
921     }
922 
923     p = tmp2;
924     p = lcl_eatWhiteSpace( p+1 );
925     tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 );
926     if( !tmp1 ) // strange, but valid singleton
927         return nFlags;
928 
929     tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
930     if( !tmp2 ) // strange, but valid singleton
931         return nFlags;
932 
933     if ( *tmp2 != 0 )
934     {
935         // any trailing invalid character must invalidate the range.
936         nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
937                     SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
938         return nFlags;
939     }
940 
941     nFlags |= (nFlags2 << 4);
942     return nFlags;
943 }
944 
945 /**
946     @param pRange   pointer to range where rAddr effectively is *pRange->aEnd,
947                     used in conjunction with pExtInfo to determine the tab span
948                     of a 3D reference.
949  */
950 static sal_uInt16
lcl_ScAddress_Parse_OOo(const sal_Unicode * p,ScDocument * pDoc,ScAddress & rAddr,ScAddress::ExternalInfo * pExtInfo=NULL,ScRange * pRange=NULL)951 lcl_ScAddress_Parse_OOo( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
952                          ScAddress::ExternalInfo* pExtInfo = NULL, ScRange* pRange = NULL )
953 {
954     sal_uInt16  nRes = 0;
955     String  aDocName;       // der pure Dokumentenname
956     String  aTab;
957     bool    bExtDoc = false;
958     bool    bExtDocInherited = false;
959     const ScAddress aCurPos(rAddr);
960 
961     // Lets see if this is a reference to something in an external file.  A
962     // document name is always quoted and has a trailing #.
963     if (*p == '\'')
964     {
965         const sal_Unicode* pStart = p;
966         p = lcl_ParseQuotedName(p, aDocName);
967         if (*p++ == SC_COMPILER_FILE_TAB_SEP)
968             bExtDoc = true;
969         else
970             // This is not a document name.  Perhaps a quoted relative table
971             // name.
972             p = pStart;
973     }
974     else if (pExtInfo && pExtInfo->mbExternal)
975     {
976         // This is an external reference.
977         bExtDoc = bExtDocInherited = true;
978     }
979 
980     SCCOL   nCol = 0;
981     SCROW   nRow = 0;
982     SCTAB   nTab = 0;
983     sal_uInt16  nBits = SCA_VALID_TAB;
984     const sal_Unicode* q;
985     if ( ScGlobal::FindUnquoted( p, '.') )
986     {
987         nRes |= SCA_TAB_3D;
988         if ( bExtDoc )
989             nRes |= SCA_TAB_ABSOLUTE;
990         if (*p == '$')
991             nRes |= SCA_TAB_ABSOLUTE, p++;
992 
993         if (*p == '\'')
994         {
995             // Tokens that start at ' can have anything in them until a final
996             // ' but '' marks an escaped '.  We've earlier guaranteed that a
997             // string containing '' will be surrounded by '.
998             p = lcl_ParseQuotedName(p, aTab);
999         }
1000         else
1001         {
1002             while (*p)
1003             {
1004                 if( *p == '.')
1005                     break;
1006 
1007                 if( *p == '\'' )
1008                 {
1009                     p++; break;
1010                 }
1011                 aTab += *p++;
1012             }
1013         }
1014         if( *p++ != '.' )
1015             nBits = 0;
1016 
1017         if (!bExtDoc && (!pDoc || !pDoc->GetTable( aTab, nTab )))
1018             nBits = 0;
1019     }
1020     else
1021     {
1022         if (bExtDoc && !bExtDocInherited)
1023             return nRes;        // After a document a sheet must follow.
1024         nTab = rAddr.Tab();
1025     }
1026     nRes |= nBits;
1027 
1028     q = p;
1029     if (*p)
1030     {
1031         nBits = SCA_VALID_COL;
1032         if (*p == '$')
1033             nBits |= SCA_COL_ABSOLUTE, p++;
1034 
1035         if (CharClass::isAsciiAlpha( *p ))
1036         {
1037             nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
1038             while (nCol < MAXCOL && CharClass::isAsciiAlpha(*p))
1039                 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
1040         }
1041         else
1042             nBits = 0;
1043 
1044         if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) )
1045             nBits = 0;
1046         nRes |= nBits;
1047         if( !nBits )
1048             p = q;
1049     }
1050 
1051     q = p;
1052     if (*p)
1053     {
1054         nBits = SCA_VALID_ROW;
1055         if (*p == '$')
1056             nBits |= SCA_ROW_ABSOLUTE, p++;
1057         if( !CharClass::isAsciiDigit( *p ) )
1058         {
1059             nBits = 0;
1060             nRow = SCROW(-1);
1061         }
1062         else
1063         {
1064             String aTmp( p );
1065             long n = aTmp.ToInt32() - 1;
1066             while (CharClass::isAsciiDigit( *p ))
1067                 p++;
1068             if( n < 0 || n > MAXROW )
1069                 nBits = 0;
1070             nRow = static_cast<SCROW>(n);
1071         }
1072         nRes |= nBits;
1073         if( !nBits )
1074             p = q;
1075     }
1076 
1077     rAddr.Set( nCol, nRow, nTab );
1078 
1079     if (!*p && bExtDoc)
1080     {
1081         if (!pDoc)
1082             nRes = 0;
1083         else
1084         {
1085             ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
1086 
1087             // Need document name if inherited.
1088             if (bExtDocInherited)
1089             {
1090                 const String* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId);
1091                 if (pFileName)
1092                     aDocName = *pFileName;
1093                 else
1094                     nRes = 0;
1095             }
1096             pRefMgr->convertToAbsName(aDocName);
1097 
1098             if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
1099             {
1100                 if (!pDoc->GetTable( aTab, nTab ))
1101                     nRes = 0;
1102                 else
1103                 {
1104                     rAddr.SetTab( nTab);
1105                     nRes |= SCA_VALID_TAB;
1106                 }
1107             }
1108             else
1109             {
1110                 if (!pExtInfo)
1111                     nRes = 0;
1112                 else
1113                 {
1114                     if (!pExtInfo->mbExternal)
1115                     {
1116                         sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
1117 
1118                         pExtInfo->mbExternal = true;
1119                         pExtInfo->maTabName = aTab;
1120                         pExtInfo->mnFileId = nFileId;
1121 
1122                         if (pRefMgr->getSingleRefToken(nFileId, aTab,
1123                                     ScAddress(nCol, nRow, 0), NULL,
1124                                     &nTab).get())
1125                         {
1126                             rAddr.SetTab( nTab);
1127                             nRes |= SCA_VALID_TAB;
1128                         }
1129                         else
1130                             nRes = 0;
1131                     }
1132                     else
1133                     {
1134                         // This is a call for the second part of the reference,
1135                         // we must have the range to adapt tab span.
1136                         if (!pRange)
1137                             nRes = 0;
1138                         else
1139                         {
1140                             sal_uInt16 nFlags = nRes | SCA_VALID_TAB2;
1141                             if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
1142                                         pExtInfo, aDocName,
1143                                         pExtInfo->maTabName, aTab, pDoc))
1144                                 nRes &= ~SCA_VALID_TAB;
1145                             else
1146                             {
1147                                 if (nFlags & SCA_VALID_TAB2)
1148                                 {
1149                                     rAddr.SetTab( pRange->aEnd.Tab());
1150                                     nRes |= SCA_VALID_TAB;
1151                                 }
1152                                 else
1153                                     nRes &= ~SCA_VALID_TAB;
1154                             }
1155                         }
1156                     }
1157                 }
1158             }
1159         }
1160     }
1161 
1162     if ( !(nRes & SCA_VALID_ROW) && (nRes & SCA_VALID_COL)
1163             && !( (nRes & SCA_TAB_3D) && (nRes & SCA_VALID_TAB)) )
1164     {   // no Row, no Tab, but Col => DM (...), B (...) et al
1165         nRes = 0;
1166     }
1167     if( !*p )
1168     {
1169         sal_uInt16 nMask = nRes & ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1170         if( nMask == ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ) )
1171             nRes |= SCA_VALID;
1172     }
1173     else
1174         nRes = 0;
1175     return nRes;
1176 }
1177 
1178 static sal_uInt16
lcl_ScAddress_Parse(const sal_Unicode * p,ScDocument * pDoc,ScAddress & rAddr,const ScAddress::Details & rDetails,ScAddress::ExternalInfo * pExtInfo=NULL,const uno::Sequence<const sheet::ExternalLinkInfo> * pExternalLinks=NULL)1179 lcl_ScAddress_Parse ( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
1180                       const ScAddress::Details& rDetails,
1181                       ScAddress::ExternalInfo* pExtInfo = NULL,
1182                       const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL )
1183 {
1184     if( !*p )
1185         return 0;
1186 
1187     switch (rDetails.eConv)
1188     {
1189     default :
1190     case formula::FormulaGrammar::CONV_OOO:
1191         {
1192             return lcl_ScAddress_Parse_OOo( p, pDoc, rAddr, pExtInfo, NULL );
1193         }
1194 
1195     case formula::FormulaGrammar::CONV_XL_A1:
1196     case formula::FormulaGrammar::CONV_XL_OOX:
1197         {
1198             ScRange r = rAddr;
1199             sal_uInt16 nFlags = lcl_ScRange_Parse_XL_A1( r, p, pDoc, true, pExtInfo,
1200                     (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1201             rAddr = r.aStart;
1202             return nFlags;
1203         }
1204     case formula::FormulaGrammar::CONV_XL_R1C1:
1205         {
1206             ScRange r = rAddr;
1207             sal_uInt16 nFlags = lcl_ScRange_Parse_XL_R1C1( r, p, pDoc, rDetails, true, pExtInfo );
1208             rAddr = r.aStart;
1209             return nFlags;
1210         }
1211     }
1212 }
1213 
1214 
ConvertSingleRef(ScDocument * pDoc,const String & rRefString,SCTAB nDefTab,ScRefAddress & rRefAddress,const ScAddress::Details & rDetails,ScAddress::ExternalInfo * pExtInfo)1215 bool ConvertSingleRef( ScDocument* pDoc, const String& rRefString,
1216                        SCTAB nDefTab, ScRefAddress& rRefAddress,
1217                        const ScAddress::Details& rDetails,
1218                        ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1219 {
1220     bool bRet = false;
1221     if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND))
1222     {
1223         ScAddress aAddr( 0, 0, nDefTab );
1224         sal_uInt16 nRes = aAddr.Parse( rRefString, pDoc, rDetails, pExtInfo);
1225         if ( nRes & SCA_VALID )
1226         {
1227             rRefAddress.Set( aAddr,
1228                     ((nRes & SCA_COL_ABSOLUTE) == 0),
1229                     ((nRes & SCA_ROW_ABSOLUTE) == 0),
1230                     ((nRes & SCA_TAB_ABSOLUTE) == 0));
1231             bRet = true;
1232         }
1233     }
1234     return bRet;
1235 }
1236 
1237 
ConvertDoubleRef(ScDocument * pDoc,const String & rRefString,SCTAB nDefTab,ScRefAddress & rStartRefAddress,ScRefAddress & rEndRefAddress,const ScAddress::Details & rDetails,ScAddress::ExternalInfo * pExtInfo)1238 bool ConvertDoubleRef( ScDocument* pDoc, const String& rRefString, SCTAB nDefTab,
1239                        ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1240                        const ScAddress::Details& rDetails,
1241                        ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1242 {
1243     bool bRet = false;
1244     if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND))
1245     {
1246         ScRange aRange( ScAddress( 0, 0, nDefTab));
1247         sal_uInt16 nRes = aRange.Parse( rRefString, pDoc, rDetails, pExtInfo);
1248         if ( nRes & SCA_VALID )
1249         {
1250             rStartRefAddress.Set( aRange.aStart,
1251                     ((nRes & SCA_COL_ABSOLUTE) == 0),
1252                     ((nRes & SCA_ROW_ABSOLUTE) == 0),
1253                     ((nRes & SCA_TAB_ABSOLUTE) == 0));
1254             rEndRefAddress.Set( aRange.aEnd,
1255                     ((nRes & SCA_COL2_ABSOLUTE) == 0),
1256                     ((nRes & SCA_ROW2_ABSOLUTE) == 0),
1257                     ((nRes & SCA_TAB2_ABSOLUTE) == 0));
1258             bRet = true;
1259         }
1260     }
1261     return bRet;
1262 }
1263 
1264 
Parse(const String & r,ScDocument * pDoc,const Details & rDetails,ExternalInfo * pExtInfo,const uno::Sequence<const sheet::ExternalLinkInfo> * pExternalLinks)1265 sal_uInt16 ScAddress::Parse( const String& r, ScDocument* pDoc,
1266                          const Details& rDetails,
1267                          ExternalInfo* pExtInfo,
1268                          const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
1269 {
1270     return lcl_ScAddress_Parse( r.GetBuffer(), pDoc, *this, rDetails, pExtInfo, pExternalLinks );
1271 }
1272 
1273 
Intersects(const ScRange & r) const1274 bool ScRange::Intersects( const ScRange& r ) const
1275 {
1276     return !(
1277         Min( aEnd.Col(), r.aEnd.Col() ) < Max( aStart.Col(), r.aStart.Col() )
1278      || Min( aEnd.Row(), r.aEnd.Row() ) < Max( aStart.Row(), r.aStart.Row() )
1279      || Min( aEnd.Tab(), r.aEnd.Tab() ) < Max( aStart.Tab(), r.aStart.Tab() )
1280         );
1281 }
1282 
1283 
Justify()1284 void ScRange::Justify()
1285 {
1286     SCCOL nTempCol;
1287     if ( aEnd.Col() < (nTempCol = aStart.Col()) )
1288     {
1289         aStart.SetCol(aEnd.Col()); aEnd.SetCol(nTempCol);
1290     }
1291     SCROW nTempRow;
1292     if ( aEnd.Row() < (nTempRow = aStart.Row()) )
1293     {
1294         aStart.SetRow(aEnd.Row()); aEnd.SetRow(nTempRow);
1295     }
1296     SCTAB nTempTab;
1297     if ( aEnd.Tab() < (nTempTab = aStart.Tab()) )
1298     {
1299         aStart.SetTab(aEnd.Tab()); aEnd.SetTab(nTempTab);
1300     }
1301 }
1302 
ExtendTo(const ScRange & rRange)1303 void ScRange::ExtendTo( const ScRange& rRange )
1304 {
1305     DBG_ASSERT( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1306     if( IsValid() )
1307     {
1308         aStart.SetCol( ::std::min( aStart.Col(), rRange.aStart.Col() ) );
1309         aStart.SetRow( ::std::min( aStart.Row(), rRange.aStart.Row() ) );
1310         aStart.SetTab( ::std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1311         aEnd.SetCol(   ::std::max( aEnd.Col(),   rRange.aEnd.Col() ) );
1312         aEnd.SetRow(   ::std::max( aEnd.Row(),   rRange.aEnd.Row() ) );
1313         aEnd.SetTab(   ::std::max( aEnd.Tab(),   rRange.aEnd.Tab() ) );
1314     }
1315     else
1316         *this = rRange;
1317 }
1318 
1319 static sal_uInt16
lcl_ScRange_Parse_OOo(ScRange & aRange,const String & r,ScDocument * pDoc,ScAddress::ExternalInfo * pExtInfo=NULL)1320 lcl_ScRange_Parse_OOo( ScRange &aRange, const String& r, ScDocument* pDoc, ScAddress::ExternalInfo* pExtInfo = NULL )
1321 {
1322     sal_uInt16 nRes1 = 0, nRes2 = 0;
1323     xub_StrLen nPos = ScGlobal::FindUnquoted( r, ':');
1324     if (nPos != STRING_NOTFOUND)
1325     {
1326         String aTmp( r );
1327         sal_Unicode* p = aTmp.GetBufferAccess();
1328         p[ nPos ] = 0;
1329         if( (nRes1 = lcl_ScAddress_Parse_OOo( p, pDoc, aRange.aStart, pExtInfo, NULL ) ) != 0 )
1330         {
1331             aRange.aEnd = aRange.aStart;  // sheet must be initialized identical to first sheet
1332             if ( (nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, pDoc, aRange.aEnd, pExtInfo, &aRange ) ) != 0 )
1333             {
1334                 // PutInOrder / Justify
1335                 sal_uInt16 nMask, nBits1, nBits2;
1336                 SCCOL nTempCol;
1337                 if ( aRange.aEnd.Col() < (nTempCol = aRange.aStart.Col()) )
1338                 {
1339                     aRange.aStart.SetCol(aRange.aEnd.Col()); aRange.aEnd.SetCol(nTempCol);
1340                     nMask = (SCA_VALID_COL | SCA_COL_ABSOLUTE);
1341                     nBits1 = nRes1 & nMask;
1342                     nBits2 = nRes2 & nMask;
1343                     nRes1 = (nRes1 & ~nMask) | nBits2;
1344                     nRes2 = (nRes2 & ~nMask) | nBits1;
1345                 }
1346                 SCROW nTempRow;
1347                 if ( aRange.aEnd.Row() < (nTempRow = aRange.aStart.Row()) )
1348                 {
1349                     aRange.aStart.SetRow(aRange.aEnd.Row()); aRange.aEnd.SetRow(nTempRow);
1350                     nMask = (SCA_VALID_ROW | SCA_ROW_ABSOLUTE);
1351                     nBits1 = nRes1 & nMask;
1352                     nBits2 = nRes2 & nMask;
1353                     nRes1 = (nRes1 & ~nMask) | nBits2;
1354                     nRes2 = (nRes2 & ~nMask) | nBits1;
1355                 }
1356                 SCTAB nTempTab;
1357                 if ( aRange.aEnd.Tab() < (nTempTab = aRange.aStart.Tab()) )
1358                 {
1359                     aRange.aStart.SetTab(aRange.aEnd.Tab()); aRange.aEnd.SetTab(nTempTab);
1360                     nMask = (SCA_VALID_TAB | SCA_TAB_ABSOLUTE | SCA_TAB_3D);
1361                     nBits1 = nRes1 & nMask;
1362                     nBits2 = nRes2 & nMask;
1363                     nRes1 = (nRes1 & ~nMask) | nBits2;
1364                     nRes2 = (nRes2 & ~nMask) | nBits1;
1365                 }
1366                 if ( ((nRes1 & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1367                         == ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1368                         && !(nRes2 & SCA_TAB_3D) )
1369                     nRes2 |= SCA_TAB_ABSOLUTE;
1370             }
1371             else
1372                 nRes1 = 0;      // #38840# keine Tokens aus halben Sachen
1373         }
1374     }
1375     nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID )
1376           | nRes1
1377           | ( ( nRes2 & 0x070F ) << 4 );
1378     return nRes1;
1379 }
1380 
Parse(const String & r,ScDocument * pDoc,const ScAddress::Details & rDetails,ScAddress::ExternalInfo * pExtInfo,const uno::Sequence<const sheet::ExternalLinkInfo> * pExternalLinks)1381 sal_uInt16 ScRange::Parse( const String& r, ScDocument* pDoc,
1382                        const ScAddress::Details& rDetails,
1383                        ScAddress::ExternalInfo* pExtInfo,
1384                        const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
1385 {
1386     if ( r.Len() <= 0 )
1387         return 0;
1388 
1389     switch (rDetails.eConv)
1390     {
1391     default :
1392     case formula::FormulaGrammar::CONV_OOO:
1393         return lcl_ScRange_Parse_OOo( *this, r, pDoc, pExtInfo );
1394 
1395     case formula::FormulaGrammar::CONV_XL_A1:
1396     case formula::FormulaGrammar::CONV_XL_OOX:
1397         return lcl_ScRange_Parse_XL_A1( *this, r.GetBuffer(), pDoc, false, pExtInfo,
1398                 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1399 
1400     case formula::FormulaGrammar::CONV_XL_R1C1:
1401         return lcl_ScRange_Parse_XL_R1C1( *this, r.GetBuffer(), pDoc, rDetails, false, pExtInfo );
1402     }
1403 }
1404 
1405 
1406 // Accept a full range, or an address
ParseAny(const String & r,ScDocument * pDoc,const ScAddress::Details & rDetails)1407 sal_uInt16 ScRange::ParseAny( const String& r, ScDocument* pDoc,
1408                           const ScAddress::Details& rDetails )
1409 {
1410     sal_uInt16 nRet = Parse( r, pDoc, rDetails );
1411     const sal_uInt16 nValid = SCA_VALID | SCA_VALID_COL2 | SCA_VALID_ROW2 |
1412         SCA_VALID_TAB2;
1413 
1414     if ( (nRet & nValid) != nValid )
1415     {
1416         ScAddress aAdr;
1417         nRet = aAdr.Parse( r, pDoc, rDetails );
1418         if ( nRet & SCA_VALID )
1419             aStart = aEnd = aAdr;
1420     }
1421     return nRet;
1422 }
1423 
1424 // Parse only full row references
ParseCols(const String & rStr,ScDocument * pDoc,const ScAddress::Details & rDetails)1425 sal_uInt16 ScRange::ParseCols( const String& rStr, ScDocument* pDoc,
1426                            const ScAddress::Details& rDetails )
1427 {
1428     const sal_Unicode* p = rStr.GetBuffer();
1429     sal_uInt16 nRes = 0, ignored = 0;
1430 
1431     if( NULL == p )
1432         return 0;
1433 
1434     pDoc = NULL; // make compiler shutup we may need this later
1435 
1436     switch (rDetails.eConv)
1437     {
1438     default :
1439     case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1440     case formula::FormulaGrammar::CONV_XL_A1:
1441     case formula::FormulaGrammar::CONV_XL_OOX:
1442         if (NULL != (p = lcl_a1_get_col( p, &aStart, &ignored ) ) )
1443         {
1444             if( p[0] == ':')
1445             {
1446                 if( NULL != (p = lcl_a1_get_col( p+1, &aEnd, &ignored )))
1447                 {
1448                     nRes = SCA_VALID_COL;
1449                 }
1450             }
1451             else
1452             {
1453                 aEnd = aStart;
1454                 nRes = SCA_VALID_COL;
1455             }
1456         }
1457         break;
1458 
1459     case formula::FormulaGrammar::CONV_XL_R1C1:
1460         if ((p[0] == 'C' || p[0] != 'c') &&
1461             NULL != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored )))
1462         {
1463             if( p[0] == ':')
1464             {
1465                 if( (p[1] == 'C' || p[1] == 'c') &&
1466                     NULL != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored )))
1467                 {
1468                     nRes = SCA_VALID_COL;
1469                 }
1470             }
1471             else
1472             {
1473                 aEnd = aStart;
1474                 nRes = SCA_VALID_COL;
1475             }
1476         }
1477         break;
1478     }
1479 
1480     return (p != NULL && *p == '\0') ? nRes : 0;
1481 }
1482 
1483 // Parse only full row references
ParseRows(const String & rStr,ScDocument * pDoc,const ScAddress::Details & rDetails)1484 sal_uInt16 ScRange::ParseRows( const String& rStr, ScDocument* pDoc,
1485                            const ScAddress::Details& rDetails )
1486 {
1487     const sal_Unicode* p = rStr.GetBuffer();
1488     sal_uInt16 nRes = 0, ignored = 0;
1489 
1490     if( NULL == p )
1491         return 0;
1492 
1493     pDoc = NULL; // make compiler shutup we may need this later
1494 
1495     switch (rDetails.eConv)
1496     {
1497     default :
1498     case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1499     case formula::FormulaGrammar::CONV_XL_A1:
1500     case formula::FormulaGrammar::CONV_XL_OOX:
1501         if (NULL != (p = lcl_a1_get_row( p, &aStart, &ignored ) ) )
1502         {
1503             if( p[0] == ':')
1504             {
1505                 if( NULL != (p = lcl_a1_get_row( p+1, &aEnd, &ignored )))
1506                 {
1507                     nRes = SCA_VALID_COL;
1508                 }
1509             }
1510             else
1511             {
1512                 aEnd = aStart;
1513                 nRes = SCA_VALID_COL;
1514             }
1515         }
1516         break;
1517 
1518     case formula::FormulaGrammar::CONV_XL_R1C1:
1519         if ((p[0] == 'R' || p[0] != 'r') &&
1520             NULL != (p = lcl_r1c1_get_row( p, rDetails, &aStart, &ignored )))
1521         {
1522             if( p[0] == ':')
1523             {
1524                 if( (p[1] == 'R' || p[1] == 'r') &&
1525                     NULL != (p = lcl_r1c1_get_row( p+1, rDetails, &aEnd, &ignored )))
1526                 {
1527                     nRes = SCA_VALID_COL;
1528                 }
1529             }
1530             else
1531             {
1532                 aEnd = aStart;
1533                 nRes = SCA_VALID_COL;
1534             }
1535         }
1536         break;
1537     }
1538 
1539     return (p != NULL && *p == '\0') ? nRes : 0;
1540 }
1541 
1542 static inline void
lcl_a1_append_c(String & r,int nCol,bool bIsAbs)1543 lcl_a1_append_c ( String &r, int nCol, bool bIsAbs )
1544 {
1545     if( bIsAbs )
1546         r += '$';
1547     ScColToAlpha( r, sal::static_int_cast<SCCOL>(nCol) );
1548 }
1549 
1550 static inline void
lcl_a1_append_r(String & r,int nRow,bool bIsAbs)1551 lcl_a1_append_r ( String &r, int nRow, bool bIsAbs )
1552 {
1553     if ( bIsAbs )
1554         r += '$';
1555     r += String::CreateFromInt32( nRow+1 );
1556 }
1557 
1558 static inline void
lcl_r1c1_append_c(String & r,int nCol,bool bIsAbs,const ScAddress::Details & rDetails)1559 lcl_r1c1_append_c ( String &r, int nCol, bool bIsAbs,
1560                     const ScAddress::Details& rDetails )
1561 {
1562     r += 'C';
1563     if (bIsAbs)
1564     {
1565         r += String::CreateFromInt32( nCol + 1 );
1566     }
1567     else
1568     {
1569         nCol -= rDetails.nCol;
1570         if (nCol != 0) {
1571             r += '[';
1572             r += String::CreateFromInt32( nCol );
1573             r += ']';
1574         }
1575     }
1576 }
1577 static inline void
lcl_r1c1_append_r(String & r,int nRow,bool bIsAbs,const ScAddress::Details & rDetails)1578 lcl_r1c1_append_r ( String &r, int nRow, bool bIsAbs,
1579                     const ScAddress::Details& rDetails )
1580 {
1581     r += 'R';
1582     if (bIsAbs)
1583     {
1584         r += String::CreateFromInt32( nRow + 1 );
1585     }
1586     else
1587     {
1588         nRow -= rDetails.nRow;
1589         if (nRow != 0) {
1590             r += '[';
1591             r += String::CreateFromInt32( nRow );
1592             r += ']';
1593         }
1594     }
1595 }
1596 
1597 static String
getFileNameFromDoc(const ScDocument * pDoc)1598 getFileNameFromDoc( const ScDocument* pDoc )
1599 {
1600     // TODO : er points at ScGlobal::GetAbsDocName()
1601     // as a better template.  Look into it
1602     String sFileName;
1603     SfxObjectShell* pShell;
1604 
1605     if( NULL != pDoc &&
1606         NULL != (pShell = pDoc->GetDocumentShell() ) )
1607     {
1608         uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY );
1609         if( xModel.is() )
1610         {
1611             if( xModel->getURL().getLength() )
1612             {
1613                 INetURLObject aURL( xModel->getURL() );
1614                 sFileName = aURL.GetLastName();
1615             }
1616             else
1617                 sFileName = pShell->GetTitle();
1618         }
1619     }
1620 #if 0
1621         {
1622             ByteString  aStr( sFileName, RTL_TEXTENCODING_UTF8 );
1623             aStr.Append(static_cast< char >(0));
1624             std::cerr << "docname \'" << aStr.GetBuffer() << '\'' << std::endl;
1625         }
1626 #endif
1627     return sFileName;
1628 }
1629 
Format(String & r,sal_uInt16 nFlags,ScDocument * pDoc,const Details & rDetails) const1630 void ScAddress::Format( String& r, sal_uInt16 nFlags, ScDocument* pDoc,
1631                         const Details& rDetails) const
1632 {
1633     r.Erase();
1634     if( nFlags & SCA_VALID )
1635         nFlags |= ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1636     if( pDoc && (nFlags & SCA_VALID_TAB ) )
1637     {
1638         if ( nTab >= pDoc->GetTableCount() )
1639         {
1640             r = ScGlobal::GetRscString( STR_NOREF_STR );
1641             return;
1642         }
1643 //      if( nFlags & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ) )
1644         if( nFlags & SCA_TAB_3D )
1645         {
1646             String aTabName, aDocName;
1647             pDoc->GetName( nTab, aTabName );
1648             // External Reference, same as in ScCompiler::MakeTabStr()
1649             if( aTabName.GetChar(0) == '\'' )
1650             {   // "'Doc'#Tab"
1651                 xub_StrLen nPos = ScCompiler::GetDocTabPos( aTabName);
1652                 if (nPos != STRING_NOTFOUND)
1653                 {
1654                     aDocName = aTabName.Copy( 0, nPos + 1 );
1655                     aTabName.Erase( 0, nPos + 1 );
1656                 }
1657             }
1658             else if( nFlags & SCA_FORCE_DOC )
1659             {
1660                 // VBA has an 'external' flag that forces the addition of the
1661                 // tab name _and_ the doc name.  The VBA code would be
1662                 // needlessly complicated if it constructed an actual external
1663                 // reference so we add this somewhat cheesy kludge to force the
1664                 // addition of the document name even for non-external references
1665                 aDocName = getFileNameFromDoc( pDoc );
1666             }
1667             ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
1668 
1669             switch( rDetails.eConv )
1670             {
1671             default :
1672             case formula::FormulaGrammar::CONV_OOO:
1673                 r += aDocName;
1674                 if( nFlags & SCA_TAB_ABSOLUTE )
1675                     r += '$';
1676                 r += aTabName;
1677                 r += '.';
1678                 break;
1679 
1680             case formula::FormulaGrammar::CONV_XL_A1:
1681             case formula::FormulaGrammar::CONV_XL_R1C1:
1682             case formula::FormulaGrammar::CONV_XL_OOX:
1683                 if (aDocName.Len() > 0)
1684                 {
1685                     r += '[';
1686                     r += aDocName;
1687                     r += ']';
1688                 }
1689                 r += aTabName;
1690                 r += '!';
1691                 break;
1692             }
1693         }
1694     }
1695     switch( rDetails.eConv )
1696     {
1697     default :
1698     case formula::FormulaGrammar::CONV_OOO:
1699     case formula::FormulaGrammar::CONV_XL_A1:
1700     case formula::FormulaGrammar::CONV_XL_OOX:
1701         if( nFlags & SCA_VALID_COL )
1702             lcl_a1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE );
1703         if( nFlags & SCA_VALID_ROW )
1704             lcl_a1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE );
1705         break;
1706 
1707     case formula::FormulaGrammar::CONV_XL_R1C1:
1708         if( nFlags & SCA_VALID_ROW )
1709             lcl_r1c1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE, rDetails );
1710         if( nFlags & SCA_VALID_COL )
1711             lcl_r1c1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE, rDetails );
1712         break;
1713     }
1714 }
1715 
1716 static void
lcl_Split_DocTab(const ScDocument * pDoc,SCTAB nTab,const ScAddress::Details & rDetails,sal_uInt16 nFlags,String & rTabName,String & rDocName)1717 lcl_Split_DocTab( const ScDocument* pDoc,  SCTAB nTab,
1718                   const ScAddress::Details& rDetails,
1719                   sal_uInt16 nFlags,
1720                   String& rTabName, String& rDocName )
1721 {
1722     pDoc->GetName( nTab, rTabName );
1723     rDocName.Erase();
1724 #if 0
1725     {
1726         ByteString  aStr(rTabName, RTL_TEXTENCODING_UTF8);
1727         aStr.Append(static_cast< char >(0));
1728         std::cerr << "tabname \'" << aStr.GetBuffer() << '\'' << std::endl;
1729     }
1730 #endif
1731     // External reference, same as in ScCompiler::MakeTabStr()
1732     if ( rTabName.GetChar(0) == '\'' )
1733     {   // "'Doc'#Tab"
1734         xub_StrLen nPos = ScCompiler::GetDocTabPos( rTabName);
1735         if (nPos != STRING_NOTFOUND)
1736         {
1737             rDocName = rTabName.Copy( 0, nPos + 1 );
1738             rTabName.Erase( 0, nPos + 1 );
1739         }
1740     }
1741     else if( nFlags & SCA_FORCE_DOC )
1742     {
1743         // VBA has an 'external' flag that forces the addition of the
1744         // tab name _and_ the doc name.  The VBA code would be
1745         // needlessly complicated if it constructed an actual external
1746         // reference so we add this somewhat cheesy kludge to force the
1747         // addition of the document name even for non-external references
1748         rDocName = getFileNameFromDoc( pDoc );
1749     }
1750     ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
1751 }
1752 
1753 static void
lcl_ScRange_Format_XL_Header(String & r,const ScRange & rRange,sal_uInt16 nFlags,ScDocument * pDoc,const ScAddress::Details & rDetails)1754 lcl_ScRange_Format_XL_Header( String& r, const ScRange& rRange,
1755                               sal_uInt16 nFlags, ScDocument* pDoc,
1756                               const ScAddress::Details& rDetails )
1757 {
1758     if( nFlags & SCA_TAB_3D )
1759     {
1760         String aTabName, aDocName;
1761         lcl_Split_DocTab( pDoc, rRange.aStart.Tab(), rDetails, nFlags,
1762                           aTabName, aDocName );
1763         if( aDocName.Len() > 0 )
1764         {
1765             r += '[';
1766             r += aDocName;
1767             r += ']';
1768         }
1769         r += aTabName;
1770 
1771         if( nFlags & SCA_TAB2_3D )
1772         {
1773             lcl_Split_DocTab( pDoc, rRange.aEnd.Tab(), rDetails, nFlags,
1774                               aTabName, aDocName );
1775             r += ':';
1776             r += aTabName;
1777         }
1778         r += '!';
1779     }
1780 }
1781 
Format(String & r,sal_uInt16 nFlags,ScDocument * pDoc,const ScAddress::Details & rDetails) const1782 void ScRange::Format( String& r, sal_uInt16 nFlags, ScDocument* pDoc,
1783                       const ScAddress::Details& rDetails ) const
1784 {
1785     r.Erase();
1786     if( !( nFlags & SCA_VALID ) )
1787     {
1788         r = ScGlobal::GetRscString( STR_NOREF_STR );
1789         return;
1790     }
1791 
1792 #define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask)))
1793     switch( rDetails.eConv ) {
1794     default :
1795     case formula::FormulaGrammar::CONV_OOO: {
1796         sal_Bool bOneTab = (aStart.Tab() == aEnd.Tab());
1797         if ( !bOneTab )
1798             nFlags |= SCA_TAB_3D;
1799         aStart.Format( r, nFlags, pDoc, rDetails );
1800         if( aStart != aEnd ||
1801             absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1802             absrel_differ( nFlags, SCA_ROW_ABSOLUTE ))
1803         {
1804             String aName;
1805             nFlags = ( nFlags & SCA_VALID ) | ( ( nFlags >> 4 ) & 0x070F );
1806             if ( bOneTab )
1807                 pDoc = NULL;
1808             else
1809                 nFlags |= SCA_TAB_3D;
1810             aEnd.Format( aName, nFlags, pDoc, rDetails );
1811             r += ':';
1812             r += aName;
1813         }
1814     }
1815     break;
1816 
1817     case formula::FormulaGrammar::CONV_XL_A1:
1818     case formula::FormulaGrammar::CONV_XL_OOX:
1819         lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1820         if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1821         {
1822             // Full col refs always require 2 rows (2:2)
1823             lcl_a1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE );
1824             r += ':';
1825             lcl_a1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE );
1826         }
1827         else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1828         {
1829             // Full row refs always require 2 cols (A:A)
1830             lcl_a1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE );
1831             r += ':';
1832             lcl_a1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE );
1833         }
1834         else
1835         {
1836             lcl_a1_append_c ( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE );
1837             lcl_a1_append_r ( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE );
1838             if( aStart.Col() != aEnd.Col() ||
1839                 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1840                 aStart.Row() != aEnd.Row() ||
1841                 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1842                 r += ':';
1843                 lcl_a1_append_c ( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE );
1844                 lcl_a1_append_r ( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE );
1845             }
1846         }
1847     break;
1848 
1849     case formula::FormulaGrammar::CONV_XL_R1C1:
1850         lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1851         if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1852         {
1853             lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails );
1854             if( aStart.Row() != aEnd.Row() ||
1855                 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1856                 r += ':';
1857                 lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails );
1858             }
1859         }
1860         else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1861         {
1862             lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails );
1863             if( aStart.Col() != aEnd.Col() ||
1864                 absrel_differ( nFlags, SCA_COL_ABSOLUTE )) {
1865                 r += ':';
1866                 lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails );
1867             }
1868         }
1869         else
1870         {
1871             lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails );
1872             lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails );
1873             if( aStart.Col() != aEnd.Col() ||
1874                 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1875                 aStart.Row() != aEnd.Row() ||
1876                 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1877                 r += ':';
1878                 lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails );
1879                 lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails );
1880             }
1881         }
1882     }
1883 #undef  absrel_differ
1884 }
1885 
Move(SCsCOL dx,SCsROW dy,SCsTAB dz,ScDocument * pDoc)1886 bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1887 {
1888     SCsTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB+1;
1889     dx = Col() + dx;
1890     dy = Row() + dy;
1891     dz = Tab() + dz;
1892     sal_Bool bValid = sal_True;
1893     if( dx < 0 )
1894         dx = 0, bValid = sal_False;
1895     else if( dx > MAXCOL )
1896         dx = MAXCOL, bValid =sal_False;
1897     if( dy < 0 )
1898         dy = 0, bValid = sal_False;
1899     else if( dy > MAXROW )
1900         dy = MAXROW, bValid =sal_False;
1901     if( dz < 0 )
1902         dz = 0, bValid = sal_False;
1903     else if( dz >= nMaxTab )
1904         dz = nMaxTab-1, bValid =sal_False;
1905     Set( dx, dy, dz );
1906     return bValid;
1907 }
1908 
1909 
Move(SCsCOL dx,SCsROW dy,SCsTAB dz,ScDocument * pDoc)1910 bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1911 {
1912     // Einfahces &, damit beides ausgefuehrt wird!!
1913     return aStart.Move( dx, dy, dz, pDoc ) & aEnd.Move( dx, dy, dz, pDoc );
1914 }
1915 
1916 
GetColRowString(bool bAbsolute,const Details & rDetails) const1917 String ScAddress::GetColRowString( bool bAbsolute,
1918                                    const Details& rDetails ) const
1919 {
1920     String aString;
1921 
1922     switch( rDetails.eConv )
1923     {
1924     default :
1925     case formula::FormulaGrammar::CONV_OOO:
1926     case formula::FormulaGrammar::CONV_XL_A1:
1927     case formula::FormulaGrammar::CONV_XL_OOX:
1928     if (bAbsolute)
1929         aString.Append( '$' );
1930 
1931     ScColToAlpha( aString, nCol);
1932 
1933     if ( bAbsolute )
1934         aString.Append( '$' );
1935 
1936     aString += String::CreateFromInt32(nRow+1);
1937         break;
1938 
1939     case formula::FormulaGrammar::CONV_XL_R1C1:
1940         lcl_r1c1_append_r ( aString, nRow, bAbsolute, rDetails );
1941         lcl_r1c1_append_c ( aString, nCol, bAbsolute, rDetails );
1942         break;
1943     }
1944 
1945     return aString;
1946 }
1947 
1948 
GetRefString(ScDocument * pDoc,SCTAB nActTab,const ScAddress::Details & rDetails) const1949 String ScRefAddress::GetRefString( ScDocument* pDoc, SCTAB nActTab,
1950                                    const ScAddress::Details& rDetails ) const
1951 {
1952     if ( !pDoc )
1953         return EMPTY_STRING;
1954     if ( Tab()+1 > pDoc->GetTableCount() )
1955         return ScGlobal::GetRscString( STR_NOREF_STR );
1956 
1957     String aString;
1958     sal_uInt16 nFlags = SCA_VALID;
1959     if ( nActTab != Tab() )
1960     {
1961         nFlags |= SCA_TAB_3D;
1962         if ( !bRelTab )
1963             nFlags |= SCA_TAB_ABSOLUTE;
1964     }
1965     if ( !bRelCol )
1966         nFlags |= SCA_COL_ABSOLUTE;
1967     if ( !bRelRow )
1968         nFlags |= SCA_ROW_ABSOLUTE;
1969 
1970     aAdr.Format( aString, nFlags, pDoc, rDetails );
1971 
1972     return aString;
1973 }
1974 
1975 //------------------------------------------------------------------------
1976 
ScColToAlpha(rtl::OUStringBuffer & rBuf,SCCOL nCol)1977 void ScColToAlpha( rtl::OUStringBuffer& rBuf, SCCOL nCol )
1978 {
1979     if (nCol < 26*26)
1980     {
1981         if (nCol < 26)
1982             rBuf.append( static_cast<sal_Unicode>( 'A' +
1983                         static_cast<sal_uInt16>(nCol)));
1984         else
1985         {
1986             rBuf.append( static_cast<sal_Unicode>( 'A' +
1987                         (static_cast<sal_uInt16>(nCol) / 26) - 1));
1988             rBuf.append( static_cast<sal_Unicode>( 'A' +
1989                         (static_cast<sal_uInt16>(nCol) % 26)));
1990         }
1991     }
1992     else
1993     {
1994         String aStr;
1995         while (nCol >= 26)
1996         {
1997             SCCOL nC = nCol % 26;
1998             aStr += static_cast<sal_Unicode>( 'A' +
1999                     static_cast<sal_uInt16>(nC));
2000             nCol = sal::static_int_cast<SCCOL>( nCol - nC );
2001             nCol = nCol / 26 - 1;
2002         }
2003         aStr += static_cast<sal_Unicode>( 'A' +
2004                 static_cast<sal_uInt16>(nCol));
2005         aStr.Reverse();
2006         rBuf.append( aStr);
2007     }
2008 }
2009 
2010 
AlphaToCol(SCCOL & rCol,const String & rStr)2011 bool AlphaToCol( SCCOL& rCol, const String& rStr)
2012 {
2013     SCCOL nResult = 0;
2014     xub_StrLen nStop = rStr.Len();
2015     xub_StrLen nPos = 0;
2016     sal_Unicode c;
2017     while (nResult <= MAXCOL && nPos < nStop && (c = rStr.GetChar( nPos)) != 0 &&
2018             CharClass::isAsciiAlpha(c))
2019     {
2020         if (nPos > 0)
2021             nResult = (nResult + 1) * 26;
2022         nResult += ScGlobal::ToUpperAlpha(c) - 'A';
2023         ++nPos;
2024     }
2025     bool bOk = (ValidCol(nResult) && nPos > 0);
2026     if (bOk)
2027         rCol = nResult;
2028     return bOk;
2029 }
2030