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