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