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 0 : const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 );
40 :
41 0 : ScAddress::Details::Details ( const ScDocument* pDoc,
42 : const ScAddress & rAddr ) :
43 0 : eConv( pDoc->GetAddressConvention() ),
44 0 : nRow( rAddr.Row() ),
45 0 : nCol( rAddr.Col() )
46 0 : {}
47 :
48 : namespace {
49 :
50 0 : 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 0 : OUStringBuffer aBuf(OUString(pStart+1, p-pStart-1));
57 :
58 0 : ++p; // Skip the 2nd quote.
59 0 : sal_Unicode cPrev = 0;
60 0 : for (; *p; ++p)
61 : {
62 0 : if (*p == '\'')
63 : {
64 0 : if (cPrev == '\'')
65 : {
66 : // double single-quote equals one single quote.
67 0 : aBuf.append(*p);
68 0 : cPrev = 0;
69 0 : continue;
70 : }
71 : }
72 0 : else if (cPrev == '\'')
73 : {
74 : // We are past the closing quote. We're done!
75 0 : rName = aBuf.makeStringAndClear();
76 0 : return p;
77 : }
78 : else
79 0 : aBuf.append(*p);
80 0 : 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 0 : const sal_Unicode* parseQuotedName( const sal_Unicode* p, OUString& rName )
99 : {
100 0 : if (*p != '\'')
101 0 : return p;
102 :
103 0 : const sal_Unicode* pStart = p;
104 0 : sal_Unicode cPrev = 0;
105 0 : for (++p; *p; ++p)
106 : {
107 0 : if (*p == '\'')
108 : {
109 0 : if (cPrev == '\'')
110 : {
111 : // double single-quote equals one single quote.
112 0 : return parseQuotedNameWithBuffer(pStart, p, rName);
113 : }
114 : }
115 0 : else if (cPrev == '\'')
116 : {
117 : // We are past the closing quote. We're done! Skip the opening
118 : // and closing quotes.
119 0 : rName = OUString(pStart+1, p - pStart-2);
120 0 : return p;
121 : }
122 :
123 0 : cPrev = *p;
124 : }
125 :
126 0 : rName = "";
127 0 : return pStart;
128 : }
129 :
130 : }
131 :
132 0 : static long int sal_Unicode_strtol ( const sal_Unicode* p, const sal_Unicode** pEnd )
133 : {
134 0 : long int accum = 0, prev = 0;
135 0 : bool is_neg = false;
136 :
137 0 : if( *p == '-' )
138 : {
139 0 : is_neg = true;
140 0 : p++;
141 : }
142 0 : else if( *p == '+' )
143 0 : p++;
144 :
145 0 : while (rtl::isAsciiDigit( *p ))
146 : {
147 0 : accum = accum * 10 + *p - '0';
148 0 : if( accum < prev )
149 : {
150 0 : *pEnd = NULL;
151 0 : return 0;
152 : }
153 0 : prev = accum;
154 0 : p++;
155 : }
156 :
157 0 : *pEnd = p;
158 0 : return is_neg ? -accum : accum;
159 : }
160 :
161 0 : static const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p )
162 : {
163 0 : if ( p )
164 : {
165 0 : while( *p == ' ' )
166 0 : ++p;
167 : }
168 0 : 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 0 : 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 0 : if (rExternDocName.isEmpty())
189 0 : return !pExtInfo || !pExtInfo->mbExternal;
190 :
191 0 : ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
192 0 : 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 0 : sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName);
213 :
214 0 : if (pExtInfo)
215 : {
216 0 : if (pExtInfo->mbExternal)
217 : {
218 0 : if (pExtInfo->mnFileId != nFileId)
219 0 : return false;
220 : }
221 : else
222 : {
223 0 : pExtInfo->mbExternal = true;
224 0 : pExtInfo->maTabName = rStartTabName;
225 0 : pExtInfo->mnFileId = nFileId;
226 : }
227 : }
228 :
229 0 : if (rEndTabName.isEmpty() || rStartTabName == rEndTabName)
230 : {
231 0 : rRange.aEnd.SetTab( rRange.aStart.Tab());
232 0 : 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 0 : static const sal_Unicode * lcl_XL_ParseSheetRef( const sal_Unicode* start,
259 : OUString& rExternTabName,
260 : bool bAllow3D,
261 : const sal_Unicode* pMsoxlQuoteStop )
262 : {
263 0 : OUString aTabName;
264 0 : const sal_Unicode *p = start;
265 :
266 : // XL only seems to use single quotes for sheet names.
267 0 : 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 0 : else if( *p == '\'')
299 : {
300 0 : p = parseQuotedName(p, aTabName);
301 0 : if (aTabName.isEmpty())
302 0 : return NULL;
303 : }
304 : else
305 : {
306 0 : 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 0 : const sal_Unicode uc = *p;
326 0 : if( rtl::isAsciiAlpha( uc ) || uc == '_' )
327 : {
328 0 : if( only_digits && p != start &&
329 0 : (uc == 'e' || uc == 'E' ) )
330 : {
331 0 : p = start;
332 0 : break;
333 : }
334 0 : only_digits = false;
335 0 : p++;
336 : }
337 0 : else if( rtl::isAsciiDigit( uc ))
338 : {
339 0 : p++;
340 : }
341 0 : else if( uc == '.' )
342 : {
343 0 : if( only_digits ) // Valid, except after only digits.
344 : {
345 0 : p = start;
346 0 : break;
347 : }
348 0 : p++;
349 : }
350 0 : else if (uc > 127)
351 : {
352 : // non ASCII character is allowed.
353 0 : ++p;
354 : }
355 : else
356 0 : break;
357 : }
358 :
359 0 : if( *p != '!' && ( !bAllow3D || *p != ':' ) )
360 0 : return (!bAllow3D && *p == ':') ? p : start;
361 :
362 0 : aTabName += OUString( start, sal::static_int_cast<sal_Int32>( p - start ) );
363 : }
364 :
365 0 : rExternTabName = aTabName;
366 0 : 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 0 : 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 0 : if (pExternalLinks && pExternalLinks->hasElements())
386 : {
387 : // A numeric "document name" is an index into the sequence.
388 0 : if (CharClass::isAsciiNumeric( rExternDocName))
389 : {
390 0 : sal_Int32 i = rExternDocName.toInt32();
391 0 : if (i < 0 || i >= pExternalLinks->getLength())
392 0 : return false; // with default *ppErrRet
393 0 : const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i];
394 0 : switch (rInfo.Type)
395 : {
396 : case sheet::ExternalLinkType::DOCUMENT :
397 : {
398 0 : OUString aStr;
399 0 : 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 0 : rExternDocName = aStr;
406 : }
407 0 : 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 0 : return true;
423 : }
424 :
425 0 : 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 0 : const sal_Unicode* startTabs, *start = p;
436 0 : sal_uInt16 nSaveFlags = nFlags;
437 :
438 : // Is this an external reference ?
439 0 : rStartTabName = "";
440 0 : rEndTabName = "";
441 0 : rExternDocName = "";
442 0 : const sal_Unicode* pMsoxlQuoteStop = NULL;
443 0 : if (*p == '[')
444 : {
445 0 : ++p;
446 : // Only single quotes are correct, and a double single quote escapes a
447 : // single quote text inside the quoted text.
448 0 : 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 0 : p = ScGlobal::UnicodeStrChr( start+1, ']' );
461 0 : if( p == NULL )
462 0 : return start;
463 0 : rExternDocName += OUString( start+1, sal::static_int_cast<sal_Int32>( p-(start+1) ) );
464 : }
465 0 : ++p;
466 :
467 0 : const sal_Unicode* pErrRet = start;
468 0 : if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
469 0 : return pErrRet;
470 :
471 0 : rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, pDoc->GetDocumentShell());
472 : }
473 0 : 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 0 : startTabs = p;
526 0 : p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop);
527 0 : if( NULL == p )
528 0 : return start; // invalid tab
529 0 : if (bOnlyAcceptSingle && *p == ':')
530 0 : return NULL; // 3D
531 0 : if( p != startTabs )
532 : {
533 0 : nFlags |= SCA_VALID_TAB | SCA_TAB_3D | SCA_TAB_ABSOLUTE;
534 0 : if( *p == ':' ) // 3d ref
535 : {
536 0 : p = lcl_XL_ParseSheetRef( p+1, rEndTabName, false, pMsoxlQuoteStop);
537 0 : if( p == NULL )
538 : {
539 0 : nFlags = nSaveFlags;
540 0 : return start; // invalid tab
541 : }
542 0 : 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 0 : nFlags |= SCA_VALID_TAB2 | SCA_TAB2_ABSOLUTE;
549 0 : aEnd.SetTab( aStart.Tab() );
550 : }
551 :
552 0 : if( *p++ != '!' )
553 : {
554 0 : nFlags = nSaveFlags;
555 0 : return start; // syntax error
556 : }
557 : else
558 0 : p = lcl_eatWhiteSpace( p );
559 : }
560 : else
561 : {
562 0 : nFlags |= SCA_VALID_TAB | SCA_VALID_TAB2;
563 : // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
564 : }
565 :
566 0 : if (!rExternDocName.isEmpty())
567 : {
568 0 : ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
569 0 : pRefMgr->convertToAbsName(rExternDocName);
570 : }
571 : else
572 : {
573 : // Internal reference.
574 0 : if (rStartTabName.isEmpty())
575 : {
576 0 : nFlags = nSaveFlags;
577 0 : return start;
578 : }
579 :
580 : SCTAB nTab;
581 0 : if (!pDoc->GetTable(rStartTabName, nTab))
582 : {
583 : // invalid table name.
584 0 : nFlags &= ~SCA_VALID_TAB;
585 0 : nTab = -1;
586 : }
587 :
588 0 : aStart.SetTab(nTab);
589 0 : aEnd.SetTab(nTab);
590 :
591 0 : 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 0 : return p;
604 : }
605 :
606 0 : 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 0 : if( p[0] == '\0' )
615 0 : return NULL;
616 :
617 0 : p++;
618 0 : if( (isRelative = (*p == '[') ) != false )
619 0 : p++;
620 0 : n = sal_Unicode_strtol( p, &pEnd );
621 0 : if( NULL == pEnd )
622 0 : return NULL;
623 :
624 0 : if( p == pEnd ) // C is a relative ref with offset 0
625 : {
626 0 : if( isRelative )
627 0 : return NULL;
628 0 : n = rDetails.nCol;
629 : }
630 0 : else if( isRelative )
631 : {
632 0 : if( *pEnd != ']' )
633 0 : return NULL;
634 0 : n += rDetails.nCol;
635 0 : pEnd++;
636 : }
637 : else
638 : {
639 0 : *nFlags |= SCA_COL_ABSOLUTE;
640 0 : n--;
641 : }
642 :
643 0 : if( n < 0 || n >= MAXCOLCOUNT )
644 0 : return NULL;
645 0 : pAddr->SetCol( static_cast<SCCOL>( n ) );
646 0 : *nFlags |= SCA_VALID_COL;
647 :
648 0 : return pEnd;
649 : }
650 :
651 0 : 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 0 : if( p[0] == '\0' )
661 0 : return NULL;
662 :
663 0 : p++;
664 0 : if( (isRelative = (*p == '[') ) != false )
665 0 : p++;
666 0 : n = sal_Unicode_strtol( p, &pEnd );
667 0 : if( NULL == pEnd )
668 0 : return NULL;
669 :
670 0 : if( p == pEnd ) // R is a relative ref with offset 0
671 : {
672 0 : if( isRelative )
673 0 : return NULL;
674 0 : n = rDetails.nRow;
675 : }
676 0 : else if( isRelative )
677 : {
678 0 : if( *pEnd != ']' )
679 0 : return NULL;
680 0 : n += rDetails.nRow;
681 0 : pEnd++;
682 : }
683 : else
684 : {
685 0 : *nFlags |= SCA_ROW_ABSOLUTE;
686 0 : n--;
687 : }
688 :
689 0 : if( n < 0 || n >= MAXROWCOUNT )
690 0 : return NULL;
691 0 : pAddr->SetRow( static_cast<SCROW>( n ) );
692 0 : *nFlags |= SCA_VALID_ROW;
693 :
694 0 : return pEnd;
695 : }
696 :
697 0 : 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 0 : const sal_Unicode* pTmp = NULL;
705 0 : OUString aExternDocName, aStartTabName, aEndTabName;
706 0 : sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB;
707 : // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
708 0 : sal_uInt16 nFlags2 = SCA_VALID_TAB;
709 :
710 : p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
711 0 : aEndTabName, nFlags, bOnlyAcceptSingle, NULL );
712 :
713 0 : if (!aExternDocName.isEmpty())
714 : lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
715 0 : aStartTabName, aEndTabName, pDoc);
716 :
717 0 : if( NULL == p )
718 0 : return 0;
719 :
720 0 : if( *p == 'R' || *p == 'r' )
721 : {
722 0 : if( NULL == (p = lcl_r1c1_get_row( p, rDetails, &r.aStart, &nFlags )) )
723 0 : return 0;
724 :
725 0 : if( *p != 'C' && *p != 'c' ) // full row R#
726 : {
727 0 : 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 0 : nFlags |= (nFlags << 4);
733 0 : 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 0 : 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 0 : SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
747 0 : 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 0 : else if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
759 : {
760 0 : return 0;
761 : }
762 :
763 0 : if( p[0] != ':' ||
764 0 : (p[1] != 'R' && p[1] != 'r') ||
765 0 : NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )) ||
766 0 : (*pTmp != 'C' && *pTmp != 'c') ||
767 0 : NULL == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 )))
768 : {
769 : // single cell reference
770 :
771 0 : 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 0 : return bOnlyAcceptSingle ? nFlags : 0;
779 : }
780 0 : p = pTmp;
781 :
782 : // double reference
783 :
784 0 : 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 0 : nFlags |= (nFlags2 << 4);
793 0 : return bOnlyAcceptSingle ? 0 : nFlags;
794 : }
795 0 : else if( *p == 'C' || *p == 'c' ) // full col C#
796 : {
797 0 : if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
798 0 : return 0;
799 :
800 0 : 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 0 : nFlags |= (nFlags << 4);
804 0 : r.aEnd.SetCol( r.aStart.Col() );
805 : }
806 : else
807 : {
808 0 : nFlags |= (nFlags2 << 4);
809 0 : p = pTmp;
810 : }
811 :
812 0 : 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 0 : SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
817 0 : 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 0 : return 0;
830 : }
831 :
832 0 : 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 0 : if( *p == '$' )
839 0 : *nFlags |= SCA_COL_ABSOLUTE, p++;
840 :
841 0 : if( !rtl::isAsciiAlpha( *p ) )
842 0 : return NULL;
843 :
844 0 : nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
845 0 : while (nCol <= MAXCOL && rtl::isAsciiAlpha(*p))
846 0 : nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
847 0 : if( nCol > MAXCOL || rtl::isAsciiAlpha( *p ) )
848 0 : return NULL;
849 :
850 0 : *nFlags |= SCA_VALID_COL;
851 0 : pAddr->SetCol( nCol );
852 :
853 0 : return p;
854 : }
855 :
856 0 : 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 0 : if( *p == '$' )
864 0 : *nFlags |= SCA_ROW_ABSOLUTE, p++;
865 :
866 0 : n = sal_Unicode_strtol( p, &pEnd ) - 1;
867 0 : if( NULL == pEnd || p == pEnd || n < 0 || n > MAXROW )
868 0 : return NULL;
869 :
870 0 : *nFlags |= SCA_VALID_ROW;
871 0 : pAddr->SetRow( static_cast<SCROW>(n) );
872 :
873 0 : return pEnd;
874 : }
875 :
876 0 : static sal_uInt16 lcl_ScRange_Parse_XL_A1( ScRange& r,
877 : const sal_Unicode* p,
878 : ScDocument* pDoc,
879 : bool bOnlyAcceptSingle,
880 : ScAddress::ExternalInfo* pExtInfo,
881 : const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
882 : {
883 : const sal_Unicode* tmp1, *tmp2;
884 0 : OUString aExternDocName, aStartTabName, aEndTabName; // for external link table
885 0 : sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB;
886 :
887 : p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
888 0 : aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks );
889 :
890 0 : if (!aExternDocName.isEmpty())
891 : lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
892 0 : aStartTabName, aEndTabName, pDoc);
893 :
894 0 : if( NULL == p )
895 0 : return 0;
896 :
897 0 : tmp1 = lcl_a1_get_col( p, &r.aStart, &nFlags );
898 0 : if( tmp1 == NULL ) // Is it a row only reference 3:5
899 : {
900 0 : if( bOnlyAcceptSingle ) // by definition full row refs are ranges
901 0 : return 0;
902 :
903 0 : tmp1 = lcl_a1_get_row( p, &r.aStart, &nFlags );
904 :
905 0 : tmp1 = lcl_eatWhiteSpace( tmp1 );
906 0 : if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
907 0 : return 0;
908 :
909 0 : tmp1 = lcl_eatWhiteSpace( tmp1 );
910 0 : tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
911 0 : if( !tmp2 )
912 0 : return 0;
913 :
914 0 : r.aStart.SetCol( 0 ); r.aEnd.SetCol( MAXCOL );
915 : nFlags |=
916 : SCA_VALID_COL | SCA_VALID_COL2 |
917 0 : SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
918 0 : nFlags |= (nFlags2 << 4);
919 0 : return nFlags;
920 : }
921 :
922 0 : tmp2 = lcl_a1_get_row( tmp1, &r.aStart, &nFlags );
923 0 : if( tmp2 == NULL ) // check for col only reference F:H
924 : {
925 0 : if( bOnlyAcceptSingle ) // by definition full col refs are ranges
926 0 : return 0;
927 :
928 0 : tmp1 = lcl_eatWhiteSpace( tmp1 );
929 0 : if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F)
930 0 : return 0;
931 :
932 0 : tmp1 = lcl_eatWhiteSpace( tmp1 );
933 0 : tmp2 = lcl_a1_get_col( tmp1, &r.aEnd, &nFlags2 );
934 0 : if( !tmp2 )
935 0 : return 0;
936 :
937 0 : r.aStart.SetRow( 0 ); r.aEnd.SetRow( MAXROW );
938 : nFlags |=
939 : SCA_VALID_ROW | SCA_VALID_ROW2 |
940 0 : SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
941 0 : nFlags |= (nFlags2 << 4);
942 0 : return nFlags;
943 : }
944 :
945 : // prepare as if it's a singleton, in case we want to fall back */
946 0 : r.aEnd.SetCol( r.aStart.Col() );
947 0 : r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header()
948 :
949 0 : if ( bOnlyAcceptSingle )
950 : {
951 0 : if ( *tmp2 == 0 )
952 0 : return nFlags;
953 : else
954 : {
955 : // any trailing invalid character must invalidate the address.
956 0 : nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
957 0 : return nFlags;
958 : }
959 : }
960 :
961 0 : tmp2 = lcl_eatWhiteSpace( tmp2 );
962 0 : if( *tmp2 != ':' )
963 : {
964 : // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
965 : // not. Any trailing invalid character invalidates the range.
966 0 : if (*tmp2 == 0 && (nFlags & SCA_TAB2_3D))
967 : {
968 0 : if (nFlags & SCA_COL_ABSOLUTE)
969 0 : nFlags |= SCA_COL2_ABSOLUTE;
970 0 : if (nFlags & SCA_ROW_ABSOLUTE)
971 0 : nFlags |= SCA_ROW2_ABSOLUTE;
972 : }
973 : else
974 : nFlags &= ~(SCA_VALID |
975 : SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
976 0 : SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
977 0 : return nFlags;
978 : }
979 :
980 0 : p = tmp2;
981 0 : p = lcl_eatWhiteSpace( p+1 );
982 0 : tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 );
983 0 : if( !tmp1 && aEndTabName.isEmpty() ) // Probably the aEndTabName was specified after the first range
984 : {
985 0 : p = lcl_XL_ParseSheetRef( p, aEndTabName, false, NULL );
986 0 : if( p )
987 : {
988 0 : SCTAB nTab = 0;
989 0 : if( !aEndTabName.isEmpty() && pDoc->GetTable( aEndTabName, nTab ) )
990 : {
991 0 : r.aEnd.SetTab( nTab );
992 0 : nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE;
993 : }
994 0 : p = lcl_eatWhiteSpace( p+1 );
995 0 : tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 );
996 : }
997 : }
998 0 : if( !tmp1 ) // strange, but valid singleton
999 0 : return nFlags;
1000 :
1001 0 : tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
1002 0 : if( !tmp2 ) // strange, but valid singleton
1003 0 : return nFlags;
1004 :
1005 0 : if ( *tmp2 != 0 )
1006 : {
1007 : // any trailing invalid character must invalidate the range.
1008 : nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
1009 0 : SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
1010 0 : return nFlags;
1011 : }
1012 :
1013 0 : nFlags |= (nFlags2 << 4);
1014 0 : return nFlags;
1015 : }
1016 :
1017 : /**
1018 : @param pRange pointer to range where rAddr effectively is *pRange->aEnd,
1019 : used in conjunction with pExtInfo to determine the tab span
1020 : of a 3D reference.
1021 : */
1022 0 : static sal_uInt16 lcl_ScAddress_Parse_OOo( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
1023 : ScAddress::ExternalInfo* pExtInfo = NULL, ScRange* pRange = NULL )
1024 : {
1025 0 : sal_uInt16 nRes = 0;
1026 0 : OUString aDocName; // der pure Dokumentenname
1027 0 : OUString aTab;
1028 0 : bool bExtDoc = false;
1029 0 : bool bExtDocInherited = false;
1030 0 : const ScAddress aCurPos(rAddr);
1031 :
1032 : // Lets see if this is a reference to something in an external file. A
1033 : // document name is always quoted and has a trailing #.
1034 0 : if (*p == '\'')
1035 : {
1036 0 : const sal_Unicode* pStart = p;
1037 0 : OUString aTmp;
1038 0 : p = parseQuotedName(p, aTmp);
1039 0 : aDocName = aTmp;
1040 0 : if (*p++ == SC_COMPILER_FILE_TAB_SEP)
1041 0 : bExtDoc = true;
1042 : else
1043 : // This is not a document name. Perhaps a quoted relative table
1044 : // name.
1045 0 : p = pStart;
1046 : }
1047 0 : else if (pExtInfo && pExtInfo->mbExternal)
1048 : {
1049 : // This is an external reference.
1050 0 : bExtDoc = bExtDocInherited = true;
1051 : }
1052 :
1053 0 : SCCOL nCol = 0;
1054 0 : SCROW nRow = 0;
1055 0 : SCTAB nTab = 0;
1056 0 : sal_uInt16 nBits = SCA_VALID_TAB;
1057 : const sal_Unicode* q;
1058 0 : if ( ScGlobal::FindUnquoted( p, '.') )
1059 : {
1060 0 : nRes |= SCA_TAB_3D;
1061 0 : if ( bExtDoc )
1062 0 : nRes |= SCA_TAB_ABSOLUTE;
1063 0 : if (*p == '$')
1064 0 : nRes |= SCA_TAB_ABSOLUTE, p++;
1065 :
1066 0 : if (*p == '\'')
1067 : {
1068 : // Tokens that start at ' can have anything in them until a final
1069 : // ' but '' marks an escaped '. We've earlier guaranteed that a
1070 : // string containing '' will be surrounded by '.
1071 0 : p = parseQuotedName(p, aTab);
1072 : }
1073 : else
1074 : {
1075 0 : while (*p)
1076 : {
1077 0 : if( *p == '.')
1078 0 : break;
1079 :
1080 0 : if( *p == '\'' )
1081 : {
1082 0 : p++; break;
1083 : }
1084 0 : aTab += OUString(*p);
1085 0 : p++;
1086 : }
1087 : }
1088 0 : if( *p++ != '.' )
1089 0 : nBits = 0;
1090 :
1091 0 : if (!bExtDoc && (!pDoc || !pDoc->GetTable( aTab, nTab )))
1092 : {
1093 : // Specified table name is not found in this document. Assume this is an external document.
1094 0 : aDocName = aTab;
1095 0 : sal_Int32 n = aDocName.lastIndexOf('.');
1096 0 : if (n != -1 && n > 0)
1097 : {
1098 : // Extension found. Strip it.
1099 0 : aTab = aTab.replaceAt(n, 1, "");
1100 0 : bExtDoc = true;
1101 : }
1102 : else
1103 : // No extension found. This is probably not an external document.
1104 0 : nBits = 0;
1105 : }
1106 : }
1107 : else
1108 : {
1109 0 : if (bExtDoc && !bExtDocInherited)
1110 0 : return nRes; // After a document a sheet must follow.
1111 0 : nTab = rAddr.Tab();
1112 : }
1113 0 : nRes |= nBits;
1114 :
1115 0 : q = p;
1116 0 : if (*p)
1117 : {
1118 0 : nBits = SCA_VALID_COL;
1119 0 : if (*p == '$')
1120 0 : nBits |= SCA_COL_ABSOLUTE, p++;
1121 :
1122 0 : if (rtl::isAsciiAlpha( *p ))
1123 : {
1124 0 : nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
1125 0 : while (nCol < MAXCOL && rtl::isAsciiAlpha(*p))
1126 0 : nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
1127 : }
1128 : else
1129 0 : nBits = 0;
1130 :
1131 0 : if( nCol > MAXCOL || rtl::isAsciiAlpha( *p ) )
1132 0 : nBits = 0;
1133 0 : nRes |= nBits;
1134 0 : if( !nBits )
1135 0 : p = q;
1136 : }
1137 :
1138 0 : q = p;
1139 0 : if (*p)
1140 : {
1141 0 : nBits = SCA_VALID_ROW;
1142 0 : if (*p == '$')
1143 0 : nBits |= SCA_ROW_ABSOLUTE, p++;
1144 0 : if( !rtl::isAsciiDigit( *p ) )
1145 : {
1146 0 : nBits = 0;
1147 0 : nRow = SCROW(-1);
1148 : }
1149 : else
1150 : {
1151 0 : OUString aTmp( p );
1152 0 : long n = aTmp.toInt32() - 1;
1153 0 : while (rtl::isAsciiDigit( *p ))
1154 0 : p++;
1155 0 : if( n < 0 || n > MAXROW )
1156 0 : nBits = 0;
1157 0 : nRow = static_cast<SCROW>(n);
1158 : }
1159 0 : nRes |= nBits;
1160 0 : if( !nBits )
1161 0 : p = q;
1162 : }
1163 :
1164 0 : rAddr.Set( nCol, nRow, nTab );
1165 :
1166 0 : if (!*p && bExtDoc)
1167 : {
1168 0 : if (!pDoc)
1169 0 : nRes = 0;
1170 : else
1171 : {
1172 0 : ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
1173 :
1174 : // Need document name if inherited.
1175 0 : if (bExtDocInherited)
1176 : {
1177 0 : const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId);
1178 0 : if (pFileName)
1179 0 : aDocName = *pFileName;
1180 : else
1181 0 : nRes = 0;
1182 : }
1183 0 : pRefMgr->convertToAbsName(aDocName);
1184 :
1185 0 : if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
1186 : {
1187 0 : if (!pDoc->GetTable( aTab, nTab ))
1188 0 : nRes = 0;
1189 : else
1190 : {
1191 0 : rAddr.SetTab( nTab);
1192 0 : nRes |= SCA_VALID_TAB;
1193 : }
1194 : }
1195 : else
1196 : {
1197 0 : if (!pExtInfo)
1198 0 : nRes = 0;
1199 : else
1200 : {
1201 0 : if (!pExtInfo->mbExternal)
1202 : {
1203 0 : sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
1204 :
1205 0 : pExtInfo->mbExternal = true;
1206 0 : pExtInfo->maTabName = aTab;
1207 0 : pExtInfo->mnFileId = nFileId;
1208 :
1209 0 : if (pRefMgr->getSingleRefToken(nFileId, aTab,
1210 : ScAddress(nCol, nRow, 0), NULL,
1211 0 : &nTab).get())
1212 : {
1213 0 : rAddr.SetTab( nTab);
1214 0 : nRes |= SCA_VALID_TAB;
1215 : }
1216 : else
1217 0 : nRes = 0;
1218 : }
1219 : else
1220 : {
1221 : // This is a call for the second part of the reference,
1222 : // we must have the range to adapt tab span.
1223 0 : if (!pRange)
1224 0 : nRes = 0;
1225 : else
1226 : {
1227 0 : sal_uInt16 nFlags = nRes | SCA_VALID_TAB2;
1228 0 : if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
1229 : pExtInfo, aDocName,
1230 0 : pExtInfo->maTabName, aTab, pDoc))
1231 0 : nRes &= ~SCA_VALID_TAB;
1232 : else
1233 : {
1234 0 : if (nFlags & SCA_VALID_TAB2)
1235 : {
1236 0 : rAddr.SetTab( pRange->aEnd.Tab());
1237 0 : nRes |= SCA_VALID_TAB;
1238 : }
1239 : else
1240 0 : nRes &= ~SCA_VALID_TAB;
1241 : }
1242 : }
1243 : }
1244 : }
1245 : }
1246 : }
1247 : }
1248 :
1249 0 : if ( !(nRes & SCA_VALID_ROW) && (nRes & SCA_VALID_COL)
1250 0 : && !( (nRes & SCA_TAB_3D) && (nRes & SCA_VALID_TAB)) )
1251 : { // no Row, no Tab, but Col => DM (...), B (...) et al
1252 0 : nRes = 0;
1253 : }
1254 0 : if( !*p )
1255 : {
1256 0 : sal_uInt16 nMask = nRes & ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1257 0 : if( nMask == ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ) )
1258 0 : nRes |= SCA_VALID;
1259 : }
1260 : else
1261 0 : nRes = 0;
1262 0 : return nRes;
1263 : }
1264 :
1265 0 : static sal_uInt16 lcl_ScAddress_Parse ( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
1266 : const ScAddress::Details& rDetails,
1267 : ScAddress::ExternalInfo* pExtInfo = NULL,
1268 : const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks = NULL )
1269 : {
1270 0 : if( !*p )
1271 0 : return 0;
1272 :
1273 0 : switch (rDetails.eConv)
1274 : {
1275 : case formula::FormulaGrammar::CONV_XL_A1:
1276 : case formula::FormulaGrammar::CONV_XL_OOX:
1277 : {
1278 0 : ScRange rRange = rAddr;
1279 : sal_uInt16 nFlags = lcl_ScRange_Parse_XL_A1(
1280 : rRange, p, pDoc, true, pExtInfo,
1281 0 : (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1282 0 : rAddr = rRange.aStart;
1283 0 : return nFlags;
1284 : }
1285 : case formula::FormulaGrammar::CONV_XL_R1C1:
1286 : {
1287 0 : ScRange rRange = rAddr;
1288 0 : sal_uInt16 nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, pDoc, rDetails, true, pExtInfo );
1289 0 : rAddr = rRange.aStart;
1290 0 : return nFlags;
1291 : }
1292 : default :
1293 : case formula::FormulaGrammar::CONV_OOO:
1294 : {
1295 0 : return lcl_ScAddress_Parse_OOo( p, pDoc, rAddr, pExtInfo, NULL );
1296 : }
1297 : }
1298 : }
1299 :
1300 0 : bool ConvertSingleRef( ScDocument* pDoc, const OUString& rRefString,
1301 : SCTAB nDefTab, ScRefAddress& rRefAddress,
1302 : const ScAddress::Details& rDetails,
1303 : ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1304 : {
1305 0 : bool bRet = false;
1306 0 : if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1307 : {
1308 0 : ScAddress aAddr( 0, 0, nDefTab );
1309 0 : sal_uInt16 nRes = aAddr.Parse( rRefString, pDoc, rDetails, pExtInfo);
1310 0 : if ( nRes & SCA_VALID )
1311 : {
1312 : rRefAddress.Set( aAddr,
1313 0 : ((nRes & SCA_COL_ABSOLUTE) == 0),
1314 0 : ((nRes & SCA_ROW_ABSOLUTE) == 0),
1315 0 : ((nRes & SCA_TAB_ABSOLUTE) == 0));
1316 0 : bRet = true;
1317 : }
1318 : }
1319 0 : return bRet;
1320 : }
1321 :
1322 0 : bool ConvertDoubleRef( ScDocument* pDoc, const OUString& rRefString, SCTAB nDefTab,
1323 : ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1324 : const ScAddress::Details& rDetails,
1325 : ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1326 : {
1327 0 : bool bRet = false;
1328 0 : if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1329 : {
1330 0 : ScRange aRange( ScAddress( 0, 0, nDefTab));
1331 0 : sal_uInt16 nRes = aRange.Parse( rRefString, pDoc, rDetails, pExtInfo);
1332 0 : if ( nRes & SCA_VALID )
1333 : {
1334 : rStartRefAddress.Set( aRange.aStart,
1335 0 : ((nRes & SCA_COL_ABSOLUTE) == 0),
1336 0 : ((nRes & SCA_ROW_ABSOLUTE) == 0),
1337 0 : ((nRes & SCA_TAB_ABSOLUTE) == 0));
1338 : rEndRefAddress.Set( aRange.aEnd,
1339 0 : ((nRes & SCA_COL2_ABSOLUTE) == 0),
1340 0 : ((nRes & SCA_ROW2_ABSOLUTE) == 0),
1341 0 : ((nRes & SCA_TAB2_ABSOLUTE) == 0));
1342 0 : bRet = true;
1343 : }
1344 : }
1345 0 : return bRet;
1346 : }
1347 :
1348 0 : sal_uInt16 ScAddress::Parse( const OUString& r, ScDocument* pDoc,
1349 : const Details& rDetails,
1350 : ExternalInfo* pExtInfo,
1351 : const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
1352 : {
1353 0 : return lcl_ScAddress_Parse( r.getStr(), pDoc, *this, rDetails, pExtInfo, pExternalLinks );
1354 : }
1355 :
1356 0 : bool ScRange::Intersects( const ScRange& rRange ) const
1357 : {
1358 : return !(
1359 0 : std::min( aEnd.Col(), rRange.aEnd.Col() ) < std::max( aStart.Col(), rRange.aStart.Col() )
1360 0 : || std::min( aEnd.Row(), rRange.aEnd.Row() ) < std::max( aStart.Row(), rRange.aStart.Row() )
1361 0 : || std::min( aEnd.Tab(), rRange.aEnd.Tab() ) < std::max( aStart.Tab(), rRange.aStart.Tab() )
1362 0 : );
1363 : }
1364 :
1365 0 : void ScRange::PutInOrder()
1366 : {
1367 0 : SCCOL nCol1 = aStart.Col(), nCol2 = aEnd.Col();
1368 0 : SCROW nRow1 = aStart.Row(), nRow2 = aEnd.Row();
1369 0 : SCTAB nTab1 = aStart.Tab(), nTab2 = aEnd.Tab();
1370 :
1371 0 : ::PutInOrder(nCol1, nCol2);
1372 0 : ::PutInOrder(nRow1, nRow2);
1373 0 : ::PutInOrder(nTab1, nTab2);
1374 :
1375 0 : aStart.SetCol(nCol1);
1376 0 : aStart.SetRow(nRow1);
1377 0 : aStart.SetTab(nTab1);
1378 :
1379 0 : aEnd.SetCol(nCol2);
1380 0 : aEnd.SetRow(nRow2);
1381 0 : aEnd.SetTab(nTab2);
1382 0 : }
1383 :
1384 0 : void ScRange::Justify()
1385 : {
1386 : SCCOL nTempCol;
1387 0 : if ( aEnd.Col() < (nTempCol = aStart.Col()) )
1388 : {
1389 0 : aStart.SetCol(aEnd.Col()); aEnd.SetCol(nTempCol);
1390 : }
1391 : SCROW nTempRow;
1392 0 : if ( aEnd.Row() < (nTempRow = aStart.Row()) )
1393 : {
1394 0 : aStart.SetRow(aEnd.Row()); aEnd.SetRow(nTempRow);
1395 : }
1396 : SCTAB nTempTab;
1397 0 : if ( aEnd.Tab() < (nTempTab = aStart.Tab()) )
1398 : {
1399 0 : aStart.SetTab(aEnd.Tab()); aEnd.SetTab(nTempTab);
1400 : }
1401 0 : }
1402 :
1403 0 : void ScRange::ExtendTo( const ScRange& rRange )
1404 : {
1405 : OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1406 0 : if( IsValid() )
1407 : {
1408 0 : aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) );
1409 0 : aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) );
1410 0 : aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1411 0 : aEnd.SetCol( std::max( aEnd.Col(), rRange.aEnd.Col() ) );
1412 0 : aEnd.SetRow( std::max( aEnd.Row(), rRange.aEnd.Row() ) );
1413 0 : aEnd.SetTab( std::max( aEnd.Tab(), rRange.aEnd.Tab() ) );
1414 : }
1415 : else
1416 0 : *this = rRange;
1417 0 : }
1418 :
1419 0 : static sal_uInt16 lcl_ScRange_Parse_OOo( ScRange& rRange,
1420 : const OUString& r,
1421 : ScDocument* pDoc,
1422 : ScAddress::ExternalInfo* pExtInfo = NULL )
1423 : {
1424 0 : sal_uInt16 nRes1 = 0, nRes2 = 0;
1425 0 : sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':');
1426 0 : if (nPos != -1)
1427 : {
1428 0 : OUStringBuffer aTmp(r);
1429 0 : aTmp[nPos] = 0;
1430 0 : const sal_Unicode* p = aTmp.getStr();
1431 0 : if( (nRes1 = lcl_ScAddress_Parse_OOo( p, pDoc, rRange.aStart, pExtInfo, NULL ) ) != 0 )
1432 : {
1433 0 : rRange.aEnd = rRange.aStart; // sheet must be initialized identical to first sheet
1434 0 : if ( (nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, pDoc, rRange.aEnd, pExtInfo, &rRange ) ) != 0 )
1435 : {
1436 : // PutInOrder / Justify
1437 : sal_uInt16 nMask, nBits1, nBits2;
1438 : SCCOL nTempCol;
1439 0 : if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) )
1440 : {
1441 0 : rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol);
1442 0 : nMask = (SCA_VALID_COL | SCA_COL_ABSOLUTE);
1443 0 : nBits1 = nRes1 & nMask;
1444 0 : nBits2 = nRes2 & nMask;
1445 0 : nRes1 = (nRes1 & ~nMask) | nBits2;
1446 0 : nRes2 = (nRes2 & ~nMask) | nBits1;
1447 : }
1448 : SCROW nTempRow;
1449 0 : if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) )
1450 : {
1451 0 : rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow);
1452 0 : nMask = (SCA_VALID_ROW | SCA_ROW_ABSOLUTE);
1453 0 : nBits1 = nRes1 & nMask;
1454 0 : nBits2 = nRes2 & nMask;
1455 0 : nRes1 = (nRes1 & ~nMask) | nBits2;
1456 0 : nRes2 = (nRes2 & ~nMask) | nBits1;
1457 : }
1458 : SCTAB nTempTab;
1459 0 : if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) )
1460 : {
1461 0 : rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab);
1462 0 : nMask = (SCA_VALID_TAB | SCA_TAB_ABSOLUTE | SCA_TAB_3D);
1463 0 : nBits1 = nRes1 & nMask;
1464 0 : nBits2 = nRes2 & nMask;
1465 0 : nRes1 = (nRes1 & ~nMask) | nBits2;
1466 0 : nRes2 = (nRes2 & ~nMask) | nBits1;
1467 : }
1468 0 : if ( ((nRes1 & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1469 : == ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1470 0 : && !(nRes2 & SCA_TAB_3D) )
1471 0 : nRes2 |= SCA_TAB_ABSOLUTE;
1472 : }
1473 : else
1474 0 : nRes1 = 0; // keine Tokens aus halben Sachen
1475 0 : }
1476 : }
1477 : nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID )
1478 : | nRes1
1479 0 : | ( ( nRes2 & SCA_BITS ) << 4 );
1480 0 : return nRes1;
1481 : }
1482 :
1483 0 : sal_uInt16 ScRange::Parse( const OUString& rString, ScDocument* pDoc,
1484 : const ScAddress::Details& rDetails,
1485 : ScAddress::ExternalInfo* pExtInfo,
1486 : const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
1487 : {
1488 0 : if (rString.isEmpty())
1489 0 : return 0;
1490 :
1491 0 : switch (rDetails.eConv)
1492 : {
1493 : case formula::FormulaGrammar::CONV_XL_A1:
1494 : case formula::FormulaGrammar::CONV_XL_OOX:
1495 : {
1496 : return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), pDoc, false, pExtInfo,
1497 0 : (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1498 : }
1499 :
1500 : case formula::FormulaGrammar::CONV_XL_R1C1:
1501 : {
1502 0 : return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), pDoc, rDetails, false, pExtInfo );
1503 : }
1504 :
1505 : default:
1506 : case formula::FormulaGrammar::CONV_OOO:
1507 : {
1508 0 : return lcl_ScRange_Parse_OOo( *this, rString, pDoc, pExtInfo );
1509 : }
1510 : }
1511 : }
1512 :
1513 : // Accept a full range, or an address
1514 0 : sal_uInt16 ScRange::ParseAny( const OUString& rString, ScDocument* pDoc,
1515 : const ScAddress::Details& rDetails )
1516 : {
1517 0 : sal_uInt16 nRet = Parse( rString, pDoc, rDetails );
1518 0 : const sal_uInt16 nValid = SCA_VALID | SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2;
1519 :
1520 0 : if ( (nRet & nValid) != nValid )
1521 : {
1522 0 : ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number
1523 0 : nRet = aAdr.Parse( rString, pDoc, rDetails );
1524 0 : if ( nRet & SCA_VALID )
1525 0 : aStart = aEnd = aAdr;
1526 : }
1527 0 : return nRet;
1528 : }
1529 :
1530 : // Parse only full row references
1531 0 : sal_uInt16 ScRange::ParseCols( const OUString& rStr, ScDocument* pDoc,
1532 : const ScAddress::Details& rDetails )
1533 : {
1534 0 : if (rStr.isEmpty())
1535 0 : return 0;
1536 :
1537 0 : const sal_Unicode* p = rStr.getStr();
1538 0 : sal_uInt16 nRes = 0, ignored = 0;
1539 :
1540 : (void)pDoc; // make compiler shutup we may need this later
1541 :
1542 0 : switch (rDetails.eConv)
1543 : {
1544 : default :
1545 : case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1546 : case formula::FormulaGrammar::CONV_XL_A1:
1547 : case formula::FormulaGrammar::CONV_XL_OOX:
1548 0 : if (NULL != (p = lcl_a1_get_col( p, &aStart, &ignored ) ) )
1549 : {
1550 0 : if( p[0] == ':')
1551 : {
1552 0 : if( NULL != (p = lcl_a1_get_col( p+1, &aEnd, &ignored )))
1553 : {
1554 0 : nRes = SCA_VALID_COL;
1555 : }
1556 : }
1557 : else
1558 : {
1559 0 : aEnd = aStart;
1560 0 : nRes = SCA_VALID_COL;
1561 : }
1562 : }
1563 0 : break;
1564 :
1565 : case formula::FormulaGrammar::CONV_XL_R1C1:
1566 0 : if ((p[0] == 'C' || p[0] != 'c') &&
1567 0 : NULL != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored )))
1568 : {
1569 0 : if( p[0] == ':')
1570 : {
1571 0 : if( (p[1] == 'C' || p[1] == 'c') &&
1572 0 : NULL != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored )))
1573 : {
1574 0 : nRes = SCA_VALID_COL;
1575 : }
1576 : }
1577 : else
1578 : {
1579 0 : aEnd = aStart;
1580 0 : nRes = SCA_VALID_COL;
1581 : }
1582 : }
1583 0 : break;
1584 : }
1585 :
1586 0 : return (p != NULL && *p == '\0') ? nRes : 0;
1587 : }
1588 :
1589 : // Parse only full row references
1590 0 : sal_uInt16 ScRange::ParseRows( const OUString& rStr, ScDocument* pDoc,
1591 : const ScAddress::Details& rDetails )
1592 : {
1593 0 : if (rStr.isEmpty())
1594 0 : return 0;
1595 :
1596 0 : const sal_Unicode* p = rStr.getStr();
1597 0 : sal_uInt16 nRes = 0, ignored = 0;
1598 :
1599 : (void)pDoc; // make compiler shutup we may need this later
1600 :
1601 0 : switch (rDetails.eConv)
1602 : {
1603 : default :
1604 : case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1605 : case formula::FormulaGrammar::CONV_XL_A1:
1606 : case formula::FormulaGrammar::CONV_XL_OOX:
1607 0 : if (NULL != (p = lcl_a1_get_row( p, &aStart, &ignored ) ) )
1608 : {
1609 0 : if( p[0] == ':')
1610 : {
1611 0 : if( NULL != (p = lcl_a1_get_row( p+1, &aEnd, &ignored )))
1612 : {
1613 0 : nRes = SCA_VALID_COL;
1614 : }
1615 : }
1616 : else
1617 : {
1618 0 : aEnd = aStart;
1619 0 : nRes = SCA_VALID_COL;
1620 : }
1621 : }
1622 0 : break;
1623 :
1624 : case formula::FormulaGrammar::CONV_XL_R1C1:
1625 0 : if ((p[0] == 'R' || p[0] != 'r') &&
1626 0 : NULL != (p = lcl_r1c1_get_row( p, rDetails, &aStart, &ignored )))
1627 : {
1628 0 : if( p[0] == ':')
1629 : {
1630 0 : if( (p[1] == 'R' || p[1] == 'r') &&
1631 0 : NULL != (p = lcl_r1c1_get_row( p+1, rDetails, &aEnd, &ignored )))
1632 : {
1633 0 : nRes = SCA_VALID_COL;
1634 : }
1635 : }
1636 : else
1637 : {
1638 0 : aEnd = aStart;
1639 0 : nRes = SCA_VALID_COL;
1640 : }
1641 : }
1642 0 : break;
1643 : }
1644 :
1645 0 : return (p != NULL && *p == '\0') ? nRes : 0;
1646 : }
1647 :
1648 0 : static inline void lcl_a1_append_c ( OUString &rString, int nCol, bool bIsAbs )
1649 : {
1650 0 : if( bIsAbs )
1651 0 : rString += "$";
1652 0 : ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) );
1653 0 : }
1654 :
1655 0 : static inline void lcl_a1_append_r ( OUString &rString, int nRow, bool bIsAbs )
1656 : {
1657 0 : if ( bIsAbs )
1658 0 : rString += "$";
1659 0 : rString += OUString::number( nRow+1 );
1660 0 : }
1661 :
1662 0 : static inline void lcl_r1c1_append_c ( OUString &rString, int nCol, bool bIsAbs,
1663 : const ScAddress::Details& rDetails )
1664 : {
1665 0 : rString += "C";
1666 0 : if (bIsAbs)
1667 : {
1668 0 : rString += OUString::number( nCol + 1 );
1669 : }
1670 : else
1671 : {
1672 0 : nCol -= rDetails.nCol;
1673 0 : if (nCol != 0) {
1674 0 : rString += "[" + OUString::number( nCol ) + "]";
1675 : }
1676 : }
1677 0 : }
1678 :
1679 0 : static inline void lcl_r1c1_append_r ( OUString &rString, int nRow, bool bIsAbs,
1680 : const ScAddress::Details& rDetails )
1681 : {
1682 0 : rString += "R";
1683 0 : if (bIsAbs)
1684 : {
1685 0 : rString += OUString::number( nRow + 1 );
1686 : }
1687 : else
1688 : {
1689 0 : nRow -= rDetails.nRow;
1690 0 : if (nRow != 0) {
1691 0 : rString += "[" + OUString::number( nRow ) + "]";
1692 : }
1693 : }
1694 0 : }
1695 :
1696 0 : static OUString getFileNameFromDoc( const ScDocument* pDoc )
1697 : {
1698 : // TODO : er points at ScGlobal::GetAbsDocName()
1699 : // as a better template. Look into it
1700 0 : OUString sFileName;
1701 : SfxObjectShell* pShell;
1702 :
1703 0 : if( NULL != pDoc &&
1704 : NULL != (pShell = pDoc->GetDocumentShell() ) )
1705 : {
1706 0 : uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY );
1707 0 : if( xModel.is() )
1708 : {
1709 0 : if( !xModel->getURL().isEmpty() )
1710 : {
1711 0 : INetURLObject aURL( xModel->getURL() );
1712 0 : sFileName = aURL.GetLastName();
1713 : }
1714 : else
1715 0 : sFileName = pShell->GetTitle();
1716 0 : }
1717 : }
1718 0 : return sFileName;
1719 : }
1720 :
1721 0 : OUString ScAddress::Format(sal_uInt16 nFlags, const ScDocument* pDoc,
1722 : const Details& rDetails) const
1723 : {
1724 0 : OUString r;
1725 0 : if( nFlags & SCA_VALID )
1726 0 : nFlags |= ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1727 0 : if( pDoc && (nFlags & SCA_VALID_TAB ) )
1728 : {
1729 0 : if ( nTab >= pDoc->GetTableCount() )
1730 : {
1731 0 : return ScGlobal::GetRscString( STR_NOREF_STR );
1732 : }
1733 0 : if( nFlags & SCA_TAB_3D )
1734 : {
1735 0 : OUString aTabName, aDocName;
1736 0 : OUString aTmp;
1737 0 : pDoc->GetName(nTab, aTmp);
1738 0 : aTabName = aTmp; // TODO: remove use of String here.
1739 : // External Reference, same as in ScCompiler::MakeTabStr()
1740 0 : if( aTabName[0] == '\'' )
1741 : { // "'Doc'#Tab"
1742 0 : sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName);
1743 0 : if (nPos != -1)
1744 : {
1745 0 : aDocName = aTabName.copy( 0, nPos + 1 );
1746 0 : aTabName = aTabName.copy( nPos + 1 );
1747 : }
1748 : }
1749 0 : else if( nFlags & SCA_FORCE_DOC )
1750 : {
1751 : // VBA has an 'external' flag that forces the addition of the
1752 : // tab name _and_ the doc name. The VBA code would be
1753 : // needlessly complicated if it constructed an actual external
1754 : // reference so we add this somewhat cheesy kludge to force the
1755 : // addition of the document name even for non-external references
1756 0 : aDocName = getFileNameFromDoc( pDoc );
1757 : }
1758 0 : ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
1759 :
1760 0 : switch( rDetails.eConv )
1761 : {
1762 : default :
1763 : case formula::FormulaGrammar::CONV_OOO:
1764 0 : r += aDocName;
1765 0 : if( nFlags & SCA_TAB_ABSOLUTE )
1766 0 : r += "$";
1767 0 : r += aTabName;
1768 0 : r += ".";
1769 0 : break;
1770 :
1771 : case formula::FormulaGrammar::CONV_XL_A1:
1772 : case formula::FormulaGrammar::CONV_XL_R1C1:
1773 : case formula::FormulaGrammar::CONV_XL_OOX:
1774 0 : if (!aDocName.isEmpty())
1775 : {
1776 0 : r += "[" + aDocName + "]";
1777 : }
1778 0 : r += aTabName;
1779 0 : r += "!";
1780 0 : break;
1781 0 : }
1782 : }
1783 : }
1784 0 : switch( rDetails.eConv )
1785 : {
1786 : default :
1787 : case formula::FormulaGrammar::CONV_OOO:
1788 : case formula::FormulaGrammar::CONV_XL_A1:
1789 : case formula::FormulaGrammar::CONV_XL_OOX:
1790 0 : if( nFlags & SCA_VALID_COL )
1791 0 : lcl_a1_append_c ( r, nCol, (nFlags & SCA_COL_ABSOLUTE) != 0 );
1792 0 : if( nFlags & SCA_VALID_ROW )
1793 0 : lcl_a1_append_r ( r, nRow, (nFlags & SCA_ROW_ABSOLUTE) != 0 );
1794 0 : break;
1795 :
1796 : case formula::FormulaGrammar::CONV_XL_R1C1:
1797 0 : if( nFlags & SCA_VALID_ROW )
1798 0 : lcl_r1c1_append_r ( r, nRow, (nFlags & SCA_ROW_ABSOLUTE) != 0, rDetails );
1799 0 : if( nFlags & SCA_VALID_COL )
1800 0 : lcl_r1c1_append_c ( r, nCol, (nFlags & SCA_COL_ABSOLUTE) != 0, rDetails );
1801 0 : break;
1802 : }
1803 0 : return r;
1804 : }
1805 :
1806 0 : static void lcl_Split_DocTab( const ScDocument* pDoc, SCTAB nTab,
1807 : const ScAddress::Details& rDetails,
1808 : sal_uInt16 nFlags,
1809 : OUString& rTabName, OUString& rDocName )
1810 : {
1811 0 : pDoc->GetName(nTab, rTabName);
1812 0 : rDocName = "";
1813 : // External reference, same as in ScCompiler::MakeTabStr()
1814 0 : if ( rTabName[0] == '\'' )
1815 : { // "'Doc'#Tab"
1816 0 : sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName);
1817 0 : if (nPos != -1)
1818 : {
1819 0 : rDocName = rTabName.copy( 0, nPos + 1 );
1820 0 : rTabName = rTabName.copy( nPos + 1 );
1821 : }
1822 : }
1823 0 : else if( nFlags & SCA_FORCE_DOC )
1824 : {
1825 : // VBA has an 'external' flag that forces the addition of the
1826 : // tab name _and_ the doc name. The VBA code would be
1827 : // needlessly complicated if it constructed an actual external
1828 : // reference so we add this somewhat cheesy kludge to force the
1829 : // addition of the document name even for non-external references
1830 0 : rDocName = getFileNameFromDoc( pDoc );
1831 : }
1832 0 : ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
1833 0 : }
1834 :
1835 0 : static void lcl_ScRange_Format_XL_Header( OUString& rString, const ScRange& rRange,
1836 : sal_uInt16 nFlags, const ScDocument* pDoc,
1837 : const ScAddress::Details& rDetails )
1838 : {
1839 0 : if( nFlags & SCA_TAB_3D )
1840 : {
1841 0 : OUString aTabName, aDocName;
1842 0 : lcl_Split_DocTab( pDoc, rRange.aStart.Tab(), rDetails, nFlags,
1843 0 : aTabName, aDocName );
1844 0 : if( !aDocName.isEmpty() )
1845 : {
1846 0 : rString += "[" + aDocName + "]";
1847 : }
1848 0 : rString += aTabName;
1849 :
1850 0 : if( nFlags & SCA_TAB2_3D )
1851 : {
1852 0 : lcl_Split_DocTab( pDoc, rRange.aEnd.Tab(), rDetails, nFlags,
1853 0 : aTabName, aDocName );
1854 0 : rString += ":";
1855 0 : rString += aTabName;
1856 : }
1857 0 : rString += "!";
1858 : }
1859 0 : }
1860 :
1861 0 : OUString ScRange::Format( sal_uInt16 nFlags, const ScDocument* pDoc,
1862 : const ScAddress::Details& rDetails ) const
1863 : {
1864 0 : if( !( nFlags & SCA_VALID ) )
1865 : {
1866 0 : return ScGlobal::GetRscString( STR_NOREF_STR );
1867 : }
1868 :
1869 0 : OUString r;
1870 : #define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask)))
1871 0 : switch( rDetails.eConv ) {
1872 : default :
1873 : case formula::FormulaGrammar::CONV_OOO: {
1874 0 : bool bOneTab = (aStart.Tab() == aEnd.Tab());
1875 0 : if ( !bOneTab )
1876 0 : nFlags |= SCA_TAB_3D;
1877 0 : r = aStart.Format(nFlags, pDoc, rDetails);
1878 0 : if( aStart != aEnd ||
1879 0 : absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1880 0 : absrel_differ( nFlags, SCA_ROW_ABSOLUTE ))
1881 : {
1882 0 : nFlags = ( nFlags & SCA_VALID ) | ( ( nFlags >> 4 ) & 0x070F );
1883 0 : if ( bOneTab )
1884 0 : pDoc = NULL;
1885 : else
1886 0 : nFlags |= SCA_TAB_3D;
1887 0 : OUString aName(aEnd.Format(nFlags, pDoc, rDetails));
1888 0 : r += ":";
1889 0 : r += aName;
1890 : }
1891 : }
1892 0 : break;
1893 :
1894 : case formula::FormulaGrammar::CONV_XL_A1:
1895 : case formula::FormulaGrammar::CONV_XL_OOX:
1896 0 : lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1897 0 : if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1898 : {
1899 : // Full col refs always require 2 rows (2:2)
1900 0 : lcl_a1_append_r( r, aStart.Row(), (nFlags & SCA_ROW_ABSOLUTE) != 0 );
1901 0 : r += ":";
1902 0 : lcl_a1_append_r( r, aEnd.Row(), (nFlags & SCA_ROW2_ABSOLUTE) != 0 );
1903 : }
1904 0 : else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1905 : {
1906 : // Full row refs always require 2 cols (A:A)
1907 0 : lcl_a1_append_c( r, aStart.Col(), (nFlags & SCA_COL_ABSOLUTE) != 0 );
1908 0 : r += ":";
1909 0 : lcl_a1_append_c( r, aEnd.Col(), (nFlags & SCA_COL2_ABSOLUTE) != 0 );
1910 : }
1911 : else
1912 : {
1913 0 : lcl_a1_append_c ( r, aStart.Col(), (nFlags & SCA_COL_ABSOLUTE) != 0 );
1914 0 : lcl_a1_append_r ( r, aStart.Row(), (nFlags & SCA_ROW_ABSOLUTE) != 0 );
1915 0 : if( aStart.Col() != aEnd.Col() ||
1916 0 : absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1917 0 : aStart.Row() != aEnd.Row() ||
1918 0 : absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1919 0 : r += ":";
1920 0 : lcl_a1_append_c ( r, aEnd.Col(), (nFlags & SCA_COL2_ABSOLUTE) != 0 );
1921 0 : lcl_a1_append_r ( r, aEnd.Row(), (nFlags & SCA_ROW2_ABSOLUTE) != 0 );
1922 : }
1923 : }
1924 0 : break;
1925 :
1926 : case formula::FormulaGrammar::CONV_XL_R1C1:
1927 0 : lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1928 0 : if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1929 : {
1930 0 : lcl_r1c1_append_r( r, aStart.Row(), (nFlags & SCA_ROW_ABSOLUTE) != 0, rDetails );
1931 0 : if( aStart.Row() != aEnd.Row() ||
1932 0 : absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1933 0 : r += ":";
1934 0 : lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & SCA_ROW2_ABSOLUTE) != 0, rDetails );
1935 : }
1936 : }
1937 0 : else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1938 : {
1939 0 : lcl_r1c1_append_c( r, aStart.Col(), (nFlags & SCA_COL_ABSOLUTE) != 0, rDetails );
1940 0 : if( aStart.Col() != aEnd.Col() ||
1941 0 : absrel_differ( nFlags, SCA_COL_ABSOLUTE )) {
1942 0 : r += ":";
1943 0 : lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & SCA_COL2_ABSOLUTE) != 0, rDetails );
1944 : }
1945 : }
1946 : else
1947 : {
1948 0 : lcl_r1c1_append_r( r, aStart.Row(), (nFlags & SCA_ROW_ABSOLUTE) != 0, rDetails );
1949 0 : lcl_r1c1_append_c( r, aStart.Col(), (nFlags & SCA_COL_ABSOLUTE) != 0, rDetails );
1950 0 : if( aStart.Col() != aEnd.Col() ||
1951 0 : absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1952 0 : aStart.Row() != aEnd.Row() ||
1953 0 : absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1954 0 : r += ":";
1955 0 : lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & SCA_ROW2_ABSOLUTE) != 0, rDetails );
1956 0 : lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & SCA_COL2_ABSOLUTE) != 0, rDetails );
1957 : }
1958 : }
1959 : }
1960 : #undef absrel_differ
1961 0 : return r;
1962 : }
1963 :
1964 0 : bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1965 : {
1966 0 : SCsTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB;
1967 0 : dx = Col() + dx;
1968 0 : dy = Row() + dy;
1969 0 : dz = Tab() + dz;
1970 0 : bool bValid = true;
1971 0 : if( dx < 0 )
1972 0 : dx = 0, bValid = false;
1973 0 : else if( dx > MAXCOL )
1974 0 : dx = MAXCOL, bValid =false;
1975 0 : if( dy < 0 )
1976 0 : dy = 0, bValid = false;
1977 0 : else if( dy > MAXROW )
1978 0 : dy = MAXROW, bValid =false;
1979 0 : if( dz < 0 )
1980 0 : dz = 0, bValid = false;
1981 0 : else if( dz > nMaxTab )
1982 0 : dz = nMaxTab, bValid =false;
1983 0 : Set( dx, dy, dz );
1984 0 : return bValid;
1985 : }
1986 :
1987 0 : bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1988 : {
1989 0 : bool b = aStart.Move( dx, dy, dz, pDoc );
1990 0 : b &= aEnd.Move( dx, dy, dz, pDoc );
1991 0 : return b;
1992 : }
1993 :
1994 0 : OUString ScAddress::GetColRowString( bool bAbsolute,
1995 : const Details& rDetails ) const
1996 : {
1997 0 : OUString aString;
1998 :
1999 0 : switch( rDetails.eConv )
2000 : {
2001 : default :
2002 : case formula::FormulaGrammar::CONV_OOO:
2003 : case formula::FormulaGrammar::CONV_XL_A1:
2004 : case formula::FormulaGrammar::CONV_XL_OOX:
2005 0 : if (bAbsolute)
2006 0 : aString += "$";
2007 :
2008 0 : ScColToAlpha( aString, nCol);
2009 :
2010 0 : if ( bAbsolute )
2011 0 : aString += "$";
2012 :
2013 0 : aString += OUString::number(nRow+1);
2014 0 : break;
2015 :
2016 : case formula::FormulaGrammar::CONV_XL_R1C1:
2017 0 : lcl_r1c1_append_r ( aString, nRow, bAbsolute, rDetails );
2018 0 : lcl_r1c1_append_c ( aString, nCol, bAbsolute, rDetails );
2019 0 : break;
2020 : }
2021 :
2022 0 : return aString;
2023 : }
2024 :
2025 0 : OUString ScRefAddress::GetRefString( ScDocument* pDoc, SCTAB nActTab,
2026 : const ScAddress::Details& rDetails ) const
2027 : {
2028 0 : if ( !pDoc )
2029 0 : return EMPTY_OUSTRING;
2030 0 : if ( Tab()+1 > pDoc->GetTableCount() )
2031 0 : return ScGlobal::GetRscString( STR_NOREF_STR );
2032 :
2033 0 : sal_uInt16 nFlags = SCA_VALID;
2034 0 : if ( nActTab != Tab() )
2035 : {
2036 0 : nFlags |= SCA_TAB_3D;
2037 0 : if ( !bRelTab )
2038 0 : nFlags |= SCA_TAB_ABSOLUTE;
2039 : }
2040 0 : if ( !bRelCol )
2041 0 : nFlags |= SCA_COL_ABSOLUTE;
2042 0 : if ( !bRelRow )
2043 0 : nFlags |= SCA_ROW_ABSOLUTE;
2044 :
2045 0 : return aAdr.Format(nFlags, pDoc, rDetails);
2046 : }
2047 :
2048 0 : void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol )
2049 : {
2050 0 : if (nCol < 26*26)
2051 : {
2052 0 : if (nCol < 26)
2053 : rBuf.append( static_cast<sal_Unicode>( 'A' +
2054 0 : static_cast<sal_uInt16>(nCol)));
2055 : else
2056 : {
2057 : rBuf.append( static_cast<sal_Unicode>( 'A' +
2058 0 : (static_cast<sal_uInt16>(nCol) / 26) - 1));
2059 : rBuf.append( static_cast<sal_Unicode>( 'A' +
2060 0 : (static_cast<sal_uInt16>(nCol) % 26)));
2061 : }
2062 : }
2063 : else
2064 : {
2065 0 : OUString aStr;
2066 0 : while (nCol >= 26)
2067 : {
2068 0 : SCCOL nC = nCol % 26;
2069 0 : aStr += OUString( static_cast<sal_Unicode>( 'A' +
2070 0 : static_cast<sal_uInt16>(nC)));
2071 0 : nCol = sal::static_int_cast<SCCOL>( nCol - nC );
2072 0 : nCol = nCol / 26 - 1;
2073 : }
2074 0 : aStr += OUString(static_cast<sal_Unicode>( 'A' +
2075 0 : static_cast<sal_uInt16>(nCol)));
2076 0 : rBuf.append(comphelper::string::reverseString(aStr));
2077 : }
2078 0 : }
2079 :
2080 0 : bool AlphaToCol( SCCOL& rCol, const OUString& rStr)
2081 : {
2082 0 : SCCOL nResult = 0;
2083 0 : sal_Int32 nStop = rStr.getLength();
2084 0 : sal_Int32 nPos = 0;
2085 : sal_Unicode c;
2086 0 : while (nResult <= MAXCOL && nPos < nStop && (c = rStr[nPos]) != 0 &&
2087 0 : rtl::isAsciiAlpha(c))
2088 : {
2089 0 : if (nPos > 0)
2090 0 : nResult = (nResult + 1) * 26;
2091 0 : nResult += ScGlobal::ToUpperAlpha(c) - 'A';
2092 0 : ++nPos;
2093 : }
2094 0 : bool bOk = (ValidCol(nResult) && nPos > 0);
2095 0 : if (bOk)
2096 0 : rCol = nResult;
2097 0 : return bOk;
2098 0 : }
2099 :
2100 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|