merge the formfield patch from ooo-build
[ooovba.git] / sc / source / core / tool / address.cxx
blobe85ec8d1d814a22fbaac349773399706ffd6daa2
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: address.cxx,v $
10 * $Revision: 1.11.30.3 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 #include "address.hxx"
35 #include "global.hxx"
36 #include "compiler.hxx"
37 #include "document.hxx"
38 #include "externalrefmgr.hxx"
40 #include "globstr.hrc"
41 #include <sal/alloca.h>
43 #include <com/sun/star/frame/XModel.hpp>
44 #include <com/sun/star/beans/XPropertySet.hpp>
45 #include <com/sun/star/sheet/ExternalLinkInfo.hpp>
46 #include <com/sun/star/sheet/ExternalLinkType.hpp>
47 #include <sfx2/objsh.hxx>
48 #include <tools/urlobj.hxx>
49 using namespace ::com::sun::star;
51 ////////////////////////////////////////////////////////////////////////////
52 const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 );
54 ScAddress::Details::Details ( const ScDocument* pDoc,
55 const ScAddress & rAddr ) :
56 eConv( pDoc->GetAddressConvention() ),
57 nRow( rAddr.Row() ),
58 nCol( rAddr.Col() )
62 //UNUSED2009-05 void ScAddress::Details::SetPos ( const ScDocument* pDoc,
63 //UNUSED2009-05 const ScAddress & rAddr )
64 //UNUSED2009-05 {
65 //UNUSED2009-05 nRow = rAddr.Row();
66 //UNUSED2009-05 nCol = rAddr.Col();
67 //UNUSED2009-05 eConv = pDoc->GetAddressConvention();
68 //UNUSED2009-05 }
70 ////////////////////////////////////////////////////////////////////////////
72 #include <iostream>
74 /**
75 * Parse from the opening single quote to the closing single quote. Inside
76 * the quotes, a single quote character is encoded by double single-quote
77 * characters.
79 * @param p pointer to the first character to begin parsing.
80 * @param rName (reference) parsed name within the quotes. If the name is
81 * empty, either the parsing failed or it's an empty quote.
83 * @return pointer to the character immediately after the closing single
84 * quote.
86 static const sal_Unicode* lcl_ParseQuotedName( const sal_Unicode* p, String& rName )
88 rName.Erase();
89 if (*p != '\'')
90 return p;
92 const sal_Unicode* pStart = p;
93 sal_Unicode cPrev = 0;
94 for (++p; *p; ++p)
96 if (*p == '\'')
98 if (cPrev == '\'')
100 // double single-quote equals one single quote.
101 rName += *p;
102 cPrev = 0;
103 continue;
106 else if (cPrev == '\'')
107 // We are past the closing quote. We're done!
108 return p;
109 else
110 rName += *p;
111 cPrev = *p;
113 rName.Erase();
114 return pStart;
117 static long int
118 sal_Unicode_strtol ( const sal_Unicode* p,
119 const sal_Unicode** pEnd )
121 long int accum = 0, prev = 0;
122 bool is_neg = false;
124 if( *p == '-' )
126 is_neg = true;
127 p++;
129 else if( *p == '+' )
130 p++;
132 while (CharClass::isAsciiDigit( *p ))
134 accum = accum * 10 + *p - '0';
135 if( accum < prev )
137 *pEnd = NULL;
138 return 0;
140 prev = accum;
141 p++;
144 *pEnd = p;
145 return is_neg ? -accum : accum;
148 const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p )
150 if ( p )
152 while( *p == ' ' )
153 ++p;
155 return p;
158 /** Determines the number of sheets an external reference spans and sets
159 rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
160 bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
161 cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
162 is set to rEndTabName.
163 @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
164 result in the identical file ID. Else <TRUE/>.
166 static bool lcl_ScRange_External_TabSpan(
167 ScRange & rRange,
168 USHORT & rFlags,
169 ScAddress::ExternalInfo* pExtInfo,
170 const String & rExternDocName,
171 const String & rStartTabName,
172 const String & rEndTabName,
173 ScDocument* pDoc )
175 if (!rExternDocName.Len())
176 return !pExtInfo || !pExtInfo->mbExternal;
178 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
179 if (pRefMgr->isOwnDocument( rExternDocName))
180 return !pExtInfo || !pExtInfo->mbExternal;
182 sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName);
184 if (pExtInfo)
186 if (pExtInfo->mbExternal)
188 if (pExtInfo->mnFileId != nFileId)
189 return false;
191 else
193 pExtInfo->mbExternal = true;
194 pExtInfo->maTabName = rStartTabName;
195 pExtInfo->mnFileId = nFileId;
199 if (!rEndTabName.Len() || rStartTabName == rEndTabName)
201 rRange.aEnd.SetTab( rRange.aStart.Tab());
202 return true;
205 SCsTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName);
206 if (nSpan == -1)
207 rFlags &= ~(SCA_VALID_TAB | SCA_VALID_TAB2);
208 else if (nSpan == 0)
209 rFlags &= ~SCA_VALID_TAB2;
210 else if (nSpan >= 1)
211 rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1);
212 else // (nSpan < -1)
214 rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1);
215 if (pExtInfo)
216 pExtInfo->maTabName = rEndTabName;
218 return true;
221 /** Returns NULL if the string should be a sheet name, but is invalid.
222 Returns a pointer to the first character after the sheet name, if there was
223 any, else pointer to start.
224 @param pMsoxlQuoteStop
225 Starting _within_ a quoted name, but still may be 3D; quoted name stops
226 at pMsoxlQuoteStop
228 static const sal_Unicode *
229 lcl_XL_ParseSheetRef( const sal_Unicode* start,
230 String& rExternTabName,
231 bool allow_3d,
232 const sal_Unicode* pMsoxlQuoteStop )
234 String aTabName;
235 const sal_Unicode *p = start;
237 // XL only seems to use single quotes for sheet names.
238 if (pMsoxlQuoteStop)
240 const sal_Unicode* pCurrentStart = p;
241 while (p < pMsoxlQuoteStop)
243 if (*p == '\'')
245 // We pre-analyzed the quoting, no checks needed here.
246 if (*++p == '\'')
248 aTabName.Append( pCurrentStart,
249 sal::static_int_cast<xub_StrLen>( p - pCurrentStart));
250 pCurrentStart = ++p;
253 else if (*p == ':')
255 break; // while
257 else
258 ++p;
260 if (pCurrentStart < p)
261 aTabName.Append( pCurrentStart, sal::static_int_cast<xub_StrLen>( p - pCurrentStart));
262 if (!aTabName.Len())
263 return NULL;
264 if (p == pMsoxlQuoteStop)
265 ++p; // position on ! of ...'!...
266 if( *p != '!' && ( !allow_3d || *p != ':' ) )
267 return (!allow_3d && *p == ':') ? p : start;
269 else if( *p == '\'')
271 p = lcl_ParseQuotedName(p, aTabName);
272 if (!aTabName.Len())
273 return NULL;
275 else
277 bool only_digits = TRUE;
280 * Valid: Normal!a1
281 * Valid: x.y!a1
282 * Invalid: .y!a1
284 * Some names starting with digits are actually valid, but
285 * unparse quoted. Things are quite tricky: most sheet names
286 * starting with a digit are ok, but not those starting with
287 * "[0-9]*\." or "[0-9]+[eE]".
289 * Valid: 42!a1
290 * Valid: 4x!a1
291 * Invalid: 1.!a1
292 * Invalid: 1e!a1
294 while( 1 )
296 const sal_Unicode uc = *p;
297 if( CharClass::isAsciiAlpha( uc ) || uc == '_' )
299 if( only_digits && p != start &&
300 (uc == 'e' || uc == 'E' ) )
302 p = start;
303 break;
305 only_digits = FALSE;
306 p++;
308 else if( CharClass::isAsciiDigit( uc ))
310 p++;
312 else if( uc == '.' )
314 if( only_digits ) // Valid, except after only digits.
316 p = start;
317 break;
319 p++;
321 else if (uc > 127)
323 // non ASCII character is allowed.
324 ++p;
326 else
327 break;
330 if( *p != '!' && ( !allow_3d || *p != ':' ) )
331 return (!allow_3d && *p == ':') ? p : start;
333 aTabName.Append( start, sal::static_int_cast<xub_StrLen>( p - start ) );
336 rExternTabName = aTabName;
337 return p;
341 const sal_Unicode* ScRange::Parse_XL_Header(
342 const sal_Unicode* p,
343 const ScDocument* pDoc,
344 String& rExternDocName,
345 String& rStartTabName,
346 String& rEndTabName,
347 USHORT& nFlags,
348 bool bOnlyAcceptSingle,
349 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
351 const sal_Unicode* startTabs, *start = p;
352 USHORT nSaveFlags = nFlags;
354 // Is this an external reference ?
355 rStartTabName.Erase();
356 rEndTabName.Erase();
357 rExternDocName.Erase();
358 const sal_Unicode* pMsoxlQuoteStop = NULL;
359 if (*p == '[')
361 ++p;
362 // Only single quotes are correct, and a double single quote escapes a
363 // single quote text inside the quoted text.
364 if (*p == '\'')
366 p = lcl_ParseQuotedName(p, rExternDocName);
367 if (!*p || *p != ']' || !rExternDocName.Len())
368 return start;
370 else
372 // non-quoted file name.
373 p = ScGlobal::UnicodeStrChr( start+1, ']' );
374 if( p == NULL )
375 return start;
376 rExternDocName.Append( start+1, sal::static_int_cast<xub_StrLen>( p-(start+1) ) );
378 ++p;
380 // 1-based, sequence starts with an empty element.
381 if (pExternalLinks && pExternalLinks->getLength() > 1)
383 // A numeric "document name" is an index into the sequence.
384 if (CharClass::isAsciiNumeric( rExternDocName))
386 sal_Int32 i = rExternDocName.ToInt32();
387 if (i <= 0 || i >= pExternalLinks->getLength())
388 return start;
389 const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i];
390 switch (rInfo.Type)
392 case sheet::ExternalLinkType::DOCUMENT :
394 rtl::OUString aStr;
395 if (!(rInfo.Data >>= aStr))
397 DBG_ERROR1( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i);
398 return NULL;
400 rExternDocName = aStr;
402 break;
403 default:
404 DBG_ERROR2( "ScRange::Parse_XL_Header: unhandled ExternalLinkType %d for index %d",
405 rInfo.Type, i);
406 return NULL;
410 rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, pDoc->GetDocumentShell());
412 else if (*p == '\'')
414 // Sickness in Excel's ODF msoxl namespace:
415 // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or
416 // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
417 // But, 'Sheet1'!B3 would also be a valid!
418 // Excel does not allow [ and ] characters in sheet names though.
419 p = lcl_ParseQuotedName(p, rExternDocName);
420 if (!*p || *p != '!')
421 return start;
422 if (rExternDocName.Len())
424 xub_StrLen nOpen = rExternDocName.Search( '[');
425 if (nOpen == STRING_NOTFOUND)
426 rExternDocName.Erase();
427 else
429 xub_StrLen nClose = rExternDocName.Search( ']', nOpen+1);
430 if (nClose == STRING_NOTFOUND)
431 rExternDocName.Erase();
432 else
434 rExternDocName.Erase( nClose);
435 rExternDocName.Erase( nOpen, 1);
436 pMsoxlQuoteStop = p - 1; // the ' quote char
437 // There may be embedded escaped quotes, just matching the
438 // doc name's length may not work.
439 for (p = start; *p != '['; ++p)
441 for ( ; *p != ']'; ++p)
443 ++p;
447 if (!rExternDocName.Len())
448 p = start;
451 startTabs = p;
452 p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop);
453 if( NULL == p )
454 return start; // invalid tab
455 if (bOnlyAcceptSingle && *p == ':')
456 return NULL; // 3D
457 if( p != startTabs )
459 nFlags |= SCA_VALID_TAB | SCA_TAB_3D | SCA_TAB_ABSOLUTE;
460 if( *p == ':' ) // 3d ref
462 p = lcl_XL_ParseSheetRef( p+1, rEndTabName, false, pMsoxlQuoteStop);
463 if( p == NULL )
465 nFlags = nSaveFlags;
466 return start; // invalid tab
468 nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE;
470 else
472 // If only one sheet is given, the full reference is still valid,
473 // only the second 3D flag is not set.
474 nFlags |= SCA_VALID_TAB2 | SCA_TAB2_ABSOLUTE;
475 aEnd.SetTab( aStart.Tab() );
478 if( *p++ != '!' )
480 nFlags = nSaveFlags;
481 return start; // syntax error
483 else
484 p = lcl_eatWhiteSpace( p );
486 else
488 nFlags |= SCA_VALID_TAB | SCA_VALID_TAB2;
489 // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
492 if (rExternDocName.Len())
494 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
495 pRefMgr->convertToAbsName( rExternDocName);
497 else
499 // Internal reference.
500 if (!rStartTabName.Len())
502 nFlags = nSaveFlags;
503 return start;
506 SCTAB nTab;
507 if (!pDoc->GetTable(rStartTabName, nTab))
509 // invalid table name.
510 nFlags &= ~SCA_VALID_TAB;
511 nTab = -1;
514 aStart.SetTab(nTab);
515 aEnd.SetTab(nTab);
517 if (rEndTabName.Len())
519 if (!pDoc->GetTable(rEndTabName, nTab))
521 // invalid table name.
522 nFlags &= ~SCA_VALID_TAB2;
523 nTab = -1;
526 aEnd.SetTab(nTab);
529 return p;
533 static const sal_Unicode*
534 lcl_r1c1_get_col( const sal_Unicode* p,
535 const ScAddress::Details& rDetails,
536 ScAddress* pAddr, USHORT* nFlags )
538 const sal_Unicode *pEnd;
539 long int n;
540 bool isRelative;
542 if( p[0] == '\0' )
543 return NULL;
545 p++;
546 if( (isRelative = (*p == '[') ) != false )
547 p++;
548 n = sal_Unicode_strtol( p, &pEnd );
549 if( NULL == pEnd )
550 return NULL;
552 if( p == pEnd ) // C is a relative ref with offset 0
554 if( isRelative )
555 return NULL;
556 n = rDetails.nCol;
558 else if( isRelative )
560 if( *pEnd != ']' )
561 return NULL;
562 n += rDetails.nCol;
563 pEnd++;
565 else
567 *nFlags |= SCA_COL_ABSOLUTE;
568 n--;
571 if( n < 0 || n >= MAXCOLCOUNT )
572 return NULL;
573 pAddr->SetCol( static_cast<SCCOL>( n ) );
574 *nFlags |= SCA_VALID_COL;
576 return pEnd;
578 static inline const sal_Unicode*
579 lcl_r1c1_get_row( const sal_Unicode* p,
580 const ScAddress::Details& rDetails,
581 ScAddress* pAddr, USHORT* nFlags )
583 const sal_Unicode *pEnd;
584 long int n;
585 bool isRelative;
587 if( p[0] == '\0' )
588 return NULL;
590 p++;
591 if( (isRelative = (*p == '[') ) != false )
592 p++;
593 n = sal_Unicode_strtol( p, &pEnd );
594 if( NULL == pEnd )
595 return NULL;
597 if( p == pEnd ) // R is a relative ref with offset 0
599 if( isRelative )
600 return NULL;
601 n = rDetails.nRow;
603 else if( isRelative )
605 if( *pEnd != ']' )
606 return NULL;
607 n += rDetails.nRow;
608 pEnd++;
610 else
612 *nFlags |= SCA_ROW_ABSOLUTE;
613 n--;
616 if( n < 0 || n >= MAXROWCOUNT )
617 return NULL;
618 pAddr->SetRow( static_cast<SCROW>( n ) );
619 *nFlags |= SCA_VALID_ROW;
621 return pEnd;
624 static USHORT
625 lcl_ScRange_Parse_XL_R1C1( ScRange& r,
626 const sal_Unicode* p,
627 ScDocument* pDoc,
628 const ScAddress::Details& rDetails,
629 bool bOnlyAcceptSingle,
630 ScAddress::ExternalInfo* pExtInfo )
632 const sal_Unicode* pTmp = NULL;
633 String aExternDocName, aStartTabName, aEndTabName;
634 USHORT nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB2;
636 #if 0
638 ByteString aStr(p, RTL_TEXTENCODING_UTF8);
639 aStr.Append(static_cast< char >(0));
640 std::cerr << "parse::XL::R1C1 \'" << aStr.GetBuffer() << '\'' << std::endl;
642 #endif
643 p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
644 aEndTabName, nFlags, bOnlyAcceptSingle, NULL );
646 if (aExternDocName.Len() > 0)
647 lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
648 aStartTabName, aEndTabName, pDoc);
650 if( NULL == p )
651 return 0;
653 if( *p == 'R' || *p == 'r' )
655 if( NULL == (p = lcl_r1c1_get_row( p, rDetails, &r.aStart, &nFlags )) )
656 goto failed;
658 if( *p != 'C' && *p != 'c' ) // full row R#
660 if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) ||
661 NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )))
663 // Only the initial row number is given, or the second row
664 // number is invalid. Fallback to just the initial R
665 nFlags |= (nFlags << 4);
666 r.aEnd.SetRow( r.aStart.Row() );
668 else
670 // Full row range successfully parsed.
671 nFlags |= (nFlags2 << 4);
672 p = pTmp;
675 if (p && p[0] != 0)
677 // any trailing invalid character must invalidate the whole address.
678 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
679 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
680 return nFlags;
683 nFlags |=
684 SCA_VALID_COL | SCA_VALID_COL2 |
685 SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
686 r.aStart.SetCol( 0 );
687 r.aEnd.SetCol( MAXCOL );
689 return bOnlyAcceptSingle ? 0 : nFlags;
691 else if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
692 goto failed;
694 if( p[0] != ':' ||
695 (p[1] != 'R' && p[1] != 'r') ||
696 NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )) ||
697 (*pTmp != 'C' && *pTmp != 'c') ||
698 NULL == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 )))
700 // single cell reference
702 if (p && p[0] != 0)
704 // any trailing invalid character must invalidate the whole address.
705 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
706 return nFlags;
709 return bOnlyAcceptSingle ? nFlags : 0;
711 p = pTmp;
713 // double reference
715 if (p && p[0] != 0)
717 // any trailing invalid character must invalidate the whole range.
718 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
719 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
720 return nFlags;
723 nFlags |= (nFlags2 << 4);
724 return bOnlyAcceptSingle ? 0 : nFlags;
726 else if( *p == 'C' || *p == 'c' ) // full col C#
728 if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
729 goto failed;
731 if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') ||
732 NULL == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, &nFlags2 )))
733 { // Fallback to just the initial C
734 nFlags |= (nFlags << 4);
735 r.aEnd.SetCol( r.aStart.Col() );
737 else
739 nFlags |= (nFlags2 << 4);
740 p = pTmp;
743 if (p && p[0] != 0)
745 // any trailing invalid character must invalidate the whole address.
746 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
747 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
748 return nFlags;
751 nFlags |=
752 SCA_VALID_ROW | SCA_VALID_ROW2 |
753 SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
754 r.aStart.SetRow( 0 );
755 r.aEnd.SetRow( MAXROW );
757 return bOnlyAcceptSingle ? 0 : nFlags;
760 failed :
761 return 0;
764 static inline const sal_Unicode*
765 lcl_a1_get_col( const sal_Unicode* p, ScAddress* pAddr, USHORT* nFlags )
767 SCCOL nCol;
769 if( *p == '$' )
770 *nFlags |= SCA_COL_ABSOLUTE, p++;
772 if( !CharClass::isAsciiAlpha( *p ) )
773 return NULL;
775 nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
776 while (nCol <= MAXCOL && CharClass::isAsciiAlpha(*p))
777 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
778 if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) )
779 return NULL;
781 *nFlags |= SCA_VALID_COL;
782 pAddr->SetCol( nCol );
784 return p;
787 static inline const sal_Unicode*
788 lcl_a1_get_row( const sal_Unicode* p, ScAddress* pAddr, USHORT* nFlags )
790 const sal_Unicode *pEnd;
791 long int n;
793 if( *p == '$' )
794 *nFlags |= SCA_ROW_ABSOLUTE, p++;
796 n = sal_Unicode_strtol( p, &pEnd ) - 1;
797 if( NULL == pEnd || p == pEnd || n < 0 || n > MAXROW )
798 return NULL;
800 *nFlags |= SCA_VALID_ROW;
801 pAddr->SetRow( static_cast<SCROW>(n) );
803 return pEnd;
806 static USHORT
807 lcl_ScRange_Parse_XL_A1( ScRange& r,
808 const sal_Unicode* p,
809 ScDocument* pDoc,
810 bool bOnlyAcceptSingle,
811 ScAddress::ExternalInfo* pExtInfo,
812 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
814 const sal_Unicode* tmp1, *tmp2;
815 String aExternDocName, aStartTabName, aEndTabName; // for external link table
816 USHORT nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB;
818 #if 0
820 ByteString aStr(p, RTL_TEXTENCODING_UTF8);
821 aStr.Append(static_cast< char >(0));
822 std::cerr << "parse::XL::A1 \'" << aStr.GetBuffer() << '\'' << std::endl;
824 #endif
825 p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
826 aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks );
828 if (aExternDocName.Len() > 0)
829 lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
830 aStartTabName, aEndTabName, pDoc);
832 if( NULL == p )
833 return 0;
835 tmp1 = lcl_a1_get_col( p, &r.aStart, &nFlags );
836 if( tmp1 == NULL ) // Is it a row only reference 3:5
838 if( bOnlyAcceptSingle ) // by definition full row refs are ranges
839 return 0;
841 tmp1 = lcl_a1_get_row( p, &r.aStart, &nFlags );
843 tmp1 = lcl_eatWhiteSpace( tmp1 );
844 if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
845 return 0;
847 tmp1 = lcl_eatWhiteSpace( tmp1 );
848 tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
849 if( !tmp2 )
850 return 0;
852 r.aStart.SetCol( 0 ); r.aEnd.SetCol( MAXCOL );
853 nFlags |=
854 SCA_VALID_COL | SCA_VALID_COL2 |
855 SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
856 nFlags |= (nFlags2 << 4);
857 return nFlags;
860 tmp2 = lcl_a1_get_row( tmp1, &r.aStart, &nFlags );
861 if( tmp2 == NULL ) // check for col only reference F:H
863 if( bOnlyAcceptSingle ) // by definition full col refs are ranges
864 return 0;
866 tmp1 = lcl_eatWhiteSpace( tmp1 );
867 if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F)
868 return 0;
870 tmp1 = lcl_eatWhiteSpace( tmp1 );
871 tmp2 = lcl_a1_get_col( tmp1, &r.aEnd, &nFlags2 );
872 if( !tmp2 )
873 return 0;
875 r.aStart.SetRow( 0 ); r.aEnd.SetRow( MAXROW );
876 nFlags |=
877 SCA_VALID_ROW | SCA_VALID_ROW2 |
878 SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
879 nFlags |= (nFlags2 << 4);
880 return nFlags;
883 // prepare as if it's a singleton, in case we want to fall back */
884 r.aEnd.SetCol( r.aStart.Col() );
885 r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header()
887 if ( bOnlyAcceptSingle )
889 if ( *tmp2 == 0 )
890 return nFlags;
891 else
893 // any trailing invalid character must invalidate the address.
894 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
895 return nFlags;
899 tmp2 = lcl_eatWhiteSpace( tmp2 );
900 if( *tmp2 != ':' )
902 // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
903 // not. Any trailing invalid character invalidates the range.
904 if (*tmp2 == 0 && (nFlags & SCA_TAB2_3D))
906 if (nFlags & SCA_COL_ABSOLUTE)
907 nFlags |= SCA_COL2_ABSOLUTE;
908 if (nFlags & SCA_ROW_ABSOLUTE)
909 nFlags |= SCA_ROW2_ABSOLUTE;
911 else
912 nFlags &= ~(SCA_VALID |
913 SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
914 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
915 return nFlags;
918 p = tmp2;
919 p = lcl_eatWhiteSpace( p+1 );
920 tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 );
921 if( !tmp1 ) // strange, but valid singleton
922 return nFlags;
924 tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
925 if( !tmp2 ) // strange, but valid singleton
926 return nFlags;
928 if ( *tmp2 != 0 )
930 // any trailing invalid character must invalidate the range.
931 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
932 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
933 return nFlags;
936 nFlags |= (nFlags2 << 4);
937 return nFlags;
941 @param pRange pointer to range where rAddr effectively is *pRange->aEnd,
942 used in conjunction with pExtInfo to determine the tab span
943 of a 3D reference.
945 static USHORT
946 lcl_ScAddress_Parse_OOo( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
947 ScAddress::ExternalInfo* pExtInfo = NULL, ScRange* pRange = NULL )
949 USHORT nRes = 0;
950 String aDocName; // der pure Dokumentenname
951 String aTab;
952 bool bExtDoc = false;
953 bool bExtDocInherited = false;
954 const ScAddress aCurPos(rAddr);
956 // Lets see if this is a reference to something in an external file. A
957 // document name is always quoted and has a trailing #.
958 if (*p == '\'')
960 const sal_Unicode* pStart = p;
961 p = lcl_ParseQuotedName(p, aDocName);
962 if (*p++ == SC_COMPILER_FILE_TAB_SEP)
963 bExtDoc = true;
964 else
965 // This is not a document name. Perhaps a quoted relative table
966 // name.
967 p = pStart;
969 else if (pExtInfo && pExtInfo->mbExternal)
971 // This is an external reference.
972 bExtDoc = bExtDocInherited = true;
975 SCCOL nCol = 0;
976 SCROW nRow = 0;
977 SCTAB nTab = 0;
978 USHORT nBits = SCA_VALID_TAB;
979 const sal_Unicode* q;
980 if ( ScGlobal::FindUnquoted( p, '.') )
982 nRes |= SCA_TAB_3D;
983 if ( bExtDoc )
984 nRes |= SCA_TAB_ABSOLUTE;
985 if (*p == '$')
986 nRes |= SCA_TAB_ABSOLUTE, p++;
988 if (*p == '\'')
990 // Tokens that start at ' can have anything in them until a final
991 // ' but '' marks an escaped '. We've earlier guaranteed that a
992 // string containing '' will be surrounded by '.
993 p = lcl_ParseQuotedName(p, aTab);
995 else
997 while (*p)
999 if( *p == '.')
1000 break;
1002 if( *p == '\'' )
1004 p++; break;
1006 aTab += *p++;
1009 if( *p++ != '.' )
1010 nBits = 0;
1012 if (!bExtDoc && (!pDoc || !pDoc->GetTable( aTab, nTab )))
1013 nBits = 0;
1015 else
1017 if (bExtDoc && !bExtDocInherited)
1018 return nRes; // After a document a sheet must follow.
1019 nTab = rAddr.Tab();
1021 nRes |= nBits;
1023 q = p;
1024 if (*p)
1026 nBits = SCA_VALID_COL;
1027 if (*p == '$')
1028 nBits |= SCA_COL_ABSOLUTE, p++;
1030 if (CharClass::isAsciiAlpha( *p ))
1032 nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
1033 while (nCol < MAXCOL && CharClass::isAsciiAlpha(*p))
1034 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
1036 else
1037 nBits = 0;
1039 if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) )
1040 nBits = 0;
1041 nRes |= nBits;
1042 if( !nBits )
1043 p = q;
1046 q = p;
1047 if (*p)
1049 nBits = SCA_VALID_ROW;
1050 if (*p == '$')
1051 nBits |= SCA_ROW_ABSOLUTE, p++;
1052 if( !CharClass::isAsciiDigit( *p ) )
1054 nBits = 0;
1055 nRow = SCROW(-1);
1057 else
1059 String aTmp( p );
1060 long n = aTmp.ToInt32() - 1;
1061 while (CharClass::isAsciiDigit( *p ))
1062 p++;
1063 if( n < 0 || n > MAXROW )
1064 nBits = 0;
1065 nRow = static_cast<SCROW>(n);
1067 nRes |= nBits;
1068 if( !nBits )
1069 p = q;
1072 rAddr.Set( nCol, nRow, nTab );
1074 if (!*p && bExtDoc)
1076 if (!pDoc)
1077 nRes = 0;
1078 else
1080 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
1082 // Need document name if inherited.
1083 if (bExtDocInherited)
1085 const String* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId);
1086 if (pFileName)
1087 aDocName = *pFileName;
1088 else
1089 nRes = 0;
1091 pRefMgr->convertToAbsName(aDocName);
1093 if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
1095 if (!pDoc->GetTable( aTab, nTab ))
1096 nRes = 0;
1097 else
1099 rAddr.SetTab( nTab);
1100 nRes |= SCA_VALID_TAB;
1103 else
1105 if (!pExtInfo)
1106 nRes = 0;
1107 else
1109 if (!pExtInfo->mbExternal)
1111 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
1113 pExtInfo->mbExternal = true;
1114 pExtInfo->maTabName = aTab;
1115 pExtInfo->mnFileId = nFileId;
1117 if (pRefMgr->getSingleRefToken(nFileId, aTab,
1118 ScAddress(nCol, nRow, 0), NULL,
1119 &nTab).get())
1121 rAddr.SetTab( nTab);
1122 nRes |= SCA_VALID_TAB;
1124 else
1125 nRes = 0;
1127 else
1129 // This is a call for the second part of the reference,
1130 // we must have the range to adapt tab span.
1131 if (!pRange)
1132 nRes = 0;
1133 else
1135 USHORT nFlags = nRes | SCA_VALID_TAB2;
1136 if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
1137 pExtInfo, aDocName,
1138 pExtInfo->maTabName, aTab, pDoc))
1139 nRes &= ~SCA_VALID_TAB;
1140 else
1142 if (nFlags & SCA_VALID_TAB2)
1144 rAddr.SetTab( pRange->aEnd.Tab());
1145 nRes |= SCA_VALID_TAB;
1147 else
1148 nRes &= ~SCA_VALID_TAB;
1157 if ( !(nRes & SCA_VALID_ROW) && (nRes & SCA_VALID_COL)
1158 && !( (nRes & SCA_TAB_3D) && (nRes & SCA_VALID_TAB)) )
1159 { // no Row, no Tab, but Col => DM (...), B (...) et al
1160 nRes = 0;
1162 if( !*p )
1164 USHORT nMask = nRes & ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1165 if( nMask == ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ) )
1166 nRes |= SCA_VALID;
1168 else
1169 nRes = 0;
1170 return nRes;
1173 static USHORT
1174 lcl_ScAddress_Parse ( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
1175 const ScAddress::Details& rDetails,
1176 ScAddress::ExternalInfo* pExtInfo = NULL,
1177 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL )
1179 if( !*p )
1180 return 0;
1182 switch (rDetails.eConv)
1184 default :
1185 case formula::FormulaGrammar::CONV_OOO:
1187 return lcl_ScAddress_Parse_OOo( p, pDoc, rAddr, pExtInfo, NULL );
1190 case formula::FormulaGrammar::CONV_XL_A1:
1191 case formula::FormulaGrammar::CONV_XL_OOX:
1193 ScRange r = rAddr;
1194 USHORT nFlags = lcl_ScRange_Parse_XL_A1( r, p, pDoc, true, pExtInfo,
1195 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1196 rAddr = r.aStart;
1197 return nFlags;
1199 case formula::FormulaGrammar::CONV_XL_R1C1:
1201 ScRange r = rAddr;
1202 USHORT nFlags = lcl_ScRange_Parse_XL_R1C1( r, p, pDoc, rDetails, true, pExtInfo );
1203 rAddr = r.aStart;
1204 return nFlags;
1210 bool ConvertSingleRef( ScDocument* pDoc, const String& rRefString,
1211 SCTAB nDefTab, ScRefAddress& rRefAddress,
1212 const ScAddress::Details& rDetails,
1213 ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1215 bool bRet = false;
1216 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND))
1218 ScAddress aAddr( 0, 0, nDefTab );
1219 USHORT nRes = aAddr.Parse( rRefString, pDoc, rDetails, pExtInfo);
1220 if ( nRes & SCA_VALID )
1222 rRefAddress.Set( aAddr,
1223 ((nRes & SCA_COL_ABSOLUTE) == 0),
1224 ((nRes & SCA_ROW_ABSOLUTE) == 0),
1225 ((nRes & SCA_TAB_ABSOLUTE) == 0));
1226 bRet = true;
1229 return bRet;
1233 bool ConvertDoubleRef( ScDocument* pDoc, const String& rRefString, SCTAB nDefTab,
1234 ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1235 const ScAddress::Details& rDetails,
1236 ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1238 bool bRet = false;
1239 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND))
1241 ScRange aRange( ScAddress( 0, 0, nDefTab));
1242 USHORT nRes = aRange.Parse( rRefString, pDoc, rDetails, pExtInfo);
1243 if ( nRes & SCA_VALID )
1245 rStartRefAddress.Set( aRange.aStart,
1246 ((nRes & SCA_COL_ABSOLUTE) == 0),
1247 ((nRes & SCA_ROW_ABSOLUTE) == 0),
1248 ((nRes & SCA_TAB_ABSOLUTE) == 0));
1249 rEndRefAddress.Set( aRange.aEnd,
1250 ((nRes & SCA_COL2_ABSOLUTE) == 0),
1251 ((nRes & SCA_ROW2_ABSOLUTE) == 0),
1252 ((nRes & SCA_TAB2_ABSOLUTE) == 0));
1253 bRet = true;
1256 return bRet;
1260 USHORT ScAddress::Parse( const String& r, ScDocument* pDoc,
1261 const Details& rDetails,
1262 ExternalInfo* pExtInfo,
1263 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
1265 return lcl_ScAddress_Parse( r.GetBuffer(), pDoc, *this, rDetails, pExtInfo, pExternalLinks );
1269 bool ScRange::Intersects( const ScRange& r ) const
1271 return !(
1272 Min( aEnd.Col(), r.aEnd.Col() ) < Max( aStart.Col(), r.aStart.Col() )
1273 || Min( aEnd.Row(), r.aEnd.Row() ) < Max( aStart.Row(), r.aStart.Row() )
1274 || Min( aEnd.Tab(), r.aEnd.Tab() ) < Max( aStart.Tab(), r.aStart.Tab() )
1279 void ScRange::Justify()
1281 SCCOL nTempCol;
1282 if ( aEnd.Col() < (nTempCol = aStart.Col()) )
1284 aStart.SetCol(aEnd.Col()); aEnd.SetCol(nTempCol);
1286 SCROW nTempRow;
1287 if ( aEnd.Row() < (nTempRow = aStart.Row()) )
1289 aStart.SetRow(aEnd.Row()); aEnd.SetRow(nTempRow);
1291 SCTAB nTempTab;
1292 if ( aEnd.Tab() < (nTempTab = aStart.Tab()) )
1294 aStart.SetTab(aEnd.Tab()); aEnd.SetTab(nTempTab);
1298 void ScRange::ExtendTo( const ScRange& rRange )
1300 DBG_ASSERT( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1301 if( IsValid() )
1303 aStart.SetCol( ::std::min( aStart.Col(), rRange.aStart.Col() ) );
1304 aStart.SetRow( ::std::min( aStart.Row(), rRange.aStart.Row() ) );
1305 aStart.SetTab( ::std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1306 aEnd.SetCol( ::std::max( aEnd.Col(), rRange.aEnd.Col() ) );
1307 aEnd.SetRow( ::std::max( aEnd.Row(), rRange.aEnd.Row() ) );
1308 aEnd.SetTab( ::std::max( aEnd.Tab(), rRange.aEnd.Tab() ) );
1310 else
1311 *this = rRange;
1314 static USHORT
1315 lcl_ScRange_Parse_OOo( ScRange &aRange, const String& r, ScDocument* pDoc, ScAddress::ExternalInfo* pExtInfo = NULL )
1317 USHORT nRes1 = 0, nRes2 = 0;
1318 xub_StrLen nPos = ScGlobal::FindUnquoted( r, ':');
1319 if (nPos != STRING_NOTFOUND)
1321 String aTmp( r );
1322 sal_Unicode* p = aTmp.GetBufferAccess();
1323 p[ nPos ] = 0;
1324 if( (nRes1 = lcl_ScAddress_Parse_OOo( p, pDoc, aRange.aStart, pExtInfo, NULL ) ) != 0 )
1326 aRange.aEnd = aRange.aStart; // sheet must be initialized identical to first sheet
1327 if ( (nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, pDoc, aRange.aEnd, pExtInfo, &aRange ) ) != 0 )
1329 // PutInOrder / Justify
1330 USHORT nMask, nBits1, nBits2;
1331 SCCOL nTempCol;
1332 if ( aRange.aEnd.Col() < (nTempCol = aRange.aStart.Col()) )
1334 aRange.aStart.SetCol(aRange.aEnd.Col()); aRange.aEnd.SetCol(nTempCol);
1335 nMask = (SCA_VALID_COL | SCA_COL_ABSOLUTE);
1336 nBits1 = nRes1 & nMask;
1337 nBits2 = nRes2 & nMask;
1338 nRes1 = (nRes1 & ~nMask) | nBits2;
1339 nRes2 = (nRes2 & ~nMask) | nBits1;
1341 SCROW nTempRow;
1342 if ( aRange.aEnd.Row() < (nTempRow = aRange.aStart.Row()) )
1344 aRange.aStart.SetRow(aRange.aEnd.Row()); aRange.aEnd.SetRow(nTempRow);
1345 nMask = (SCA_VALID_ROW | SCA_ROW_ABSOLUTE);
1346 nBits1 = nRes1 & nMask;
1347 nBits2 = nRes2 & nMask;
1348 nRes1 = (nRes1 & ~nMask) | nBits2;
1349 nRes2 = (nRes2 & ~nMask) | nBits1;
1351 SCTAB nTempTab;
1352 if ( aRange.aEnd.Tab() < (nTempTab = aRange.aStart.Tab()) )
1354 aRange.aStart.SetTab(aRange.aEnd.Tab()); aRange.aEnd.SetTab(nTempTab);
1355 nMask = (SCA_VALID_TAB | SCA_TAB_ABSOLUTE | SCA_TAB_3D);
1356 nBits1 = nRes1 & nMask;
1357 nBits2 = nRes2 & nMask;
1358 nRes1 = (nRes1 & ~nMask) | nBits2;
1359 nRes2 = (nRes2 & ~nMask) | nBits1;
1361 if ( ((nRes1 & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1362 == ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1363 && !(nRes2 & SCA_TAB_3D) )
1364 nRes2 |= SCA_TAB_ABSOLUTE;
1366 else
1367 nRes1 = 0; // #38840# keine Tokens aus halben Sachen
1370 nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID )
1371 | nRes1
1372 | ( ( nRes2 & SCA_BITS ) << 4 );
1373 return nRes1;
1376 USHORT ScRange::Parse( const String& r, ScDocument* pDoc,
1377 const ScAddress::Details& rDetails,
1378 ScAddress::ExternalInfo* pExtInfo,
1379 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
1381 if ( r.Len() <= 0 )
1382 return 0;
1384 switch (rDetails.eConv)
1386 default :
1387 case formula::FormulaGrammar::CONV_OOO:
1388 return lcl_ScRange_Parse_OOo( *this, r, pDoc, pExtInfo );
1390 case formula::FormulaGrammar::CONV_XL_A1:
1391 case formula::FormulaGrammar::CONV_XL_OOX:
1392 return lcl_ScRange_Parse_XL_A1( *this, r.GetBuffer(), pDoc, false, pExtInfo,
1393 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1395 case formula::FormulaGrammar::CONV_XL_R1C1:
1396 return lcl_ScRange_Parse_XL_R1C1( *this, r.GetBuffer(), pDoc, rDetails, false, pExtInfo );
1401 // Accept a full range, or an address
1402 USHORT ScRange::ParseAny( const String& r, ScDocument* pDoc,
1403 const ScAddress::Details& rDetails )
1405 USHORT nRet = Parse( r, pDoc, rDetails );
1406 const USHORT nValid = SCA_VALID | SCA_VALID_COL2 | SCA_VALID_ROW2 |
1407 SCA_VALID_TAB2;
1409 if ( (nRet & nValid) != nValid )
1411 ScAddress aAdr;
1412 nRet = aAdr.Parse( r, pDoc, rDetails );
1413 if ( nRet & SCA_VALID )
1414 aStart = aEnd = aAdr;
1416 return nRet;
1419 // Parse only full row references
1420 USHORT ScRange::ParseCols( const String& rStr, ScDocument* pDoc,
1421 const ScAddress::Details& rDetails )
1423 const sal_Unicode* p = rStr.GetBuffer();
1424 USHORT nRes = 0, ignored = 0;
1426 if( NULL == p )
1427 return 0;
1429 pDoc = NULL; // make compiler shutup we may need this later
1431 switch (rDetails.eConv)
1433 default :
1434 case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1435 case formula::FormulaGrammar::CONV_XL_A1:
1436 case formula::FormulaGrammar::CONV_XL_OOX:
1437 if (NULL != (p = lcl_a1_get_col( p, &aStart, &ignored ) ) )
1439 if( p[0] == ':')
1441 if( NULL != (p = lcl_a1_get_col( p+1, &aEnd, &ignored )))
1443 nRes = SCA_VALID_COL;
1446 else
1448 aEnd = aStart;
1449 nRes = SCA_VALID_COL;
1452 break;
1454 case formula::FormulaGrammar::CONV_XL_R1C1:
1455 if ((p[0] == 'C' || p[0] != 'c') &&
1456 NULL != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored )))
1458 if( p[0] == ':')
1460 if( (p[1] == 'C' || p[1] == 'c') &&
1461 NULL != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored )))
1463 nRes = SCA_VALID_COL;
1466 else
1468 aEnd = aStart;
1469 nRes = SCA_VALID_COL;
1472 break;
1475 return (p != NULL && *p == '\0') ? nRes : 0;
1478 // Parse only full row references
1479 USHORT ScRange::ParseRows( const String& rStr, ScDocument* pDoc,
1480 const ScAddress::Details& rDetails )
1482 const sal_Unicode* p = rStr.GetBuffer();
1483 USHORT nRes = 0, ignored = 0;
1485 if( NULL == p )
1486 return 0;
1488 pDoc = NULL; // make compiler shutup we may need this later
1490 switch (rDetails.eConv)
1492 default :
1493 case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1494 case formula::FormulaGrammar::CONV_XL_A1:
1495 case formula::FormulaGrammar::CONV_XL_OOX:
1496 if (NULL != (p = lcl_a1_get_row( p, &aStart, &ignored ) ) )
1498 if( p[0] == ':')
1500 if( NULL != (p = lcl_a1_get_row( p+1, &aEnd, &ignored )))
1502 nRes = SCA_VALID_COL;
1505 else
1507 aEnd = aStart;
1508 nRes = SCA_VALID_COL;
1511 break;
1513 case formula::FormulaGrammar::CONV_XL_R1C1:
1514 if ((p[0] == 'R' || p[0] != 'r') &&
1515 NULL != (p = lcl_r1c1_get_row( p, rDetails, &aStart, &ignored )))
1517 if( p[0] == ':')
1519 if( (p[1] == 'R' || p[1] == 'r') &&
1520 NULL != (p = lcl_r1c1_get_row( p+1, rDetails, &aEnd, &ignored )))
1522 nRes = SCA_VALID_COL;
1525 else
1527 aEnd = aStart;
1528 nRes = SCA_VALID_COL;
1531 break;
1534 return (p != NULL && *p == '\0') ? nRes : 0;
1537 static inline void
1538 lcl_a1_append_c ( String &r, int nCol, bool bIsAbs )
1540 if( bIsAbs )
1541 r += '$';
1542 ScColToAlpha( r, sal::static_int_cast<SCCOL>(nCol) );
1545 static inline void
1546 lcl_a1_append_r ( String &r, int nRow, bool bIsAbs )
1548 if ( bIsAbs )
1549 r += '$';
1550 r += String::CreateFromInt32( nRow+1 );
1553 static inline void
1554 lcl_r1c1_append_c ( String &r, int nCol, bool bIsAbs,
1555 const ScAddress::Details& rDetails )
1557 r += 'C';
1558 if (bIsAbs)
1560 r += String::CreateFromInt32( nCol + 1 );
1562 else
1564 nCol -= rDetails.nCol;
1565 if (nCol != 0) {
1566 r += '[';
1567 r += String::CreateFromInt32( nCol );
1568 r += ']';
1572 static inline void
1573 lcl_r1c1_append_r ( String &r, int nRow, bool bIsAbs,
1574 const ScAddress::Details& rDetails )
1576 r += 'R';
1577 if (bIsAbs)
1579 r += String::CreateFromInt32( nRow + 1 );
1581 else
1583 nRow -= rDetails.nRow;
1584 if (nRow != 0) {
1585 r += '[';
1586 r += String::CreateFromInt32( nRow );
1587 r += ']';
1592 static String
1593 getFileNameFromDoc( const ScDocument* pDoc )
1595 // TODO : er points at ScGlobal::GetAbsDocName()
1596 // as a better template. Look into it
1597 String sFileName;
1598 SfxObjectShell* pShell;
1600 if( NULL != pDoc &&
1601 NULL != (pShell = pDoc->GetDocumentShell() ) )
1603 uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY );
1604 if( xModel.is() )
1606 if( xModel->getURL().getLength() )
1608 INetURLObject aURL( xModel->getURL() );
1609 sFileName = aURL.GetLastName();
1611 else
1612 sFileName = pShell->GetTitle();
1615 #if 0
1617 ByteString aStr( sFileName, RTL_TEXTENCODING_UTF8 );
1618 aStr.Append(static_cast< char >(0));
1619 std::cerr << "docname \'" << aStr.GetBuffer() << '\'' << std::endl;
1621 #endif
1622 return sFileName;
1625 void ScAddress::Format( String& r, USHORT nFlags, ScDocument* pDoc,
1626 const Details& rDetails) const
1628 r.Erase();
1629 if( nFlags & SCA_VALID )
1630 nFlags |= ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1631 if( pDoc && (nFlags & SCA_VALID_TAB ) )
1633 if ( nTab >= pDoc->GetTableCount() )
1635 r = ScGlobal::GetRscString( STR_NOREF_STR );
1636 return;
1638 // if( nFlags & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ) )
1639 if( nFlags & SCA_TAB_3D )
1641 String aTabName, aDocName;
1642 pDoc->GetName( nTab, aTabName );
1643 // External Reference, same as in ScCompiler::MakeTabStr()
1644 if( aTabName.GetChar(0) == '\'' )
1645 { // "'Doc'#Tab"
1646 xub_StrLen nPos = ScGlobal::FindUnquoted( aTabName, SC_COMPILER_FILE_TAB_SEP);
1647 if (nPos != STRING_NOTFOUND && nPos > 0 && aTabName.GetChar(nPos-1) == '\'')
1649 aDocName = aTabName.Copy( 0, nPos + 1 );
1650 aTabName.Erase( 0, nPos + 1 );
1653 else if( nFlags & SCA_FORCE_DOC )
1655 // VBA has an 'external' flag that forces the addition of the
1656 // tab name _and_ the doc name. The VBA code would be
1657 // needlessly complicated if it constructed an actual external
1658 // reference so we add this somewhat cheesy kludge to force the
1659 // addition of the document name even for non-external references
1660 aDocName = getFileNameFromDoc( pDoc );
1662 ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
1664 switch( rDetails.eConv )
1666 default :
1667 case formula::FormulaGrammar::CONV_OOO:
1668 r += aDocName;
1669 if( nFlags & SCA_TAB_ABSOLUTE )
1670 r += '$';
1671 r += aTabName;
1672 r += '.';
1673 break;
1675 case formula::FormulaGrammar::CONV_XL_A1:
1676 case formula::FormulaGrammar::CONV_XL_R1C1:
1677 case formula::FormulaGrammar::CONV_XL_OOX:
1678 if (aDocName.Len() > 0)
1680 r += '[';
1681 r += aDocName;
1682 r += ']';
1684 r += aTabName;
1685 r += '!';
1686 break;
1690 switch( rDetails.eConv )
1692 default :
1693 case formula::FormulaGrammar::CONV_OOO:
1694 case formula::FormulaGrammar::CONV_XL_A1:
1695 case formula::FormulaGrammar::CONV_XL_OOX:
1696 if( nFlags & SCA_VALID_COL )
1697 lcl_a1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE );
1698 if( nFlags & SCA_VALID_ROW )
1699 lcl_a1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE );
1700 break;
1702 case formula::FormulaGrammar::CONV_XL_R1C1:
1703 if( nFlags & SCA_VALID_ROW )
1704 lcl_r1c1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE, rDetails );
1705 if( nFlags & SCA_VALID_COL )
1706 lcl_r1c1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE, rDetails );
1707 break;
1711 static void
1712 lcl_Split_DocTab( const ScDocument* pDoc, SCTAB nTab,
1713 const ScAddress::Details& rDetails,
1714 USHORT nFlags,
1715 String& rTabName, String& rDocName )
1717 pDoc->GetName( nTab, rTabName );
1718 rDocName.Erase();
1719 #if 0
1721 ByteString aStr(rTabName, RTL_TEXTENCODING_UTF8);
1722 aStr.Append(static_cast< char >(0));
1723 std::cerr << "tabname \'" << aStr.GetBuffer() << '\'' << std::endl;
1725 #endif
1726 // External reference, same as in ScCompiler::MakeTabStr()
1727 if ( rTabName.GetChar(0) == '\'' )
1728 { // "'Doc'#Tab"
1729 xub_StrLen nPos = ScGlobal::FindUnquoted( rTabName, SC_COMPILER_FILE_TAB_SEP);
1730 if (nPos != STRING_NOTFOUND && nPos > 0 && rTabName.GetChar(nPos-1) == '\'')
1732 rDocName = rTabName.Copy( 0, nPos + 1 );
1733 rTabName.Erase( 0, nPos + 1 );
1736 else if( nFlags & SCA_FORCE_DOC )
1738 // VBA has an 'external' flag that forces the addition of the
1739 // tab name _and_ the doc name. The VBA code would be
1740 // needlessly complicated if it constructed an actual external
1741 // reference so we add this somewhat cheesy kludge to force the
1742 // addition of the document name even for non-external references
1743 rDocName = getFileNameFromDoc( pDoc );
1745 ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
1748 static void
1749 lcl_ScRange_Format_XL_Header( String& r, const ScRange& rRange,
1750 USHORT nFlags, ScDocument* pDoc,
1751 const ScAddress::Details& rDetails )
1753 if( nFlags & SCA_TAB_3D )
1755 String aTabName, aDocName;
1756 lcl_Split_DocTab( pDoc, rRange.aStart.Tab(), rDetails, nFlags,
1757 aTabName, aDocName );
1758 if( aDocName.Len() > 0 )
1760 r += '[';
1761 r += aDocName;
1762 r += ']';
1764 r += aTabName;
1766 if( nFlags & SCA_TAB2_3D )
1768 lcl_Split_DocTab( pDoc, rRange.aEnd.Tab(), rDetails, nFlags,
1769 aTabName, aDocName );
1770 r += ':';
1771 r += aTabName;
1773 r += '!';
1777 void ScRange::Format( String& r, USHORT nFlags, ScDocument* pDoc,
1778 const ScAddress::Details& rDetails ) const
1780 r.Erase();
1781 if( !( nFlags & SCA_VALID ) )
1783 r = ScGlobal::GetRscString( STR_NOREF_STR );
1784 return;
1787 #define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask)))
1788 switch( rDetails.eConv ) {
1789 default :
1790 case formula::FormulaGrammar::CONV_OOO: {
1791 BOOL bOneTab = (aStart.Tab() == aEnd.Tab());
1792 if ( !bOneTab )
1793 nFlags |= SCA_TAB_3D;
1794 aStart.Format( r, nFlags, pDoc, rDetails );
1795 if( aStart != aEnd ||
1796 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1797 absrel_differ( nFlags, SCA_ROW_ABSOLUTE ))
1799 String aName;
1800 nFlags = ( nFlags & SCA_VALID ) | ( ( nFlags >> 4 ) & 0x070F );
1801 if ( bOneTab )
1802 pDoc = NULL;
1803 else
1804 nFlags |= SCA_TAB_3D;
1805 aEnd.Format( aName, nFlags, pDoc, rDetails );
1806 r += ':';
1807 r += aName;
1810 break;
1812 case formula::FormulaGrammar::CONV_XL_A1:
1813 case formula::FormulaGrammar::CONV_XL_OOX:
1814 lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1815 if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1817 // Full col refs always require 2 rows (2:2)
1818 lcl_a1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE );
1819 r += ':';
1820 lcl_a1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE );
1822 else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1824 // Full row refs always require 2 cols (A:A)
1825 lcl_a1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE );
1826 r += ':';
1827 lcl_a1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE );
1829 else
1831 lcl_a1_append_c ( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE );
1832 lcl_a1_append_r ( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE );
1833 if( aStart.Col() != aEnd.Col() ||
1834 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1835 aStart.Row() != aEnd.Row() ||
1836 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1837 r += ':';
1838 lcl_a1_append_c ( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE );
1839 lcl_a1_append_r ( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE );
1842 break;
1844 case formula::FormulaGrammar::CONV_XL_R1C1:
1845 lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1846 if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1848 lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails );
1849 if( aStart.Row() != aEnd.Row() ||
1850 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1851 r += ':';
1852 lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails );
1855 else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1857 lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails );
1858 if( aStart.Col() != aEnd.Col() ||
1859 absrel_differ( nFlags, SCA_COL_ABSOLUTE )) {
1860 r += ':';
1861 lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails );
1864 else
1866 lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails );
1867 lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails );
1868 if( aStart.Col() != aEnd.Col() ||
1869 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1870 aStart.Row() != aEnd.Row() ||
1871 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1872 r += ':';
1873 lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails );
1874 lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails );
1878 #undef absrel_differ
1881 bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1883 SCsTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB+1;
1884 dx = Col() + dx;
1885 dy = Row() + dy;
1886 dz = Tab() + dz;
1887 BOOL bValid = TRUE;
1888 if( dx < 0 )
1889 dx = 0, bValid = FALSE;
1890 else if( dx > MAXCOL )
1891 dx = MAXCOL, bValid =FALSE;
1892 if( dy < 0 )
1893 dy = 0, bValid = FALSE;
1894 else if( dy > MAXROW )
1895 dy = MAXROW, bValid =FALSE;
1896 if( dz < 0 )
1897 dz = 0, bValid = FALSE;
1898 else if( dz >= nMaxTab )
1899 dz = nMaxTab-1, bValid =FALSE;
1900 Set( dx, dy, dz );
1901 return bValid;
1905 bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1907 // Einfahces &, damit beides ausgefuehrt wird!!
1908 return aStart.Move( dx, dy, dz, pDoc ) & aEnd.Move( dx, dy, dz, pDoc );
1912 String ScAddress::GetColRowString( bool bAbsolute,
1913 const Details& rDetails ) const
1915 String aString;
1917 switch( rDetails.eConv )
1919 default :
1920 case formula::FormulaGrammar::CONV_OOO:
1921 case formula::FormulaGrammar::CONV_XL_A1:
1922 case formula::FormulaGrammar::CONV_XL_OOX:
1923 if (bAbsolute)
1924 aString.Append( '$' );
1926 ScColToAlpha( aString, nCol);
1928 if ( bAbsolute )
1929 aString.Append( '$' );
1931 aString += String::CreateFromInt32(nRow+1);
1932 break;
1934 case formula::FormulaGrammar::CONV_XL_R1C1:
1935 lcl_r1c1_append_r ( aString, nRow, bAbsolute, rDetails );
1936 lcl_r1c1_append_c ( aString, nCol, bAbsolute, rDetails );
1937 break;
1940 return aString;
1944 String ScRefAddress::GetRefString( ScDocument* pDoc, SCTAB nActTab,
1945 const ScAddress::Details& rDetails ) const
1947 if ( !pDoc )
1948 return EMPTY_STRING;
1949 if ( Tab()+1 > pDoc->GetTableCount() )
1950 return ScGlobal::GetRscString( STR_NOREF_STR );
1952 String aString;
1953 USHORT nFlags = SCA_VALID;
1954 if ( nActTab != Tab() )
1956 nFlags |= SCA_TAB_3D;
1957 if ( !bRelTab )
1958 nFlags |= SCA_TAB_ABSOLUTE;
1960 if ( !bRelCol )
1961 nFlags |= SCA_COL_ABSOLUTE;
1962 if ( !bRelRow )
1963 nFlags |= SCA_ROW_ABSOLUTE;
1965 aAdr.Format( aString, nFlags, pDoc, rDetails );
1967 return aString;
1970 //------------------------------------------------------------------------
1972 void ScColToAlpha( rtl::OUStringBuffer& rBuf, SCCOL nCol )
1974 if (nCol < 26*26)
1976 if (nCol < 26)
1977 rBuf.append( static_cast<sal_Unicode>( 'A' +
1978 static_cast<sal_uInt16>(nCol)));
1979 else
1981 rBuf.append( static_cast<sal_Unicode>( 'A' +
1982 (static_cast<sal_uInt16>(nCol) / 26) - 1));
1983 rBuf.append( static_cast<sal_Unicode>( 'A' +
1984 (static_cast<sal_uInt16>(nCol) % 26)));
1987 else
1989 String aStr;
1990 while (nCol >= 26)
1992 SCCOL nC = nCol % 26;
1993 aStr += static_cast<sal_Unicode>( 'A' +
1994 static_cast<sal_uInt16>(nC));
1995 nCol = sal::static_int_cast<SCCOL>( nCol - nC );
1996 nCol = nCol / 26 - 1;
1998 aStr += static_cast<sal_Unicode>( 'A' +
1999 static_cast<sal_uInt16>(nCol));
2000 aStr.Reverse();
2001 rBuf.append( aStr);
2006 bool AlphaToCol( SCCOL& rCol, const String& rStr)
2008 SCCOL nResult = 0;
2009 xub_StrLen nStop = rStr.Len();
2010 xub_StrLen nPos = 0;
2011 sal_Unicode c;
2012 while (nResult <= MAXCOL && nPos < nStop && (c = rStr.GetChar( nPos)) != 0 &&
2013 CharClass::isAsciiAlpha(c))
2015 if (nPos > 0)
2016 nResult = (nResult + 1) * 26;
2017 nResult += ScGlobal::ToUpperAlpha(c) - 'A';
2018 ++nPos;
2020 bool bOk = (ValidCol(nResult) && nPos > 0);
2021 if (bOk)
2022 rCol = nResult;
2023 return bOk;