diff options
Diffstat (limited to 'sc/source/core/tool/address.cxx')
-rw-r--r-- | sc/source/core/tool/address.cxx | 2039 |
1 files changed, 2039 insertions, 0 deletions
diff --git a/sc/source/core/tool/address.cxx b/sc/source/core/tool/address.cxx new file mode 100644 index 000000000000..16b821a28738 --- /dev/null +++ b/sc/source/core/tool/address.cxx @@ -0,0 +1,2039 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "address.hxx" +#include "global.hxx" +#include "compiler.hxx" +#include "document.hxx" +#include "externalrefmgr.hxx" + +#include "globstr.hrc" +#include <sal/alloca.h> + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sheet/ExternalLinkInfo.hpp> +#include <com/sun/star/sheet/ExternalLinkType.hpp> +#include <sfx2/objsh.hxx> +#include <tools/urlobj.hxx> + +using namespace ::com::sun::star; +using ::rtl::OUString; + +const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 ); + +ScAddress::Details::Details ( const ScDocument* pDoc, + const ScAddress & rAddr ) : + eConv( pDoc->GetAddressConvention() ), + nRow( rAddr.Row() ), + nCol( rAddr.Col() ) +{ +} + + +//////////////////////////////////////////////////////////////////////////// + +#include <iostream> + +/** + * Parse from the opening single quote to the closing single quote. Inside + * the quotes, a single quote character is encoded by double single-quote + * characters. + * + * @param p pointer to the first character to begin parsing. + * @param rName (reference) parsed name within the quotes. If the name is + * empty, either the parsing failed or it's an empty quote. + * + * @return pointer to the character immediately after the closing single + * quote. + */ +static const sal_Unicode* lcl_ParseQuotedName( const sal_Unicode* p, String& rName ) +{ + rName.Erase(); + if (*p != '\'') + return p; + + const sal_Unicode* pStart = p; + sal_Unicode cPrev = 0; + for (++p; *p; ++p) + { + if (*p == '\'') + { + if (cPrev == '\'') + { + // double single-quote equals one single quote. + rName += *p; + cPrev = 0; + continue; + } + } + else if (cPrev == '\'') + // We are past the closing quote. We're done! + return p; + else + rName += *p; + cPrev = *p; + } + rName.Erase(); + return pStart; +} + +static long int +sal_Unicode_strtol ( const sal_Unicode* p, + const sal_Unicode** pEnd ) +{ + long int accum = 0, prev = 0; + bool is_neg = false; + + if( *p == '-' ) + { + is_neg = true; + p++; + } + else if( *p == '+' ) + p++; + + while (CharClass::isAsciiDigit( *p )) + { + accum = accum * 10 + *p - '0'; + if( accum < prev ) + { + *pEnd = NULL; + return 0; + } + prev = accum; + p++; + } + + *pEnd = p; + return is_neg ? -accum : accum; +} + +const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p ) +{ + if ( p ) + { + while( *p == ' ' ) + ++p; + } + return p; +} + +/** Determines the number of sheets an external reference spans and sets + rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding + bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in + cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName + is set to rEndTabName. + @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not + result in the identical file ID. Else <TRUE/>. + */ +static bool lcl_ScRange_External_TabSpan( + ScRange & rRange, + sal_uInt16 & rFlags, + ScAddress::ExternalInfo* pExtInfo, + const String & rExternDocName, + const String & rStartTabName, + const String & rEndTabName, + ScDocument* pDoc ) +{ + if (!rExternDocName.Len()) + return !pExtInfo || !pExtInfo->mbExternal; + + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + if (pRefMgr->isOwnDocument( rExternDocName)) + return !pExtInfo || !pExtInfo->mbExternal; + + sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName); + + if (pExtInfo) + { + if (pExtInfo->mbExternal) + { + if (pExtInfo->mnFileId != nFileId) + return false; + } + else + { + pExtInfo->mbExternal = true; + pExtInfo->maTabName = rStartTabName; + pExtInfo->mnFileId = nFileId; + } + } + + if (!rEndTabName.Len() || rStartTabName == rEndTabName) + { + rRange.aEnd.SetTab( rRange.aStart.Tab()); + return true; + } + + SCsTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName); + if (nSpan == -1) + rFlags &= ~(SCA_VALID_TAB | SCA_VALID_TAB2); + else if (nSpan == 0) + rFlags &= ~SCA_VALID_TAB2; + else if (nSpan >= 1) + rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1); + else // (nSpan < -1) + { + rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1); + if (pExtInfo) + pExtInfo->maTabName = rEndTabName; + } + return true; +} + +/** Returns NULL if the string should be a sheet name, but is invalid. + Returns a pointer to the first character after the sheet name, if there was + any, else pointer to start. + @param pMsoxlQuoteStop + Starting _within_ a quoted name, but still may be 3D; quoted name stops + at pMsoxlQuoteStop + */ +static const sal_Unicode * +lcl_XL_ParseSheetRef( const sal_Unicode* start, + String& rExternTabName, + bool allow_3d, + const sal_Unicode* pMsoxlQuoteStop ) +{ + String aTabName; + const sal_Unicode *p = start; + + // XL only seems to use single quotes for sheet names. + if (pMsoxlQuoteStop) + { + const sal_Unicode* pCurrentStart = p; + while (p < pMsoxlQuoteStop) + { + if (*p == '\'') + { + // We pre-analyzed the quoting, no checks needed here. + if (*++p == '\'') + { + aTabName.Append( pCurrentStart, + sal::static_int_cast<xub_StrLen>( p - pCurrentStart)); + pCurrentStart = ++p; + } + } + else if (*p == ':') + { + break; // while + } + else + ++p; + } + if (pCurrentStart < p) + aTabName.Append( pCurrentStart, sal::static_int_cast<xub_StrLen>( p - pCurrentStart)); + if (!aTabName.Len()) + return NULL; + if (p == pMsoxlQuoteStop) + ++p; // position on ! of ...'!... + if( *p != '!' && ( !allow_3d || *p != ':' ) ) + return (!allow_3d && *p == ':') ? p : start; + } + else if( *p == '\'') + { + p = lcl_ParseQuotedName(p, aTabName); + if (!aTabName.Len()) + return NULL; + } + else + { + bool only_digits = sal_True; + + /* + * Valid: Normal!a1 + * Valid: x.y!a1 + * Invalid: .y!a1 + * + * Some names starting with digits are actually valid, but + * unparse quoted. Things are quite tricky: most sheet names + * starting with a digit are ok, but not those starting with + * "[0-9]*\." or "[0-9]+[eE]". + * + * Valid: 42!a1 + * Valid: 4x!a1 + * Invalid: 1.!a1 + * Invalid: 1e!a1 + */ + while( 1 ) + { + const sal_Unicode uc = *p; + if( CharClass::isAsciiAlpha( uc ) || uc == '_' ) + { + if( only_digits && p != start && + (uc == 'e' || uc == 'E' ) ) + { + p = start; + break; + } + only_digits = false; + p++; + } + else if( CharClass::isAsciiDigit( uc )) + { + p++; + } + else if( uc == '.' ) + { + if( only_digits ) // Valid, except after only digits. + { + p = start; + break; + } + p++; + } + else if (uc > 127) + { + // non ASCII character is allowed. + ++p; + } + else + break; + } + + if( *p != '!' && ( !allow_3d || *p != ':' ) ) + return (!allow_3d && *p == ':') ? p : start; + + aTabName.Append( start, sal::static_int_cast<xub_StrLen>( p - start ) ); + } + + rExternTabName = aTabName; + return p; +} + + +const sal_Unicode* ScRange::Parse_XL_Header( + const sal_Unicode* p, + const ScDocument* pDoc, + String& rExternDocName, + String& rStartTabName, + String& rEndTabName, + sal_uInt16& nFlags, + bool bOnlyAcceptSingle, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) +{ + const sal_Unicode* startTabs, *start = p; + sal_uInt16 nSaveFlags = nFlags; + + // Is this an external reference ? + rStartTabName.Erase(); + rEndTabName.Erase(); + rExternDocName.Erase(); + const sal_Unicode* pMsoxlQuoteStop = NULL; + if (*p == '[') + { + ++p; + // Only single quotes are correct, and a double single quote escapes a + // single quote text inside the quoted text. + if (*p == '\'') + { + p = lcl_ParseQuotedName(p, rExternDocName); + if (!*p || *p != ']' || !rExternDocName.Len()) + { + rExternDocName.Erase(); + return start; + } + } + else + { + // non-quoted file name. + p = ScGlobal::UnicodeStrChr( start+1, ']' ); + if( p == NULL ) + return start; + rExternDocName.Append( start+1, sal::static_int_cast<xub_StrLen>( p-(start+1) ) ); + } + ++p; + + // 1-based, sequence starts with an empty element. + if (pExternalLinks && pExternalLinks->getLength() > 1) + { + // A numeric "document name" is an index into the sequence. + if (CharClass::isAsciiNumeric( rExternDocName)) + { + sal_Int32 i = rExternDocName.ToInt32(); + if (i <= 0 || i >= pExternalLinks->getLength()) + return start; + const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i]; + switch (rInfo.Type) + { + case sheet::ExternalLinkType::DOCUMENT : + { + rtl::OUString aStr; + if (!(rInfo.Data >>= aStr)) + { + OSL_TRACE( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i); + return NULL; + } + rExternDocName = aStr; + } + break; + default: + OSL_TRACE( "ScRange::Parse_XL_Header: unhandled ExternalLinkType %d for index %d", + rInfo.Type, i); + return NULL; + } + } + } + rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, pDoc->GetDocumentShell()); + } + else if (*p == '\'') + { + // Sickness in Excel's ODF msoxl namespace: + // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or + // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11 + // But, 'Sheet1'!B3 would also be a valid! + // Excel does not allow [ and ] characters in sheet names though. + p = lcl_ParseQuotedName(p, rExternDocName); + if (!*p || *p != '!') + { + rExternDocName.Erase(); + return start; + } + if (rExternDocName.Len()) + { + xub_StrLen nOpen = rExternDocName.Search( '['); + if (nOpen == STRING_NOTFOUND) + rExternDocName.Erase(); + else + { + xub_StrLen nClose = rExternDocName.Search( ']', nOpen+1); + if (nClose == STRING_NOTFOUND) + rExternDocName.Erase(); + else + { + rExternDocName.Erase( nClose); + rExternDocName.Erase( nOpen, 1); + pMsoxlQuoteStop = p - 1; // the ' quote char + // There may be embedded escaped quotes, just matching the + // doc name's length may not work. + for (p = start; *p != '['; ++p) + ; + for ( ; *p != ']'; ++p) + ; + ++p; + } + } + } + if (!rExternDocName.Len()) + p = start; + } + + startTabs = p; + p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop); + if( NULL == p ) + return start; // invalid tab + if (bOnlyAcceptSingle && *p == ':') + return NULL; // 3D + if( p != startTabs ) + { + nFlags |= SCA_VALID_TAB | SCA_TAB_3D | SCA_TAB_ABSOLUTE; + if( *p == ':' ) // 3d ref + { + p = lcl_XL_ParseSheetRef( p+1, rEndTabName, false, pMsoxlQuoteStop); + if( p == NULL ) + { + nFlags = nSaveFlags; + return start; // invalid tab + } + nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE; + } + else + { + // If only one sheet is given, the full reference is still valid, + // only the second 3D flag is not set. + nFlags |= SCA_VALID_TAB2 | SCA_TAB2_ABSOLUTE; + aEnd.SetTab( aStart.Tab() ); + } + + if( *p++ != '!' ) + { + nFlags = nSaveFlags; + return start; // syntax error + } + else + p = lcl_eatWhiteSpace( p ); + } + else + { + nFlags |= SCA_VALID_TAB | SCA_VALID_TAB2; + // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. ); + } + + if (rExternDocName.Len()) + { + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + pRefMgr->convertToAbsName( rExternDocName); + } + else + { + // Internal reference. + if (!rStartTabName.Len()) + { + nFlags = nSaveFlags; + return start; + } + + SCTAB nTab; + if (!pDoc->GetTable(rStartTabName, nTab)) + { + // invalid table name. + nFlags &= ~SCA_VALID_TAB; + nTab = -1; + } + + aStart.SetTab(nTab); + aEnd.SetTab(nTab); + + if (rEndTabName.Len()) + { + if (!pDoc->GetTable(rEndTabName, nTab)) + { + // invalid table name. + nFlags &= ~SCA_VALID_TAB2; + nTab = -1; + } + + aEnd.SetTab(nTab); + } + } + return p; +} + + +static const sal_Unicode* +lcl_r1c1_get_col( const sal_Unicode* p, + const ScAddress::Details& rDetails, + ScAddress* pAddr, sal_uInt16* nFlags ) +{ + const sal_Unicode *pEnd; + long int n; + bool isRelative; + + if( p[0] == '\0' ) + return NULL; + + p++; + if( (isRelative = (*p == '[') ) != false ) + p++; + n = sal_Unicode_strtol( p, &pEnd ); + if( NULL == pEnd ) + return NULL; + + if( p == pEnd ) // C is a relative ref with offset 0 + { + if( isRelative ) + return NULL; + n = rDetails.nCol; + } + else if( isRelative ) + { + if( *pEnd != ']' ) + return NULL; + n += rDetails.nCol; + pEnd++; + } + else + { + *nFlags |= SCA_COL_ABSOLUTE; + n--; + } + + if( n < 0 || n >= MAXCOLCOUNT ) + return NULL; + pAddr->SetCol( static_cast<SCCOL>( n ) ); + *nFlags |= SCA_VALID_COL; + + return pEnd; +} +static inline const sal_Unicode* +lcl_r1c1_get_row( const sal_Unicode* p, + const ScAddress::Details& rDetails, + ScAddress* pAddr, sal_uInt16* nFlags ) +{ + const sal_Unicode *pEnd; + long int n; + bool isRelative; + + if( p[0] == '\0' ) + return NULL; + + p++; + if( (isRelative = (*p == '[') ) != false ) + p++; + n = sal_Unicode_strtol( p, &pEnd ); + if( NULL == pEnd ) + return NULL; + + if( p == pEnd ) // R is a relative ref with offset 0 + { + if( isRelative ) + return NULL; + n = rDetails.nRow; + } + else if( isRelative ) + { + if( *pEnd != ']' ) + return NULL; + n += rDetails.nRow; + pEnd++; + } + else + { + *nFlags |= SCA_ROW_ABSOLUTE; + n--; + } + + if( n < 0 || n >= MAXROWCOUNT ) + return NULL; + pAddr->SetRow( static_cast<SCROW>( n ) ); + *nFlags |= SCA_VALID_ROW; + + return pEnd; +} + +static sal_uInt16 +lcl_ScRange_Parse_XL_R1C1( ScRange& r, + const sal_Unicode* p, + ScDocument* pDoc, + const ScAddress::Details& rDetails, + bool bOnlyAcceptSingle, + ScAddress::ExternalInfo* pExtInfo ) +{ + const sal_Unicode* pTmp = NULL; + String aExternDocName, aStartTabName, aEndTabName; + sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB; + // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged. + sal_uInt16 nFlags2 = SCA_VALID_TAB; + + p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName, + aEndTabName, nFlags, bOnlyAcceptSingle, NULL ); + + if (aExternDocName.Len() > 0) + lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName, + aStartTabName, aEndTabName, pDoc); + + if( NULL == p ) + return 0; + + if( *p == 'R' || *p == 'r' ) + { + if( NULL == (p = lcl_r1c1_get_row( p, rDetails, &r.aStart, &nFlags )) ) + goto failed; + + if( *p != 'C' && *p != 'c' ) // full row R# + { + if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) || + NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 ))) + { + // Only the initial row number is given, or the second row + // number is invalid. Fallback to just the initial R + nFlags |= (nFlags << 4); + r.aEnd.SetRow( r.aStart.Row() ); + } + else + { + // Full row range successfully parsed. + nFlags |= (nFlags2 << 4); + p = pTmp; + } + + if (p && p[0] != 0) + { + // any trailing invalid character must invalidate the whole address. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + nFlags |= + SCA_VALID_COL | SCA_VALID_COL2 | + SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE; + r.aStart.SetCol( 0 ); + r.aEnd.SetCol( MAXCOL ); + + return bOnlyAcceptSingle ? 0 : nFlags; + } + else if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags ))) + goto failed; + + if( p[0] != ':' || + (p[1] != 'R' && p[1] != 'r') || + NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )) || + (*pTmp != 'C' && *pTmp != 'c') || + NULL == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 ))) + { + // single cell reference + + if (p && p[0] != 0) + { + // any trailing invalid character must invalidate the whole address. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB); + return nFlags; + } + + return bOnlyAcceptSingle ? nFlags : 0; + } + p = pTmp; + + // double reference + + if (p && p[0] != 0) + { + // any trailing invalid character must invalidate the whole range. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + nFlags |= (nFlags2 << 4); + return bOnlyAcceptSingle ? 0 : nFlags; + } + else if( *p == 'C' || *p == 'c' ) // full col C# + { + if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags ))) + goto failed; + + if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') || + NULL == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, &nFlags2 ))) + { // Fallback to just the initial C + nFlags |= (nFlags << 4); + r.aEnd.SetCol( r.aStart.Col() ); + } + else + { + nFlags |= (nFlags2 << 4); + p = pTmp; + } + + if (p && p[0] != 0) + { + // any trailing invalid character must invalidate the whole address. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + nFlags |= + SCA_VALID_ROW | SCA_VALID_ROW2 | + SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE; + r.aStart.SetRow( 0 ); + r.aEnd.SetRow( MAXROW ); + + return bOnlyAcceptSingle ? 0 : nFlags; + } + +failed : + return 0; +} + +static inline const sal_Unicode* +lcl_a1_get_col( const sal_Unicode* p, ScAddress* pAddr, sal_uInt16* nFlags ) +{ + SCCOL nCol; + + if( *p == '$' ) + *nFlags |= SCA_COL_ABSOLUTE, p++; + + if( !CharClass::isAsciiAlpha( *p ) ) + return NULL; + + nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' ); + while (nCol <= MAXCOL && CharClass::isAsciiAlpha(*p)) + nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' ); + if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) ) + return NULL; + + *nFlags |= SCA_VALID_COL; + pAddr->SetCol( nCol ); + + return p; +} + +static inline const sal_Unicode* +lcl_a1_get_row( const sal_Unicode* p, ScAddress* pAddr, sal_uInt16* nFlags ) +{ + const sal_Unicode *pEnd; + long int n; + + if( *p == '$' ) + *nFlags |= SCA_ROW_ABSOLUTE, p++; + + n = sal_Unicode_strtol( p, &pEnd ) - 1; + if( NULL == pEnd || p == pEnd || n < 0 || n > MAXROW ) + return NULL; + + *nFlags |= SCA_VALID_ROW; + pAddr->SetRow( static_cast<SCROW>(n) ); + + return pEnd; +} + +static sal_uInt16 +lcl_ScRange_Parse_XL_A1( ScRange& r, + const sal_Unicode* p, + ScDocument* pDoc, + bool bOnlyAcceptSingle, + ScAddress::ExternalInfo* pExtInfo, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) +{ + const sal_Unicode* tmp1, *tmp2; + String aExternDocName, aStartTabName, aEndTabName; // for external link table + sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB; + + p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName, + aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks ); + + if (aExternDocName.Len() > 0) + lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName, + aStartTabName, aEndTabName, pDoc); + + if( NULL == p ) + return 0; + + tmp1 = lcl_a1_get_col( p, &r.aStart, &nFlags ); + if( tmp1 == NULL ) // Is it a row only reference 3:5 + { + if( bOnlyAcceptSingle ) // by definition full row refs are ranges + return 0; + + tmp1 = lcl_a1_get_row( p, &r.aStart, &nFlags ); + + tmp1 = lcl_eatWhiteSpace( tmp1 ); + if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2) + return 0; + + tmp1 = lcl_eatWhiteSpace( tmp1 ); + tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 ); + if( !tmp2 ) + return 0; + + r.aStart.SetCol( 0 ); r.aEnd.SetCol( MAXCOL ); + nFlags |= + SCA_VALID_COL | SCA_VALID_COL2 | + SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE; + nFlags |= (nFlags2 << 4); + return nFlags; + } + + tmp2 = lcl_a1_get_row( tmp1, &r.aStart, &nFlags ); + if( tmp2 == NULL ) // check for col only reference F:H + { + if( bOnlyAcceptSingle ) // by definition full col refs are ranges + return 0; + + tmp1 = lcl_eatWhiteSpace( tmp1 ); + if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F) + return 0; + + tmp1 = lcl_eatWhiteSpace( tmp1 ); + tmp2 = lcl_a1_get_col( tmp1, &r.aEnd, &nFlags2 ); + if( !tmp2 ) + return 0; + + r.aStart.SetRow( 0 ); r.aEnd.SetRow( MAXROW ); + nFlags |= + SCA_VALID_ROW | SCA_VALID_ROW2 | + SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE; + nFlags |= (nFlags2 << 4); + return nFlags; + } + + // prepare as if it's a singleton, in case we want to fall back */ + r.aEnd.SetCol( r.aStart.Col() ); + r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header() + + if ( bOnlyAcceptSingle ) + { + if ( *tmp2 == 0 ) + return nFlags; + else + { + // any trailing invalid character must invalidate the address. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB); + return nFlags; + } + } + + tmp2 = lcl_eatWhiteSpace( tmp2 ); + if( *tmp2 != ':' ) + { + // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is + // not. Any trailing invalid character invalidates the range. + if (*tmp2 == 0 && (nFlags & SCA_TAB2_3D)) + { + if (nFlags & SCA_COL_ABSOLUTE) + nFlags |= SCA_COL2_ABSOLUTE; + if (nFlags & SCA_ROW_ABSOLUTE) + nFlags |= SCA_ROW2_ABSOLUTE; + } + else + nFlags &= ~(SCA_VALID | + SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + p = tmp2; + p = lcl_eatWhiteSpace( p+1 ); + tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 ); + if( !tmp1 && !aEndTabName.Len() ) // Probably the aEndTabName was specified after the first range + { + p = lcl_XL_ParseSheetRef( p, aEndTabName, false, NULL ); + if( p ) + { + SCTAB nTab = 0; + if( aEndTabName.Len() && pDoc->GetTable( aEndTabName, nTab ) ) + { + r.aEnd.SetTab( nTab ); + nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE; + } + p = lcl_eatWhiteSpace( p+1 ); + tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 ); + } + } + if( !tmp1 ) // strange, but valid singleton + return nFlags; + + tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 ); + if( !tmp2 ) // strange, but valid singleton + return nFlags; + + if ( *tmp2 != 0 ) + { + // any trailing invalid character must invalidate the range. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + nFlags |= (nFlags2 << 4); + return nFlags; +} + +/** + @param pRange pointer to range where rAddr effectively is *pRange->aEnd, + used in conjunction with pExtInfo to determine the tab span + of a 3D reference. + */ +static sal_uInt16 +lcl_ScAddress_Parse_OOo( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr, + ScAddress::ExternalInfo* pExtInfo = NULL, ScRange* pRange = NULL ) +{ + sal_uInt16 nRes = 0; + String aDocName; // der pure Dokumentenname + String aTab; + bool bExtDoc = false; + bool bExtDocInherited = false; + const ScAddress aCurPos(rAddr); + + // Lets see if this is a reference to something in an external file. A + // document name is always quoted and has a trailing #. + if (*p == '\'') + { + const sal_Unicode* pStart = p; + p = lcl_ParseQuotedName(p, aDocName); + if (*p++ == SC_COMPILER_FILE_TAB_SEP) + bExtDoc = true; + else + // This is not a document name. Perhaps a quoted relative table + // name. + p = pStart; + } + else if (pExtInfo && pExtInfo->mbExternal) + { + // This is an external reference. + bExtDoc = bExtDocInherited = true; + } + + SCCOL nCol = 0; + SCROW nRow = 0; + SCTAB nTab = 0; + sal_uInt16 nBits = SCA_VALID_TAB; + const sal_Unicode* q; + if ( ScGlobal::FindUnquoted( p, '.') ) + { + nRes |= SCA_TAB_3D; + if ( bExtDoc ) + nRes |= SCA_TAB_ABSOLUTE; + if (*p == '$') + nRes |= SCA_TAB_ABSOLUTE, p++; + + if (*p == '\'') + { + // Tokens that start at ' can have anything in them until a final + // ' but '' marks an escaped '. We've earlier guaranteed that a + // string containing '' will be surrounded by '. + p = lcl_ParseQuotedName(p, aTab); + } + else + { + while (*p) + { + if( *p == '.') + break; + + if( *p == '\'' ) + { + p++; break; + } + aTab += *p++; + } + } + if( *p++ != '.' ) + nBits = 0; + + if (!bExtDoc && (!pDoc || !pDoc->GetTable( aTab, nTab ))) + { + // Specified table name is not found in this document. Assume this is an external document. + bExtDoc = true; + aDocName = aTab; + xub_StrLen n = aTab.SearchBackward('.'); + if (n != STRING_NOTFOUND && n > 0) + // Extension found. Strip it. + aTab.Erase(n); + else + // No extension found. This is probably not an external document. + nBits = 0; + } + } + else + { + if (bExtDoc && !bExtDocInherited) + return nRes; // After a document a sheet must follow. + nTab = rAddr.Tab(); + } + nRes |= nBits; + + q = p; + if (*p) + { + nBits = SCA_VALID_COL; + if (*p == '$') + nBits |= SCA_COL_ABSOLUTE, p++; + + if (CharClass::isAsciiAlpha( *p )) + { + nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' ); + while (nCol < MAXCOL && CharClass::isAsciiAlpha(*p)) + nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' ); + } + else + nBits = 0; + + if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) ) + nBits = 0; + nRes |= nBits; + if( !nBits ) + p = q; + } + + q = p; + if (*p) + { + nBits = SCA_VALID_ROW; + if (*p == '$') + nBits |= SCA_ROW_ABSOLUTE, p++; + if( !CharClass::isAsciiDigit( *p ) ) + { + nBits = 0; + nRow = SCROW(-1); + } + else + { + String aTmp( p ); + long n = aTmp.ToInt32() - 1; + while (CharClass::isAsciiDigit( *p )) + p++; + if( n < 0 || n > MAXROW ) + nBits = 0; + nRow = static_cast<SCROW>(n); + } + nRes |= nBits; + if( !nBits ) + p = q; + } + + rAddr.Set( nCol, nRow, nTab ); + + if (!*p && bExtDoc) + { + if (!pDoc) + nRes = 0; + else + { + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + + // Need document name if inherited. + if (bExtDocInherited) + { + const String* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId); + if (pFileName) + aDocName = *pFileName; + else + nRes = 0; + } + pRefMgr->convertToAbsName(aDocName); + + if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName)) + { + if (!pDoc->GetTable( aTab, nTab )) + nRes = 0; + else + { + rAddr.SetTab( nTab); + nRes |= SCA_VALID_TAB; + } + } + else + { + if (!pExtInfo) + nRes = 0; + else + { + if (!pExtInfo->mbExternal) + { + sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName); + + pExtInfo->mbExternal = true; + pExtInfo->maTabName = aTab; + pExtInfo->mnFileId = nFileId; + + if (pRefMgr->getSingleRefToken(nFileId, aTab, + ScAddress(nCol, nRow, 0), NULL, + &nTab).get()) + { + rAddr.SetTab( nTab); + nRes |= SCA_VALID_TAB; + } + else + nRes = 0; + } + else + { + // This is a call for the second part of the reference, + // we must have the range to adapt tab span. + if (!pRange) + nRes = 0; + else + { + sal_uInt16 nFlags = nRes | SCA_VALID_TAB2; + if (!lcl_ScRange_External_TabSpan( *pRange, nFlags, + pExtInfo, aDocName, + pExtInfo->maTabName, aTab, pDoc)) + nRes &= ~SCA_VALID_TAB; + else + { + if (nFlags & SCA_VALID_TAB2) + { + rAddr.SetTab( pRange->aEnd.Tab()); + nRes |= SCA_VALID_TAB; + } + else + nRes &= ~SCA_VALID_TAB; + } + } + } + } + } + } + } + + if ( !(nRes & SCA_VALID_ROW) && (nRes & SCA_VALID_COL) + && !( (nRes & SCA_TAB_3D) && (nRes & SCA_VALID_TAB)) ) + { // no Row, no Tab, but Col => DM (...), B (...) et al + nRes = 0; + } + if( !*p ) + { + sal_uInt16 nMask = nRes & ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ); + if( nMask == ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ) ) + nRes |= SCA_VALID; + } + else + nRes = 0; + return nRes; +} + +static sal_uInt16 +lcl_ScAddress_Parse ( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo = NULL, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL ) +{ + if( !*p ) + return 0; + + switch (rDetails.eConv) + { + default : + case formula::FormulaGrammar::CONV_OOO: + { + return lcl_ScAddress_Parse_OOo( p, pDoc, rAddr, pExtInfo, NULL ); + } + + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + { + ScRange r = rAddr; + sal_uInt16 nFlags = lcl_ScRange_Parse_XL_A1( r, p, pDoc, true, pExtInfo, + (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) ); + rAddr = r.aStart; + return nFlags; + } + case formula::FormulaGrammar::CONV_XL_R1C1: + { + ScRange r = rAddr; + sal_uInt16 nFlags = lcl_ScRange_Parse_XL_R1C1( r, p, pDoc, rDetails, true, pExtInfo ); + rAddr = r.aStart; + return nFlags; + } + } +} + + +bool ConvertSingleRef( ScDocument* pDoc, const String& rRefString, + SCTAB nDefTab, ScRefAddress& rRefAddress, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) +{ + bool bRet = false; + if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND)) + { + ScAddress aAddr( 0, 0, nDefTab ); + sal_uInt16 nRes = aAddr.Parse( rRefString, pDoc, rDetails, pExtInfo); + if ( nRes & SCA_VALID ) + { + rRefAddress.Set( aAddr, + ((nRes & SCA_COL_ABSOLUTE) == 0), + ((nRes & SCA_ROW_ABSOLUTE) == 0), + ((nRes & SCA_TAB_ABSOLUTE) == 0)); + bRet = true; + } + } + return bRet; +} + + +bool ConvertDoubleRef( ScDocument* pDoc, const String& rRefString, SCTAB nDefTab, + ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) +{ + bool bRet = false; + if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND)) + { + ScRange aRange( ScAddress( 0, 0, nDefTab)); + sal_uInt16 nRes = aRange.Parse( rRefString, pDoc, rDetails, pExtInfo); + if ( nRes & SCA_VALID ) + { + rStartRefAddress.Set( aRange.aStart, + ((nRes & SCA_COL_ABSOLUTE) == 0), + ((nRes & SCA_ROW_ABSOLUTE) == 0), + ((nRes & SCA_TAB_ABSOLUTE) == 0)); + rEndRefAddress.Set( aRange.aEnd, + ((nRes & SCA_COL2_ABSOLUTE) == 0), + ((nRes & SCA_ROW2_ABSOLUTE) == 0), + ((nRes & SCA_TAB2_ABSOLUTE) == 0)); + bRet = true; + } + } + return bRet; +} + + +sal_uInt16 ScAddress::Parse( const String& r, ScDocument* pDoc, + const Details& rDetails, + ExternalInfo* pExtInfo, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) +{ + return lcl_ScAddress_Parse( r.GetBuffer(), pDoc, *this, rDetails, pExtInfo, pExternalLinks ); +} + + +bool ScRange::Intersects( const ScRange& r ) const +{ + return !( + Min( aEnd.Col(), r.aEnd.Col() ) < Max( aStart.Col(), r.aStart.Col() ) + || Min( aEnd.Row(), r.aEnd.Row() ) < Max( aStart.Row(), r.aStart.Row() ) + || Min( aEnd.Tab(), r.aEnd.Tab() ) < Max( aStart.Tab(), r.aStart.Tab() ) + ); +} + + +void ScRange::Justify() +{ + SCCOL nTempCol; + if ( aEnd.Col() < (nTempCol = aStart.Col()) ) + { + aStart.SetCol(aEnd.Col()); aEnd.SetCol(nTempCol); + } + SCROW nTempRow; + if ( aEnd.Row() < (nTempRow = aStart.Row()) ) + { + aStart.SetRow(aEnd.Row()); aEnd.SetRow(nTempRow); + } + SCTAB nTempTab; + if ( aEnd.Tab() < (nTempTab = aStart.Tab()) ) + { + aStart.SetTab(aEnd.Tab()); aEnd.SetTab(nTempTab); + } +} + +void ScRange::ExtendTo( const ScRange& rRange ) +{ + DBG_ASSERT( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" ); + if( IsValid() ) + { + aStart.SetCol( ::std::min( aStart.Col(), rRange.aStart.Col() ) ); + aStart.SetRow( ::std::min( aStart.Row(), rRange.aStart.Row() ) ); + aStart.SetTab( ::std::min( aStart.Tab(), rRange.aStart.Tab() ) ); + aEnd.SetCol( ::std::max( aEnd.Col(), rRange.aEnd.Col() ) ); + aEnd.SetRow( ::std::max( aEnd.Row(), rRange.aEnd.Row() ) ); + aEnd.SetTab( ::std::max( aEnd.Tab(), rRange.aEnd.Tab() ) ); + } + else + *this = rRange; +} + +static sal_uInt16 +lcl_ScRange_Parse_OOo( ScRange &aRange, const String& r, ScDocument* pDoc, ScAddress::ExternalInfo* pExtInfo = NULL ) +{ + sal_uInt16 nRes1 = 0, nRes2 = 0; + xub_StrLen nPos = ScGlobal::FindUnquoted( r, ':'); + if (nPos != STRING_NOTFOUND) + { + String aTmp( r ); + sal_Unicode* p = aTmp.GetBufferAccess(); + p[ nPos ] = 0; + if( (nRes1 = lcl_ScAddress_Parse_OOo( p, pDoc, aRange.aStart, pExtInfo, NULL ) ) != 0 ) + { + aRange.aEnd = aRange.aStart; // sheet must be initialized identical to first sheet + if ( (nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, pDoc, aRange.aEnd, pExtInfo, &aRange ) ) != 0 ) + { + // PutInOrder / Justify + sal_uInt16 nMask, nBits1, nBits2; + SCCOL nTempCol; + if ( aRange.aEnd.Col() < (nTempCol = aRange.aStart.Col()) ) + { + aRange.aStart.SetCol(aRange.aEnd.Col()); aRange.aEnd.SetCol(nTempCol); + nMask = (SCA_VALID_COL | SCA_COL_ABSOLUTE); + nBits1 = nRes1 & nMask; + nBits2 = nRes2 & nMask; + nRes1 = (nRes1 & ~nMask) | nBits2; + nRes2 = (nRes2 & ~nMask) | nBits1; + } + SCROW nTempRow; + if ( aRange.aEnd.Row() < (nTempRow = aRange.aStart.Row()) ) + { + aRange.aStart.SetRow(aRange.aEnd.Row()); aRange.aEnd.SetRow(nTempRow); + nMask = (SCA_VALID_ROW | SCA_ROW_ABSOLUTE); + nBits1 = nRes1 & nMask; + nBits2 = nRes2 & nMask; + nRes1 = (nRes1 & ~nMask) | nBits2; + nRes2 = (nRes2 & ~nMask) | nBits1; + } + SCTAB nTempTab; + if ( aRange.aEnd.Tab() < (nTempTab = aRange.aStart.Tab()) ) + { + aRange.aStart.SetTab(aRange.aEnd.Tab()); aRange.aEnd.SetTab(nTempTab); + nMask = (SCA_VALID_TAB | SCA_TAB_ABSOLUTE | SCA_TAB_3D); + nBits1 = nRes1 & nMask; + nBits2 = nRes2 & nMask; + nRes1 = (nRes1 & ~nMask) | nBits2; + nRes2 = (nRes2 & ~nMask) | nBits1; + } + if ( ((nRes1 & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D )) + == ( SCA_TAB_ABSOLUTE | SCA_TAB_3D )) + && !(nRes2 & SCA_TAB_3D) ) + nRes2 |= SCA_TAB_ABSOLUTE; + } + else + nRes1 = 0; // keine Tokens aus halben Sachen + } + } + nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID ) + | nRes1 + | ( ( nRes2 & SCA_BITS ) << 4 ); + return nRes1; +} + +sal_uInt16 ScRange::Parse( const String& r, ScDocument* pDoc, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) +{ + if ( r.Len() <= 0 ) + return 0; + + switch (rDetails.eConv) + { + default : + case formula::FormulaGrammar::CONV_OOO: + return lcl_ScRange_Parse_OOo( *this, r, pDoc, pExtInfo ); + + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + return lcl_ScRange_Parse_XL_A1( *this, r.GetBuffer(), pDoc, false, pExtInfo, + (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) ); + + case formula::FormulaGrammar::CONV_XL_R1C1: + return lcl_ScRange_Parse_XL_R1C1( *this, r.GetBuffer(), pDoc, rDetails, false, pExtInfo ); + } +} + + +// Accept a full range, or an address +sal_uInt16 ScRange::ParseAny( const String& r, ScDocument* pDoc, + const ScAddress::Details& rDetails ) +{ + sal_uInt16 nRet = Parse( r, pDoc, rDetails ); + const sal_uInt16 nValid = SCA_VALID | SCA_VALID_COL2 | SCA_VALID_ROW2 | + SCA_VALID_TAB2; + + if ( (nRet & nValid) != nValid ) + { + ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number + nRet = aAdr.Parse( r, pDoc, rDetails ); + if ( nRet & SCA_VALID ) + aStart = aEnd = aAdr; + } + return nRet; +} + +// Parse only full row references +sal_uInt16 ScRange::ParseCols( const String& rStr, ScDocument* pDoc, + const ScAddress::Details& rDetails ) +{ + const sal_Unicode* p = rStr.GetBuffer(); + sal_uInt16 nRes = 0, ignored = 0; + + if( NULL == p ) + return 0; + + (void)pDoc; // make compiler shutup we may need this later + + switch (rDetails.eConv) + { + default : + case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + if (NULL != (p = lcl_a1_get_col( p, &aStart, &ignored ) ) ) + { + if( p[0] == ':') + { + if( NULL != (p = lcl_a1_get_col( p+1, &aEnd, &ignored ))) + { + nRes = SCA_VALID_COL; + } + } + else + { + aEnd = aStart; + nRes = SCA_VALID_COL; + } + } + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + if ((p[0] == 'C' || p[0] != 'c') && + NULL != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored ))) + { + if( p[0] == ':') + { + if( (p[1] == 'C' || p[1] == 'c') && + NULL != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored ))) + { + nRes = SCA_VALID_COL; + } + } + else + { + aEnd = aStart; + nRes = SCA_VALID_COL; + } + } + break; + } + + return (p != NULL && *p == '\0') ? nRes : 0; +} + +// Parse only full row references +sal_uInt16 ScRange::ParseRows( const String& rStr, ScDocument* pDoc, + const ScAddress::Details& rDetails ) +{ + const sal_Unicode* p = rStr.GetBuffer(); + sal_uInt16 nRes = 0, ignored = 0; + + if( NULL == p ) + return 0; + + (void)pDoc; // make compiler shutup we may need this later + + switch (rDetails.eConv) + { + default : + case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + if (NULL != (p = lcl_a1_get_row( p, &aStart, &ignored ) ) ) + { + if( p[0] == ':') + { + if( NULL != (p = lcl_a1_get_row( p+1, &aEnd, &ignored ))) + { + nRes = SCA_VALID_COL; + } + } + else + { + aEnd = aStart; + nRes = SCA_VALID_COL; + } + } + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + if ((p[0] == 'R' || p[0] != 'r') && + NULL != (p = lcl_r1c1_get_row( p, rDetails, &aStart, &ignored ))) + { + if( p[0] == ':') + { + if( (p[1] == 'R' || p[1] == 'r') && + NULL != (p = lcl_r1c1_get_row( p+1, rDetails, &aEnd, &ignored ))) + { + nRes = SCA_VALID_COL; + } + } + else + { + aEnd = aStart; + nRes = SCA_VALID_COL; + } + } + break; + } + + return (p != NULL && *p == '\0') ? nRes : 0; +} + +static inline void +lcl_a1_append_c ( String &r, int nCol, bool bIsAbs ) +{ + if( bIsAbs ) + r += '$'; + ScColToAlpha( r, sal::static_int_cast<SCCOL>(nCol) ); +} + +static inline void +lcl_a1_append_r ( String &r, int nRow, bool bIsAbs ) +{ + if ( bIsAbs ) + r += '$'; + r += String::CreateFromInt32( nRow+1 ); +} + +static inline void +lcl_r1c1_append_c ( String &r, int nCol, bool bIsAbs, + const ScAddress::Details& rDetails ) +{ + r += 'C'; + if (bIsAbs) + { + r += String::CreateFromInt32( nCol + 1 ); + } + else + { + nCol -= rDetails.nCol; + if (nCol != 0) { + r += '['; + r += String::CreateFromInt32( nCol ); + r += ']'; + } + } +} +static inline void +lcl_r1c1_append_r ( String &r, int nRow, bool bIsAbs, + const ScAddress::Details& rDetails ) +{ + r += 'R'; + if (bIsAbs) + { + r += String::CreateFromInt32( nRow + 1 ); + } + else + { + nRow -= rDetails.nRow; + if (nRow != 0) { + r += '['; + r += String::CreateFromInt32( nRow ); + r += ']'; + } + } +} + +static String +getFileNameFromDoc( const ScDocument* pDoc ) +{ + // TODO : er points at ScGlobal::GetAbsDocName() + // as a better template. Look into it + String sFileName; + SfxObjectShell* pShell; + + if( NULL != pDoc && + NULL != (pShell = pDoc->GetDocumentShell() ) ) + { + uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY ); + if( xModel.is() ) + { + if( xModel->getURL().getLength() ) + { + INetURLObject aURL( xModel->getURL() ); + sFileName = aURL.GetLastName(); + } + else + sFileName = pShell->GetTitle(); + } + } + return sFileName; +} + +void ScAddress::Format( OUString& r, sal_uInt16 nFlags, ScDocument* pDoc, + const Details& rDetails) const +{ + String aStr; + Format(aStr, nFlags, pDoc, rDetails); + r = aStr; +} + +void ScAddress::Format( String& r, sal_uInt16 nFlags, ScDocument* pDoc, + const Details& rDetails) const +{ + r.Erase(); + if( nFlags & SCA_VALID ) + nFlags |= ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ); + if( pDoc && (nFlags & SCA_VALID_TAB ) ) + { + if ( nTab >= pDoc->GetTableCount() ) + { + r = ScGlobal::GetRscString( STR_NOREF_STR ); + return; + } + if( nFlags & SCA_TAB_3D ) + { + String aTabName, aDocName; + pDoc->GetName( nTab, aTabName ); + // External Reference, same as in ScCompiler::MakeTabStr() + if( aTabName.GetChar(0) == '\'' ) + { // "'Doc'#Tab" + xub_StrLen nPos = ScGlobal::FindUnquoted( aTabName, SC_COMPILER_FILE_TAB_SEP); + if (nPos != STRING_NOTFOUND && nPos > 0 && aTabName.GetChar(nPos-1) == '\'') + { + aDocName = aTabName.Copy( 0, nPos + 1 ); + aTabName.Erase( 0, nPos + 1 ); + } + } + else if( nFlags & SCA_FORCE_DOC ) + { + // VBA has an 'external' flag that forces the addition of the + // tab name _and_ the doc name. The VBA code would be + // needlessly complicated if it constructed an actual external + // reference so we add this somewhat cheesy kludge to force the + // addition of the document name even for non-external references + aDocName = getFileNameFromDoc( pDoc ); + } + ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv); + + switch( rDetails.eConv ) + { + default : + case formula::FormulaGrammar::CONV_OOO: + r += aDocName; + if( nFlags & SCA_TAB_ABSOLUTE ) + r += '$'; + r += aTabName; + r += '.'; + break; + + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_R1C1: + case formula::FormulaGrammar::CONV_XL_OOX: + if (aDocName.Len() > 0) + { + r += '['; + r += aDocName; + r += ']'; + } + r += aTabName; + r += '!'; + break; + } + } + } + switch( rDetails.eConv ) + { + default : + case formula::FormulaGrammar::CONV_OOO: + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + if( nFlags & SCA_VALID_COL ) + lcl_a1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE ); + if( nFlags & SCA_VALID_ROW ) + lcl_a1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE ); + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + if( nFlags & SCA_VALID_ROW ) + lcl_r1c1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE, rDetails ); + if( nFlags & SCA_VALID_COL ) + lcl_r1c1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE, rDetails ); + break; + } +} + +static void +lcl_Split_DocTab( const ScDocument* pDoc, SCTAB nTab, + const ScAddress::Details& rDetails, + sal_uInt16 nFlags, + String& rTabName, String& rDocName ) +{ + pDoc->GetName( nTab, rTabName ); + rDocName.Erase(); + // External reference, same as in ScCompiler::MakeTabStr() + if ( rTabName.GetChar(0) == '\'' ) + { // "'Doc'#Tab" + xub_StrLen nPos = ScGlobal::FindUnquoted( rTabName, SC_COMPILER_FILE_TAB_SEP); + if (nPos != STRING_NOTFOUND && nPos > 0 && rTabName.GetChar(nPos-1) == '\'') + { + rDocName = rTabName.Copy( 0, nPos + 1 ); + rTabName.Erase( 0, nPos + 1 ); + } + } + else if( nFlags & SCA_FORCE_DOC ) + { + // VBA has an 'external' flag that forces the addition of the + // tab name _and_ the doc name. The VBA code would be + // needlessly complicated if it constructed an actual external + // reference so we add this somewhat cheesy kludge to force the + // addition of the document name even for non-external references + rDocName = getFileNameFromDoc( pDoc ); + } + ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv); +} + +static void +lcl_ScRange_Format_XL_Header( String& r, const ScRange& rRange, + sal_uInt16 nFlags, ScDocument* pDoc, + const ScAddress::Details& rDetails ) +{ + if( nFlags & SCA_TAB_3D ) + { + String aTabName, aDocName; + lcl_Split_DocTab( pDoc, rRange.aStart.Tab(), rDetails, nFlags, + aTabName, aDocName ); + if( aDocName.Len() > 0 ) + { + r += '['; + r += aDocName; + r += ']'; + } + r += aTabName; + + if( nFlags & SCA_TAB2_3D ) + { + lcl_Split_DocTab( pDoc, rRange.aEnd.Tab(), rDetails, nFlags, + aTabName, aDocName ); + r += ':'; + r += aTabName; + } + r += '!'; + } +} + +void ScRange::Format( String& r, sal_uInt16 nFlags, ScDocument* pDoc, + const ScAddress::Details& rDetails ) const +{ + r.Erase(); + if( !( nFlags & SCA_VALID ) ) + { + r = ScGlobal::GetRscString( STR_NOREF_STR ); + return; + } + +#define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask))) + switch( rDetails.eConv ) { + default : + case formula::FormulaGrammar::CONV_OOO: { + sal_Bool bOneTab = (aStart.Tab() == aEnd.Tab()); + if ( !bOneTab ) + nFlags |= SCA_TAB_3D; + aStart.Format( r, nFlags, pDoc, rDetails ); + if( aStart != aEnd || + absrel_differ( nFlags, SCA_COL_ABSOLUTE ) || + absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) + { + String aName; + nFlags = ( nFlags & SCA_VALID ) | ( ( nFlags >> 4 ) & 0x070F ); + if ( bOneTab ) + pDoc = NULL; + else + nFlags |= SCA_TAB_3D; + aEnd.Format( aName, nFlags, pDoc, rDetails ); + r += ':'; + r += aName; + } + } + break; + + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails ); + if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL ) + { + // Full col refs always require 2 rows (2:2) + lcl_a1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE ); + r += ':'; + lcl_a1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE ); + } + else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW ) + { + // Full row refs always require 2 cols (A:A) + lcl_a1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE ); + r += ':'; + lcl_a1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE ); + } + else + { + lcl_a1_append_c ( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE ); + lcl_a1_append_r ( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE ); + if( aStart.Col() != aEnd.Col() || + absrel_differ( nFlags, SCA_COL_ABSOLUTE ) || + aStart.Row() != aEnd.Row() || + absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) { + r += ':'; + lcl_a1_append_c ( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE ); + lcl_a1_append_r ( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE ); + } + } + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails ); + if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL ) + { + lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails ); + if( aStart.Row() != aEnd.Row() || + absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) { + r += ':'; + lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails ); + } + } + else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW ) + { + lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails ); + if( aStart.Col() != aEnd.Col() || + absrel_differ( nFlags, SCA_COL_ABSOLUTE )) { + r += ':'; + lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails ); + } + } + else + { + lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails ); + lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails ); + if( aStart.Col() != aEnd.Col() || + absrel_differ( nFlags, SCA_COL_ABSOLUTE ) || + aStart.Row() != aEnd.Row() || + absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) { + r += ':'; + lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails ); + lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails ); + } + } + } +#undef absrel_differ +} + +void ScRange::Format( OUString& r, sal_uInt16 nFlags, ScDocument* pDoc, + const ScAddress::Details& rDetails ) const +{ + String aStr; + Format(aStr, nFlags, pDoc, rDetails); + r = aStr; +} + +bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc ) +{ + SCsTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB+1; + dx = Col() + dx; + dy = Row() + dy; + dz = Tab() + dz; + sal_Bool bValid = sal_True; + if( dx < 0 ) + dx = 0, bValid = false; + else if( dx > MAXCOL ) + dx = MAXCOL, bValid =false; + if( dy < 0 ) + dy = 0, bValid = false; + else if( dy > MAXROW ) + dy = MAXROW, bValid =false; + if( dz < 0 ) + dz = 0, bValid = false; + else if( dz >= nMaxTab ) + dz = nMaxTab-1, bValid =false; + Set( dx, dy, dz ); + return bValid; +} + + +bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc ) +{ + // Einfahces &, damit beides ausgefuehrt wird!! + return aStart.Move( dx, dy, dz, pDoc ) & aEnd.Move( dx, dy, dz, pDoc ); +} + + +String ScAddress::GetColRowString( bool bAbsolute, + const Details& rDetails ) const +{ + String aString; + + switch( rDetails.eConv ) + { + default : + case formula::FormulaGrammar::CONV_OOO: + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + if (bAbsolute) + aString.Append( '$' ); + + ScColToAlpha( aString, nCol); + + if ( bAbsolute ) + aString.Append( '$' ); + + aString += String::CreateFromInt32(nRow+1); + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + lcl_r1c1_append_r ( aString, nRow, bAbsolute, rDetails ); + lcl_r1c1_append_c ( aString, nCol, bAbsolute, rDetails ); + break; + } + + return aString; +} + + +String ScRefAddress::GetRefString( ScDocument* pDoc, SCTAB nActTab, + const ScAddress::Details& rDetails ) const +{ + if ( !pDoc ) + return EMPTY_STRING; + if ( Tab()+1 > pDoc->GetTableCount() ) + return ScGlobal::GetRscString( STR_NOREF_STR ); + + String aString; + sal_uInt16 nFlags = SCA_VALID; + if ( nActTab != Tab() ) + { + nFlags |= SCA_TAB_3D; + if ( !bRelTab ) + nFlags |= SCA_TAB_ABSOLUTE; + } + if ( !bRelCol ) + nFlags |= SCA_COL_ABSOLUTE; + if ( !bRelRow ) + nFlags |= SCA_ROW_ABSOLUTE; + + aAdr.Format( aString, nFlags, pDoc, rDetails ); + + return aString; +} + +//------------------------------------------------------------------------ + +void ScColToAlpha( rtl::OUStringBuffer& rBuf, SCCOL nCol ) +{ + if (nCol < 26*26) + { + if (nCol < 26) + rBuf.append( static_cast<sal_Unicode>( 'A' + + static_cast<sal_uInt16>(nCol))); + else + { + rBuf.append( static_cast<sal_Unicode>( 'A' + + (static_cast<sal_uInt16>(nCol) / 26) - 1)); + rBuf.append( static_cast<sal_Unicode>( 'A' + + (static_cast<sal_uInt16>(nCol) % 26))); + } + } + else + { + String aStr; + while (nCol >= 26) + { + SCCOL nC = nCol % 26; + aStr += static_cast<sal_Unicode>( 'A' + + static_cast<sal_uInt16>(nC)); + nCol = sal::static_int_cast<SCCOL>( nCol - nC ); + nCol = nCol / 26 - 1; + } + aStr += static_cast<sal_Unicode>( 'A' + + static_cast<sal_uInt16>(nCol)); + aStr.Reverse(); + rBuf.append( aStr); + } +} + + +bool AlphaToCol( SCCOL& rCol, const String& rStr) +{ + SCCOL nResult = 0; + xub_StrLen nStop = rStr.Len(); + xub_StrLen nPos = 0; + sal_Unicode c; + while (nResult <= MAXCOL && nPos < nStop && (c = rStr.GetChar( nPos)) != 0 && + CharClass::isAsciiAlpha(c)) + { + if (nPos > 0) + nResult = (nResult + 1) * 26; + nResult += ScGlobal::ToUpperAlpha(c) - 'A'; + ++nPos; + } + bool bOk = (ValidCol(nResult) && nPos > 0); + if (bOk) + rCol = nResult; + return bOk; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |