/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the 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 #include #include #include #include #include #include using namespace ::com::sun::star; //////////////////////////////////////////////////////////////////////////// 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() ) { } //UNUSED2009-05 void ScAddress::Details::SetPos ( const ScDocument* pDoc, //UNUSED2009-05 const ScAddress & rAddr ) //UNUSED2009-05 { //UNUSED2009-05 nRow = rAddr.Row(); //UNUSED2009-05 nCol = rAddr.Col(); //UNUSED2009-05 eConv = pDoc->GetAddressConvention(); //UNUSED2009-05 } //////////////////////////////////////////////////////////////////////////// #include /** * 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 if pExtInfo is already filled and rExternDocName does not result in the identical file ID. Else . */ 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( p - pCurrentStart)); pCurrentStart = ++p; } } else if (*p == ':') { break; // while } else ++p; } if (pCurrentStart < p) aTabName.Append( pCurrentStart, sal::static_int_cast( 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 = sal_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( 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( p-(start+1) ) ); } ++p; if (pExternalLinks && pExternalLinks->hasElements()) { // 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)) { DBG_ERROR1( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i); return NULL; } rExternDocName = aStr; } break; case sheet::ExternalLinkType::SELF : return start; // ??? case sheet::ExternalLinkType::SPECIAL : // silently return nothing (do not assert), caller has to handle this return NULL; default: DBG_ERROR2( "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( 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( 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; #if 0 { ByteString aStr(p, RTL_TEXTENCODING_UTF8); aStr.Append(static_cast< char >(0)); std::cerr << "parse::XL::R1C1 \'" << aStr.GetBuffer() << '\'' << std::endl; } #endif 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( toupper( char(*p++) ) - 'A' ); while (nCol <= MAXCOL && CharClass::isAsciiAlpha(*p)) nCol = sal::static_int_cast( ((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(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; #if 0 { ByteString aStr(p, RTL_TEXTENCODING_UTF8); aStr.Append(static_cast< char >(0)); std::cerr << "parse::XL::A1 \'" << aStr.GetBuffer() << '\'' << std::endl; } #endif 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 ) // 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 ))) 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( toupper( char(*p++) ) - 'A' ); while (nCol < MAXCOL && CharClass::isAsciiAlpha(*p)) nCol = sal::static_int_cast( ((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(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; // #38840# keine Tokens aus halben Sachen } } nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID ) | nRes1 | ( ( nRes2 & 0x070F ) << 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; 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; pDoc = NULL; // 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; pDoc = NULL; // 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(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(); } } #if 0 { ByteString aStr( sFileName, RTL_TEXTENCODING_UTF8 ); aStr.Append(static_cast< char >(0)); std::cerr << "docname \'" << aStr.GetBuffer() << '\'' << std::endl; } #endif return sFileName; } 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_ABSOLUTE | SCA_TAB_3D ) ) 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 = ScCompiler::GetDocTabPos( aTabName); if (nPos != STRING_NOTFOUND) { 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(); #if 0 { ByteString aStr(rTabName, RTL_TEXTENCODING_UTF8); aStr.Append(static_cast< char >(0)); std::cerr << "tabname \'" << aStr.GetBuffer() << '\'' << std::endl; } #endif // External reference, same as in ScCompiler::MakeTabStr() if ( rTabName.GetChar(0) == '\'' ) { // "'Doc'#Tab" xub_StrLen nPos = ScCompiler::GetDocTabPos( rTabName); if (nPos != STRING_NOTFOUND) { 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 } 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 = sal_False; else if( dx > MAXCOL ) dx = MAXCOL, bValid =sal_False; if( dy < 0 ) dy = 0, bValid = sal_False; else if( dy > MAXROW ) dy = MAXROW, bValid =sal_False; if( dz < 0 ) dz = 0, bValid = sal_False; else if( dz >= nMaxTab ) dz = nMaxTab-1, bValid =sal_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( 'A' + static_cast(nCol))); else { rBuf.append( static_cast( 'A' + (static_cast(nCol) / 26) - 1)); rBuf.append( static_cast( 'A' + (static_cast(nCol) % 26))); } } else { String aStr; while (nCol >= 26) { SCCOL nC = nCol % 26; aStr += static_cast( 'A' + static_cast(nC)); nCol = sal::static_int_cast( nCol - nC ); nCol = nCol / 26 - 1; } aStr += static_cast( 'A' + static_cast(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; }