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