update dev300-m57
[ooovba.git] / sc / source / core / tool / address.cxx
blob553f6667c794ea6bca6e050a6e704b51fa635c87
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 static const sal_Unicode *
225 lcl_XL_ParseSheetRef( const sal_Unicode* start,
226 String& rExternTabName,
227 bool allow_3d )
229 String aTabName;
230 const sal_Unicode *p = start;
232 if( *p == '\'' ) // XL only seems to use single quotes for sheet names
234 p = lcl_ParseQuotedName(p, aTabName);
235 if (!aTabName.Len())
236 return NULL;
238 else
240 bool only_digits = TRUE;
243 * Valid: Normal!a1
244 * Valid: x.y!a1
245 * Invalid: .y!a1
247 * Some names starting with digits are actually valid, but
248 * unparse quoted. Things are quite tricky: most sheet names
249 * starting with a digit are ok, but not those starting with
250 * "[0-9]*\." or "[0-9]+[eE]".
252 * Valid: 42!a1
253 * Valid: 4x!a1
254 * Invalid: 1.!a1
255 * Invalid: 1e!a1
257 while( 1 )
259 const sal_Unicode uc = *p;
260 if( CharClass::isAsciiAlpha( uc ) || uc == '_' )
262 if( only_digits && p != start &&
263 (uc == 'e' || uc == 'E' ) )
265 p = start;
266 break;
268 only_digits = FALSE;
269 p++;
271 else if( CharClass::isAsciiDigit( uc ))
273 p++;
275 else if( uc == '.' )
277 if( only_digits ) // Valid, except after only digits.
279 p = start;
280 break;
282 p++;
284 else if (uc > 127)
286 // non ASCII character is allowed.
287 ++p;
289 else
290 break;
293 if( *p != '!' &&( !allow_3d || *p != ':' ) )
294 return start;
296 aTabName.Append( start, sal::static_int_cast<xub_StrLen>( p - start ) );
299 rExternTabName = aTabName;
300 return p;
304 const sal_Unicode* ScRange::Parse_XL_Header(
305 const sal_Unicode* p,
306 const ScDocument* pDoc,
307 String& rExternDocName,
308 String& rStartTabName,
309 String& rEndTabName,
310 USHORT& nFlags,
311 bool bOnlyAcceptSingle,
312 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
314 const sal_Unicode* startTabs, *start = p;
315 USHORT nSaveFlags = nFlags;
317 // Is this an external reference ?
318 rStartTabName.Erase();
319 rEndTabName.Erase();
320 rExternDocName.Erase();
321 if (*p == '[')
323 ++p;
324 // Only single quotes are correct, and a double single quote escapes a
325 // single quote text inside the quoted text.
326 if (*p == '\'')
328 p = lcl_ParseQuotedName(p, rExternDocName);
329 if (!*p || *p != ']' || !rExternDocName.Len())
330 return start;
332 else
334 // non-quoted file name.
335 p = ScGlobal::UnicodeStrChr( start+1, ']' );
336 if( p == NULL )
337 return start;
338 rExternDocName.Append( start+1, sal::static_int_cast<xub_StrLen>( p-(start+1) ) );
340 ++p;
342 // 1-based, sequence starts with an empty element.
343 if (pExternalLinks && pExternalLinks->getLength() > 1)
345 // A numeric "document name" is an index into the sequence.
346 if (CharClass::isAsciiNumeric( rExternDocName))
348 sal_Int32 i = rExternDocName.ToInt32();
349 if (i <= 0 || i >= pExternalLinks->getLength())
350 return start;
351 const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i];
352 switch (rInfo.Type)
354 case sheet::ExternalLinkType::DOCUMENT :
356 rtl::OUString aStr;
357 if (!(rInfo.Data >>= aStr))
359 DBG_ERROR1( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i);
360 return NULL;
362 rExternDocName = aStr;
364 break;
365 default:
366 DBG_ERROR2( "ScRange::Parse_XL_Header: unhandled ExternalLinkType %d for index %d",
367 rInfo.Type, i);
368 return NULL;
372 rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, pDoc->GetDocumentShell());
375 startTabs = p;
376 p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle );
377 if( NULL == p )
378 return start; // invalid tab
379 if (bOnlyAcceptSingle && *p == ':')
380 return NULL; // 3D
381 if( p != startTabs )
383 nFlags |= SCA_VALID_TAB | SCA_TAB_3D | SCA_TAB_ABSOLUTE;
384 if( *p == ':' ) // 3d ref
386 p = lcl_XL_ParseSheetRef( p+1, rEndTabName, false );
387 if( p == NULL )
389 nFlags = nSaveFlags;
390 return start; // invalid tab
392 nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE;
394 else
396 // If only one sheet is given, the full reference is still valid,
397 // only the second 3D flag is not set.
398 nFlags |= SCA_VALID_TAB2 | SCA_TAB2_ABSOLUTE;
399 aEnd.SetTab( aStart.Tab() );
402 if( *p++ != '!' )
404 nFlags = nSaveFlags;
405 return start; // syntax error
407 else
408 p = lcl_eatWhiteSpace( p );
410 else
412 nFlags |= SCA_VALID_TAB | SCA_VALID_TAB2;
413 // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
416 if (!rExternDocName.Len())
418 // Internal reference.
419 if (!rStartTabName.Len())
421 nFlags = nSaveFlags;
422 return start;
425 SCTAB nTab;
426 if (!pDoc->GetTable(rStartTabName, nTab))
428 // invalid table name.
429 nFlags &= ~SCA_VALID_TAB;
430 nTab = -1;
433 aStart.SetTab(nTab);
434 aEnd.SetTab(nTab);
436 if (rEndTabName.Len())
438 if (!pDoc->GetTable(rEndTabName, nTab))
440 // invalid table name.
441 nFlags &= ~SCA_VALID_TAB2;
442 nTab = -1;
445 aEnd.SetTab(nTab);
448 return p;
452 static const sal_Unicode*
453 lcl_r1c1_get_col( const sal_Unicode* p,
454 const ScAddress::Details& rDetails,
455 ScAddress* pAddr, USHORT* nFlags )
457 const sal_Unicode *pEnd;
458 long int n;
459 bool isRelative;
461 if( p[0] == '\0' )
462 return NULL;
464 p++;
465 if( (isRelative = (*p == '[') ) != false )
466 p++;
467 n = sal_Unicode_strtol( p, &pEnd );
468 if( NULL == pEnd )
469 return NULL;
471 if( p == pEnd ) // C is a relative ref with offset 0
473 if( isRelative )
474 return NULL;
475 n = rDetails.nCol;
477 else if( isRelative )
479 if( *pEnd != ']' )
480 return NULL;
481 n += rDetails.nCol;
482 pEnd++;
484 else
486 *nFlags |= SCA_COL_ABSOLUTE;
487 n--;
490 if( n < 0 || n >= MAXCOLCOUNT )
491 return NULL;
492 pAddr->SetCol( static_cast<SCCOL>( n ) );
493 *nFlags |= SCA_VALID_COL;
495 return pEnd;
497 static inline const sal_Unicode*
498 lcl_r1c1_get_row( const sal_Unicode* p,
499 const ScAddress::Details& rDetails,
500 ScAddress* pAddr, USHORT* nFlags )
502 const sal_Unicode *pEnd;
503 long int n;
504 bool isRelative;
506 if( p[0] == '\0' )
507 return NULL;
509 p++;
510 if( (isRelative = (*p == '[') ) != false )
511 p++;
512 n = sal_Unicode_strtol( p, &pEnd );
513 if( NULL == pEnd )
514 return NULL;
516 if( p == pEnd ) // R is a relative ref with offset 0
518 if( isRelative )
519 return NULL;
520 n = rDetails.nRow;
522 else if( isRelative )
524 if( *pEnd != ']' )
525 return NULL;
526 n += rDetails.nRow;
527 pEnd++;
529 else
531 *nFlags |= SCA_ROW_ABSOLUTE;
532 n--;
535 if( n < 0 || n >= MAXROWCOUNT )
536 return NULL;
537 pAddr->SetRow( static_cast<SCROW>( n ) );
538 *nFlags |= SCA_VALID_ROW;
540 return pEnd;
543 static USHORT
544 lcl_ScRange_Parse_XL_R1C1( ScRange& r,
545 const sal_Unicode* p,
546 ScDocument* pDoc,
547 const ScAddress::Details& rDetails,
548 bool bOnlyAcceptSingle,
549 ScAddress::ExternalInfo* pExtInfo )
551 const sal_Unicode* pTmp = NULL;
552 String aExternDocName, aStartTabName, aEndTabName;
553 USHORT nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB2;
555 #if 0
557 ByteString aStr(p, RTL_TEXTENCODING_UTF8);
558 aStr.Append(static_cast< char >(0));
559 std::cerr << "parse::XL::R1C1 \'" << aStr.GetBuffer() << '\'' << std::endl;
561 #endif
562 p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
563 aEndTabName, nFlags, bOnlyAcceptSingle, NULL );
565 if (aExternDocName.Len() > 0)
566 lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
567 aStartTabName, aEndTabName, pDoc);
569 if( NULL == p )
570 return 0;
572 if( *p == 'R' || *p == 'r' )
574 if( NULL == (p = lcl_r1c1_get_row( p, rDetails, &r.aStart, &nFlags )) )
575 goto failed;
577 if( *p != 'C' && *p != 'c' ) // full row R#
579 if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) ||
580 NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )))
582 // Only the initial row number is given, or the second row
583 // number is invalid. Fallback to just the initial R
584 nFlags |= (nFlags << 4);
585 r.aEnd.SetRow( r.aStart.Row() );
587 else
589 // Full row range successfully parsed.
590 nFlags |= (nFlags2 << 4);
591 p = pTmp;
594 if (p && p[0] != 0)
596 // any trailing invalid character must invalidate the whole address.
597 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
598 return nFlags;
601 nFlags |=
602 SCA_VALID_COL | SCA_VALID_COL2 |
603 SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
604 r.aStart.SetCol( 0 );
605 r.aEnd.SetCol( MAXCOL );
607 return bOnlyAcceptSingle ? 0 : nFlags;
609 else if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
610 goto failed;
612 if( p[0] != ':' ||
613 (p[1] != 'R' && p[1] != 'r') ||
614 NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )) ||
615 (*pTmp != 'C' && *pTmp != 'c') ||
616 NULL == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 )))
618 // single cell reference
620 if (p && p[0] != 0)
622 // any trailing invalid character must invalidate the whole address.
623 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
624 return nFlags;
627 return bOnlyAcceptSingle ? nFlags : 0;
629 p = pTmp;
631 // double reference
633 if (p && p[0] != 0)
635 // any trailing invalid character must invalidate the whole range.
636 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
637 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
638 return nFlags;
641 nFlags |= (nFlags2 << 4);
642 return bOnlyAcceptSingle ? 0 : nFlags;
644 else if( *p == 'C' || *p == 'c' ) // full col C#
646 if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
647 goto failed;
649 if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') ||
650 NULL == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, &nFlags2 )))
651 { // Fallback to just the initial C
652 nFlags |= (nFlags << 4);
653 r.aEnd.SetCol( r.aStart.Col() );
655 else
657 nFlags |= (nFlags2 << 4);
658 p = pTmp;
661 if (p && p[0] != 0)
663 // any trailing invalid character must invalidate the whole address.
664 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
665 return nFlags;
668 nFlags |=
669 SCA_VALID_ROW | SCA_VALID_ROW2 |
670 SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
671 r.aStart.SetRow( 0 );
672 r.aEnd.SetRow( MAXROW );
674 return bOnlyAcceptSingle ? 0 : nFlags;
677 failed :
678 return 0;
681 static inline const sal_Unicode*
682 lcl_a1_get_col( const sal_Unicode* p, ScAddress* pAddr, USHORT* nFlags )
684 SCCOL nCol;
686 if( *p == '$' )
687 *nFlags |= SCA_COL_ABSOLUTE, p++;
689 if( !CharClass::isAsciiAlpha( *p ) )
690 return NULL;
692 nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
693 while (nCol <= MAXCOL && CharClass::isAsciiAlpha(*p))
694 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
695 if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) )
696 return NULL;
698 *nFlags |= SCA_VALID_COL;
699 pAddr->SetCol( nCol );
701 return p;
704 static inline const sal_Unicode*
705 lcl_a1_get_row( const sal_Unicode* p, ScAddress* pAddr, USHORT* nFlags )
707 const sal_Unicode *pEnd;
708 long int n;
710 if( *p == '$' )
711 *nFlags |= SCA_ROW_ABSOLUTE, p++;
713 n = sal_Unicode_strtol( p, &pEnd ) - 1;
714 if( NULL == pEnd || p == pEnd || n < 0 || n > MAXROW )
715 return NULL;
717 *nFlags |= SCA_VALID_ROW;
718 pAddr->SetRow( static_cast<SCROW>(n) );
720 return pEnd;
723 static USHORT
724 lcl_ScRange_Parse_XL_A1( ScRange& r,
725 const sal_Unicode* p,
726 ScDocument* pDoc,
727 bool bOnlyAcceptSingle,
728 ScAddress::ExternalInfo* pExtInfo,
729 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
731 const sal_Unicode* tmp1, *tmp2;
732 String aExternDocName, aStartTabName, aEndTabName; // for external link table
733 USHORT nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB;
735 #if 0
737 ByteString aStr(p, RTL_TEXTENCODING_UTF8);
738 aStr.Append(static_cast< char >(0));
739 std::cerr << "parse::XL::A1 \'" << aStr.GetBuffer() << '\'' << std::endl;
741 #endif
742 p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
743 aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks );
745 if (aExternDocName.Len() > 0)
746 lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
747 aStartTabName, aEndTabName, pDoc);
749 if( NULL == p )
750 return 0;
752 tmp1 = lcl_a1_get_col( p, &r.aStart, &nFlags );
753 if( tmp1 == NULL ) // Is it a row only reference 3:5
755 if( bOnlyAcceptSingle ) // by definition full row refs are ranges
756 return 0;
758 tmp1 = lcl_a1_get_row( p, &r.aStart, &nFlags );
760 tmp1 = lcl_eatWhiteSpace( tmp1 );
761 if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
762 return 0;
764 tmp1 = lcl_eatWhiteSpace( tmp1 );
765 tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
766 if( !tmp2 )
767 return 0;
769 r.aStart.SetCol( 0 ); r.aEnd.SetCol( MAXCOL );
770 nFlags |=
771 SCA_VALID_COL | SCA_VALID_COL2 |
772 SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
773 nFlags |= (nFlags2 << 4);
774 return nFlags;
777 tmp2 = lcl_a1_get_row( tmp1, &r.aStart, &nFlags );
778 if( tmp2 == NULL ) // check for col only reference F:H
780 if( bOnlyAcceptSingle ) // by definition full col refs are ranges
781 return 0;
783 tmp1 = lcl_eatWhiteSpace( tmp1 );
784 if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F)
785 return 0;
787 tmp1 = lcl_eatWhiteSpace( tmp1 );
788 tmp2 = lcl_a1_get_col( tmp1, &r.aEnd, &nFlags2 );
789 if( !tmp2 )
790 return 0;
792 r.aStart.SetRow( 0 ); r.aEnd.SetRow( MAXROW );
793 nFlags |=
794 SCA_VALID_ROW | SCA_VALID_ROW2 |
795 SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
796 nFlags |= (nFlags2 << 4);
797 return nFlags;
800 // prepare as if it's a singleton, in case we want to fall back */
801 r.aEnd.SetCol( r.aStart.Col() );
802 r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header()
804 if ( bOnlyAcceptSingle )
806 if ( *tmp2 == 0 )
807 return nFlags;
808 else
810 // any trailing invalid character must invalidate the address.
811 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
812 return nFlags;
816 tmp2 = lcl_eatWhiteSpace( tmp2 );
817 if( *tmp2 != ':' )
819 // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
820 // not. Any trailing invalid character invalidates the range.
821 if (*tmp2 == 0 && (nFlags & SCA_TAB2_3D))
823 if (nFlags & SCA_COL_ABSOLUTE)
824 nFlags |= SCA_COL2_ABSOLUTE;
825 if (nFlags & SCA_ROW_ABSOLUTE)
826 nFlags |= SCA_ROW2_ABSOLUTE;
828 else
829 nFlags &= ~(SCA_VALID |
830 SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
831 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
832 return nFlags;
835 p = tmp2;
836 p = lcl_eatWhiteSpace( p+1 );
837 tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 );
838 if( !tmp1 ) // strange, but valid singleton
839 return nFlags;
841 tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
842 if( !tmp2 ) // strange, but valid singleton
843 return nFlags;
845 if ( *tmp2 != 0 )
847 // any trailing invalid character must invalidate the range.
848 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
849 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
850 return nFlags;
853 nFlags |= (nFlags2 << 4);
854 return nFlags;
858 @param pRange pointer to range where rAddr effectively is *pRange->aEnd,
859 used in conjunction with pExtInfo to determine the tab span
860 of a 3D reference.
862 static USHORT
863 lcl_ScAddress_Parse_OOo( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
864 ScAddress::ExternalInfo* pExtInfo = NULL, ScRange* pRange = NULL )
866 USHORT nRes = 0;
867 String aDocName; // der pure Dokumentenname
868 String aTab;
869 bool bExtDoc = false;
870 bool bExtDocInherited = false;
871 const ScAddress aCurPos(rAddr);
873 // Lets see if this is a reference to something in an external file. A
874 // document name is always quoted and has a trailing #.
875 if (*p == '\'')
877 const sal_Unicode* pStart = p;
878 p = lcl_ParseQuotedName(p, aDocName);
879 if (*p++ == SC_COMPILER_FILE_TAB_SEP)
880 bExtDoc = true;
881 else
882 // This is not a document name. Perhaps a quoted relative table
883 // name.
884 p = pStart;
886 else if (pExtInfo && pExtInfo->mbExternal)
888 // This is an external reference.
889 bExtDoc = bExtDocInherited = true;
892 SCCOL nCol = 0;
893 SCROW nRow = 0;
894 SCTAB nTab = 0;
895 USHORT nBits = SCA_VALID_TAB;
896 const sal_Unicode* q;
897 if ( ScGlobal::FindUnquoted( p, '.') )
899 nRes |= SCA_TAB_3D;
900 if ( bExtDoc )
901 nRes |= SCA_TAB_ABSOLUTE;
902 if (*p == '$')
903 nRes |= SCA_TAB_ABSOLUTE, p++;
905 if (*p == '\'')
907 // Tokens that start at ' can have anything in them until a final
908 // ' but '' marks an escaped '. We've earlier guaranteed that a
909 // string containing '' will be surrounded by '.
910 p = lcl_ParseQuotedName(p, aTab);
912 else
914 while (*p)
916 if( *p == '.')
917 break;
919 if( *p == '\'' )
921 p++; break;
923 aTab += *p++;
926 if( *p++ != '.' )
927 nBits = 0;
929 if (!bExtDoc && (!pDoc || !pDoc->GetTable( aTab, nTab )))
930 nBits = 0;
932 else
934 if (bExtDoc && !bExtDocInherited)
935 return nRes; // After a document a sheet must follow.
936 nTab = rAddr.Tab();
938 nRes |= nBits;
940 q = p;
941 if (*p)
943 nBits = SCA_VALID_COL;
944 if (*p == '$')
945 nBits |= SCA_COL_ABSOLUTE, p++;
947 if (CharClass::isAsciiAlpha( *p ))
949 nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
950 while (nCol < MAXCOL && CharClass::isAsciiAlpha(*p))
951 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
953 else
954 nBits = 0;
956 if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) )
957 nBits = 0;
958 nRes |= nBits;
959 if( !nBits )
960 p = q;
963 q = p;
964 if (*p)
966 nBits = SCA_VALID_ROW;
967 if (*p == '$')
968 nBits |= SCA_ROW_ABSOLUTE, p++;
969 if( !CharClass::isAsciiDigit( *p ) )
971 nBits = 0;
972 nRow = SCROW(-1);
974 else
976 String aTmp( p );
977 long n = aTmp.ToInt32() - 1;
978 while (CharClass::isAsciiDigit( *p ))
979 p++;
980 if( n < 0 || n > MAXROW )
981 nBits = 0;
982 nRow = static_cast<SCROW>(n);
984 nRes |= nBits;
985 if( !nBits )
986 p = q;
989 rAddr.Set( nCol, nRow, nTab );
991 if (!*p && bExtDoc)
993 if (!pDoc)
994 nRes = 0;
995 else
997 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
999 // Need document name if inherited.
1000 if (bExtDocInherited)
1002 const String* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId);
1003 if (pFileName)
1004 aDocName = *pFileName;
1005 else
1006 nRes = 0;
1008 pRefMgr->convertToAbsName(aDocName);
1010 if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
1012 if (!pDoc->GetTable( aTab, nTab ))
1013 nRes = 0;
1014 else
1016 rAddr.SetTab( nTab);
1017 nRes |= SCA_VALID_TAB;
1020 else
1022 if (!pExtInfo)
1023 nRes = 0;
1024 else
1026 if (!pExtInfo->mbExternal)
1028 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
1030 pExtInfo->mbExternal = true;
1031 pExtInfo->maTabName = aTab;
1032 pExtInfo->mnFileId = nFileId;
1034 if (pRefMgr->getSingleRefToken(nFileId, aTab,
1035 ScAddress(nCol, nRow, 0), NULL,
1036 &nTab).get())
1038 rAddr.SetTab( nTab);
1039 nRes |= SCA_VALID_TAB;
1041 else
1042 nRes = 0;
1044 else
1046 // This is a call for the second part of the reference,
1047 // we must have the range to adapt tab span.
1048 if (!pRange)
1049 nRes = 0;
1050 else
1052 USHORT nFlags = nRes | SCA_VALID_TAB2;
1053 if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
1054 pExtInfo, aDocName,
1055 pExtInfo->maTabName, aTab, pDoc))
1056 nRes &= ~SCA_VALID_TAB;
1057 else
1059 if (nFlags & SCA_VALID_TAB2)
1061 rAddr.SetTab( pRange->aEnd.Tab());
1062 nRes |= SCA_VALID_TAB;
1064 else
1065 nRes &= ~SCA_VALID_TAB;
1074 if ( !(nRes & SCA_VALID_ROW) && (nRes & SCA_VALID_COL)
1075 && !( (nRes & SCA_TAB_3D) && (nRes & SCA_VALID_TAB)) )
1076 { // no Row, no Tab, but Col => DM (...), B (...) et al
1077 nRes = 0;
1079 if( !*p )
1081 USHORT nMask = nRes & ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1082 if( nMask == ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ) )
1083 nRes |= SCA_VALID;
1085 else
1086 nRes = 0;
1087 return nRes;
1090 static USHORT
1091 lcl_ScAddress_Parse ( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
1092 const ScAddress::Details& rDetails,
1093 ScAddress::ExternalInfo* pExtInfo = NULL,
1094 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL )
1096 if( !*p )
1097 return 0;
1099 switch (rDetails.eConv)
1101 default :
1102 case formula::FormulaGrammar::CONV_OOO:
1104 return lcl_ScAddress_Parse_OOo( p, pDoc, rAddr, pExtInfo, NULL );
1107 case formula::FormulaGrammar::CONV_XL_A1:
1108 case formula::FormulaGrammar::CONV_XL_OOX:
1110 ScRange r = rAddr;
1111 USHORT nFlags = lcl_ScRange_Parse_XL_A1( r, p, pDoc, true, pExtInfo,
1112 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1113 rAddr = r.aStart;
1114 return nFlags;
1116 case formula::FormulaGrammar::CONV_XL_R1C1:
1118 ScRange r = rAddr;
1119 USHORT nFlags = lcl_ScRange_Parse_XL_R1C1( r, p, pDoc, rDetails, true, pExtInfo );
1120 rAddr = r.aStart;
1121 return nFlags;
1127 bool ConvertSingleRef( ScDocument* pDoc, const String& rRefString,
1128 SCTAB nDefTab, ScRefAddress& rRefAddress,
1129 const ScAddress::Details& rDetails,
1130 ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1132 bool bRet = false;
1133 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND))
1135 ScAddress aAddr( 0, 0, nDefTab );
1136 USHORT nRes = aAddr.Parse( rRefString, pDoc, rDetails, pExtInfo);
1137 if ( nRes & SCA_VALID )
1139 rRefAddress.Set( aAddr,
1140 ((nRes & SCA_COL_ABSOLUTE) == 0),
1141 ((nRes & SCA_ROW_ABSOLUTE) == 0),
1142 ((nRes & SCA_TAB_ABSOLUTE) == 0));
1143 bRet = true;
1146 return bRet;
1150 bool ConvertDoubleRef( ScDocument* pDoc, const String& rRefString, SCTAB nDefTab,
1151 ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1152 const ScAddress::Details& rDetails,
1153 ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1155 bool bRet = false;
1156 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND))
1158 ScRange aRange( ScAddress( 0, 0, nDefTab));
1159 USHORT nRes = aRange.Parse( rRefString, pDoc, rDetails, pExtInfo);
1160 if ( nRes & SCA_VALID )
1162 rStartRefAddress.Set( aRange.aStart,
1163 ((nRes & SCA_COL_ABSOLUTE) == 0),
1164 ((nRes & SCA_ROW_ABSOLUTE) == 0),
1165 ((nRes & SCA_TAB_ABSOLUTE) == 0));
1166 rEndRefAddress.Set( aRange.aEnd,
1167 ((nRes & SCA_COL2_ABSOLUTE) == 0),
1168 ((nRes & SCA_ROW2_ABSOLUTE) == 0),
1169 ((nRes & SCA_TAB2_ABSOLUTE) == 0));
1170 bRet = true;
1173 return bRet;
1177 USHORT ScAddress::Parse( const String& r, ScDocument* pDoc,
1178 const Details& rDetails,
1179 ExternalInfo* pExtInfo,
1180 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
1182 return lcl_ScAddress_Parse( r.GetBuffer(), pDoc, *this, rDetails, pExtInfo, pExternalLinks );
1186 bool ScRange::Intersects( const ScRange& r ) const
1188 return !(
1189 Min( aEnd.Col(), r.aEnd.Col() ) < Max( aStart.Col(), r.aStart.Col() )
1190 || Min( aEnd.Row(), r.aEnd.Row() ) < Max( aStart.Row(), r.aStart.Row() )
1191 || Min( aEnd.Tab(), r.aEnd.Tab() ) < Max( aStart.Tab(), r.aStart.Tab() )
1196 void ScRange::Justify()
1198 SCCOL nTempCol;
1199 if ( aEnd.Col() < (nTempCol = aStart.Col()) )
1201 aStart.SetCol(aEnd.Col()); aEnd.SetCol(nTempCol);
1203 SCROW nTempRow;
1204 if ( aEnd.Row() < (nTempRow = aStart.Row()) )
1206 aStart.SetRow(aEnd.Row()); aEnd.SetRow(nTempRow);
1208 SCTAB nTempTab;
1209 if ( aEnd.Tab() < (nTempTab = aStart.Tab()) )
1211 aStart.SetTab(aEnd.Tab()); aEnd.SetTab(nTempTab);
1215 void ScRange::ExtendTo( const ScRange& rRange )
1217 DBG_ASSERT( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1218 if( IsValid() )
1220 aStart.SetCol( ::std::min( aStart.Col(), rRange.aStart.Col() ) );
1221 aStart.SetRow( ::std::min( aStart.Row(), rRange.aStart.Row() ) );
1222 aStart.SetTab( ::std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1223 aEnd.SetCol( ::std::max( aEnd.Col(), rRange.aEnd.Col() ) );
1224 aEnd.SetRow( ::std::max( aEnd.Row(), rRange.aEnd.Row() ) );
1225 aEnd.SetTab( ::std::max( aEnd.Tab(), rRange.aEnd.Tab() ) );
1227 else
1228 *this = rRange;
1231 static USHORT
1232 lcl_ScRange_Parse_OOo( ScRange &aRange, const String& r, ScDocument* pDoc, ScAddress::ExternalInfo* pExtInfo = NULL )
1234 USHORT nRes1 = 0, nRes2 = 0;
1235 xub_StrLen nPos = ScGlobal::FindUnquoted( r, ':');
1236 if (nPos != STRING_NOTFOUND)
1238 String aTmp( r );
1239 sal_Unicode* p = aTmp.GetBufferAccess();
1240 p[ nPos ] = 0;
1241 if( (nRes1 = lcl_ScAddress_Parse_OOo( p, pDoc, aRange.aStart, pExtInfo, NULL ) ) != 0 )
1243 aRange.aEnd = aRange.aStart; // sheet must be initialized identical to first sheet
1244 if ( (nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, pDoc, aRange.aEnd, pExtInfo, &aRange ) ) != 0 )
1246 // PutInOrder / Justify
1247 USHORT nMask, nBits1, nBits2;
1248 SCCOL nTempCol;
1249 if ( aRange.aEnd.Col() < (nTempCol = aRange.aStart.Col()) )
1251 aRange.aStart.SetCol(aRange.aEnd.Col()); aRange.aEnd.SetCol(nTempCol);
1252 nMask = (SCA_VALID_COL | SCA_COL_ABSOLUTE);
1253 nBits1 = nRes1 & nMask;
1254 nBits2 = nRes2 & nMask;
1255 nRes1 = (nRes1 & ~nMask) | nBits2;
1256 nRes2 = (nRes2 & ~nMask) | nBits1;
1258 SCROW nTempRow;
1259 if ( aRange.aEnd.Row() < (nTempRow = aRange.aStart.Row()) )
1261 aRange.aStart.SetRow(aRange.aEnd.Row()); aRange.aEnd.SetRow(nTempRow);
1262 nMask = (SCA_VALID_ROW | SCA_ROW_ABSOLUTE);
1263 nBits1 = nRes1 & nMask;
1264 nBits2 = nRes2 & nMask;
1265 nRes1 = (nRes1 & ~nMask) | nBits2;
1266 nRes2 = (nRes2 & ~nMask) | nBits1;
1268 SCTAB nTempTab;
1269 if ( aRange.aEnd.Tab() < (nTempTab = aRange.aStart.Tab()) )
1271 aRange.aStart.SetTab(aRange.aEnd.Tab()); aRange.aEnd.SetTab(nTempTab);
1272 nMask = (SCA_VALID_TAB | SCA_TAB_ABSOLUTE | SCA_TAB_3D);
1273 nBits1 = nRes1 & nMask;
1274 nBits2 = nRes2 & nMask;
1275 nRes1 = (nRes1 & ~nMask) | nBits2;
1276 nRes2 = (nRes2 & ~nMask) | nBits1;
1278 if ( ((nRes1 & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1279 == ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1280 && !(nRes2 & SCA_TAB_3D) )
1281 nRes2 |= SCA_TAB_ABSOLUTE;
1283 else
1284 nRes1 = 0; // #38840# keine Tokens aus halben Sachen
1287 nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID )
1288 | nRes1
1289 | ( ( nRes2 & SCA_BITS ) << 4 );
1290 return nRes1;
1293 USHORT ScRange::Parse( const String& r, ScDocument* pDoc,
1294 const ScAddress::Details& rDetails,
1295 ScAddress::ExternalInfo* pExtInfo,
1296 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
1298 if ( r.Len() <= 0 )
1299 return 0;
1301 switch (rDetails.eConv)
1303 default :
1304 case formula::FormulaGrammar::CONV_OOO:
1305 return lcl_ScRange_Parse_OOo( *this, r, pDoc, pExtInfo );
1307 case formula::FormulaGrammar::CONV_XL_A1:
1308 case formula::FormulaGrammar::CONV_XL_OOX:
1309 return lcl_ScRange_Parse_XL_A1( *this, r.GetBuffer(), pDoc, false, pExtInfo,
1310 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1312 case formula::FormulaGrammar::CONV_XL_R1C1:
1313 return lcl_ScRange_Parse_XL_R1C1( *this, r.GetBuffer(), pDoc, rDetails, false, pExtInfo );
1318 // Accept a full range, or an address
1319 USHORT ScRange::ParseAny( const String& r, ScDocument* pDoc,
1320 const ScAddress::Details& rDetails )
1322 USHORT nRet = Parse( r, pDoc, rDetails );
1323 const USHORT nValid = SCA_VALID | SCA_VALID_COL2 | SCA_VALID_ROW2 |
1324 SCA_VALID_TAB2;
1326 if ( (nRet & nValid) != nValid )
1328 ScAddress aAdr;
1329 nRet = aAdr.Parse( r, pDoc, rDetails );
1330 if ( nRet & SCA_VALID )
1331 aStart = aEnd = aAdr;
1333 return nRet;
1336 // Parse only full row references
1337 USHORT ScRange::ParseCols( const String& rStr, ScDocument* pDoc,
1338 const ScAddress::Details& rDetails )
1340 const sal_Unicode* p = rStr.GetBuffer();
1341 USHORT nRes = 0, ignored = 0;
1343 if( NULL == p )
1344 return 0;
1346 pDoc = NULL; // make compiler shutup we may need this later
1348 switch (rDetails.eConv)
1350 default :
1351 case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1352 case formula::FormulaGrammar::CONV_XL_A1:
1353 case formula::FormulaGrammar::CONV_XL_OOX:
1354 if (NULL != (p = lcl_a1_get_col( p, &aStart, &ignored ) ) )
1356 if( p[0] == ':')
1358 if( NULL != (p = lcl_a1_get_col( p+1, &aEnd, &ignored )))
1360 nRes = SCA_VALID_COL;
1363 else
1365 aEnd = aStart;
1366 nRes = SCA_VALID_COL;
1369 break;
1371 case formula::FormulaGrammar::CONV_XL_R1C1:
1372 if ((p[0] == 'C' || p[0] != 'c') &&
1373 NULL != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored )))
1375 if( p[0] == ':')
1377 if( (p[1] == 'C' || p[1] == 'c') &&
1378 NULL != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored )))
1380 nRes = SCA_VALID_COL;
1383 else
1385 aEnd = aStart;
1386 nRes = SCA_VALID_COL;
1389 break;
1392 return (p != NULL && *p == '\0') ? nRes : 0;
1395 // Parse only full row references
1396 USHORT ScRange::ParseRows( const String& rStr, ScDocument* pDoc,
1397 const ScAddress::Details& rDetails )
1399 const sal_Unicode* p = rStr.GetBuffer();
1400 USHORT nRes = 0, ignored = 0;
1402 if( NULL == p )
1403 return 0;
1405 pDoc = NULL; // make compiler shutup we may need this later
1407 switch (rDetails.eConv)
1409 default :
1410 case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1411 case formula::FormulaGrammar::CONV_XL_A1:
1412 case formula::FormulaGrammar::CONV_XL_OOX:
1413 if (NULL != (p = lcl_a1_get_row( p, &aStart, &ignored ) ) )
1415 if( p[0] == ':')
1417 if( NULL != (p = lcl_a1_get_row( p+1, &aEnd, &ignored )))
1419 nRes = SCA_VALID_COL;
1422 else
1424 aEnd = aStart;
1425 nRes = SCA_VALID_COL;
1428 break;
1430 case formula::FormulaGrammar::CONV_XL_R1C1:
1431 if ((p[0] == 'R' || p[0] != 'r') &&
1432 NULL != (p = lcl_r1c1_get_row( p, rDetails, &aStart, &ignored )))
1434 if( p[0] == ':')
1436 if( (p[1] == 'R' || p[1] == 'r') &&
1437 NULL != (p = lcl_r1c1_get_row( p+1, rDetails, &aEnd, &ignored )))
1439 nRes = SCA_VALID_COL;
1442 else
1444 aEnd = aStart;
1445 nRes = SCA_VALID_COL;
1448 break;
1451 return (p != NULL && *p == '\0') ? nRes : 0;
1454 static inline void
1455 lcl_a1_append_c ( String &r, int nCol, bool bIsAbs )
1457 if( bIsAbs )
1458 r += '$';
1459 ScColToAlpha( r, sal::static_int_cast<SCCOL>(nCol) );
1462 static inline void
1463 lcl_a1_append_r ( String &r, int nRow, bool bIsAbs )
1465 if ( bIsAbs )
1466 r += '$';
1467 r += String::CreateFromInt32( nRow+1 );
1470 static inline void
1471 lcl_r1c1_append_c ( String &r, int nCol, bool bIsAbs,
1472 const ScAddress::Details& rDetails )
1474 r += 'C';
1475 if (bIsAbs)
1477 r += String::CreateFromInt32( nCol + 1 );
1479 else
1481 nCol -= rDetails.nCol;
1482 if (nCol != 0) {
1483 r += '[';
1484 r += String::CreateFromInt32( nCol );
1485 r += ']';
1489 static inline void
1490 lcl_r1c1_append_r ( String &r, int nRow, bool bIsAbs,
1491 const ScAddress::Details& rDetails )
1493 r += 'R';
1494 if (bIsAbs)
1496 r += String::CreateFromInt32( nRow + 1 );
1498 else
1500 nRow -= rDetails.nRow;
1501 if (nRow != 0) {
1502 r += '[';
1503 r += String::CreateFromInt32( nRow );
1504 r += ']';
1509 static String
1510 getFileNameFromDoc( const ScDocument* pDoc )
1512 // TODO : er points at ScGlobal::GetAbsDocName()
1513 // as a better template. Look into it
1514 String sFileName;
1515 SfxObjectShell* pShell;
1517 if( NULL != pDoc &&
1518 NULL != (pShell = pDoc->GetDocumentShell() ) )
1520 uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY );
1521 if( xModel.is() )
1523 if( xModel->getURL().getLength() )
1525 INetURLObject aURL( xModel->getURL() );
1526 sFileName = aURL.GetLastName();
1528 else
1529 sFileName = pShell->GetTitle();
1532 #if 0
1534 ByteString aStr( sFileName, RTL_TEXTENCODING_UTF8 );
1535 aStr.Append(static_cast< char >(0));
1536 std::cerr << "docname \'" << aStr.GetBuffer() << '\'' << std::endl;
1538 #endif
1539 return sFileName;
1542 void ScAddress::Format( String& r, USHORT nFlags, ScDocument* pDoc,
1543 const Details& rDetails) const
1545 r.Erase();
1546 if( nFlags & SCA_VALID )
1547 nFlags |= ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1548 if( pDoc && (nFlags & SCA_VALID_TAB ) )
1550 if ( nTab >= pDoc->GetTableCount() )
1552 r = ScGlobal::GetRscString( STR_NOREF_STR );
1553 return;
1555 // if( nFlags & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ) )
1556 if( nFlags & SCA_TAB_3D )
1558 String aTabName, aDocName;
1559 pDoc->GetName( nTab, aTabName );
1560 // External Reference, same as in ScCompiler::MakeTabStr()
1561 if( aTabName.GetChar(0) == '\'' )
1562 { // "'Doc'#Tab"
1563 xub_StrLen nPos = ScGlobal::FindUnquoted( aTabName, SC_COMPILER_FILE_TAB_SEP);
1564 if (nPos != STRING_NOTFOUND && nPos > 0 && aTabName.GetChar(nPos-1) == '\'')
1566 aDocName = aTabName.Copy( 0, nPos + 1 );
1567 aTabName.Erase( 0, nPos + 1 );
1570 else if( nFlags & SCA_FORCE_DOC )
1572 // VBA has an 'external' flag that forces the addition of the
1573 // tab name _and_ the doc name. The VBA code would be
1574 // needlessly complicated if it constructed an actual external
1575 // reference so we add this somewhat cheesy kludge to force the
1576 // addition of the document name even for non-external references
1577 aDocName = getFileNameFromDoc( pDoc );
1579 ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
1581 switch( rDetails.eConv )
1583 default :
1584 case formula::FormulaGrammar::CONV_OOO:
1585 r += aDocName;
1586 if( nFlags & SCA_TAB_ABSOLUTE )
1587 r += '$';
1588 r += aTabName;
1589 r += '.';
1590 break;
1592 case formula::FormulaGrammar::CONV_XL_A1:
1593 case formula::FormulaGrammar::CONV_XL_R1C1:
1594 case formula::FormulaGrammar::CONV_XL_OOX:
1595 if (aDocName.Len() > 0)
1597 r += '[';
1598 r += aDocName;
1599 r += ']';
1601 r += aTabName;
1602 r += '!';
1603 break;
1607 switch( rDetails.eConv )
1609 default :
1610 case formula::FormulaGrammar::CONV_OOO:
1611 case formula::FormulaGrammar::CONV_XL_A1:
1612 case formula::FormulaGrammar::CONV_XL_OOX:
1613 if( nFlags & SCA_VALID_COL )
1614 lcl_a1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE );
1615 if( nFlags & SCA_VALID_ROW )
1616 lcl_a1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE );
1617 break;
1619 case formula::FormulaGrammar::CONV_XL_R1C1:
1620 if( nFlags & SCA_VALID_ROW )
1621 lcl_r1c1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE, rDetails );
1622 if( nFlags & SCA_VALID_COL )
1623 lcl_r1c1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE, rDetails );
1624 break;
1628 static void
1629 lcl_Split_DocTab( const ScDocument* pDoc, SCTAB nTab,
1630 const ScAddress::Details& rDetails,
1631 USHORT nFlags,
1632 String& rTabName, String& rDocName )
1634 pDoc->GetName( nTab, rTabName );
1635 rDocName.Erase();
1636 #if 0
1638 ByteString aStr(rTabName, RTL_TEXTENCODING_UTF8);
1639 aStr.Append(static_cast< char >(0));
1640 std::cerr << "tabname \'" << aStr.GetBuffer() << '\'' << std::endl;
1642 #endif
1643 // External reference, same as in ScCompiler::MakeTabStr()
1644 if ( rTabName.GetChar(0) == '\'' )
1645 { // "'Doc'#Tab"
1646 xub_StrLen nPos = ScGlobal::FindUnquoted( rTabName, SC_COMPILER_FILE_TAB_SEP);
1647 if (nPos != STRING_NOTFOUND && nPos > 0 && rTabName.GetChar(nPos-1) == '\'')
1649 rDocName = rTabName.Copy( 0, nPos + 1 );
1650 rTabName.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 rDocName = getFileNameFromDoc( pDoc );
1662 ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
1665 static void
1666 lcl_ScRange_Format_XL_Header( String& r, const ScRange& rRange,
1667 USHORT nFlags, ScDocument* pDoc,
1668 const ScAddress::Details& rDetails )
1670 if( nFlags & SCA_TAB_3D )
1672 String aTabName, aDocName;
1673 lcl_Split_DocTab( pDoc, rRange.aStart.Tab(), rDetails, nFlags,
1674 aTabName, aDocName );
1675 if( aDocName.Len() > 0 )
1677 r += '[';
1678 r += aDocName;
1679 r += ']';
1681 r += aTabName;
1683 if( nFlags & SCA_TAB2_3D )
1685 lcl_Split_DocTab( pDoc, rRange.aEnd.Tab(), rDetails, nFlags,
1686 aTabName, aDocName );
1687 r += ':';
1688 r += aTabName;
1690 r += '!';
1694 void ScRange::Format( String& r, USHORT nFlags, ScDocument* pDoc,
1695 const ScAddress::Details& rDetails ) const
1697 r.Erase();
1698 if( !( nFlags & SCA_VALID ) )
1700 r = ScGlobal::GetRscString( STR_NOREF_STR );
1701 return;
1704 #define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask)))
1705 switch( rDetails.eConv ) {
1706 default :
1707 case formula::FormulaGrammar::CONV_OOO: {
1708 BOOL bOneTab = (aStart.Tab() == aEnd.Tab());
1709 if ( !bOneTab )
1710 nFlags |= SCA_TAB_3D;
1711 aStart.Format( r, nFlags, pDoc, rDetails );
1712 if( aStart != aEnd ||
1713 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1714 absrel_differ( nFlags, SCA_ROW_ABSOLUTE ))
1716 String aName;
1717 nFlags = ( nFlags & SCA_VALID ) | ( ( nFlags >> 4 ) & 0x070F );
1718 if ( bOneTab )
1719 pDoc = NULL;
1720 else
1721 nFlags |= SCA_TAB_3D;
1722 aEnd.Format( aName, nFlags, pDoc, rDetails );
1723 r += ':';
1724 r += aName;
1727 break;
1729 case formula::FormulaGrammar::CONV_XL_A1:
1730 case formula::FormulaGrammar::CONV_XL_OOX:
1731 lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1732 if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1734 // Full col refs always require 2 rows (2:2)
1735 lcl_a1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE );
1736 r += ':';
1737 lcl_a1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE );
1739 else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1741 // Full row refs always require 2 cols (A:A)
1742 lcl_a1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE );
1743 r += ':';
1744 lcl_a1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE );
1746 else
1748 lcl_a1_append_c ( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE );
1749 lcl_a1_append_r ( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE );
1750 if( aStart.Col() != aEnd.Col() ||
1751 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1752 aStart.Row() != aEnd.Row() ||
1753 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1754 r += ':';
1755 lcl_a1_append_c ( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE );
1756 lcl_a1_append_r ( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE );
1759 break;
1761 case formula::FormulaGrammar::CONV_XL_R1C1:
1762 lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1763 if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1765 lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails );
1766 if( aStart.Row() != aEnd.Row() ||
1767 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1768 r += ':';
1769 lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails );
1772 else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1774 lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails );
1775 if( aStart.Col() != aEnd.Col() ||
1776 absrel_differ( nFlags, SCA_COL_ABSOLUTE )) {
1777 r += ':';
1778 lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails );
1781 else
1783 lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails );
1784 lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails );
1785 if( aStart.Col() != aEnd.Col() ||
1786 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1787 aStart.Row() != aEnd.Row() ||
1788 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1789 r += ':';
1790 lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails );
1791 lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails );
1795 #undef absrel_differ
1798 bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1800 SCsTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB+1;
1801 dx = Col() + dx;
1802 dy = Row() + dy;
1803 dz = Tab() + dz;
1804 BOOL bValid = TRUE;
1805 if( dx < 0 )
1806 dx = 0, bValid = FALSE;
1807 else if( dx > MAXCOL )
1808 dx = MAXCOL, bValid =FALSE;
1809 if( dy < 0 )
1810 dy = 0, bValid = FALSE;
1811 else if( dy > MAXROW )
1812 dy = MAXROW, bValid =FALSE;
1813 if( dz < 0 )
1814 dz = 0, bValid = FALSE;
1815 else if( dz >= nMaxTab )
1816 dz = nMaxTab-1, bValid =FALSE;
1817 Set( dx, dy, dz );
1818 return bValid;
1822 bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1824 // Einfahces &, damit beides ausgefuehrt wird!!
1825 return aStart.Move( dx, dy, dz, pDoc ) & aEnd.Move( dx, dy, dz, pDoc );
1829 String ScAddress::GetColRowString( bool bAbsolute,
1830 const Details& rDetails ) const
1832 String aString;
1834 switch( rDetails.eConv )
1836 default :
1837 case formula::FormulaGrammar::CONV_OOO:
1838 case formula::FormulaGrammar::CONV_XL_A1:
1839 case formula::FormulaGrammar::CONV_XL_OOX:
1840 if (bAbsolute)
1841 aString.Append( '$' );
1843 ScColToAlpha( aString, nCol);
1845 if ( bAbsolute )
1846 aString.Append( '$' );
1848 aString += String::CreateFromInt32(nRow+1);
1849 break;
1851 case formula::FormulaGrammar::CONV_XL_R1C1:
1852 lcl_r1c1_append_r ( aString, nRow, bAbsolute, rDetails );
1853 lcl_r1c1_append_c ( aString, nCol, bAbsolute, rDetails );
1854 break;
1857 return aString;
1861 String ScRefAddress::GetRefString( ScDocument* pDoc, SCTAB nActTab,
1862 const ScAddress::Details& rDetails ) const
1864 if ( !pDoc )
1865 return EMPTY_STRING;
1866 if ( Tab()+1 > pDoc->GetTableCount() )
1867 return ScGlobal::GetRscString( STR_NOREF_STR );
1869 String aString;
1870 USHORT nFlags = SCA_VALID;
1871 if ( nActTab != Tab() )
1873 nFlags |= SCA_TAB_3D;
1874 if ( !bRelTab )
1875 nFlags |= SCA_TAB_ABSOLUTE;
1877 if ( !bRelCol )
1878 nFlags |= SCA_COL_ABSOLUTE;
1879 if ( !bRelRow )
1880 nFlags |= SCA_ROW_ABSOLUTE;
1882 aAdr.Format( aString, nFlags, pDoc, rDetails );
1884 return aString;
1887 //------------------------------------------------------------------------
1889 void ScColToAlpha( rtl::OUStringBuffer& rBuf, SCCOL nCol )
1891 if (nCol < 26*26)
1893 if (nCol < 26)
1894 rBuf.append( static_cast<sal_Unicode>( 'A' +
1895 static_cast<sal_uInt16>(nCol)));
1896 else
1898 rBuf.append( static_cast<sal_Unicode>( 'A' +
1899 (static_cast<sal_uInt16>(nCol) / 26) - 1));
1900 rBuf.append( static_cast<sal_Unicode>( 'A' +
1901 (static_cast<sal_uInt16>(nCol) % 26)));
1904 else
1906 String aStr;
1907 while (nCol >= 26)
1909 SCCOL nC = nCol % 26;
1910 aStr += static_cast<sal_Unicode>( 'A' +
1911 static_cast<sal_uInt16>(nC));
1912 nCol = sal::static_int_cast<SCCOL>( nCol - nC );
1913 nCol = nCol / 26 - 1;
1915 aStr += static_cast<sal_Unicode>( 'A' +
1916 static_cast<sal_uInt16>(nCol));
1917 aStr.Reverse();
1918 rBuf.append( aStr);
1923 bool AlphaToCol( SCCOL& rCol, const String& rStr)
1925 SCCOL nResult = 0;
1926 xub_StrLen nStop = rStr.Len();
1927 xub_StrLen nPos = 0;
1928 sal_Unicode c;
1929 while (nResult <= MAXCOL && nPos < nStop && (c = rStr.GetChar( nPos)) != 0 &&
1930 CharClass::isAsciiAlpha(c))
1932 if (nPos > 0)
1933 nResult = (nResult + 1) * 26;
1934 nResult += ScGlobal::ToUpperAlpha(c) - 'A';
1935 ++nPos;
1937 bool bOk = (ValidCol(nResult) && nPos > 0);
1938 if (bOk)
1939 rCol = nResult;
1940 return bOk;