blob: dfbb48a0da3442cb85eaccb8425e676ec9629186 [file] [log] [blame]
/**************************************************************
*
* 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 <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;
////////////////////////////////////////////////////////////////////////////
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 <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 = 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<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;
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<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;
#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<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;
#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<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; // #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<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();
}
}
#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<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;
}